Skip to content

Commit

Permalink
Implement prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
oror-sine committed Mar 31, 2024
1 parent 420d386 commit 7c7fd08
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__pycache__
venv
.env
187 changes: 187 additions & 0 deletions Velog-Selenium-Action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from functools import partial
from typing import Callable
import clipboard

# TODO: Implement post deletion
# TODO: Read post information from JSON and validate its structure and data types
# TODO: Refactor the module as a class
# TODO: Implement file upload
# - js in selenium 활용해서 drag & drop 구현
# - pyvirtualdisplay, pyautogui 활용 gui 조작


def wait_find_element(driver, by, value, timeout=10):
return WebDriverWait(driver, timeout=timeout).until(
# EC.presence_of_element_located((by, value))
EC.element_to_be_clickable((by, value))
)

def clear_in_element(driver:webdriver.Chrome, by, value):
wait_find_element(driver, by, value).click()
webdriver.ActionChains(driver=driver).key_down(Keys.CONTROL).send_keys('A', Keys.BACKSPACE).perform()


def copy_paste_in_element(driver:webdriver.Chrome, by, value, content):
wait_find_element(driver, by, value).click()
clipboard.copy(content)
webdriver.ActionChains(driver=driver).key_down(Keys.CONTROL).send_keys('v').perform()


def login_by_github(
driver:webdriver.Chrome,
username:str,
password:str
):
XPATH:Callable[[str],WebElement] = partial(wait_find_element, driver, By.XPATH)

login_page = 'https://github.com/login?client_id=7c3902d881910d52ae3e&return_to=%2Flogin%2Foauth%2Fauthorize%3Fclient_id%3D7c3902d881910d52ae3e%26integrateState%3D%26isIntegrate%3D0%26redirect_uri%3Dhttps%253A%252F%252Fv2.velog.io%252Fapi%252Fv2%252Fauth%252Fsocial%252Fcallback%252Fgithub%253Fnext%253D%26scope%3Duser%253Aemail'

login_field = '//*[@id="login_field"]'
password_field = '//*[@id="password"]'
submit_button = '//*[@id="login"]/div[3]/form/div/input[13]'

driver.get(login_page)

XPATH(login_field).send_keys(username)
XPATH(password_field).send_keys(password)
XPATH(submit_button).click()

# Reauthorization required 처리
authorize_button = '/html/body/div[1]/div[6]/main/div/div[2]/div[1]/div[2]/div[1]/form/div/button[2]'

try:
XPATH(authorize_button, 2).click()
except:
...

def write_post(
driver:webdriver.Chrome,
title,
content,
tags=[],
description=None,
private=True,
url_slug=None,
series=None,
thumbnail=None
):
XPATH:Callable[[str],WebElement] = partial(wait_find_element, driver, By.XPATH)
copy_paste:Callable[[str, str]] = partial(copy_paste_in_element, driver, By.XPATH)
clear:Callable[[str]] = partial(clear_in_element, driver, By.XPATH)

title_field = '//*[@id="root"]/div[2]/div/div[1]/div/div[1]/div[1]/div/textarea'
tag_field = '//*[@id="root"]/div[2]/div/div[1]/div/div[1]/div[1]/div/div[2]/div/input'
content_field = '//*[@id="root"]/div[2]/div/div[1]/div/div[1]/div[3]/div/div[6]/div[1]/div/div'
save_button = '//*[@id="root"]/div[2]/div/div[1]/div/div[2]/div/div/button[1]'
publish_button = '//*[@id="root"]/div[2]/div/div[1]/div/div[2]/div/div/button[2]'
public_button = '//*[@id="root"]/div[2]/div/div[1]/div/div[2]/div/div/button[1]'
private_button = '//*[@id="root"]/div[2]/div[2]/div/div[3]/div[1]/section[1]/div/button[2]'
url_slug_field = '//*[@id="root"]/div[2]/div[2]/div/div[3]/div[1]/section[2]/div/div/input'
add_to_series_button = '//*[@id="root"]/div[2]/div[2]/div/div[3]/div[1]/section[3]/div/button'
new_series_button = '//*[@id="root"]/div[2]/div[2]/div/div[3]/section/div/div[1]/div/form/div/div[2]/button[2]'
series_ul = '//*[@id="root"]/div[2]/div[2]/div/div[3]/section/div/div[1]/ul'
series_field = '//*[@id="root"]/div[2]/div[2]/div/div[3]/section/div/div[1]/div/form/input'
select_series_button = '//*[@id="root"]/div[2]/div[2]/div/div[3]/section/div/div[2]/button[2]'
submit_button = '//*[@id="root"]/div[2]/div[2]/div/div[3]/div[2]/button[2]'
description_field = '//*[@id="root"]/div[2]/div[2]/div/div[1]/section/div/div[2]/textarea'

driver.get('https://velog.io/write')

copy_paste(title_field, title)

for tag in tags:
copy_paste(tag_field, tag)
XPATH(tag_field).send_keys(Keys.RETURN)

content += '\n\n#### Auto-generated by [Velog-Selenium-Action](https://github.com/oror-sine/Velog-Selenium-Action)'
clipboard.copy(content)
copy_paste(content_field, content)

XPATH(publish_button).click()

if description is not None:
copy_paste(description_field, description)

if private:
XPATH(private_button).click()
else:
XPATH(public_button).click()

if url_slug is not None:
clear(url_slug_field)
copy_paste(url_slug_field, url_slug)

if series is not None:
XPATH(add_to_series_button).click()
XPATH(series_field).click()
copy_paste(series_field, series)
XPATH(new_series_button).click()
for series_li in XPATH(series_ul).find_elements(By.TAG_NAME, 'li'):
if series_li.text == series:
series_li.click()
break
XPATH(select_series_button).click()

if thumbnail is not None:
raise NotImplementedError()

XPATH(submit_button).click()

title_element = '//*[@id="root"]/div[2]/div[3]/div/h1'
print(XPATH(title_element).text)


def edit_post():
NotImplementedError()




if __name__ == '__main__':
from datetime import datetime
from get_driver import get_driver
import sys

_, username, password = (None, )*3

if len(sys.argv) == 1:
from dotenv import dotenv_values
username, password = dotenv_values('.env').values()

elif len(sys.argv) == 3:
_, username, password = sys.argv

if username is None or password is None:
raise Exception('missing login info')

driver = None
try:
driver = get_driver(options=[])
except Exception:
print(f'driver with display is not available: {Exception}')
driver = get_driver()


login_by_github(driver, username, password)


timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
str_date, str_time = timestamp.split()
post = {
'title': f'[TEST] {timestamp}',
'content':'This post is created for testing Velog-Selenium-Action',
'tags':['Auto-generated by Velog-Selenium-Action', str_date, str_time],
'description':None,
'private':True,
'url_slug':'test',
'series': 'Testing Velog-Selenium-Action',
'thumbnail':None
}

write_post(driver, **post)
59 changes: 59 additions & 0 deletions get_driver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from selenium import webdriver
import chromedriver_autoinstaller

chromedriver_autoinstaller.install()

_options = [
"--headless",
"--disable-gpu",
"--window-size=1920,1200",
"--ignore-certificate-errors",
"--disable-extensions",
"--no-sandbox",
"--disable-dev-shm-usage",
'--remote-debugging-port=9222'
]

def get_driver(options = _options):
chrome_options = webdriver.ChromeOptions()

for option in options:
chrome_options.add_argument(option)

return webdriver.Chrome(options = chrome_options)

if __name__ == "__main__":
print(f'{"[Test `get_driver.py`]":-^100}\n')

print(f'{"[Try Headless]":-^100}')
status = ''
try:
driver_with_headless = get_driver()
driver_with_headless.get('https://github.com/')
title = driver_with_headless.title
driver_with_headless.quit()

status = f'{"Success": <10}: {title}'

except Exception as err:
status = f'{"Fail": <10}: {err}'

print(status)
print(f'{"":-^100}')


print(f'{"[Try Display]":-^100}')
status = ''
try:
driver_with_display = get_driver()
driver_with_display.get('https://github.com/')
title = driver_with_display.title
driver_with_display.quit()

status = f'{"Success": <10}: {title}'

except Exception as err:
status = f'{"Fail": <10}: {err}'

print(status)
print(f'{"":-^100}')
21 changes: 21 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
attrs==23.2.0
certifi==2024.2.2
cffi==1.16.0
chromedriver-autoinstaller==0.6.4
clipboard==0.0.4
h11==0.14.0
idna==3.6
outcome==1.3.0.post0
packaging==24.0
pycparser==2.22
pyperclip==1.8.2
PySocks==1.7.1
python-dotenv==1.0.1
selenium==4.19.0
sniffio==1.3.1
sortedcontainers==2.4.0
trio==0.25.0
trio-websocket==0.11.1
typing_extensions==4.10.0
urllib3==2.2.1
wsproto==1.2.0

0 comments on commit 7c7fd08

Please sign in to comment.