Skip to content

Commit 747b547

Browse files
Merge pull request #98 from NamamiShanker/notion
🚀 Add Notion.so login feature to get token
2 parents 3722a50 + 3820d61 commit 747b547

File tree

7 files changed

+154
-26
lines changed

7 files changed

+154
-26
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Options: <br>
4343
`-st, --start -> Introduces Dynamic CLI` <br>
4444
`-v, --version -> Gives the Version of the CLI` <br>
4545
`-s, --search -> Search a question on Stackoverflow` <br>
46+
`-no, --notion -> Open browser to login to Notion.so` <br>
4647
`-d, --debug -> Turn on Debugging mode` <br>
4748
`-c, --custom -> Setup a custom API key` <br>
4849
`-h, --help -> Shows this message and exit` <br>

main.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@
5555
help="View and organise the playbook",
5656
action='store_true')
5757

58+
parser.add_argument("-no",
59+
"--notion",
60+
help="\
61+
Login to your Notion account to save playbook.\
62+
Opens a browser window for you to login to\
63+
your Notion accout",
64+
action='store_true')
65+
5866
ARGV = parser.parse_args()
5967

6068
search_flag = Search(ARGV)

src/arguments/error.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,20 @@ def evoke_search_error(self, error_statement):
1818
]
1919
for text_to_print in print_text:
2020
print(text_to_print)
21+
22+
class LoginError():
23+
def __init__(self, error_statement, success=False):
24+
"""
25+
Implements error printing for User Login
26+
:error_statement: Error statement to print
27+
:success: Indicates success of login attempt
28+
Prints in green if True else red
29+
"""
30+
self.error_statement = error_statement
31+
self.success = success
32+
self.evoke_search_error()
33+
34+
def evoke_search_error(self):
35+
color = "green" if self.success else "red"
36+
print_text = colored(self.error_statement, color)
37+
print(print_text)

src/arguments/notion.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import os
2+
3+
from .utility import get_browser_driver
4+
from .error import LoginError
5+
from .settings import LOGIN_PATH
6+
from .settings import TOKEN_FILE_PATH
7+
8+
from selenium.webdriver.common.by import By
9+
from selenium.webdriver.support.ui import WebDriverWait
10+
from selenium.webdriver.support import expected_conditions as EC
11+
12+
def get_token_from_cookie(cookie, token):
13+
for el in cookie:
14+
if el['name'] == token:
15+
return el
16+
17+
def get_token_from_file():
18+
try:
19+
with open(TOKEN_FILE_PATH, 'r') as f:
20+
data = f.read()
21+
except Exception as e:
22+
print(e)
23+
if(not data or data==""):
24+
raise RuntimeError("Token not found in file")
25+
else:
26+
return data
27+
28+
def get_cookies_from_login():
29+
"""Capture browser cookies for authentication."""
30+
driver = get_browser_driver()
31+
try:
32+
driver.get(LOGIN_PATH)
33+
WebDriverWait(driver, 300).until(
34+
EC.presence_of_element_located((By.CLASS_NAME,
35+
"notion-sidebar-container")))
36+
return driver.get_cookies()
37+
except Exception as e:
38+
print(e)
39+
finally:
40+
driver.quit()
41+
42+
class NotionClient():
43+
44+
"""
45+
Implements Login and token retrieval.
46+
47+
Handles the entire procedure of connecting to User's Notion account,
48+
generating Notion's tokenv2_cookie, storing it locally and uploading
49+
content to User's space
50+
"""
51+
def __init__(self):
52+
"""
53+
No input parameters required for instatiating object.
54+
55+
:tokenv2_cookie: stores the cookie containing user's tokenv2
56+
:tokenv2_key: used to create environment variable
57+
"""
58+
self.tokenv2_cookie = None
59+
self.tokenv2_key = 'TOKENV2'
60+
61+
def save_token_file(self):
62+
if(self.tokenv2_cookie):
63+
with open(TOKEN_FILE_PATH, 'w') as f:
64+
f.write(str(self.tokenv2_cookie))
65+
66+
def get_tokenv2_cookie(self):
67+
message_success = "Successfully logged into Notion \U0001F389"
68+
message_failure = "Login unsuccessful. Please try again \U0001F615"
69+
# Sets 'tokenv2_cookie equal to the particular cookie containing token_v2
70+
if not self.tokenv2_cookie:
71+
try:
72+
self.tokenv2_cookie = get_token_from_file()
73+
LoginError(message_success, success=True)
74+
except Exception:
75+
try:
76+
cookies = get_cookies_from_login()
77+
self.tokenv2_cookie = get_token_from_cookie(cookies, 'token_v2')
78+
except Exception:
79+
self.tokenv2_cookie = None
80+
finally:
81+
if self.tokenv2_cookie:
82+
os.environ[self.tokenv2_key] = str(self.tokenv2_cookie)
83+
LoginError(message_success, success=True)
84+
self.save_token_file()
85+
else:
86+
LoginError(message_failure, success=False)
87+
return self.get_tokenv2_cookie

src/arguments/search.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .save import SaveSearchResults
1010
from .update import UpdateApplication
1111
from .api_test import ApiTesting
12+
from .notion import NotionClient
1213

1314
version = "0.1.0"
1415
class Prompt():
@@ -53,6 +54,8 @@ def search_args(self):
5354
update.check_for_updates()
5455
elif self.arguments.GET:
5556
self.api_test_object.get_request()
57+
elif self.arguments.notion:
58+
NotionClient().get_tokenv2_cookie()
5659

5760
def search_for_results(self, save=False):
5861
queries = ["What do you want to search", "Tags"]

src/arguments/settings.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import os
2+
from pathlib import Path
3+
4+
NOTION_URL = "https://www.notion.so/"
5+
LOGIN_PATH = NOTION_URL + "/login"
6+
DATA_DIR = os.environ.get(
7+
"DYNAMIC_DATA_DIR", str(Path(os.path.expanduser("~")).joinpath(".dynamic"))
8+
)
9+
PLAYBOOK_FILE = str(Path(DATA_DIR).joinpath("playbook.json"))
10+
TOKEN_FILE_PATH = str(Path(DATA_DIR).joinpath("tokenv2_cookie"))
11+
12+
try:
13+
os.makedirs(DATA_DIR)
14+
except FileExistsError:
15+
pass
16+
17+
if(not os.path.isfile(TOKEN_FILE_PATH)):
18+
open(TOKEN_FILE_PATH, 'w').close()

src/arguments/utility.py

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from .error import SearchError
1616
from .save import SaveSearchResults
1717
from .markdown import MarkdownRenderer
18+
from .settings import PLAYBOOK_FILE
1819

1920
# Required for OAuth
2021
import json
@@ -34,25 +35,32 @@
3435

3536
console = Console()
3637

38+
def get_browser_driver():
39+
# Try to install web drivers for one of these browsers
40+
# Chrome, Firefox, Edge (One of them must be installed)
41+
try:
42+
return webdriver.Chrome(ChromeDriverManager().install())
43+
except ValueError:
44+
try:
45+
return webdriver.Firefox(executable_path=
46+
GeckoDriverManager().install())
47+
except ValueError:
48+
try:
49+
return webdriver.Edge(EdgeChromiumDriverManager().\
50+
install())
51+
except ValueError:
52+
print("You do not have one of these supported browsers:" +
53+
"Chrome, Firefox, Edge")
54+
3755
class Playbook():
3856
def __init__(self):
39-
self.linux_path = "/home/{}/Documents/dynamic".\
40-
format(os.getenv('USER'))
41-
self.mac_path = "/Users/{}/Documents/dynamic".\
42-
format(os.getenv('USER'))
43-
self.file_name = 'dynamic_playbook.json'
4457
self.key = 'DYNAMIC'
4558

4659
@property
4760
def playbook_path(self):
4861
"""Create an environment variable 'DYNAMIC containing the path of dynamic_playbook.json and returns i."""
4962
if not os.getenv(self.key):
50-
if(sys.platform=='linux'):
51-
os.environ[self.key] = os.path.\
52-
join(self.linux_path, self.file_name)
53-
elif(sys.platform=='darwin'):
54-
os.environ[self.key] = os.path.\
55-
join(self.mac_path, self.file_name)
63+
os.environ[self.key] = PLAYBOOK_FILE
5664
return os.getenv(self.key)
5765

5866
@property
@@ -380,21 +388,7 @@ def setCustomKey(self):
380388
scope=scopes, redirect_uri=redirect_uri)
381389
auth_url, state = stackApps.authorization_url(authorization_url)
382390

383-
# Try to install web drivers for one of these browsers
384-
# Chrome, Firefox, Edge (One of them must be installed)
385-
try:
386-
driver = webdriver.Chrome(ChromeDriverManager().install())
387-
except ValueError:
388-
try:
389-
driver = webdriver.Firefox(executable_path=
390-
GeckoDriverManager().install())
391-
except ValueError:
392-
try:
393-
driver = webdriver.Edge(EdgeChromiumDriverManager().\
394-
install())
395-
except ValueError:
396-
print("You do not have one of these supported browsers:" +
397-
"Chrome, Firefox, Edge")
391+
driver = get_browser_driver()
398392

399393
# Open auth_url in one of the supported browsers
400394
driver.get(auth_url)

0 commit comments

Comments
 (0)