5
\$\begingroup\$

This is a personal project which I created solely to torture my friends ;)

I'm pretty sure you've heard about typeracer.
And if you have friends like mine who are faster than you, you'd surely be frustrated.
This is a program that removes your frustration by automating it.

from time import sleep
from selenium import webdriver
from selenium.common.exceptions import UnexpectedAlertPresentException
from selenium.webdriver.chrome.options import Options
class TypeRacerBot:
 def __init__(self, driver, is_private, link, wpm=70):
 self.driver = driver
 self.is_private = is_private
 self.wpm = wpm
 self.link = link
 self.driver.get(self.link)
 sleep(2)
 if self.is_private:
 while not self.can_join_race():
 pass
 self.enter_private_race()
 else:
 self.enter_race()
 sleep(2)
 while not self.has_started():
 pass
 self.type_text(self.get_text())
 def enter_race(self):
 """ Click the link to enter a new race """
 self.driver.find_element_by_partial_link_text('Enter a typing race').click()
 def enter_private_race(self):
 """ Click the link to enter a new race """
 self.driver.find_element_by_partial_link_text('join race').click()
 def race_again(self):
 """ Click the link to race again """
 self.driver.find_element_by_partial_link_text('Race Again').click()
 def has_started(self):
 """ Returns whether the race has started or not """
 return self.driver.find_element_by_css_selector(
 'table > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(2) > td > input'
 ).is_enabled()
 def can_join_race(self):
 """ Returns whether the cool-down between private races has ended """
 return len(self.driver.find_elements_by_partial_link_text('join race')) > 0
 def get_text(self):
 """ Returns the text you are supposed to type """
 return self.driver.find_elements_by_css_selector(
 'table > tbody > tr:nth-child(2) > td > table > tbody'
 ' > tr:nth-child(1) > td > table > tbody > tr:nth-child(1) > td > div > div'
 )[3].text
 def type_text(self, text):
 """ Types the text with an average WPM of the parameter words_per_minute. """
 textbox = self.driver.find_element_by_css_selector(
 'table > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(2) > td > input'
 )
 words = text.split()
 pause_time = 60 / self.wpm if self.wpm else 0
 for word in words:
 textbox.send_keys(word + ' ')
 if pause_time:
 sleep(pause_time)
def main():
 is_private = input('Is the race private? (y/n): ').lower() == 'y'
 if is_private:
 link = input('Please enter the link of the race: ')
 else:
 link = 'https://play.typeracer.com'
 wpm = int(input('Please enter the WPM you would like (0 for max speed): '))
 options = Options()
 options.add_argument('--start-maximized')
 driver = webdriver.Chrome('chromedriver.exe', options=options)
 while True:
 try:
 TypeRacerBot(driver, is_private, link, wpm)
 except UnexpectedAlertPresentException:
 pass
 print()
 input('Press enter to start new race')
 wpm = int(input('Please enter the WPM you would like (0 for max speed): '))
if __name__ == '__main__':
 main()

You can participate in a public or private race without any issues.

With an average PC, the maximum speed clocks up to 500 WPM.
When setting a custom speed, the actual speed differs a lot from the target, due to selenium being a bit slow.

Right now, I don't think the code is very enjoyable to read.
How do I make it look better, and how do I increase the performance of the program?

Thanks a lot!

asked Oct 15, 2020 at 14:58
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I encourage you to examine the site in closer detail. So many instances of Selenium use are at the wrong layer of abstraction and this is no different.

The site communicates with XHR request; one example is:

POST https://play.typeracer.com/gameserv;jsessionid=B45A6C283A7C20091095F4BCD6DA1B42
Request:
7|1|6|https://play.typeracer.com/com.typeracer.guest.Guest/|5CBFBDCD9A4D280D027FF3A5E637DC0C|_|joinSinglePlayerGame|y|1w|1|2|3|4|1|5|5|0|1|0|6|crLHRPFB|
Response:
//OK[4,17,1.602780248399E12,0,-5,4000,3,16,15,14,0,13,4060062,12,11,10,9,8,7,0,0,0,6,"crLHRPFB",5,1,4,1.602780252399E12,1,3,487716,2,0,1,["1h","13","12","2w","1w","15","1i","B00WO1YUQS","Tame Impala","sleepyaf123","","https://data.typeracer.com/pit/profile?user\u003Dsleepyaf123","32","Let It Happen","All this running around. I can\u0027t fight it much longer. Something\u0027s trying to get out. And it\u0027s never been closer. If my ticker fails, make up some other story. But if I never come back, tell my mother I\u0027m sorry.","1j","27"],1,7]

The "stuff to type" is in there. It'll take a little more work to complete the rest of the necessary reverse-engineering, but in general this process is made simple by the developer tools of any modern browser. Once you have the adequate insight into how the application works, drop Selenium in favour of raw Requests.

answered Oct 15, 2020 at 17:19
\$\endgroup\$
1
  • \$\begingroup\$ +1 because "drop Selenium in favour of raw Request" \$\endgroup\$ Commented Oct 15, 2020 at 17:23

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.