AIEmailAutomationUtility 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- AIEmailAutomationUtility/Email_CategoryDetector.py +142 -0
- AIEmailAutomationUtility/Email_DocumentUploader.py +61 -0
- AIEmailAutomationUtility/Email_Drafter.py +36 -0
- AIEmailAutomationUtility/Email_Read.py +75 -0
- AIEmailAutomationUtility/__init__.py +4 -0
- AIEmailAutomationUtility-0.0.1.dist-info/LICENCE.txt +7 -0
- AIEmailAutomationUtility-0.0.1.dist-info/METADATA +16 -0
- AIEmailAutomationUtility-0.0.1.dist-info/RECORD +10 -0
- AIEmailAutomationUtility-0.0.1.dist-info/WHEEL +5 -0
- AIEmailAutomationUtility-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
import json
|
2
|
+
from openai import OpenAI
|
3
|
+
import google.generativeai as genai
|
4
|
+
import openai
|
5
|
+
import loggerutility as logger
|
6
|
+
|
7
|
+
class Email_CategoryDetector:
|
8
|
+
def detect_category_openai(self, openai_api_key, categories, email_body):
|
9
|
+
logger.log("Inside detect_category_openai::")
|
10
|
+
try:
|
11
|
+
categories_str = ', '.join(categories)
|
12
|
+
message = [{
|
13
|
+
"role": "user",
|
14
|
+
"content": f"Classify the mail into one of the following categories: {categories_str} and Others. Based on the email content: {email_body}, provide ONLY the category in JSON format as {{\"category\": \"category\"}}."
|
15
|
+
}]
|
16
|
+
|
17
|
+
logger.log(f"Final GPT message for detecting category::: {message}")
|
18
|
+
client = OpenAI(api_key=openai_api_key)
|
19
|
+
result = client.chat.completions.create(
|
20
|
+
model="gpt-4o-mini",
|
21
|
+
messages=message,
|
22
|
+
temperature=0,
|
23
|
+
max_tokens=1800,
|
24
|
+
top_p=1,
|
25
|
+
frequency_penalty=0,
|
26
|
+
presence_penalty=0,
|
27
|
+
)
|
28
|
+
category = json.loads(result.choices[0].message.content)['category']
|
29
|
+
logger.log(f"category:: {category}")
|
30
|
+
return category
|
31
|
+
except Exception as e:
|
32
|
+
logger.log(f"Error detecting category with OpenAI: {str(e)}")
|
33
|
+
raise
|
34
|
+
|
35
|
+
def detect_category_gemini(self, gemini_api_key, categories, email_body, detect_email_category=True, signature=None):
|
36
|
+
logger.log("Inside detect_category_gemini::")
|
37
|
+
try:
|
38
|
+
categories_str = ', '.join(categories)
|
39
|
+
if detect_email_category:
|
40
|
+
message = [{
|
41
|
+
"role": "user",
|
42
|
+
"content": f"Classify the mail into one of the following categories: {categories_str} and Others. Based on the email content: {email_body}, provide ONLY the category in JSON format as {{\"category\": \"category\"}}."
|
43
|
+
}]
|
44
|
+
else:
|
45
|
+
message = [{
|
46
|
+
"role": "user",
|
47
|
+
"content": f"Create a reply for the email received from a customer. Include the email signature as {signature}\nDo not include any instruction as the output will be directly in a program."
|
48
|
+
}]
|
49
|
+
|
50
|
+
logger.log(f"Final Gemini AI message for detecting category::: {message}")
|
51
|
+
message_list = str(message)
|
52
|
+
|
53
|
+
generation_config = {
|
54
|
+
"temperature": 0,
|
55
|
+
"top_p": 1,
|
56
|
+
"top_k": 1,
|
57
|
+
"max_output_tokens": 2048,
|
58
|
+
}
|
59
|
+
|
60
|
+
genai.configure(api_key=gemini_api_key)
|
61
|
+
model = genai.GenerativeModel('gemini-1.0-pro')
|
62
|
+
response = model.generate_content(message_list)
|
63
|
+
|
64
|
+
logger.log(f"Input Question ::: {email_body}\ngemini-1.0-pro Response::: {response} {type(response)}")
|
65
|
+
logger.log(f"\n\nResponse GeminiAI endpoint::::: {response} \n{type(response)}", "0")
|
66
|
+
|
67
|
+
final_result = ""
|
68
|
+
for part in response:
|
69
|
+
final_result = part.text
|
70
|
+
if final_result:
|
71
|
+
try:
|
72
|
+
final_result = final_result.replace("\\", "").replace('```', '').replace('json', '')
|
73
|
+
if final_result.startswith("{{") and final_result.endswith("}}"):
|
74
|
+
final_result = final_result[1:-1]
|
75
|
+
final_result = json.loads(final_result)
|
76
|
+
logger.log(f"finalResult::: {final_result}")
|
77
|
+
except json.JSONDecodeError:
|
78
|
+
logger.log(f"Exception : Invalid JSON Response GEMINI 1.5: {final_result} {type(final_result)}")
|
79
|
+
|
80
|
+
if detect_email_category:
|
81
|
+
category = final_result.get('category', 'Others')
|
82
|
+
return category
|
83
|
+
else:
|
84
|
+
logger.log(f"finalResult::: {final_result}")
|
85
|
+
return final_result
|
86
|
+
|
87
|
+
except Exception as e:
|
88
|
+
logger.log(f"Error with Gemini AI detection/generation: {str(e)}")
|
89
|
+
raise
|
90
|
+
|
91
|
+
def detect_category_local(self, openai_api_key, categories, email_body, detect_email_category=True, signature=None, local_ai_url=None):
|
92
|
+
logger.log("Inside detect_category_local::")
|
93
|
+
try:
|
94
|
+
categories_str = ', '.join(categories)
|
95
|
+
if detect_email_category:
|
96
|
+
message = [{
|
97
|
+
"role": "user",
|
98
|
+
"content": f"Classify the mail into one of the following categories: {categories_str} and Others. Based on the email content: {email_body}, provide ONLY the category in JSON format as {{\"category\": \"category\"}}."
|
99
|
+
}]
|
100
|
+
else:
|
101
|
+
message = [{
|
102
|
+
"role": "user",
|
103
|
+
"content": f"Create a reply for the email received from a customer. Include the email signature as {signature}\nDo not include any instruction as the output will be directly in a program."
|
104
|
+
}]
|
105
|
+
|
106
|
+
logger.log(f"Final Local AI message for detecting category::: {message}")
|
107
|
+
openai.api_key = openai_api_key
|
108
|
+
client = OpenAI(base_url=local_ai_url, api_key="lm-studio")
|
109
|
+
completion = client.chat.completions.create(
|
110
|
+
model="mistral",
|
111
|
+
messages=message,
|
112
|
+
temperature=0,
|
113
|
+
stream=False,
|
114
|
+
max_tokens=4096
|
115
|
+
)
|
116
|
+
|
117
|
+
final_result = str(completion.choices[0].message.content)
|
118
|
+
logger.log(f"\n\nInput Question ::: {email_body}\nLocalAI endpoint finalResult ::::: {final_result} \n{type(final_result)}", "0")
|
119
|
+
|
120
|
+
if detect_email_category:
|
121
|
+
try:
|
122
|
+
json_start = final_result.find("{")
|
123
|
+
json_end = final_result.rfind("}") + 1
|
124
|
+
if json_start != -1 and json_end != -1:
|
125
|
+
json_str = final_result[json_start:json_end]
|
126
|
+
final_result = json.loads(json_str)
|
127
|
+
logger.log(f"finalResult::: {final_result}")
|
128
|
+
category = final_result.get('category', 'Others')
|
129
|
+
logger.log(f"category::1037 {category}")
|
130
|
+
return category
|
131
|
+
else:
|
132
|
+
raise ValueError("No valid JSON object found in the response")
|
133
|
+
except json.JSONDecodeError as e:
|
134
|
+
logger.log(f"JSON decode error: {e}")
|
135
|
+
raise
|
136
|
+
else:
|
137
|
+
logger.log(f"finalResult:1040 {final_result}")
|
138
|
+
return final_result
|
139
|
+
|
140
|
+
except Exception as e:
|
141
|
+
logger.log(f"Error with LocalAI detection/generation: {str(e)}")
|
142
|
+
raise
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
import shutil
|
4
|
+
import requests
|
5
|
+
import loggerutility as logger
|
6
|
+
|
7
|
+
|
8
|
+
class Email_DocumentUploader:
|
9
|
+
def upload_document(self, upload_config, file_data):
|
10
|
+
try:
|
11
|
+
# Create temp directory if needed
|
12
|
+
temp_dir = "temp"
|
13
|
+
if not os.path.exists(temp_dir):
|
14
|
+
os.makedirs(temp_dir)
|
15
|
+
|
16
|
+
# Save file temporarily
|
17
|
+
file_path = os.path.join(temp_dir, file_data['filename'])
|
18
|
+
with open(file_path, 'wb') as f:
|
19
|
+
f.write(file_data['content'])
|
20
|
+
|
21
|
+
# Prepare headers and parameters
|
22
|
+
headers = {"TOKEN_ID": upload_config["token_id"]}
|
23
|
+
params = {}
|
24
|
+
|
25
|
+
param_fields = {
|
26
|
+
"DOCUMENT_TYPE": "document_type",
|
27
|
+
"OBJ_NAME": "obj_name",
|
28
|
+
"FILE_TYPE": "file_type",
|
29
|
+
"APP_ID": "app_id"
|
30
|
+
}
|
31
|
+
logger.log(f"param_fields:: {param_fields}")
|
32
|
+
|
33
|
+
for api_key, config_key in param_fields.items():
|
34
|
+
if config_key in upload_config and upload_config[config_key]:
|
35
|
+
params[api_key] = upload_config[config_key]
|
36
|
+
|
37
|
+
# Upload file
|
38
|
+
with open(file_path, 'rb') as file:
|
39
|
+
files = {'file': file}
|
40
|
+
response = requests.request(
|
41
|
+
upload_config["method"],
|
42
|
+
upload_config["url"],
|
43
|
+
headers=headers,
|
44
|
+
files=files,
|
45
|
+
data=params
|
46
|
+
)
|
47
|
+
|
48
|
+
if response.status_code == 200:
|
49
|
+
result = json.loads(response.text)
|
50
|
+
document_id = result["ID"]["Document_Id"]
|
51
|
+
return str(response.status_code), document_id
|
52
|
+
else:
|
53
|
+
return str(response.status_code), f"Upload failed: {response.text}"
|
54
|
+
|
55
|
+
except Exception as e:
|
56
|
+
print(f"Error uploading document: {str(e)}")
|
57
|
+
raise
|
58
|
+
finally:
|
59
|
+
# Cleanup
|
60
|
+
if os.path.exists(temp_dir):
|
61
|
+
shutil.rmtree(temp_dir)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import imaplib
|
2
|
+
import email
|
3
|
+
from email.message import Message
|
4
|
+
import datetime
|
5
|
+
import time
|
6
|
+
import loggerutility as logger
|
7
|
+
|
8
|
+
|
9
|
+
class Email_Drafter:
|
10
|
+
def draft_email(self, email_config, email_details, response_content):
|
11
|
+
try:
|
12
|
+
with imaplib.IMAP4_SSL(host=email_config['host'], port=imaplib.IMAP4_SSL_PORT) as imap_ssl:
|
13
|
+
imap_ssl.login(email_config['email'], email_config['password'])
|
14
|
+
logger.log(f"login successfully")
|
15
|
+
|
16
|
+
message = Message()
|
17
|
+
message["From"] = email_config['email']
|
18
|
+
message["To"] = email_details['sender']
|
19
|
+
message["CC"] = email_details['cc']
|
20
|
+
|
21
|
+
subject = email_details['subject']
|
22
|
+
if not subject.startswith("Re:"):
|
23
|
+
subject = f"Re: {subject}"
|
24
|
+
message["Subject"] = subject
|
25
|
+
|
26
|
+
mail_details = f'{datetime.datetime.now().strftime("On %a, %b %d, %Y at %I:%M %p")} {email_details["sender"]} wrote:'
|
27
|
+
message.set_payload(f"{response_content}\n\n{mail_details}\n\n{email_details['body']}")
|
28
|
+
|
29
|
+
utf8_message = str(message).encode("utf-8")
|
30
|
+
logger.log(f"utf8_message:: {utf8_message}")
|
31
|
+
imap_ssl.append("[Gmail]/Drafts", '', imaplib.Time2Internaldate(time.time()), utf8_message)
|
32
|
+
|
33
|
+
return True, utf8_message.decode("utf-8")
|
34
|
+
|
35
|
+
except Exception as e:
|
36
|
+
logger.log(f"Error creating draft: {str(e)}")
|
@@ -0,0 +1,75 @@
|
|
1
|
+
import imaplib
|
2
|
+
import email
|
3
|
+
import loggerutility as logger
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
class Email_Read:
|
8
|
+
def read_email(self, email_config):
|
9
|
+
"""
|
10
|
+
Reads emails from the inbox and returns relevant details
|
11
|
+
|
12
|
+
Args:
|
13
|
+
email_config (dict): Contains email configuration details
|
14
|
+
- host: IMAP host server
|
15
|
+
- port: IMAP port
|
16
|
+
- email: Email address
|
17
|
+
- password: Email password
|
18
|
+
|
19
|
+
Returns:
|
20
|
+
list: List of dictionaries containing email details
|
21
|
+
"""
|
22
|
+
try:
|
23
|
+
mail = imaplib.IMAP4_SSL(email_config['host'], email_config['port'])
|
24
|
+
mail.login(email_config['email'], email_config['password'])
|
25
|
+
logger.log("login successfully")
|
26
|
+
mail.select('inbox')
|
27
|
+
|
28
|
+
|
29
|
+
status, email_ids = mail.search(None, 'UNSEEN')
|
30
|
+
emails = []
|
31
|
+
|
32
|
+
if status == 'OK':
|
33
|
+
email_ids = email_ids[0].split()
|
34
|
+
|
35
|
+
for email_id in email_ids:
|
36
|
+
email_body = ""
|
37
|
+
status, data = mail.fetch(email_id, '(RFC822)')
|
38
|
+
|
39
|
+
if status == 'OK':
|
40
|
+
raw_email = data[0][1]
|
41
|
+
msg = email.message_from_bytes(raw_email)
|
42
|
+
|
43
|
+
sender_email = msg['From']
|
44
|
+
cc_email = msg['CC']
|
45
|
+
bcc_email = msg['BCC']
|
46
|
+
subject = msg['Subject']
|
47
|
+
|
48
|
+
# Extract email body
|
49
|
+
if msg.is_multipart():
|
50
|
+
for part in msg.walk():
|
51
|
+
if part.get_content_type() == "text/plain":
|
52
|
+
email_body += part.get_payload(decode=True).decode()
|
53
|
+
else:
|
54
|
+
email_body = msg.get_payload(decode=True).decode()
|
55
|
+
|
56
|
+
emails.append({
|
57
|
+
'id': email_id,
|
58
|
+
'sender': sender_email,
|
59
|
+
'cc': cc_email,
|
60
|
+
'bcc': bcc_email,
|
61
|
+
'subject': subject,
|
62
|
+
'body': email_body
|
63
|
+
})
|
64
|
+
# logger.log(f"emails:: {emails}")
|
65
|
+
return emails
|
66
|
+
|
67
|
+
except Exception as e:
|
68
|
+
logger.log(f"Error reading emails: {str(e)}")
|
69
|
+
raise
|
70
|
+
finally:
|
71
|
+
try:
|
72
|
+
mail.close()
|
73
|
+
mail.logout()
|
74
|
+
except:
|
75
|
+
pass
|
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright 2022 Proteus Technology PVT. LTD.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: AIEmailAutomationUtility
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: Created class for read_email, draft_response,catrgory_detect and upload document
|
5
|
+
Author: Proteus Technology PVT. LTD.
|
6
|
+
Author-email: <apps@baseinformation.com>
|
7
|
+
Keywords: python,first package
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
9
|
+
Classifier: Intended Audience :: Education
|
10
|
+
Classifier: Programming Language :: Python :: 2
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
12
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
13
|
+
Classifier: Operating System :: Microsoft :: Windows
|
14
|
+
License-File: LICENCE.txt
|
15
|
+
|
16
|
+
Created class for read_email, draft_response,catrgory_detect and upload document
|
@@ -0,0 +1,10 @@
|
|
1
|
+
AIEmailAutomationUtility/Email_CategoryDetector.py,sha256=E2BRB57etINb1FjP6hODj7pS1Jj4UCoaCKj3Ic-N8_Y,6779
|
2
|
+
AIEmailAutomationUtility/Email_DocumentUploader.py,sha256=VAfpoH7JD6B2cYQDTatK0wzQhhjtNnoB5tpMI8h4BOU,2148
|
3
|
+
AIEmailAutomationUtility/Email_Drafter.py,sha256=YSD2tWPHyvJKqTjTuQyXbRJO_j6I7znnr5OPKG0nPGg,1574
|
4
|
+
AIEmailAutomationUtility/Email_Read.py,sha256=OfqDmyBGRgLDWNbZbl2bxaTjSa9gydaIO8R6nAuByPw,2698
|
5
|
+
AIEmailAutomationUtility/__init__.py,sha256=JpyFFoYlqLdoj9pe4AkoP_pXy5vbwAZKxEcHshuRHUg,194
|
6
|
+
AIEmailAutomationUtility-0.0.1.dist-info/LICENCE.txt,sha256=2qX9IkEUBx0VJp1Vh9O2dsRwE-IpYId0lXDyn7OVsJ8,1073
|
7
|
+
AIEmailAutomationUtility-0.0.1.dist-info/METADATA,sha256=gH3wC2s4h_Heek-D2FTPUaVEOwaHKFSOict-hFVxNVY,660
|
8
|
+
AIEmailAutomationUtility-0.0.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
9
|
+
AIEmailAutomationUtility-0.0.1.dist-info/top_level.txt,sha256=3jTWrTUblVkaP7mpwY2UBSnrlfot5Ykpfsehyke-Uzw,25
|
10
|
+
AIEmailAutomationUtility-0.0.1.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
AIEmailAutomationUtility
|