-
Notifications
You must be signed in to change notification settings - Fork 1
/
heroku_bot.py
181 lines (133 loc) · 5.46 KB
/
heroku_bot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from time import sleep
import os
class Bot:
def __init__(self, credentials):
options = Options()
options.headless = True # comment this line if you want to debug
options.binary_location = os.environ.get("GOOGLE_CHROME_BIN")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--no-sandbox")
self.driver = webdriver.Chrome(
executable_path=os.environ.get("CHROMEDRIVER_PATH"),
options=options
)
self.wait = WebDriverWait(self.driver, 60)
self.credentials = credentials
def enter_heroku(self):
print("Entering Heroku...")
self.driver.get("https://id.heroku.com/login")
print(f"Entering form data for {self.credentials['email']}...")
# fill up form
email_field = self.wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "#email"))
)
email_field.send_keys(self.credentials["email"])
password_field = self.wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "#password"))
)
password_field.send_keys(self.credentials["password"])
# submit form
password_field.submit() # selenium will walk up the DOM and find the form to submit
# click on "Later" to tell that you don't want Two-Factor-Auth
later_button = self.wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, ".btn.btn-link"))
)
later_button.click()
print("Successfully entered Heroku!")
# wait the dashboard to load
sleep(3)
def activate_dyno(self, app_name, dyno_identifier):
# enter and log in heroku
self.enter_heroku()
print(f"Activating dyno {app_name}...")
# go to dyno page
self.driver.get(
f"https://dashboard.heroku.com/apps/{app_name}/resources")
# <code></code> element used to find the desired dyno
dyno_identifier_code_tag = self.wait.until(
EC.presence_of_element_located(
(By.XPATH, f"//*[text()='{dyno_identifier}']"))
)
# using the <code></code> tag with the identifier, get the div with the activation buttons
div_with_interaction_buttons = dyno_identifier_code_tag.find_element_by_xpath(
"./parent::div/following-sibling::div"
)
# enter edit mode
edit_button = div_with_interaction_buttons.find_element_by_xpath(
"./descendant::button"
)
edit_button.click()
# deactivate dyno
activation_slider = div_with_interaction_buttons.find_element_by_xpath(
"./descendant::input"
)
# if the slider isn't checked (toggled off), click it to activate
if not activation_slider.is_selected():
activation_slider.click()
# if it is, just close the browser - there's nothing to do here
else:
print("Dyno already activated!")
self.end_session()
return
# click confirm
confirm_button = div_with_interaction_buttons.find_element_by_xpath(
"./descendant::button"
)
confirm_button.click()
# wait for the activation to load
while activation_slider.is_enabled():
sleep(0.1)
print("Dyno succesfully activated!")
self.end_session()
def deactivate_dyno(self, app_name, dyno_identifier):
# enter and log in heroku
self.enter_heroku()
print(f"Deactivating dyno {app_name}...")
# go to dyno page
self.driver.get(
f"https://dashboard.heroku.com/apps/{app_name}/resources")
# <code></code> element used to find the desired dyno
dyno_identifier_code_tag = self.wait.until(
EC.presence_of_element_located(
(By.XPATH, f"//*[text()='{dyno_identifier}']"))
)
# using the <code></code> tag with the identifier, get the div with the activation buttons
div_with_interaction_buttons = dyno_identifier_code_tag.find_element_by_xpath(
"./parent::div/following-sibling::div"
)
# enter edit mode
edit_button = div_with_interaction_buttons.find_element_by_xpath(
"./descendant::button"
)
edit_button.click()
# deactivate dyno
deactivation_slider = div_with_interaction_buttons.find_element_by_xpath(
"./descendant::input"
)
# if the slider is checked (toggled on), click it to deactivate
if deactivation_slider.is_selected():
deactivation_slider.click()
# if it isn't, just close the browser - there's nothing to do here
else:
print("Dyno already deactivated!")
self.end_session()
return
# click confirm
confirm_button = div_with_interaction_buttons.find_element_by_xpath(
"./descendant::button"
)
confirm_button.click()
# wait for the deactivation to load
while deactivation_slider.is_enabled():
sleep(0.1)
print("Dyno succesfully deactivated!")
self.end_session()
def end_session(self):
self.driver.quit()
if __name__ == "__main__":
pass