Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DESKTOP_ASSISTANT_SMTP_PWD=YOUR_EMAIL_PASSWORD
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ $RECYCLE.BIN/
.vsix

# IntelliJ Idea
.idea
.idea

# Environment Variables
.env
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ speechrecognition~=3.10.0
urllib3~=2.2.2
wheel~=0.41.2
wikipedia~=1.4.0
python-dotenv~=1.0.1
wmi~=1.5.1
pycaw~=20240210
feedparser~=6.0.11
Expand Down
61 changes: 61 additions & 0 deletions src/assistant.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@

"""

import json
import re
import subprocess
from datetime import datetime

import commands
from infra import clear_screen
from utils import load_email_config
from voice_interface import VoiceInterface

LISTENING_ERROR = "Say that again please..."
Expand Down Expand Up @@ -133,6 +135,65 @@ def execute_query(self, query: str) -> None:
r"\b(?:of|in|at)\s+(\w+)", query
) # Extract the city name just after the word 'of'
commands.weather_reporter(self.__voice_interface, cities[0])
elif "email" in query:
query = query.lower()

data = load_email_config()

if data.get("server") == "smtp.example.com":
self.__voice_interface.speak(
"Please setup email config file before sending mail."
)
else:
self.__voice_interface.speak("who do you want to send email to?")
receiver = None
validEmail = False
while not validEmail:
receiver = self.listen_for_query()

if receiver in data.get("contacts").keys():
print(
f"Receiver selected from contacts: {data.get("contacts").get(receiver)}"
)
receiver = data.get("contacts").get(receiver)
validEmail = True
elif re.match(
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", receiver
):
validEmail = True
else:
self.__voice_interface.speak(
"Valid Email not provided or contact does not exists"
)

self.__voice_interface.speak(
"What would be the subject of the message? "
)
subject = None
while subject is None:
subject = self.listen_for_query()

self.__voice_interface.speak("What would be the body of the email?")
body = None
while body is None:
body = self.listen_for_query()

print(
f"Sender Address: {data.get("username")}\n"
f"Receiver address: {receiver}\n"
f"Subject: {subject}\n"
f"Body: {body}\n"
)

self.__voice_interface.speak("Do You Want to send this email?")
response = None
while response is None:
response = self.listen_for_query()
if "yes" in response.lower() or "sure" in response.lower():
self.__voice_interface.speak("Sending the email")
commands.send_email(self.__voice_interface, receiver, subject, body)
else:
self.__voice_interface.speak("Request aborted by user")

elif "brightness" in query:
query = query.lower()
Expand Down
37 changes: 37 additions & 0 deletions src/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@

"""

import smtplib
import ssl
import subprocess
import threading
import time
from datetime import datetime
from email.message import EmailMessage
from subprocess import CalledProcessError, TimeoutExpired

import feedparser
Expand All @@ -23,10 +26,12 @@
import wikipedia
import wmi
from comtypes import CLSCTX_ALL
from dotenv import dotenv_values
from PIL import ImageGrab
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume

from infra import __is_darwin, __is_posix, __is_windows, __system_os
from utils import load_email_config
from voice_interface import VoiceInterface

SUPPORTED_FEATURES = {
Expand All @@ -42,6 +47,8 @@
from AppOpener import open as open_app
########## Conditional Imports ##########

ENVIRONMENT_VARIABLES = dotenv_values(".env")


def explain_features(vi: VoiceInterface) -> None:
"""Explains the features available
Expand Down Expand Up @@ -451,3 +458,33 @@ def weather_reporter(vi: VoiceInterface, city_name: str) -> None:
f"The wind speed is expected to be {weather_data.get('wind_speed_10m')}{weather_units.get('wind_speed_10m')}, "
"so plan accordingly."
)


def send_email(vi: VoiceInterface, toEmail: str, subject: str, body: str):
"""
Send an email to the specified recipient.

Args:
vi (VoiceInterface): VoiceInterface instance used to speak.
toEmail (str): The recipient's email address.
subject (str): The subject of the email.
body (str): The body content of the email.

Raises:
ValueError: If any required parameters are missing or invalid.
"""

data = load_email_config()
CONTEXT = ssl.create_default_context()
msg = EmailMessage()
msg["Subject"] = subject
msg["From"] = data.get("username")
msg["To"] = [toEmail]
msg.set_content(body)
server = smtplib.SMTP_SSL(data.get("server"), data.get("port"), context=CONTEXT)
server.login(
data.get("username"), ENVIRONMENT_VARIABLES.get("DESKTOP_ASSISTANT_SMTP_PWD")
)
server.send_message(msg)
server.quit()
vi.speak(f"Email sent to {toEmail}")
9 changes: 9 additions & 0 deletions src/config/email_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"server": "smtp.example.com",
"port": 465,
"username":"[email protected]",
"contacts": {
"ajay": "[email protected]",
"raj": "[email protected]"
}
}
11 changes: 11 additions & 0 deletions src/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import json
import os

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

CONFIG_PATH = os.path.join(BASE_DIR, "config", "email_config.json")


def load_email_config():
with open(CONFIG_PATH, "r") as f:
return json.load(f)