A bot for SaltyBet that watches and records matches












2














I've been working on a bot that records the result of matches on SaltyBet.com. It uses that data to calculate the probability of winning for fighters of a given match. A fighters probability is based on the Elo rating system. I'm posting this here to share the code.



Repo: https://github.com/zakarh/salt-bot



import json

import selenium
from selenium import webdriver
from selenium.common import exceptions
from selenium.webdriver.common.keys import Keys


class SaltBot():
def __init__(self):
self.data = {}

def watch(self, browser, source="data.json", output="data.json", matches=1):
"""
Watch matches, make predictions, and record the results.
:type browser: selenium.webDriver, Example: selenium.webdriver.Chrome().
:type source: str, directory to source file.
:type target: str, directory to output file.
:type matches: int, No. matches to watch.
"""
if type(browser) != selenium.webdriver.chrome.webdriver.WebDriver:
raise TypeError(
"type(browser) != selenium.webdriver.chrome.webdriver.WebDriver, type(browser) == {}".format(type(browser)))
if type(source) != str:
raise TypeError(
"type(source) != str, type(source) == {}".format(type(source)))
if type(output) != str:
raise TypeError(
"type(output) != str, type(output) == {}".format(type(output)))
if type(matches) != int:
raise TypeError(
"type(matches) != int, type(matches) == {}".format(type(matches)))
if matches <= 0:
raise ValueError(
"matches <= 0, matches == {}, matches must be > 0".format(matches))

# Load data from source:
self.load_data(source)

# Navigate to SaltyBet.com using the browser.
url_address = "https://www.saltybet.com/"
browser.get(url_address)

print("Salt Bot is now watching {} matches.n".format(matches))

# Handle operations performed in the while loop:
bet_status = "" # Handle control flow.
recorded = False # Decide when to record the result of matches.
red = "" # Fighter.
blue = "" # Fighter.

match = 0
print("{}/{} matches watched.".format(match, matches))
while match <= matches:
try:
# Get bet status:
current_bet_status = browser.find_element_by_id(
"betstatus").text
if len(current_bet_status) == 0:
continue
elif bet_status == "":
bet_status = current_bet_status
elif bet_status != current_bet_status:
bet_status = current_bet_status
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

try:
# Get fighter names:
red_current = browser.find_element_by_id(
"sbettors1").find_element_by_tag_name("strong").text
blue_current = browser.find_element_by_id(
"sbettors2").find_element_by_tag_name("strong").text
# Calculate probability for fighters:
if len(red_current) == 0 or len(blue_current) == 0:
continue
elif red == "" or blue == "":
red = self.extract_name(red_current)
blue = self.extract_name(blue_current)
self.display(red, blue)
elif red != red_current or blue != blue_current:
red = self.extract_name(red_current)
blue = self.extract_name(blue_current)
self.display(red, blue)
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

# Record the results of a match.
try:
if "win" in bet_status:
if recorded is False:
if red in bet_status:
s = "Winner: {}, Loser: {}n".format(red, blue)
print(s)
self.save_data("data.json", red, blue)
self.log_data(s)
elif blue in bet_status:
s = "Winner: {}, Loser: {}n".format(blue, red)
print(s)
self.save_data("data.json", blue, red)
self.log_data(s)
recorded = True
match += 1
print(
"{}/{} matches watched.".format(match, matches))
else:
recorded = False
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

def get_probability(self, red, blue):
"""
Calculate the probability of red and blue winning the match using the Elo Rating System.
:type red: str, name of fighter.
:type blue: str, name of fighter.
:rtype: tuple, respective probability of red and blue winning the match.
TODO: Account for win:loss ratio of red's opponents against blue vice versa.
"""
# Validate input:
if type(red) != str:
raise TypeError("type(red) != str")
if type(blue) != str:
raise TypeError("type(blue) != str")

# Insufficient data return (0.5, 0.5)
if red not in self.data or blue not in self.data:
return (0.5, 0.5)
else:
# Compute the sum of matches won by both fighters:
red_rating = sum([self.data[red][loser]
for loser in self.data[red]])
blue_rating = sum([self.data[blue][loser]
for loser in self.data[blue]])

# Decremenet fighter rating by their total losses against the other fighter:
if red in self.data[blue]:
red_rating -= self.data[blue][red]
if blue in self.data[red]:
blue_rating -= self.data[red][blue]

# Transform fighter rating:
red_rating = pow(10, max(0, red_rating) / 400)
blue_rating = pow(10, max(0, blue_rating) / 400)

# Calculate red and blue's probability of winning:
red_probability = red_rating / (red_rating + blue_rating)
blue_probability = blue_rating / (red_rating + blue_rating)

return (red_probability, blue_probability)

def save_data(self, file_path, winner, loser):
"""
Open the target file and update the winner and loser data.
:type file_path: str, directory path to target file.
:type winner: str, name of fighter.
:type loser: str, name of fighter.
"""
# Validate input:
if type(file_path) != str:
raise TypeError("type(file_path) != str")
if type(winner) != str:
raise TypeError("type(winner) != str")
if type(loser) != str:
raise TypeError("type(loser) != str")

# Open the target file and write the results.
with open(file=file_path, mode="w+") as f:
# Add winner and loser if they don't exist in self.data:
self.data.setdefault(winner, {})
self.data.setdefault(loser, {})
self.data[winner].setdefault(loser, 0)
self.data[loser].setdefault(winner, 0)
# Increment the number of wins winner has against the loser:
self.data[winner][loser] += 1
f.write(json.dumps(self.data))

def load_data(self, file_path):
"""
Open the target file and load its contents as JSON.
:type file_path: str, directory path to target file.
:rtype: None
"""
# Validate input:
if type(file_path) != str:
raise TypeError("type(file_path) != str")
# Open the target file and load contents as json.
with open(file=file_path, mode="r+") as f:
data = json.load(f)
self.data = data

def log_data(self, data):
file_path = "data.txt"
with open(file=file_path, mode="a+") as f:
f.write(data)

def extract_name(self, raw_name):
return str(raw_name).strip(" ")

def display(self, red, blue):
s = "n{} vs. {}n{}".format(
red, blue, self.get_probability(red, blue))
print(s)


def main():
sb = SaltBot()
sb.watch(browser=webdriver.Chrome(), source="data.json",
output="data.json", matches=100)


if __name__ == '__main__':
main()









share|improve this question
























  • Have you read giantbomb.com/profile/tycobb/blog/… ?
    – Reinderien
    18 hours ago






  • 1




    Thanks for linking the article it was a good read. I expected other bots to exists but not at that level of automation. Anywho it has made me consider possible features I might add to salt bot.
    – Zakar H.
    5 hours ago
















2














I've been working on a bot that records the result of matches on SaltyBet.com. It uses that data to calculate the probability of winning for fighters of a given match. A fighters probability is based on the Elo rating system. I'm posting this here to share the code.



Repo: https://github.com/zakarh/salt-bot



import json

import selenium
from selenium import webdriver
from selenium.common import exceptions
from selenium.webdriver.common.keys import Keys


class SaltBot():
def __init__(self):
self.data = {}

def watch(self, browser, source="data.json", output="data.json", matches=1):
"""
Watch matches, make predictions, and record the results.
:type browser: selenium.webDriver, Example: selenium.webdriver.Chrome().
:type source: str, directory to source file.
:type target: str, directory to output file.
:type matches: int, No. matches to watch.
"""
if type(browser) != selenium.webdriver.chrome.webdriver.WebDriver:
raise TypeError(
"type(browser) != selenium.webdriver.chrome.webdriver.WebDriver, type(browser) == {}".format(type(browser)))
if type(source) != str:
raise TypeError(
"type(source) != str, type(source) == {}".format(type(source)))
if type(output) != str:
raise TypeError(
"type(output) != str, type(output) == {}".format(type(output)))
if type(matches) != int:
raise TypeError(
"type(matches) != int, type(matches) == {}".format(type(matches)))
if matches <= 0:
raise ValueError(
"matches <= 0, matches == {}, matches must be > 0".format(matches))

# Load data from source:
self.load_data(source)

# Navigate to SaltyBet.com using the browser.
url_address = "https://www.saltybet.com/"
browser.get(url_address)

print("Salt Bot is now watching {} matches.n".format(matches))

# Handle operations performed in the while loop:
bet_status = "" # Handle control flow.
recorded = False # Decide when to record the result of matches.
red = "" # Fighter.
blue = "" # Fighter.

match = 0
print("{}/{} matches watched.".format(match, matches))
while match <= matches:
try:
# Get bet status:
current_bet_status = browser.find_element_by_id(
"betstatus").text
if len(current_bet_status) == 0:
continue
elif bet_status == "":
bet_status = current_bet_status
elif bet_status != current_bet_status:
bet_status = current_bet_status
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

try:
# Get fighter names:
red_current = browser.find_element_by_id(
"sbettors1").find_element_by_tag_name("strong").text
blue_current = browser.find_element_by_id(
"sbettors2").find_element_by_tag_name("strong").text
# Calculate probability for fighters:
if len(red_current) == 0 or len(blue_current) == 0:
continue
elif red == "" or blue == "":
red = self.extract_name(red_current)
blue = self.extract_name(blue_current)
self.display(red, blue)
elif red != red_current or blue != blue_current:
red = self.extract_name(red_current)
blue = self.extract_name(blue_current)
self.display(red, blue)
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

# Record the results of a match.
try:
if "win" in bet_status:
if recorded is False:
if red in bet_status:
s = "Winner: {}, Loser: {}n".format(red, blue)
print(s)
self.save_data("data.json", red, blue)
self.log_data(s)
elif blue in bet_status:
s = "Winner: {}, Loser: {}n".format(blue, red)
print(s)
self.save_data("data.json", blue, red)
self.log_data(s)
recorded = True
match += 1
print(
"{}/{} matches watched.".format(match, matches))
else:
recorded = False
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

def get_probability(self, red, blue):
"""
Calculate the probability of red and blue winning the match using the Elo Rating System.
:type red: str, name of fighter.
:type blue: str, name of fighter.
:rtype: tuple, respective probability of red and blue winning the match.
TODO: Account for win:loss ratio of red's opponents against blue vice versa.
"""
# Validate input:
if type(red) != str:
raise TypeError("type(red) != str")
if type(blue) != str:
raise TypeError("type(blue) != str")

# Insufficient data return (0.5, 0.5)
if red not in self.data or blue not in self.data:
return (0.5, 0.5)
else:
# Compute the sum of matches won by both fighters:
red_rating = sum([self.data[red][loser]
for loser in self.data[red]])
blue_rating = sum([self.data[blue][loser]
for loser in self.data[blue]])

# Decremenet fighter rating by their total losses against the other fighter:
if red in self.data[blue]:
red_rating -= self.data[blue][red]
if blue in self.data[red]:
blue_rating -= self.data[red][blue]

# Transform fighter rating:
red_rating = pow(10, max(0, red_rating) / 400)
blue_rating = pow(10, max(0, blue_rating) / 400)

# Calculate red and blue's probability of winning:
red_probability = red_rating / (red_rating + blue_rating)
blue_probability = blue_rating / (red_rating + blue_rating)

return (red_probability, blue_probability)

def save_data(self, file_path, winner, loser):
"""
Open the target file and update the winner and loser data.
:type file_path: str, directory path to target file.
:type winner: str, name of fighter.
:type loser: str, name of fighter.
"""
# Validate input:
if type(file_path) != str:
raise TypeError("type(file_path) != str")
if type(winner) != str:
raise TypeError("type(winner) != str")
if type(loser) != str:
raise TypeError("type(loser) != str")

# Open the target file and write the results.
with open(file=file_path, mode="w+") as f:
# Add winner and loser if they don't exist in self.data:
self.data.setdefault(winner, {})
self.data.setdefault(loser, {})
self.data[winner].setdefault(loser, 0)
self.data[loser].setdefault(winner, 0)
# Increment the number of wins winner has against the loser:
self.data[winner][loser] += 1
f.write(json.dumps(self.data))

def load_data(self, file_path):
"""
Open the target file and load its contents as JSON.
:type file_path: str, directory path to target file.
:rtype: None
"""
# Validate input:
if type(file_path) != str:
raise TypeError("type(file_path) != str")
# Open the target file and load contents as json.
with open(file=file_path, mode="r+") as f:
data = json.load(f)
self.data = data

def log_data(self, data):
file_path = "data.txt"
with open(file=file_path, mode="a+") as f:
f.write(data)

def extract_name(self, raw_name):
return str(raw_name).strip(" ")

def display(self, red, blue):
s = "n{} vs. {}n{}".format(
red, blue, self.get_probability(red, blue))
print(s)


def main():
sb = SaltBot()
sb.watch(browser=webdriver.Chrome(), source="data.json",
output="data.json", matches=100)


if __name__ == '__main__':
main()









share|improve this question
























  • Have you read giantbomb.com/profile/tycobb/blog/… ?
    – Reinderien
    18 hours ago






  • 1




    Thanks for linking the article it was a good read. I expected other bots to exists but not at that level of automation. Anywho it has made me consider possible features I might add to salt bot.
    – Zakar H.
    5 hours ago














2












2








2







I've been working on a bot that records the result of matches on SaltyBet.com. It uses that data to calculate the probability of winning for fighters of a given match. A fighters probability is based on the Elo rating system. I'm posting this here to share the code.



Repo: https://github.com/zakarh/salt-bot



import json

import selenium
from selenium import webdriver
from selenium.common import exceptions
from selenium.webdriver.common.keys import Keys


class SaltBot():
def __init__(self):
self.data = {}

def watch(self, browser, source="data.json", output="data.json", matches=1):
"""
Watch matches, make predictions, and record the results.
:type browser: selenium.webDriver, Example: selenium.webdriver.Chrome().
:type source: str, directory to source file.
:type target: str, directory to output file.
:type matches: int, No. matches to watch.
"""
if type(browser) != selenium.webdriver.chrome.webdriver.WebDriver:
raise TypeError(
"type(browser) != selenium.webdriver.chrome.webdriver.WebDriver, type(browser) == {}".format(type(browser)))
if type(source) != str:
raise TypeError(
"type(source) != str, type(source) == {}".format(type(source)))
if type(output) != str:
raise TypeError(
"type(output) != str, type(output) == {}".format(type(output)))
if type(matches) != int:
raise TypeError(
"type(matches) != int, type(matches) == {}".format(type(matches)))
if matches <= 0:
raise ValueError(
"matches <= 0, matches == {}, matches must be > 0".format(matches))

# Load data from source:
self.load_data(source)

# Navigate to SaltyBet.com using the browser.
url_address = "https://www.saltybet.com/"
browser.get(url_address)

print("Salt Bot is now watching {} matches.n".format(matches))

# Handle operations performed in the while loop:
bet_status = "" # Handle control flow.
recorded = False # Decide when to record the result of matches.
red = "" # Fighter.
blue = "" # Fighter.

match = 0
print("{}/{} matches watched.".format(match, matches))
while match <= matches:
try:
# Get bet status:
current_bet_status = browser.find_element_by_id(
"betstatus").text
if len(current_bet_status) == 0:
continue
elif bet_status == "":
bet_status = current_bet_status
elif bet_status != current_bet_status:
bet_status = current_bet_status
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

try:
# Get fighter names:
red_current = browser.find_element_by_id(
"sbettors1").find_element_by_tag_name("strong").text
blue_current = browser.find_element_by_id(
"sbettors2").find_element_by_tag_name("strong").text
# Calculate probability for fighters:
if len(red_current) == 0 or len(blue_current) == 0:
continue
elif red == "" or blue == "":
red = self.extract_name(red_current)
blue = self.extract_name(blue_current)
self.display(red, blue)
elif red != red_current or blue != blue_current:
red = self.extract_name(red_current)
blue = self.extract_name(blue_current)
self.display(red, blue)
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

# Record the results of a match.
try:
if "win" in bet_status:
if recorded is False:
if red in bet_status:
s = "Winner: {}, Loser: {}n".format(red, blue)
print(s)
self.save_data("data.json", red, blue)
self.log_data(s)
elif blue in bet_status:
s = "Winner: {}, Loser: {}n".format(blue, red)
print(s)
self.save_data("data.json", blue, red)
self.log_data(s)
recorded = True
match += 1
print(
"{}/{} matches watched.".format(match, matches))
else:
recorded = False
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

def get_probability(self, red, blue):
"""
Calculate the probability of red and blue winning the match using the Elo Rating System.
:type red: str, name of fighter.
:type blue: str, name of fighter.
:rtype: tuple, respective probability of red and blue winning the match.
TODO: Account for win:loss ratio of red's opponents against blue vice versa.
"""
# Validate input:
if type(red) != str:
raise TypeError("type(red) != str")
if type(blue) != str:
raise TypeError("type(blue) != str")

# Insufficient data return (0.5, 0.5)
if red not in self.data or blue not in self.data:
return (0.5, 0.5)
else:
# Compute the sum of matches won by both fighters:
red_rating = sum([self.data[red][loser]
for loser in self.data[red]])
blue_rating = sum([self.data[blue][loser]
for loser in self.data[blue]])

# Decremenet fighter rating by their total losses against the other fighter:
if red in self.data[blue]:
red_rating -= self.data[blue][red]
if blue in self.data[red]:
blue_rating -= self.data[red][blue]

# Transform fighter rating:
red_rating = pow(10, max(0, red_rating) / 400)
blue_rating = pow(10, max(0, blue_rating) / 400)

# Calculate red and blue's probability of winning:
red_probability = red_rating / (red_rating + blue_rating)
blue_probability = blue_rating / (red_rating + blue_rating)

return (red_probability, blue_probability)

def save_data(self, file_path, winner, loser):
"""
Open the target file and update the winner and loser data.
:type file_path: str, directory path to target file.
:type winner: str, name of fighter.
:type loser: str, name of fighter.
"""
# Validate input:
if type(file_path) != str:
raise TypeError("type(file_path) != str")
if type(winner) != str:
raise TypeError("type(winner) != str")
if type(loser) != str:
raise TypeError("type(loser) != str")

# Open the target file and write the results.
with open(file=file_path, mode="w+") as f:
# Add winner and loser if they don't exist in self.data:
self.data.setdefault(winner, {})
self.data.setdefault(loser, {})
self.data[winner].setdefault(loser, 0)
self.data[loser].setdefault(winner, 0)
# Increment the number of wins winner has against the loser:
self.data[winner][loser] += 1
f.write(json.dumps(self.data))

def load_data(self, file_path):
"""
Open the target file and load its contents as JSON.
:type file_path: str, directory path to target file.
:rtype: None
"""
# Validate input:
if type(file_path) != str:
raise TypeError("type(file_path) != str")
# Open the target file and load contents as json.
with open(file=file_path, mode="r+") as f:
data = json.load(f)
self.data = data

def log_data(self, data):
file_path = "data.txt"
with open(file=file_path, mode="a+") as f:
f.write(data)

def extract_name(self, raw_name):
return str(raw_name).strip(" ")

def display(self, red, blue):
s = "n{} vs. {}n{}".format(
red, blue, self.get_probability(red, blue))
print(s)


def main():
sb = SaltBot()
sb.watch(browser=webdriver.Chrome(), source="data.json",
output="data.json", matches=100)


if __name__ == '__main__':
main()









share|improve this question















I've been working on a bot that records the result of matches on SaltyBet.com. It uses that data to calculate the probability of winning for fighters of a given match. A fighters probability is based on the Elo rating system. I'm posting this here to share the code.



Repo: https://github.com/zakarh/salt-bot



import json

import selenium
from selenium import webdriver
from selenium.common import exceptions
from selenium.webdriver.common.keys import Keys


class SaltBot():
def __init__(self):
self.data = {}

def watch(self, browser, source="data.json", output="data.json", matches=1):
"""
Watch matches, make predictions, and record the results.
:type browser: selenium.webDriver, Example: selenium.webdriver.Chrome().
:type source: str, directory to source file.
:type target: str, directory to output file.
:type matches: int, No. matches to watch.
"""
if type(browser) != selenium.webdriver.chrome.webdriver.WebDriver:
raise TypeError(
"type(browser) != selenium.webdriver.chrome.webdriver.WebDriver, type(browser) == {}".format(type(browser)))
if type(source) != str:
raise TypeError(
"type(source) != str, type(source) == {}".format(type(source)))
if type(output) != str:
raise TypeError(
"type(output) != str, type(output) == {}".format(type(output)))
if type(matches) != int:
raise TypeError(
"type(matches) != int, type(matches) == {}".format(type(matches)))
if matches <= 0:
raise ValueError(
"matches <= 0, matches == {}, matches must be > 0".format(matches))

# Load data from source:
self.load_data(source)

# Navigate to SaltyBet.com using the browser.
url_address = "https://www.saltybet.com/"
browser.get(url_address)

print("Salt Bot is now watching {} matches.n".format(matches))

# Handle operations performed in the while loop:
bet_status = "" # Handle control flow.
recorded = False # Decide when to record the result of matches.
red = "" # Fighter.
blue = "" # Fighter.

match = 0
print("{}/{} matches watched.".format(match, matches))
while match <= matches:
try:
# Get bet status:
current_bet_status = browser.find_element_by_id(
"betstatus").text
if len(current_bet_status) == 0:
continue
elif bet_status == "":
bet_status = current_bet_status
elif bet_status != current_bet_status:
bet_status = current_bet_status
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

try:
# Get fighter names:
red_current = browser.find_element_by_id(
"sbettors1").find_element_by_tag_name("strong").text
blue_current = browser.find_element_by_id(
"sbettors2").find_element_by_tag_name("strong").text
# Calculate probability for fighters:
if len(red_current) == 0 or len(blue_current) == 0:
continue
elif red == "" or blue == "":
red = self.extract_name(red_current)
blue = self.extract_name(blue_current)
self.display(red, blue)
elif red != red_current or blue != blue_current:
red = self.extract_name(red_current)
blue = self.extract_name(blue_current)
self.display(red, blue)
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

# Record the results of a match.
try:
if "win" in bet_status:
if recorded is False:
if red in bet_status:
s = "Winner: {}, Loser: {}n".format(red, blue)
print(s)
self.save_data("data.json", red, blue)
self.log_data(s)
elif blue in bet_status:
s = "Winner: {}, Loser: {}n".format(blue, red)
print(s)
self.save_data("data.json", blue, red)
self.log_data(s)
recorded = True
match += 1
print(
"{}/{} matches watched.".format(match, matches))
else:
recorded = False
except exceptions.StaleElementReferenceException:
pass
except Exception:
pass

def get_probability(self, red, blue):
"""
Calculate the probability of red and blue winning the match using the Elo Rating System.
:type red: str, name of fighter.
:type blue: str, name of fighter.
:rtype: tuple, respective probability of red and blue winning the match.
TODO: Account for win:loss ratio of red's opponents against blue vice versa.
"""
# Validate input:
if type(red) != str:
raise TypeError("type(red) != str")
if type(blue) != str:
raise TypeError("type(blue) != str")

# Insufficient data return (0.5, 0.5)
if red not in self.data or blue not in self.data:
return (0.5, 0.5)
else:
# Compute the sum of matches won by both fighters:
red_rating = sum([self.data[red][loser]
for loser in self.data[red]])
blue_rating = sum([self.data[blue][loser]
for loser in self.data[blue]])

# Decremenet fighter rating by their total losses against the other fighter:
if red in self.data[blue]:
red_rating -= self.data[blue][red]
if blue in self.data[red]:
blue_rating -= self.data[red][blue]

# Transform fighter rating:
red_rating = pow(10, max(0, red_rating) / 400)
blue_rating = pow(10, max(0, blue_rating) / 400)

# Calculate red and blue's probability of winning:
red_probability = red_rating / (red_rating + blue_rating)
blue_probability = blue_rating / (red_rating + blue_rating)

return (red_probability, blue_probability)

def save_data(self, file_path, winner, loser):
"""
Open the target file and update the winner and loser data.
:type file_path: str, directory path to target file.
:type winner: str, name of fighter.
:type loser: str, name of fighter.
"""
# Validate input:
if type(file_path) != str:
raise TypeError("type(file_path) != str")
if type(winner) != str:
raise TypeError("type(winner) != str")
if type(loser) != str:
raise TypeError("type(loser) != str")

# Open the target file and write the results.
with open(file=file_path, mode="w+") as f:
# Add winner and loser if they don't exist in self.data:
self.data.setdefault(winner, {})
self.data.setdefault(loser, {})
self.data[winner].setdefault(loser, 0)
self.data[loser].setdefault(winner, 0)
# Increment the number of wins winner has against the loser:
self.data[winner][loser] += 1
f.write(json.dumps(self.data))

def load_data(self, file_path):
"""
Open the target file and load its contents as JSON.
:type file_path: str, directory path to target file.
:rtype: None
"""
# Validate input:
if type(file_path) != str:
raise TypeError("type(file_path) != str")
# Open the target file and load contents as json.
with open(file=file_path, mode="r+") as f:
data = json.load(f)
self.data = data

def log_data(self, data):
file_path = "data.txt"
with open(file=file_path, mode="a+") as f:
f.write(data)

def extract_name(self, raw_name):
return str(raw_name).strip(" ")

def display(self, red, blue):
s = "n{} vs. {}n{}".format(
red, blue, self.get_probability(red, blue))
print(s)


def main():
sb = SaltBot()
sb.watch(browser=webdriver.Chrome(), source="data.json",
output="data.json", matches=100)


if __name__ == '__main__':
main()






python web-scraping






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 18 hours ago









Reinderien

4,022821




4,022821










asked yesterday









Zakar H.Zakar H.

866




866












  • Have you read giantbomb.com/profile/tycobb/blog/… ?
    – Reinderien
    18 hours ago






  • 1




    Thanks for linking the article it was a good read. I expected other bots to exists but not at that level of automation. Anywho it has made me consider possible features I might add to salt bot.
    – Zakar H.
    5 hours ago


















  • Have you read giantbomb.com/profile/tycobb/blog/… ?
    – Reinderien
    18 hours ago






  • 1




    Thanks for linking the article it was a good read. I expected other bots to exists but not at that level of automation. Anywho it has made me consider possible features I might add to salt bot.
    – Zakar H.
    5 hours ago
















Have you read giantbomb.com/profile/tycobb/blog/… ?
– Reinderien
18 hours ago




Have you read giantbomb.com/profile/tycobb/blog/… ?
– Reinderien
18 hours ago




1




1




Thanks for linking the article it was a good read. I expected other bots to exists but not at that level of automation. Anywho it has made me consider possible features I might add to salt bot.
– Zakar H.
5 hours ago




Thanks for linking the article it was a good read. I expected other bots to exists but not at that level of automation. Anywho it has made me consider possible features I might add to salt bot.
– Zakar H.
5 hours ago










2 Answers
2






active

oldest

votes


















2














Use f-strings



This:



"Salt Bot is now watching {} matches.n".format(matches)


can be



f'Salt Bot is now watching {matches} matchesn'


and so on for your other format calls.



Use more subroutines



watch() is quite long. You should break it up into multiple functions.



Redundant else



This:



            if len(current_bet_status) == 0:
continue
elif bet_status == "":
bet_status = current_bet_status


should use if instead of elif, due to the previous continue. Similar instances elsewhere.



Don't swallow exceptions



This:



        except Exception:
pass


is really dangerous, and asking for trouble. If this is in a loop that you never ever want to die, you should still at least be outputting the exception to the console when it occurs. Otherwise, debugging is going to be much more difficult.






share|improve this answer





















  • @Reinderein, is it best practices to use the latest features of a programming language? Given that I used the format() method instead of the f-strings for backward compatibility. Tips on subroutines, redundant else's, and exceptions duly noted. Thanks.
    – Zakar H.
    5 hours ago












  • @ZakarH. Language level depends on a lot of things. If you're writing an application and this is not in a business context where you're told which versions of Python you need to support, then yes, you should be using the latest version. If (for instance) you're writing a library and you want it to have broader compatibility, then your language choices will be different.
    – Reinderien
    4 hours ago



















2














In Python 3.5 type annotations were introduced. These let you add annotations to variables and functions to denote which type they should be. This is not enforced on its own, but you can get access to these annotations with the __anotations__ property:



def f(a: str) -> str:
return a
f.__annotations__
# {'a': str, 'return': str}


With this it is relatively easy to write a decorator that checks all specified types:



from functools import wraps
import inspect

def check_types(func):
annotations = func.__annotations__
params = inspect.signature(func).parameters
@wraps(func)
def wrapper(*args, **kwargs):
# positional arguments
for value, (name, param) in zip(args, params.items()):
if param.annotation is inspect._empty:
continue
if not isinstance(value, param.annotation):
raise TypeError(f"type({name}) != {param.annotation}, type({name}) == {type(value)}")
# keyword arguments
for name, value in kwargs.items():
try:
required_type = annotations[name]
except KeyError:
continue
if not isinstance(value, required_type):
raise TypeError(f"type({name}) != {required_type}, type({name}) == {type(value)}")
return func(*args, **kwargs)
return wrapper


Your methods would then simply look like this:



Chrome = selenium.webdriver.chrome.webdriver.WebDriver

class SaltBot:
...

@check_types
def watch(self, browser: Chrome, source: str = "data.json", output: str = "data.json", matches: int = 1):
"""
Watch matches, make predictions, and record the results.
:type browser: selenium.webDriver, Example: selenium.webdriver.Chrome().
:type source: str, directory to source file.
:type target: str, directory to output file.
:type matches: int, No. matches to watch.
"""
if matches <= 0:
raise ValueError(
"matches <= 0, matches == {}, matches must be > 0".format(matches))

...


This is of course only a dynamic type checking at run time and can probably be improved itself (I don't use type hints regularly). It does not conform to PEP 484 in that you can't e.g. use strings as types. You might want to go the full way and use the typing module and a static type checker, as described e.g. here.






share|improve this answer





















  • that article you linked on Medium "How to Use Static Type Checking in Python 3.6" was a fantastic read. I'm going to start using typing in my programs. Question, how have you determined use cases that require typing?
    – Zakar H.
    5 hours ago











Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f211017%2fa-bot-for-saltybet-that-watches-and-records-matches%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









2














Use f-strings



This:



"Salt Bot is now watching {} matches.n".format(matches)


can be



f'Salt Bot is now watching {matches} matchesn'


and so on for your other format calls.



Use more subroutines



watch() is quite long. You should break it up into multiple functions.



Redundant else



This:



            if len(current_bet_status) == 0:
continue
elif bet_status == "":
bet_status = current_bet_status


should use if instead of elif, due to the previous continue. Similar instances elsewhere.



Don't swallow exceptions



This:



        except Exception:
pass


is really dangerous, and asking for trouble. If this is in a loop that you never ever want to die, you should still at least be outputting the exception to the console when it occurs. Otherwise, debugging is going to be much more difficult.






share|improve this answer





















  • @Reinderein, is it best practices to use the latest features of a programming language? Given that I used the format() method instead of the f-strings for backward compatibility. Tips on subroutines, redundant else's, and exceptions duly noted. Thanks.
    – Zakar H.
    5 hours ago












  • @ZakarH. Language level depends on a lot of things. If you're writing an application and this is not in a business context where you're told which versions of Python you need to support, then yes, you should be using the latest version. If (for instance) you're writing a library and you want it to have broader compatibility, then your language choices will be different.
    – Reinderien
    4 hours ago
















2














Use f-strings



This:



"Salt Bot is now watching {} matches.n".format(matches)


can be



f'Salt Bot is now watching {matches} matchesn'


and so on for your other format calls.



Use more subroutines



watch() is quite long. You should break it up into multiple functions.



Redundant else



This:



            if len(current_bet_status) == 0:
continue
elif bet_status == "":
bet_status = current_bet_status


should use if instead of elif, due to the previous continue. Similar instances elsewhere.



Don't swallow exceptions



This:



        except Exception:
pass


is really dangerous, and asking for trouble. If this is in a loop that you never ever want to die, you should still at least be outputting the exception to the console when it occurs. Otherwise, debugging is going to be much more difficult.






share|improve this answer





















  • @Reinderein, is it best practices to use the latest features of a programming language? Given that I used the format() method instead of the f-strings for backward compatibility. Tips on subroutines, redundant else's, and exceptions duly noted. Thanks.
    – Zakar H.
    5 hours ago












  • @ZakarH. Language level depends on a lot of things. If you're writing an application and this is not in a business context where you're told which versions of Python you need to support, then yes, you should be using the latest version. If (for instance) you're writing a library and you want it to have broader compatibility, then your language choices will be different.
    – Reinderien
    4 hours ago














2












2








2






Use f-strings



This:



"Salt Bot is now watching {} matches.n".format(matches)


can be



f'Salt Bot is now watching {matches} matchesn'


and so on for your other format calls.



Use more subroutines



watch() is quite long. You should break it up into multiple functions.



Redundant else



This:



            if len(current_bet_status) == 0:
continue
elif bet_status == "":
bet_status = current_bet_status


should use if instead of elif, due to the previous continue. Similar instances elsewhere.



Don't swallow exceptions



This:



        except Exception:
pass


is really dangerous, and asking for trouble. If this is in a loop that you never ever want to die, you should still at least be outputting the exception to the console when it occurs. Otherwise, debugging is going to be much more difficult.






share|improve this answer












Use f-strings



This:



"Salt Bot is now watching {} matches.n".format(matches)


can be



f'Salt Bot is now watching {matches} matchesn'


and so on for your other format calls.



Use more subroutines



watch() is quite long. You should break it up into multiple functions.



Redundant else



This:



            if len(current_bet_status) == 0:
continue
elif bet_status == "":
bet_status = current_bet_status


should use if instead of elif, due to the previous continue. Similar instances elsewhere.



Don't swallow exceptions



This:



        except Exception:
pass


is really dangerous, and asking for trouble. If this is in a loop that you never ever want to die, you should still at least be outputting the exception to the console when it occurs. Otherwise, debugging is going to be much more difficult.







share|improve this answer












share|improve this answer



share|improve this answer










answered 18 hours ago









ReinderienReinderien

4,022821




4,022821












  • @Reinderein, is it best practices to use the latest features of a programming language? Given that I used the format() method instead of the f-strings for backward compatibility. Tips on subroutines, redundant else's, and exceptions duly noted. Thanks.
    – Zakar H.
    5 hours ago












  • @ZakarH. Language level depends on a lot of things. If you're writing an application and this is not in a business context where you're told which versions of Python you need to support, then yes, you should be using the latest version. If (for instance) you're writing a library and you want it to have broader compatibility, then your language choices will be different.
    – Reinderien
    4 hours ago


















  • @Reinderein, is it best practices to use the latest features of a programming language? Given that I used the format() method instead of the f-strings for backward compatibility. Tips on subroutines, redundant else's, and exceptions duly noted. Thanks.
    – Zakar H.
    5 hours ago












  • @ZakarH. Language level depends on a lot of things. If you're writing an application and this is not in a business context where you're told which versions of Python you need to support, then yes, you should be using the latest version. If (for instance) you're writing a library and you want it to have broader compatibility, then your language choices will be different.
    – Reinderien
    4 hours ago
















@Reinderein, is it best practices to use the latest features of a programming language? Given that I used the format() method instead of the f-strings for backward compatibility. Tips on subroutines, redundant else's, and exceptions duly noted. Thanks.
– Zakar H.
5 hours ago






@Reinderein, is it best practices to use the latest features of a programming language? Given that I used the format() method instead of the f-strings for backward compatibility. Tips on subroutines, redundant else's, and exceptions duly noted. Thanks.
– Zakar H.
5 hours ago














@ZakarH. Language level depends on a lot of things. If you're writing an application and this is not in a business context where you're told which versions of Python you need to support, then yes, you should be using the latest version. If (for instance) you're writing a library and you want it to have broader compatibility, then your language choices will be different.
– Reinderien
4 hours ago




@ZakarH. Language level depends on a lot of things. If you're writing an application and this is not in a business context where you're told which versions of Python you need to support, then yes, you should be using the latest version. If (for instance) you're writing a library and you want it to have broader compatibility, then your language choices will be different.
– Reinderien
4 hours ago













2














In Python 3.5 type annotations were introduced. These let you add annotations to variables and functions to denote which type they should be. This is not enforced on its own, but you can get access to these annotations with the __anotations__ property:



def f(a: str) -> str:
return a
f.__annotations__
# {'a': str, 'return': str}


With this it is relatively easy to write a decorator that checks all specified types:



from functools import wraps
import inspect

def check_types(func):
annotations = func.__annotations__
params = inspect.signature(func).parameters
@wraps(func)
def wrapper(*args, **kwargs):
# positional arguments
for value, (name, param) in zip(args, params.items()):
if param.annotation is inspect._empty:
continue
if not isinstance(value, param.annotation):
raise TypeError(f"type({name}) != {param.annotation}, type({name}) == {type(value)}")
# keyword arguments
for name, value in kwargs.items():
try:
required_type = annotations[name]
except KeyError:
continue
if not isinstance(value, required_type):
raise TypeError(f"type({name}) != {required_type}, type({name}) == {type(value)}")
return func(*args, **kwargs)
return wrapper


Your methods would then simply look like this:



Chrome = selenium.webdriver.chrome.webdriver.WebDriver

class SaltBot:
...

@check_types
def watch(self, browser: Chrome, source: str = "data.json", output: str = "data.json", matches: int = 1):
"""
Watch matches, make predictions, and record the results.
:type browser: selenium.webDriver, Example: selenium.webdriver.Chrome().
:type source: str, directory to source file.
:type target: str, directory to output file.
:type matches: int, No. matches to watch.
"""
if matches <= 0:
raise ValueError(
"matches <= 0, matches == {}, matches must be > 0".format(matches))

...


This is of course only a dynamic type checking at run time and can probably be improved itself (I don't use type hints regularly). It does not conform to PEP 484 in that you can't e.g. use strings as types. You might want to go the full way and use the typing module and a static type checker, as described e.g. here.






share|improve this answer





















  • that article you linked on Medium "How to Use Static Type Checking in Python 3.6" was a fantastic read. I'm going to start using typing in my programs. Question, how have you determined use cases that require typing?
    – Zakar H.
    5 hours ago
















2














In Python 3.5 type annotations were introduced. These let you add annotations to variables and functions to denote which type they should be. This is not enforced on its own, but you can get access to these annotations with the __anotations__ property:



def f(a: str) -> str:
return a
f.__annotations__
# {'a': str, 'return': str}


With this it is relatively easy to write a decorator that checks all specified types:



from functools import wraps
import inspect

def check_types(func):
annotations = func.__annotations__
params = inspect.signature(func).parameters
@wraps(func)
def wrapper(*args, **kwargs):
# positional arguments
for value, (name, param) in zip(args, params.items()):
if param.annotation is inspect._empty:
continue
if not isinstance(value, param.annotation):
raise TypeError(f"type({name}) != {param.annotation}, type({name}) == {type(value)}")
# keyword arguments
for name, value in kwargs.items():
try:
required_type = annotations[name]
except KeyError:
continue
if not isinstance(value, required_type):
raise TypeError(f"type({name}) != {required_type}, type({name}) == {type(value)}")
return func(*args, **kwargs)
return wrapper


Your methods would then simply look like this:



Chrome = selenium.webdriver.chrome.webdriver.WebDriver

class SaltBot:
...

@check_types
def watch(self, browser: Chrome, source: str = "data.json", output: str = "data.json", matches: int = 1):
"""
Watch matches, make predictions, and record the results.
:type browser: selenium.webDriver, Example: selenium.webdriver.Chrome().
:type source: str, directory to source file.
:type target: str, directory to output file.
:type matches: int, No. matches to watch.
"""
if matches <= 0:
raise ValueError(
"matches <= 0, matches == {}, matches must be > 0".format(matches))

...


This is of course only a dynamic type checking at run time and can probably be improved itself (I don't use type hints regularly). It does not conform to PEP 484 in that you can't e.g. use strings as types. You might want to go the full way and use the typing module and a static type checker, as described e.g. here.






share|improve this answer





















  • that article you linked on Medium "How to Use Static Type Checking in Python 3.6" was a fantastic read. I'm going to start using typing in my programs. Question, how have you determined use cases that require typing?
    – Zakar H.
    5 hours ago














2












2








2






In Python 3.5 type annotations were introduced. These let you add annotations to variables and functions to denote which type they should be. This is not enforced on its own, but you can get access to these annotations with the __anotations__ property:



def f(a: str) -> str:
return a
f.__annotations__
# {'a': str, 'return': str}


With this it is relatively easy to write a decorator that checks all specified types:



from functools import wraps
import inspect

def check_types(func):
annotations = func.__annotations__
params = inspect.signature(func).parameters
@wraps(func)
def wrapper(*args, **kwargs):
# positional arguments
for value, (name, param) in zip(args, params.items()):
if param.annotation is inspect._empty:
continue
if not isinstance(value, param.annotation):
raise TypeError(f"type({name}) != {param.annotation}, type({name}) == {type(value)}")
# keyword arguments
for name, value in kwargs.items():
try:
required_type = annotations[name]
except KeyError:
continue
if not isinstance(value, required_type):
raise TypeError(f"type({name}) != {required_type}, type({name}) == {type(value)}")
return func(*args, **kwargs)
return wrapper


Your methods would then simply look like this:



Chrome = selenium.webdriver.chrome.webdriver.WebDriver

class SaltBot:
...

@check_types
def watch(self, browser: Chrome, source: str = "data.json", output: str = "data.json", matches: int = 1):
"""
Watch matches, make predictions, and record the results.
:type browser: selenium.webDriver, Example: selenium.webdriver.Chrome().
:type source: str, directory to source file.
:type target: str, directory to output file.
:type matches: int, No. matches to watch.
"""
if matches <= 0:
raise ValueError(
"matches <= 0, matches == {}, matches must be > 0".format(matches))

...


This is of course only a dynamic type checking at run time and can probably be improved itself (I don't use type hints regularly). It does not conform to PEP 484 in that you can't e.g. use strings as types. You might want to go the full way and use the typing module and a static type checker, as described e.g. here.






share|improve this answer












In Python 3.5 type annotations were introduced. These let you add annotations to variables and functions to denote which type they should be. This is not enforced on its own, but you can get access to these annotations with the __anotations__ property:



def f(a: str) -> str:
return a
f.__annotations__
# {'a': str, 'return': str}


With this it is relatively easy to write a decorator that checks all specified types:



from functools import wraps
import inspect

def check_types(func):
annotations = func.__annotations__
params = inspect.signature(func).parameters
@wraps(func)
def wrapper(*args, **kwargs):
# positional arguments
for value, (name, param) in zip(args, params.items()):
if param.annotation is inspect._empty:
continue
if not isinstance(value, param.annotation):
raise TypeError(f"type({name}) != {param.annotation}, type({name}) == {type(value)}")
# keyword arguments
for name, value in kwargs.items():
try:
required_type = annotations[name]
except KeyError:
continue
if not isinstance(value, required_type):
raise TypeError(f"type({name}) != {required_type}, type({name}) == {type(value)}")
return func(*args, **kwargs)
return wrapper


Your methods would then simply look like this:



Chrome = selenium.webdriver.chrome.webdriver.WebDriver

class SaltBot:
...

@check_types
def watch(self, browser: Chrome, source: str = "data.json", output: str = "data.json", matches: int = 1):
"""
Watch matches, make predictions, and record the results.
:type browser: selenium.webDriver, Example: selenium.webdriver.Chrome().
:type source: str, directory to source file.
:type target: str, directory to output file.
:type matches: int, No. matches to watch.
"""
if matches <= 0:
raise ValueError(
"matches <= 0, matches == {}, matches must be > 0".format(matches))

...


This is of course only a dynamic type checking at run time and can probably be improved itself (I don't use type hints regularly). It does not conform to PEP 484 in that you can't e.g. use strings as types. You might want to go the full way and use the typing module and a static type checker, as described e.g. here.







share|improve this answer












share|improve this answer



share|improve this answer










answered 17 hours ago









GraipherGraipher

23.7k53585




23.7k53585












  • that article you linked on Medium "How to Use Static Type Checking in Python 3.6" was a fantastic read. I'm going to start using typing in my programs. Question, how have you determined use cases that require typing?
    – Zakar H.
    5 hours ago


















  • that article you linked on Medium "How to Use Static Type Checking in Python 3.6" was a fantastic read. I'm going to start using typing in my programs. Question, how have you determined use cases that require typing?
    – Zakar H.
    5 hours ago
















that article you linked on Medium "How to Use Static Type Checking in Python 3.6" was a fantastic read. I'm going to start using typing in my programs. Question, how have you determined use cases that require typing?
– Zakar H.
5 hours ago




that article you linked on Medium "How to Use Static Type Checking in Python 3.6" was a fantastic read. I'm going to start using typing in my programs. Question, how have you determined use cases that require typing?
– Zakar H.
5 hours ago


















draft saved

draft discarded




















































Thanks for contributing an answer to Code Review Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


Use MathJax to format equations. MathJax reference.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f211017%2fa-bot-for-saltybet-that-watches-and-records-matches%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

How to reconfigure Docker Trusted Registry 2.x.x to use CEPH FS mount instead of NFS and other traditional...

is 'sed' thread safe

How to make a Squid Proxy server?