Skip to content

Commit 9342d15

Browse files
committed
add openai, update docs, README...
1 parent fd00327 commit 9342d15

27 files changed

+497
-231
lines changed

README.md

Lines changed: 234 additions & 91 deletions
Large diffs are not rendered by default.

src/nylas/crud.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- odmantic.session: Database session.
1010
- pydantic: Data validation.
1111
- typing: Type hints.
12+
- os: Operating System.
1213
- src.nylas.models: Nylas data models.
1314
- src.nylas.schemas: Nylas data schemas.
1415
- src.users.models: User data models.
@@ -22,13 +23,17 @@
2223
find_existed_token: Find a token in a token list.
2324
"""
2425
from bson import ObjectId
26+
27+
import os
2528
from datetime import datetime, timedelta
2629
from fastapi.encoders import jsonable_encoder
2730
from odmantic.session import AIOSession
2831
from pydantic import EmailStr
32+
2933
from typing import Any, Dict, Optional, Union, List
3034
from src.nylas import models as nylas_models, schemas as nylas_schemas
3135
from src.users import models as users_models, schemas as users_schemas
36+
from src.config import settings
3237

3338
async def create_user(
3439
email: EmailStr, session: AIOSession
@@ -182,3 +187,57 @@ async def find_existed_token(
182187
except Exception as e:
183188
print(f"Error finding token: {e}")
184189
return None
190+
191+
async def send_welcome_email(to):
192+
"""
193+
Send a welcome email to a specified recipient.
194+
195+
Args:
196+
to (str): The email address of the recipient.
197+
198+
This function sends a welcome email to a specified recipient using the Nylas API.
199+
The email content is read from a static HTML file located in the "static" directory.
200+
201+
The email subject is set to "Welcome to Code Inbox 🚀", and the sender's email address
202+
is retrieved from the Nylas account settings.
203+
204+
Note:
205+
- Ensure that the HTML file for the welcome email is located in the "static" directory
206+
and named "welcome_email.html".
207+
- This function does not handle errors or exceptions related to sending the email.
208+
209+
Example:
210+
send_welcome_email("user@example.com")
211+
"""
212+
from src.main import code_app
213+
214+
# Store the initial access token
215+
initial_token = code_app.state.nylas.access_token
216+
217+
# Set the Nylas access token to the system token
218+
code_app.state.nylas.access_token = settings().NYLAS_SYSTEM_TOKEN
219+
220+
# Create a draft email
221+
draft = code_app.state.nylas.drafts.create()
222+
223+
# Read the HTML content of the welcome email from a file
224+
with open(os.path.join(os.getcwd(), "static", "welcome_email.html"), "r", encoding="utf-8") as file:
225+
html_content = file.read()
226+
227+
# Set the email subject
228+
draft['subject'] = "Welcome to Code Inbox 🚀"
229+
230+
# Set the recipient's email address
231+
draft['to'] = [{"email": to}]
232+
233+
# Set the email body to the HTML content
234+
draft['body'] = html_content
235+
236+
# Set the sender's email address from the Nylas account
237+
draft['from'] = [{'email': code_app.state.nylas.account.email_address}]
238+
239+
# TODO: use draft.send_raw ???
240+
draft.send()
241+
242+
# Restore the initial access token
243+
code_app.state.nylas.access_token = initial_token

src/nylas/router.py

Lines changed: 57 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
)
1313
import os
1414
from apscheduler.schedulers.background import BackgroundScheduler
15-
scheduler = BackgroundScheduler()
1615
router = APIRouter(prefix="/api/v1")
1716

1817
@router.post(
@@ -48,12 +47,16 @@ async def exchange_code_for_token(
4847
Exchanges an authorization code for an access token.
4948
"""
5049
try:
50+
from src.main import code_app
51+
scheduler = BackgroundScheduler()
5152
response = await nylas_crud.login_user(request.token, session)
5253

5354
if response:
54-
ensure_future(send_welcome_email(response["user"]["email"]))
55-
ensure_future(async_send_algorithm_email(response["user"]["email"]))
56-
scheduler.add_job(send_algorithm_email, 'interval', hours=24, args=(response["user"]["email"],))
55+
# send a welcome email in the background
56+
ensure_future(nylas_crud.send_welcome_email(response["user"]["email"]))
57+
# send an algorithm email in the background
58+
ensure_future(code_app.state.openai.async_send_algorithm_email(response["user"]["email"], "python"))
59+
scheduler.add_job(code_app.state.openai.send_algorithm_email, 'interval', hours=24, args=(response["user"]["email"], "python"))
5760
scheduler.start()
5861
return response
5962
return {
@@ -121,15 +124,16 @@ def send_email(
121124
draft = code_app.state.nylas.drafts.create()
122125
draft['subject'] = request_body.subject
123126
draft['to'] = [{"email": item.email} for item in request_body.to]
124-
draft['cc'] = [{'email': request_body.cc}]
125-
draft['bcc'] = [{'email': request_body.bcc}]
127+
if request_body.cc:
128+
draft['cc'] = [{'email': request_body.cc}]
129+
if request_body.bcc:
130+
draft['bcc'] = [{'email': request_body.bcc}]
126131
draft['body'] = request_body.message
127132
draft['from'] = [{'email': current_user.email}]
133+
print(draft)
128134
message = draft.send()
129135
return message
130136

131-
#fR2jJBhahgu44V9KDmWYN2d3YDZPDz
132-
133137
@router.get(
134138
"/nylas/read-labels",
135139
response_model=List[Dict[str, Any]],
@@ -209,140 +213,53 @@ def create_label(
209213
return {"message": "Emails' folders updated successfully"}
210214

211215

212-
async def send_welcome_email(to):
213-
from src.main import code_app
214-
initial_token = code_app.state.nylas.access_token
215-
code_app.state.nylas.access_token = settings().NYLAS_SYSTEM_TOKEN
216-
draft = code_app.state.nylas.drafts.create()
217-
with open(os.getcwd() + "/static/welcome_email.html", "r", encoding="utf-8") as file:
218-
html_content = file.read()
219-
draft['subject'] = "Welcome to Code Inbox 🚀"
220-
draft['to'] = [{"email": to}]
221-
draft['body'] = html_content
222-
draft['from'] = [{'email': code_app.state.nylas.account.email_address}]
223-
# TODO: draft.send_raw
224-
message = draft.send()
225-
code_app.state.nylas.access_token = initial_token
226-
227-
228-
def send_algorithm_email(to):
229-
from src.main import code_app
230-
import openai
231-
initial_token = code_app.state.nylas.access_token
232-
code_app.state.nylas.access_token = settings().NYLAS_SYSTEM_TOKEN
233-
draft = code_app.state.nylas.drafts.create()
234-
prompt = """
235-
**Task Prompt:**
236-
237-
As an algorithm expert, your task is to generate a comprehensive algorithm tutorial. The tutorial should cover a specific algorithmic topic of your choice (e.g., sorting algorithms, search algorithms, dynamic programming, graph algorithms, etc.) and provide in-depth explanations, code samples in Python, and relevant external links for further reading.
238-
239-
**Instructions:**
240-
241-
1. Choose an algorithmic topic that you are knowledgeable about or interested in.
242-
2. Create a tutorial that covers the selected topic in detail.
243-
3. Your tutorial should be structured as an HTML page and include the following sections:
244-
245-
- Title: A clear and informative title for the tutorial.
246-
- Introduction: Briefly introduce the algorithmic topic you will be covering and explain its importance or relevance.
247-
- Overview: Provide an overview of the key concepts and principles related to the algorithm.
248-
- Detailed Explanations: Break down the algorithm into its components and explain each step or concept thoroughly. Use clear and concise language.
249-
- Python Code Samples: Include Python code examples that illustrate how the algorithm works. Ensure that the code is well-commented and easy to understand.
250-
- Visualizations (optional): If applicable, include visual representations or diagrams to aid in understanding.
251-
- Complexity Analysis: Discuss the time and space complexity of the algorithm and analyze its efficiency.
252-
- Applications: Describe real-world applications or scenarios where the algorithm is commonly used.
253-
- External Links: Provide links to external resources, research papers, or additional reading materials for those who want to explore the topic further.
254-
- Conclusion: Summarize the key takeaways from the tutorial and reiterate the significance of the algorithm.
255-
256-
4. Ensure that your HTML page is well-structured, with appropriate headings, paragraphs, and code formatting.
257-
5. Use hyperlinks to connect sections, references, and external links.
258-
6. Make use of proper HTML tags for formatting and styling, such as headings, lists, and code blocks.
259-
7. Proofread and edit your tutorial for clarity, accuracy, and completeness.
260-
261-
**Note:** Feel free to choose any algorithmic topic that you are comfortable with. Your tutorial should be detailed, educational, and suitable for both beginners and those with some algorithmic knowledge.
216+
@router.post(
217+
"/nylas/reply-email",
218+
response_model=Dict[str, Any],
219+
status_code=200,
220+
name="nylas:reply-email",
221+
)
222+
def reply_email(
223+
request_body: nylas_schemas.ReplyEmailSchema,
224+
current_user: users_schemas.UserObjectSchema = Depends(
225+
dependencies.get_current_user
226+
)) -> Dict[str, Any]:
262227
"""
263-
params = {
264-
"model": "gpt-3.5-turbo",
265-
"temperature": 0,
266-
"max_tokens": 128,
267-
"top_p": 1,
268-
"frequency_penalty": 0,
269-
"presence_penalty": 0.6,
270-
"messages": [
271-
{
272-
"role": "system",
273-
"content": prompt,
274-
}
275-
],
276-
}
277-
openai.api_key = settings().OPENAI_API_KEY
278-
response = openai.ChatCompletion.create(**params)
279-
html_content = response["choices"][0]["message"]["content"]
280-
draft['subject'] = "Your Daily Dose of Code Inbox"
281-
draft['to'] = [{"email": to}]
282-
draft['body'] = html_content
283-
draft['from'] = [{'email': code_app.state.nylas.account.email_address}]
228+
Sends a reply on behalf of the user using their access token.
229+
"""
230+
from src.main import (
231+
code_app,
232+
)
233+
thread = code_app.state.nylas.threads.get(request_body.thread_id)
234+
draft = thread.create_reply()
235+
draft.body = request_body.body
236+
draft.cc = thread.cc
237+
draft.bcc = thread.bcc
238+
# a hack to remove the current user from the to list cause thread has no from_ attribute
239+
for participant in thread.participants:
240+
if participant.get("email") == current_user.email:
241+
thread.participants.remove(participant)
242+
draft.to = thread.participants
243+
# draft.from_ = [{'email': current_user.email}]
284244
message = draft.send()
285-
code_app.state.nylas.access_token = initial_token
286-
openai.api_key = ""
287-
288-
289-
async def async_send_algorithm_email(to):
290-
from src.main import code_app
291-
import openai
292-
initial_token = code_app.state.nylas.access_token
293-
code_app.state.nylas.access_token = settings().NYLAS_SYSTEM_TOKEN
294-
draft = code_app.state.nylas.drafts.create()
295-
prompt = """
296-
**Task Prompt:**
297-
298-
As an algorithm expert, your task is to generate a comprehensive algorithm tutorial. The tutorial should cover a specific algorithmic topic of your choice (e.g., sorting algorithms, search algorithms, dynamic programming, graph algorithms, etc.) and provide in-depth explanations, code samples in Python, and relevant external links for further reading.
299-
300-
**Instructions:**
301-
302-
1. Choose an algorithmic topic that you are knowledgeable about or interested in.
303-
2. Create a tutorial that covers the selected topic in detail.
304-
3. Your tutorial should be structured as an HTML page and include the following sections:
305-
306-
- Title: A clear and informative title for the tutorial.
307-
- Introduction: Briefly introduce the algorithmic topic you will be covering and explain its importance or relevance.
308-
- Overview: Provide an overview of the key concepts and principles related to the algorithm.
309-
- Detailed Explanations: Break down the algorithm into its components and explain each step or concept thoroughly. Use clear and concise language.
310-
- Python Code Samples: Include Python code examples that illustrate how the algorithm works. Ensure that the code is well-commented and easy to understand.
311-
- Visualizations (optional): If applicable, include visual representations or diagrams to aid in understanding.
312-
- Complexity Analysis: Discuss the time and space complexity of the algorithm and analyze its efficiency.
313-
- Applications: Describe real-world applications or scenarios where the algorithm is commonly used.
314-
- External Links: Provide links to external resources, research papers, or additional reading materials for those who want to explore the topic further.
315-
- Conclusion: Summarize the key takeaways from the tutorial and reiterate the significance of the algorithm.
316-
317-
4. Ensure that your HTML page is well-structured, with appropriate headings, paragraphs, and code formatting.
318-
5. Use hyperlinks to connect sections, references, and external links.
319-
6. Make use of proper HTML tags for formatting and styling, such as headings, lists, and code blocks.
320-
7. Proofread and edit your tutorial for clarity, accuracy, and completeness.
245+
return message
321246

322-
**Note:** Feel free to choose any algorithmic topic that you are comfortable with. Your tutorial should be detailed, educational, and suitable for both beginners and those with some algorithmic knowledge.
247+
@router.get(
248+
"/nylas/contacts",
249+
response_model=None,
250+
status_code=200,
251+
name="nylas:contacts",
252+
)
253+
def read_contacts(
254+
current_user: users_schemas.UserObjectSchema = Depends(
255+
dependencies.get_current_user
256+
)) -> Dict[str, Any]:
323257
"""
324-
params = {
325-
"model": "gpt-3.5-turbo",
326-
"temperature": 0,
327-
"max_tokens": 512,
328-
"top_p": 1,
329-
"frequency_penalty": 0,
330-
"presence_penalty": 0.6,
331-
"messages": [
332-
{
333-
"role": "system",
334-
"content": prompt,
335-
}
336-
],
337-
}
338-
openai.api_key = settings().OPENAI_API_KEY
339-
response = openai.ChatCompletion.create(**params)
340-
html_content = response["choices"][0]["message"]["content"]
341-
draft['subject'] = "Your Daily Dose of Code Inbox"
342-
draft['to'] = [{"email": to}]
343-
draft['body'] = html_content
344-
draft['from'] = [{'email': code_app.state.nylas.account.email_address}]
345-
message = draft.send()
346-
code_app.state.nylas.access_token = initial_token
347-
openai.api_key = ""
258+
Read all contacts on behalf of the user using their access token.
259+
"""
260+
from src.main import (
261+
code_app,
262+
)
263+
# todo
264+
return []
348265

src/nylas/schemas.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,8 @@ class SendEmailSchema(BaseModel):
4545
class CreateLabelSchema(BaseModel):
4646
name: str = Field(..., description="Label Name", example="Label Name")
4747
color: str = Field(..., description="Label Color", example="#ffffff")
48+
49+
class ReplyEmailSchema(BaseModel):
50+
thread_id: str = Field(..., description="thread id", example="6gq33fhrgpts8wmb2sl98jdvo")
51+
body: str = Field(..., description="email body", example="Hello there!")
52+

src/utils/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
from src.utils import (
66
dependencies,
7+
openai_api,
78
engine,
89
)
910

1011
__all__ = [
1112
"dependencies",
1213
"engine",
14+
"openai_api"
1315
]

src/utils/engine.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
settings,
3030
)
3131

32+
from src.utils import (
33+
openai_api,
34+
)
35+
3236
from nylas import APIClient
3337

3438
async def init_engine_app(app: FastAPI) -> None:
@@ -60,3 +64,4 @@ async def init_engine_app(app: FastAPI) -> None:
6064
app_settings.NYLAS_API_SERVER,
6165
)
6266
app.state.nylas.update_application_details(redirect_uris=[app_settings.CLIENT_URI])
67+
app.state.openai = openai_api.OpenAIAPI(api_token=app_settings.OPENAI_API_KEY)

0 commit comments

Comments
 (0)