AIEmailAutomationUtility 0.0.4__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/EmailReplyAssistant.py +96 -0
- AIEmailAutomationUtility/Email_Classification.py +191 -0
- AIEmailAutomationUtility/Email_DocumentUploader.py +101 -0
- AIEmailAutomationUtility/Email_Draft.py +121 -0
- AIEmailAutomationUtility/Email_Read.py +145 -0
- AIEmailAutomationUtility/Email_Upload_Document.py +41 -0
- AIEmailAutomationUtility/Save_Transaction.py +95 -0
- AIEmailAutomationUtility/__init__.py +8 -0
- AIEmailAutomationUtility-0.0.4.dist-info/LICENCE.txt +7 -0
- AIEmailAutomationUtility-0.0.4.dist-info/METADATA +16 -0
- AIEmailAutomationUtility-0.0.4.dist-info/RECORD +13 -0
- AIEmailAutomationUtility-0.0.4.dist-info/WHEEL +5 -0
- AIEmailAutomationUtility-0.0.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
import traceback
|
2
|
+
from openai import OpenAI
|
3
|
+
from openai import OpenAI, AssistantEventHandler
|
4
|
+
from flask import Flask,request
|
5
|
+
import json
|
6
|
+
import loggerutility as logger
|
7
|
+
|
8
|
+
class EmailReplyAssistant:
|
9
|
+
def __init__(self):
|
10
|
+
pass
|
11
|
+
|
12
|
+
def Reply_Email_Ai_Assistant(self, openAI_key, assistant_ID, email_content, subject):
|
13
|
+
try:
|
14
|
+
class EventHandler(AssistantEventHandler):
|
15
|
+
def __init__(self):
|
16
|
+
super().__init__()
|
17
|
+
self.delta_values = []
|
18
|
+
|
19
|
+
def on_text_created(self, text):
|
20
|
+
if isinstance(text, str):
|
21
|
+
print(f"\nAssistant: {text}", end="", flush=True)
|
22
|
+
|
23
|
+
def on_text_delta(self, delta, snapshot):
|
24
|
+
self.delta_values.append(delta.value)
|
25
|
+
|
26
|
+
def on_tool_call_created(self, tool_call):
|
27
|
+
print(f"\nAssistant: {tool_call.type}\n", flush=True)
|
28
|
+
|
29
|
+
def on_tool_call_delta(self, delta, snapshot):
|
30
|
+
if delta.type == 'code_interpreter':
|
31
|
+
if delta.code_interpreter.input:
|
32
|
+
print(delta.code_interpreter.input, end="", flush=True)
|
33
|
+
if delta.code_interpreter.outputs:
|
34
|
+
print(f"\n\nOutput >", flush=True)
|
35
|
+
for output in delta.code_interpreter.outputs:
|
36
|
+
if output.type == "logs":
|
37
|
+
print(output.logs, flush=True)
|
38
|
+
|
39
|
+
openAI_response = ""
|
40
|
+
client = OpenAI(api_key=openAI_key)
|
41
|
+
thread = client.beta.threads.create()
|
42
|
+
|
43
|
+
client.beta.threads.messages.create(
|
44
|
+
thread_id=thread.id,
|
45
|
+
role="user",
|
46
|
+
content=f"subject:{subject}\nemail body:{email_content}",
|
47
|
+
)
|
48
|
+
|
49
|
+
event_handler = EventHandler()
|
50
|
+
|
51
|
+
with client.beta.threads.runs.stream(
|
52
|
+
thread_id=thread.id,
|
53
|
+
assistant_id=assistant_ID,
|
54
|
+
instructions=f"Create a reply for the email received from a customer.\nDo not include any instruction as the output will be directly in a program.",
|
55
|
+
event_handler=event_handler,
|
56
|
+
) as stream:
|
57
|
+
stream.until_done()
|
58
|
+
|
59
|
+
delta_values = event_handler.delta_values
|
60
|
+
openAI_response = ''.join(delta_values)
|
61
|
+
logger.log(f"openAI_response:: {type(openAI_response)}")
|
62
|
+
logger.log(f"openAI_response:: {openAI_response}")
|
63
|
+
return {"status": "Success", "message": openAI_response}
|
64
|
+
|
65
|
+
except Exception as error:
|
66
|
+
responseStr = "<br/><br/>" + str(error)
|
67
|
+
trace = traceback.format_exc()
|
68
|
+
print(f"Exception in process_Email: {responseStr} \n {trace} \n DataType ::: {type(responseStr)}")
|
69
|
+
|
70
|
+
def email_reply_assitant(self):
|
71
|
+
try:
|
72
|
+
# while True:
|
73
|
+
data = request.get_data('jsonData', None)
|
74
|
+
data = json.loads(data[9:])
|
75
|
+
logger.log(f"jsondata:: {data}")
|
76
|
+
|
77
|
+
openAI_key = data.get("openAI_key")
|
78
|
+
assistant_ID = data.get("assistant_ID")
|
79
|
+
email_content = data.get("email_content")
|
80
|
+
subject = data.get("subject")
|
81
|
+
|
82
|
+
if not all([openAI_key, assistant_ID, email_content, subject]):
|
83
|
+
raise ValueError("Missing required email configuration fields.")
|
84
|
+
|
85
|
+
email_response = self.Reply_Email_Ai_Assistant(
|
86
|
+
openAI_key=openAI_key,
|
87
|
+
assistant_ID=assistant_ID,
|
88
|
+
email_content=email_content,
|
89
|
+
subject=subject
|
90
|
+
)
|
91
|
+
|
92
|
+
logger.log(f"Reply_Email_Ai_Assistant response: {email_response}")
|
93
|
+
return email_response
|
94
|
+
|
95
|
+
except Exception as e:
|
96
|
+
logger.log(f"Error in Read_Email: {str(e)}")
|
@@ -0,0 +1,191 @@
|
|
1
|
+
import json
|
2
|
+
from openai import OpenAI
|
3
|
+
import google.generativeai as genai
|
4
|
+
import openai
|
5
|
+
import loggerutility as logger
|
6
|
+
from flask import Flask,request
|
7
|
+
|
8
|
+
class Email_Classification:
|
9
|
+
def detect_category_openai(self, openai_api_key, categories, email_body):
|
10
|
+
logger.log("Inside detect_category_openai::")
|
11
|
+
try:
|
12
|
+
categories_str = ', '.join(categories)
|
13
|
+
message = [{
|
14
|
+
"role": "user",
|
15
|
+
"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\"}}."
|
16
|
+
}]
|
17
|
+
|
18
|
+
logger.log(f"Final GPT message for detecting category::: {message}")
|
19
|
+
client = OpenAI(api_key=openai_api_key)
|
20
|
+
result = client.chat.completions.create(
|
21
|
+
model="gpt-4o-mini",
|
22
|
+
messages=message,
|
23
|
+
temperature=0,
|
24
|
+
max_tokens=1800,
|
25
|
+
top_p=1,
|
26
|
+
frequency_penalty=0,
|
27
|
+
presence_penalty=0,
|
28
|
+
)
|
29
|
+
category = json.loads(result.choices[0].message.content)['category']
|
30
|
+
logger.log(f"category:: {category}")
|
31
|
+
return {"status": "Success", "message": category}
|
32
|
+
except Exception as e:
|
33
|
+
logger.log(f"Error detecting category with OpenAI: {str(e)}")
|
34
|
+
return {"success": "Failed", "message": f"Error detecting category with OpenAI: {str(e)}"}
|
35
|
+
|
36
|
+
def detect_category_gemini(self, gemini_api_key, categories, email_body, detect_email_category=True, signature=None):
|
37
|
+
logger.log("Inside detect_category_gemini::")
|
38
|
+
try:
|
39
|
+
categories_str = ', '.join(categories)
|
40
|
+
if detect_email_category:
|
41
|
+
message = [{
|
42
|
+
"role": "user",
|
43
|
+
"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\"}}."
|
44
|
+
}]
|
45
|
+
else:
|
46
|
+
message = [{
|
47
|
+
"role": "user",
|
48
|
+
"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."
|
49
|
+
}]
|
50
|
+
|
51
|
+
logger.log(f"Final Gemini AI message for detecting category::: {message}")
|
52
|
+
message_list = str(message)
|
53
|
+
|
54
|
+
generation_config = {
|
55
|
+
"temperature": 0,
|
56
|
+
"top_p": 1,
|
57
|
+
"top_k": 1,
|
58
|
+
"max_output_tokens": 2048,
|
59
|
+
}
|
60
|
+
|
61
|
+
genai.configure(api_key=gemini_api_key)
|
62
|
+
model = genai.GenerativeModel('gemini-1.0-pro')
|
63
|
+
response = model.generate_content(message_list)
|
64
|
+
|
65
|
+
logger.log(f"Input Question ::: {email_body}\ngemini-1.0-pro Response::: {response} {type(response)}")
|
66
|
+
logger.log(f"\n\nResponse GeminiAI endpoint::::: {response} \n{type(response)}", "0")
|
67
|
+
|
68
|
+
final_result = ""
|
69
|
+
for part in response:
|
70
|
+
final_result = part.text
|
71
|
+
if final_result:
|
72
|
+
try:
|
73
|
+
final_result = final_result.replace("\\", "").replace('```', '').replace('json', '')
|
74
|
+
if final_result.startswith("{{") and final_result.endswith("}}"):
|
75
|
+
final_result = final_result[1:-1]
|
76
|
+
final_result = json.loads(final_result)
|
77
|
+
logger.log(f"finalResult::: {final_result}")
|
78
|
+
except json.JSONDecodeError:
|
79
|
+
logger.log(f"Exception : Invalid JSON Response GEMINI 1.5: {final_result} {type(final_result)}")
|
80
|
+
|
81
|
+
if detect_email_category:
|
82
|
+
category = final_result.get('category', 'Others')
|
83
|
+
return {"status": "Success", "message": category}
|
84
|
+
else:
|
85
|
+
logger.log(f"finalResult::: {final_result}")
|
86
|
+
return {"status": "Success", "message": final_result}
|
87
|
+
|
88
|
+
except Exception as e:
|
89
|
+
logger.log(f"Error with Gemini AI detection/generation: {str(e)}")
|
90
|
+
return {"success": "Failed", "message": f"Error with Gemini AI detection/generation: {str(e)}"}
|
91
|
+
|
92
|
+
def detect_category_local(self, openai_api_key, categories, email_body, detect_email_category=True, signature=None, local_ai_url=None):
|
93
|
+
logger.log("Inside detect_category_local::")
|
94
|
+
try:
|
95
|
+
categories_str = ', '.join(categories)
|
96
|
+
if detect_email_category:
|
97
|
+
message = [{
|
98
|
+
"role": "user",
|
99
|
+
"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\"}}."
|
100
|
+
}]
|
101
|
+
else:
|
102
|
+
message = [{
|
103
|
+
"role": "user",
|
104
|
+
"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."
|
105
|
+
}]
|
106
|
+
|
107
|
+
logger.log(f"Final Local AI message for detecting category::: {message}")
|
108
|
+
openai.api_key = openai_api_key
|
109
|
+
client = OpenAI(base_url=local_ai_url, api_key="lm-studio")
|
110
|
+
completion = client.chat.completions.create(
|
111
|
+
model="mistral",
|
112
|
+
messages=message,
|
113
|
+
temperature=0,
|
114
|
+
stream=False,
|
115
|
+
max_tokens=4096
|
116
|
+
)
|
117
|
+
|
118
|
+
final_result = str(completion.choices[0].message.content)
|
119
|
+
logger.log(f"\n\nInput Question ::: {email_body}\nLocalAI endpoint finalResult ::::: {final_result} \n{type(final_result)}", "0")
|
120
|
+
|
121
|
+
if detect_email_category:
|
122
|
+
try:
|
123
|
+
json_start = final_result.find("{")
|
124
|
+
json_end = final_result.rfind("}") + 1
|
125
|
+
if json_start != -1 and json_end != -1:
|
126
|
+
json_str = final_result[json_start:json_end]
|
127
|
+
final_result = json.loads(json_str)
|
128
|
+
logger.log(f"finalResult::: {final_result}")
|
129
|
+
category = final_result.get('category', 'Others')
|
130
|
+
logger.log(f"category::1037 {category}")
|
131
|
+
return {"status": "Success", "message": category}
|
132
|
+
else:
|
133
|
+
raise ValueError("No valid JSON object found in the response")
|
134
|
+
except json.JSONDecodeError as e:
|
135
|
+
logger.log(f"JSON decode error: {e}")
|
136
|
+
raise
|
137
|
+
else:
|
138
|
+
logger.log(f"finalResult:1040 {final_result}")
|
139
|
+
return {"status": "Success", "message": final_result}
|
140
|
+
|
141
|
+
except Exception as e:
|
142
|
+
logger.log(f"Error with LocalAI detection/generation: {str(e)}")
|
143
|
+
return {"success": "Failed", "message": f"Error with LocalAI detection/generation: {str(e)}"}
|
144
|
+
|
145
|
+
def detect_category(self):
|
146
|
+
try:
|
147
|
+
data = request.get_data('jsonData', None)
|
148
|
+
data = json.loads(data[9:])
|
149
|
+
logger.log(f"jsondata:: {data}")
|
150
|
+
model_type = data.get('model_type', 'OpenAI')
|
151
|
+
|
152
|
+
required_fields = ['email_body', 'categories']
|
153
|
+
if model_type == 'OpenAI':
|
154
|
+
required_fields.append('openai_api_key')
|
155
|
+
elif model_type == 'GeminiAI':
|
156
|
+
required_fields.append('gemini_api_key')
|
157
|
+
elif model_type == 'LocalAI':
|
158
|
+
required_fields.extend(['openai_api_key', 'local_ai_url'])
|
159
|
+
|
160
|
+
if model_type == 'OpenAI':
|
161
|
+
response = self.detect_category_openai(
|
162
|
+
openai_api_key=data['openai_api_key'],
|
163
|
+
categories=data['categories'],
|
164
|
+
email_body=data['email_body']
|
165
|
+
)
|
166
|
+
elif model_type == 'GeminiAI':
|
167
|
+
response = self.detect_category_gemini(
|
168
|
+
gemini_api_key=data['gemini_api_key'],
|
169
|
+
categories=data['categories'],
|
170
|
+
email_body=data['email_body'],
|
171
|
+
detect_email_category=data.get('Detect_Email_category', True),
|
172
|
+
signature=data.get('signature', '')
|
173
|
+
)
|
174
|
+
elif model_type == 'LocalAI':
|
175
|
+
response = self.detect_category_local(
|
176
|
+
openai_api_key=data['openai_api_key'],
|
177
|
+
categories=data['categories'],
|
178
|
+
email_body=data['email_body'],
|
179
|
+
detect_email_category=data.get('Detect_Email_category', True),
|
180
|
+
signature=data.get('signature', ''),
|
181
|
+
local_ai_url=data['local_ai_url']
|
182
|
+
)
|
183
|
+
else:
|
184
|
+
return {"status": "Failed", "message": f"Invalid model_type: {model_type}"}
|
185
|
+
|
186
|
+
logger.log(f"Detect_Category response: {response}")
|
187
|
+
return response
|
188
|
+
|
189
|
+
except Exception as e:
|
190
|
+
logger.log(f"Error in Detect_Category: {str(e)}")
|
191
|
+
return e
|
@@ -0,0 +1,101 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
import shutil
|
4
|
+
import requests
|
5
|
+
import loggerutility as logger
|
6
|
+
from flask import Flask,request
|
7
|
+
|
8
|
+
|
9
|
+
class Email_DocumentUploader:
|
10
|
+
def upload_document(self, upload_config, file_data):
|
11
|
+
# try:
|
12
|
+
logger.log("inside function" )
|
13
|
+
# Create temp directory if needed
|
14
|
+
temp_dir = "temp"
|
15
|
+
if not os.path.exists(temp_dir):
|
16
|
+
os.makedirs(temp_dir)
|
17
|
+
|
18
|
+
# Save file temporarily
|
19
|
+
file_path = os.path.join(temp_dir, file_data['filename'])
|
20
|
+
logger.log(f"file_path:: {file_path}")
|
21
|
+
with open(file_path, 'wb') as f:
|
22
|
+
f.write(file_data['content'])
|
23
|
+
|
24
|
+
# Prepare headers and parameters
|
25
|
+
headers = {"TOKEN_ID": upload_config["token_id"]}
|
26
|
+
params = {}
|
27
|
+
|
28
|
+
param_fields = {
|
29
|
+
"DOCUMENT_TYPE": "document_type",
|
30
|
+
"OBJ_NAME": "obj_name",
|
31
|
+
"FILE_TYPE": "file_type",
|
32
|
+
"APP_ID": "app_id"
|
33
|
+
}
|
34
|
+
logger.log(f"param_fields:: {param_fields}")
|
35
|
+
|
36
|
+
for api_key, config_key in param_fields.items():
|
37
|
+
if config_key in upload_config and upload_config[config_key]:
|
38
|
+
params[api_key] = upload_config[config_key]
|
39
|
+
|
40
|
+
# Upload file
|
41
|
+
with open(file_path, 'rb') as file:
|
42
|
+
files = {'file': file}
|
43
|
+
response = requests.request(
|
44
|
+
upload_config["method"],
|
45
|
+
upload_config["url"],
|
46
|
+
headers=headers,
|
47
|
+
files=files,
|
48
|
+
data=params
|
49
|
+
)
|
50
|
+
logger.log("file read")
|
51
|
+
|
52
|
+
if response.status_code == 200:
|
53
|
+
result = json.loads(response.text)
|
54
|
+
document_id = result["ID"]["Document_Id"]
|
55
|
+
return str(response.status_code), document_id
|
56
|
+
else:
|
57
|
+
return str(response.status_code), f"Upload failed: {response.text}"
|
58
|
+
|
59
|
+
# except Exception as e:
|
60
|
+
# logger.log(f"Error uploading document: {str(e)}")
|
61
|
+
# raise
|
62
|
+
# finally:
|
63
|
+
# # Cleanup
|
64
|
+
# if os.path.exists(temp_dir):
|
65
|
+
# shutil.rmtree(temp_dir)
|
66
|
+
|
67
|
+
def email_document_upload(self):
|
68
|
+
try:
|
69
|
+
if 'file' not in request.files:
|
70
|
+
return "file not found"
|
71
|
+
|
72
|
+
file = request.files['file']
|
73
|
+
if file.filename == '':
|
74
|
+
return "No selected file"
|
75
|
+
|
76
|
+
upload_config = {
|
77
|
+
'token_id': request.form.get('TOKEN_ID'),
|
78
|
+
'document_type': request.form.get('DOCUMENT_TYPE', ''),
|
79
|
+
'obj_name': request.form.get('OBJ_NAME', ''),
|
80
|
+
'file_type': request.form.get('FILE_TYPE', ''),
|
81
|
+
'app_id': request.form.get('APP_ID', ''),
|
82
|
+
'method': request.form.get('Method_Type', 'POST'),
|
83
|
+
'url': request.form.get('RestAPI_Url')
|
84
|
+
}
|
85
|
+
|
86
|
+
# Validate required fields
|
87
|
+
if not upload_config['token_id'] or not upload_config['url']:
|
88
|
+
return "Missing required fields: TOKEN_ID or RestAPI_Url"
|
89
|
+
|
90
|
+
file_data = {
|
91
|
+
'filename': file.filename,
|
92
|
+
'content': file.read()
|
93
|
+
}
|
94
|
+
|
95
|
+
result = self.upload_document(upload_config, file_data)
|
96
|
+
|
97
|
+
logger.log(f"Upload_Document response result: {result}")
|
98
|
+
return "success"
|
99
|
+
|
100
|
+
except Exception as e:
|
101
|
+
logger.log(f"Error in Upload_Document: {str(e)}")
|
@@ -0,0 +1,121 @@
|
|
1
|
+
import imaplib
|
2
|
+
import email
|
3
|
+
from email.message import Message
|
4
|
+
import datetime
|
5
|
+
import time
|
6
|
+
import loggerutility as logger
|
7
|
+
from flask import Flask,request
|
8
|
+
import json
|
9
|
+
|
10
|
+
class Email_Draft:
|
11
|
+
def draft_email(self, email_config, email_details, response_content):
|
12
|
+
try:
|
13
|
+
with imaplib.IMAP4_SSL(host=email_config['host'], port=imaplib.IMAP4_SSL_PORT) as imap_ssl:
|
14
|
+
imap_ssl.login(email_config['email'], email_config['password'])
|
15
|
+
logger.log(f"login successfully")
|
16
|
+
|
17
|
+
message = Message()
|
18
|
+
message["From"] = email_config['email']
|
19
|
+
message["To"] = email_details['sender']
|
20
|
+
message["CC"] = email_details['cc']
|
21
|
+
|
22
|
+
subject = email_details['subject']
|
23
|
+
if not subject.startswith("Re:"):
|
24
|
+
subject = f"Re: {subject}"
|
25
|
+
message["Subject"] = subject
|
26
|
+
|
27
|
+
mail_details = f'{datetime.datetime.now().strftime("On %a, %b %d, %Y at %I:%M %p")} {email_details["sender"]} wrote:'
|
28
|
+
message.set_payload(f"{response_content}\n\n{mail_details}\n\n{email_details['body']}")
|
29
|
+
|
30
|
+
utf8_message = str(message).encode("utf-8")
|
31
|
+
logger.log(f"utf8_message:: {utf8_message}")
|
32
|
+
imap_ssl.append("[Gmail]/Drafts", '', imaplib.Time2Internaldate(time.time()), utf8_message)
|
33
|
+
|
34
|
+
return True, utf8_message.decode("utf-8")
|
35
|
+
|
36
|
+
except Exception as e:
|
37
|
+
logger.log(f"Error creating draft: {str(e)}")
|
38
|
+
|
39
|
+
def draft_email_response(self, email_details):
|
40
|
+
try:
|
41
|
+
logger.log("Creating draft email with the following details:")
|
42
|
+
logger.log(f"From: {email_details.get('from')}")
|
43
|
+
logger.log(f"To: {email_details.get('to')}")
|
44
|
+
logger.log(f"CC: {email_details.get('cc')}")
|
45
|
+
logger.log(f"Subject: {email_details.get('subject')}")
|
46
|
+
logger.log(f"Body: {email_details.get('body')}")
|
47
|
+
|
48
|
+
return "Success", {
|
49
|
+
"from": email_details['from'],
|
50
|
+
"to": email_details['to'],
|
51
|
+
"cc": email_details.get('cc', ""),
|
52
|
+
"subject": email_details['subject'],
|
53
|
+
"body": email_details['body']
|
54
|
+
}
|
55
|
+
|
56
|
+
except Exception as e:
|
57
|
+
logger.log(f"Error creating draft: {str(e)}")
|
58
|
+
return "Failed", None
|
59
|
+
|
60
|
+
def draft_mail(self):
|
61
|
+
try:
|
62
|
+
data = request.get_data('jsonData', None)
|
63
|
+
data = json.loads(data[9:])
|
64
|
+
logger.log(f"jsondata:: {data}")
|
65
|
+
|
66
|
+
if "reciever_email_addr" in data and data["reciever_email_addr"] != None:
|
67
|
+
reciever_email_addr = data["reciever_email_addr"]
|
68
|
+
logger.log(f"\nInside reciever_email_addr value:::\t{reciever_email_addr} \t{type(reciever_email_addr)}","0")
|
69
|
+
|
70
|
+
if "receiver_email_pwd" in data and data["receiver_email_pwd"] != None:
|
71
|
+
receiver_email_pwd = data["receiver_email_pwd"]
|
72
|
+
logger.log(f"\nInside receiver_email_pwd value:::\t{receiver_email_pwd} \t{type(receiver_email_pwd)}","0")
|
73
|
+
|
74
|
+
if "host_name" in data and data["host_name"] != None:
|
75
|
+
host_name = data["host_name"]
|
76
|
+
logger.log(f"\nInside host_name value:::\t{host_name} \t{type(host_name)}","0")
|
77
|
+
|
78
|
+
if "sender_email_addr" in data and data["sender_email_addr"] != None:
|
79
|
+
sender_email_addr = data["sender_email_addr"]
|
80
|
+
logger.log(f"\nInside sender_email_addr value:::\t{sender_email_addr} \t{type(sender_email_addr)}","0")
|
81
|
+
|
82
|
+
if "cc_email_addr" in data and data["cc_email_addr"] != None:
|
83
|
+
cc_email_addr = data["cc_email_addr"]
|
84
|
+
logger.log(f"\nInside cc_email_addr value:::\t{cc_email_addr} \t{type(cc_email_addr)}","0")
|
85
|
+
|
86
|
+
if "subject" in data and data["subject"] != None:
|
87
|
+
subject = data["subject"]
|
88
|
+
logger.log(f"\nInside subject value:::\t{subject} \t{type(subject)}","0")
|
89
|
+
|
90
|
+
if "email_body" in data and data["email_body"] != None:
|
91
|
+
email_body = data["email_body"]
|
92
|
+
logger.log(f"\nInside email_body value:::\t{email_body} \t{type(email_body)}","0")
|
93
|
+
|
94
|
+
if "signature" in data and data["signature"] != None:
|
95
|
+
signature = data["signature"]
|
96
|
+
logger.log(f"\nInside signature value:::\t{signature} \t{type(signature)}","0")
|
97
|
+
|
98
|
+
|
99
|
+
email_config = {
|
100
|
+
"email": data["reciever_email_addr"],
|
101
|
+
"password": data["receiver_email_pwd"],
|
102
|
+
"host": data["host_name"]
|
103
|
+
}
|
104
|
+
logger.log(f"data::{data}")
|
105
|
+
email_details = {
|
106
|
+
"from": data["sender_email_addr"],
|
107
|
+
"to":data["reciever_email_addr"],
|
108
|
+
"cc": cc_email_addr,
|
109
|
+
"subject": data["subject"],
|
110
|
+
"body": data["email_body"],
|
111
|
+
"signature": data["signature"]
|
112
|
+
}
|
113
|
+
|
114
|
+
success, draft_message = self.draft_email_response(email_details)
|
115
|
+
|
116
|
+
if success == "Success":
|
117
|
+
logger.log(f"draft_message {draft_message}")
|
118
|
+
return draft_message
|
119
|
+
|
120
|
+
except Exception as e:
|
121
|
+
logger.log(f"Error in Draft_Save: {str(e)}")
|
@@ -0,0 +1,145 @@
|
|
1
|
+
import imaplib
|
2
|
+
import email
|
3
|
+
import os
|
4
|
+
import json
|
5
|
+
import time
|
6
|
+
import loggerutility as logger
|
7
|
+
from flask import Flask, request
|
8
|
+
from .Save_Transaction import Save_Transaction
|
9
|
+
from .Email_Upload_Document import Email_Upload_Document
|
10
|
+
|
11
|
+
class Email_Read:
|
12
|
+
def read_email(self, email_config):
|
13
|
+
try:
|
14
|
+
logger.log("inside function")
|
15
|
+
mail = imaplib.IMAP4_SSL(email_config['host'], email_config['port'])
|
16
|
+
mail.login(email_config['email'], email_config['password'])
|
17
|
+
logger.log("login successfully")
|
18
|
+
mail.select('inbox')
|
19
|
+
|
20
|
+
while True:
|
21
|
+
status, email_ids = mail.search(None, 'UNSEEN')
|
22
|
+
emails = []
|
23
|
+
|
24
|
+
if status == 'OK':
|
25
|
+
email_ids = email_ids[0].split()
|
26
|
+
|
27
|
+
if not email_ids:
|
28
|
+
logger.log("Email not found, going to check new mail")
|
29
|
+
print("Email not found,\ngoing to check new mail \n")
|
30
|
+
else:
|
31
|
+
|
32
|
+
for email_id in email_ids:
|
33
|
+
email_body = ""
|
34
|
+
attachments = []
|
35
|
+
status, data = mail.fetch(email_id, '(RFC822)')
|
36
|
+
|
37
|
+
if status == 'OK':
|
38
|
+
raw_email = data[0][1]
|
39
|
+
msg = email.message_from_bytes(raw_email)
|
40
|
+
|
41
|
+
sender_email = msg['From']
|
42
|
+
cc_email = msg['CC']
|
43
|
+
subject = msg['Subject']
|
44
|
+
to = msg['To']
|
45
|
+
|
46
|
+
if msg.is_multipart():
|
47
|
+
for part in msg.walk():
|
48
|
+
content_type = part.get_content_type()
|
49
|
+
if content_type == "text/plain":
|
50
|
+
email_body += part.get_payload(decode=True).decode('utf-8', errors='replace')
|
51
|
+
else:
|
52
|
+
email_body = msg.get_payload(decode=True).decode('utf-8', errors='replace')
|
53
|
+
|
54
|
+
email_data = {
|
55
|
+
"email_id": email_id,
|
56
|
+
"from": sender_email,
|
57
|
+
"to": to,
|
58
|
+
"cc": cc_email,
|
59
|
+
"subject": subject,
|
60
|
+
"body": email_body
|
61
|
+
}
|
62
|
+
emails.append(email_data)
|
63
|
+
logger.log(f"emails:: {emails}")
|
64
|
+
call_save_transaction = Save_Transaction()
|
65
|
+
save_transaction_response = call_save_transaction.email_save_transaction(email_data)
|
66
|
+
logger.log(f"save_transaction_response:: {save_transaction_response}")
|
67
|
+
|
68
|
+
if save_transaction_response['status'] == "Success":
|
69
|
+
if msg.is_multipart():
|
70
|
+
for part in msg.walk():
|
71
|
+
content_disposition = str(part.get("Content-Disposition"))
|
72
|
+
|
73
|
+
if "attachment" in content_disposition:
|
74
|
+
attachment_path = self.save_attachment(part, "attachments")
|
75
|
+
if attachment_path:
|
76
|
+
logger.log(f"Attachment saved at: {attachment_path}")
|
77
|
+
attachments.append(attachment_path)
|
78
|
+
for file_path in attachments:
|
79
|
+
logger.log(f"file_path:: {file_path}")
|
80
|
+
email_upload_doc = Email_Upload_Document()
|
81
|
+
email_file_upload = email_upload_doc.upload_files(file_path)
|
82
|
+
logger.log(f"file uploaded :: {email_file_upload}")
|
83
|
+
logger.log(f"emails:: {emails}")
|
84
|
+
time.sleep(10)
|
85
|
+
|
86
|
+
except Exception as e:
|
87
|
+
return {"success": "Failed", "message": f"Error reading emails: {str(e)}"}
|
88
|
+
finally:
|
89
|
+
try:
|
90
|
+
mail.close()
|
91
|
+
mail.logout()
|
92
|
+
except Exception as close_error:
|
93
|
+
logger.log(f"Error during mail close/logout: {str(close_error)}")
|
94
|
+
|
95
|
+
def save_attachment(self, part, download_dir):
|
96
|
+
try:
|
97
|
+
filename = part.get_filename()
|
98
|
+
if filename:
|
99
|
+
# Create the directory if it doesn't exist
|
100
|
+
if not os.path.exists(download_dir):
|
101
|
+
os.makedirs(download_dir)
|
102
|
+
|
103
|
+
file_path = os.path.join(download_dir, filename)
|
104
|
+
with open(file_path, 'wb') as f:
|
105
|
+
f.write(part.get_payload(decode=True))
|
106
|
+
|
107
|
+
logger.log(f"Attachment saved: {file_path}")
|
108
|
+
return file_path
|
109
|
+
except Exception as e:
|
110
|
+
return {"success": "Failed", "message": f"Error saving attachment: {str(e)}"}
|
111
|
+
|
112
|
+
def Read_Email(self):
|
113
|
+
try:
|
114
|
+
data = request.get_data('jsonData', None)
|
115
|
+
data = json.loads(data[9:])
|
116
|
+
logger.log(f"jsondata:: {data}")
|
117
|
+
|
118
|
+
reciever_email_addr = data.get("reciever_email_addr")
|
119
|
+
receiver_email_pwd = data.get("receiver_email_pwd")
|
120
|
+
host = data.get("host")
|
121
|
+
port = data.get("port")
|
122
|
+
|
123
|
+
if not all([reciever_email_addr, receiver_email_pwd, host, port]):
|
124
|
+
raise ValueError("Missing required email configuration fields.")
|
125
|
+
|
126
|
+
logger.log(f"\nReceiver Email Address: {reciever_email_addr}\t{type(reciever_email_addr)}", "0")
|
127
|
+
logger.log(f"\nReceiver Email Password: {receiver_email_pwd}\t{type(receiver_email_pwd)}", "0")
|
128
|
+
logger.log(f"\nHost: {host}\t{type(host)}", "0")
|
129
|
+
logger.log(f"\nPort: {port}\t{type(port)}", "0")
|
130
|
+
|
131
|
+
email_config = {
|
132
|
+
'email': reciever_email_addr,
|
133
|
+
'password': receiver_email_pwd,
|
134
|
+
'host': host,
|
135
|
+
'port': int(port)
|
136
|
+
}
|
137
|
+
|
138
|
+
emails = self.read_email(email_config)
|
139
|
+
logger.log(f"Read_Email response: {emails}")
|
140
|
+
|
141
|
+
except Exception as e:
|
142
|
+
logger.log(f"Error in Read_Email: {str(e)}")
|
143
|
+
|
144
|
+
|
145
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
import requests
|
3
|
+
import loggerutility as logger
|
4
|
+
from flask import Flask, request
|
5
|
+
import json
|
6
|
+
|
7
|
+
class Email_Upload_Document:
|
8
|
+
|
9
|
+
def upload_files(self, file_path):
|
10
|
+
data = request.get_data('jsonData', None)
|
11
|
+
data = json.loads(data[9:])
|
12
|
+
logger.log(f"jsondata:: {data}")
|
13
|
+
base_url = data.get("base_url")
|
14
|
+
token_id = data.get("token_id")
|
15
|
+
|
16
|
+
url = f"{base_url}/ibase/rest/DocumentHandlerService/uploadDocument"
|
17
|
+
|
18
|
+
headers = {
|
19
|
+
"TOKEN_ID": token_id,
|
20
|
+
"Content-Type": "multipart/form-data"
|
21
|
+
}
|
22
|
+
|
23
|
+
with open(file_path, 'rb') as file_to_upload:
|
24
|
+
files = {'file': file_to_upload}
|
25
|
+
logger.log(f"files:: {files}")
|
26
|
+
logger.log(f"file_to_upload:: {file_to_upload}")
|
27
|
+
|
28
|
+
response = requests.post(url, headers=headers, files=files)
|
29
|
+
|
30
|
+
if response.status_code == 200:
|
31
|
+
logger.log("File uploaded successfully!")
|
32
|
+
logger.log("Response:", response.json())
|
33
|
+
return {"success": "Success", "message": "File uploaded"}
|
34
|
+
else:
|
35
|
+
logger.log("Failed to upload file.")
|
36
|
+
logger.log("Status Code:", response.status_code)
|
37
|
+
logger.log("Response:", response.text)
|
38
|
+
return {"success": "Failed", "message": "File not uploaded"}
|
39
|
+
|
40
|
+
|
41
|
+
|
@@ -0,0 +1,95 @@
|
|
1
|
+
import requests
|
2
|
+
from flask import Flask,request
|
3
|
+
import json
|
4
|
+
import loggerutility as logger
|
5
|
+
|
6
|
+
class Save_Transaction:
|
7
|
+
def generate_token(self, base_url, app_id, user_code, password, is_pwd_encrypt, data_format):
|
8
|
+
url = f"{base_url}/ibase/rest/E12ExtService/login"
|
9
|
+
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
10
|
+
payload = {
|
11
|
+
"USER_CODE": user_code,
|
12
|
+
"PASSWORD": password,
|
13
|
+
"IS_PWD_ENCRYPT": str(is_pwd_encrypt).lower(),
|
14
|
+
"APP_ID": app_id,
|
15
|
+
"DATA_FORMAT": data_format
|
16
|
+
}
|
17
|
+
logger.log(f"url:; {url}")
|
18
|
+
logger.log(f"payload:; {payload}")
|
19
|
+
|
20
|
+
response = requests.post(url, data=payload, headers=headers)
|
21
|
+
logger.log(f"response:: {response}")
|
22
|
+
if response.status_code == 200:
|
23
|
+
logger.log(f"response:: {response.text}")
|
24
|
+
logger.log(f"response JSON:: {response.json()}")
|
25
|
+
response_data = response.json() # Parse the response JSON
|
26
|
+
results = response_data.get("Response", {}).get("results", "")
|
27
|
+
results_dict = json.loads(results) # Parse the JSON-encoded string
|
28
|
+
token_id = results_dict.get("TOKEN_ID")
|
29
|
+
# token_id = response.json().get("TOKEN_ID")
|
30
|
+
logger.log(f"token_id:; {token_id}")
|
31
|
+
return token_id
|
32
|
+
else:
|
33
|
+
print(f"Failed to generate token. Status Code: {response.status_code}, Response: {response.text}")
|
34
|
+
return None
|
35
|
+
|
36
|
+
def email_save_transaction(self, input_data):
|
37
|
+
logger.log(f"input_data:: {input_data}")
|
38
|
+
data = request.get_data('jsonData', None)
|
39
|
+
data = json.loads(data[9:])
|
40
|
+
logger.log(f"jsondata:: {data}")
|
41
|
+
|
42
|
+
base_url = data.get("base_url")
|
43
|
+
token_id = data.get("token_id")
|
44
|
+
obj_name = data.get("obj_name")
|
45
|
+
enterprise_name = data.get("enterprise_name")
|
46
|
+
app_id = data.get("app_id")
|
47
|
+
|
48
|
+
url = f"{base_url}/ibase/rest/EDIService/setData/{obj_name}/{enterprise_name}/{app_id}/writefilesavetrans"
|
49
|
+
headers = {
|
50
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
51
|
+
'TOKEN_ID': token_id
|
52
|
+
}
|
53
|
+
payload = {
|
54
|
+
"INPUT_DATA": input_data
|
55
|
+
}
|
56
|
+
|
57
|
+
response = requests.post(url, data=payload, headers=headers)
|
58
|
+
logger.log(f"response:: {response}")
|
59
|
+
logger.log(f"response.status_code:: {response.status_code}")
|
60
|
+
if response.status_code == 200:
|
61
|
+
json_response = response.json()
|
62
|
+
return {"status": "Success", "message": json_response}
|
63
|
+
else:
|
64
|
+
print(f"Failed to push transaction. Status Code: {response.status_code}, Response: {response.text}")
|
65
|
+
return {"success": "Failed", "message": "failed in transaction"}
|
66
|
+
|
67
|
+
def save_trans(self):
|
68
|
+
|
69
|
+
data = request.get_data('jsonData', None)
|
70
|
+
data = json.loads(data[9:])
|
71
|
+
logger.log(f"jsondata:: {data}")
|
72
|
+
|
73
|
+
base_url = data.get("base_url")
|
74
|
+
app_id = data.get("app_id")
|
75
|
+
user_code = data.get("user_code")
|
76
|
+
password = data.get("password")
|
77
|
+
is_pwd_encrypt = data.get("is_pwd_encrypt")
|
78
|
+
data_format = data.get("data_format")
|
79
|
+
input_data = "What are the modules are available in Proteus Vision ERP ?\r\nCan you please highlight the specific feature of manufacturing module"
|
80
|
+
|
81
|
+
trans_details = self.generate_token(
|
82
|
+
base_url,
|
83
|
+
app_id,
|
84
|
+
user_code,
|
85
|
+
password,
|
86
|
+
is_pwd_encrypt,
|
87
|
+
data_format
|
88
|
+
)
|
89
|
+
logger.log(f"trans_details:: {trans_details}")
|
90
|
+
# return trans_details
|
91
|
+
|
92
|
+
|
93
|
+
token_id = self.email_save_transaction(base_url,input_data,trans_details)
|
94
|
+
logger.log(f"token::: {token_id}")
|
95
|
+
return token_id
|
@@ -0,0 +1,8 @@
|
|
1
|
+
from .Email_Read import Email_Read
|
2
|
+
from .Email_Classification import Email_Classification
|
3
|
+
from .Email_Draft import Email_Draft
|
4
|
+
from .Email_DocumentUploader import Email_DocumentUploader
|
5
|
+
from .EmailReplyAssistant import EmailReplyAssistant
|
6
|
+
from .Save_Transaction import Save_Transaction
|
7
|
+
from .Email_Upload_Document import Email_Upload_Document
|
8
|
+
|
@@ -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.4
|
4
|
+
Summary: Changed all classes for new changes and added save transaction class
|
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
|
+
Changed all classes for new changes and added save transaction class
|
@@ -0,0 +1,13 @@
|
|
1
|
+
AIEmailAutomationUtility/EmailReplyAssistant.py,sha256=9X4osN7JDG_2sdyq0Sww5VapxPc_u7Nc9bEbODeE6YU,3852
|
2
|
+
AIEmailAutomationUtility/Email_Classification.py,sha256=73lqzfXft0q6D9lRhW0gHiDNGyykSgEZ8fUlgBYK9Hw,9362
|
3
|
+
AIEmailAutomationUtility/Email_DocumentUploader.py,sha256=0AWcwAx7x4WiZwFBxHwdFpUfE9wjvLGcaxT_95bqRcY,3496
|
4
|
+
AIEmailAutomationUtility/Email_Draft.py,sha256=WUdaSLk4xTwMs1wSOUXE2xiDUehsZQ2wGqvWtXbjeo0,5427
|
5
|
+
AIEmailAutomationUtility/Email_Read.py,sha256=ScuF5zFimkiIUX91-WhgNL18RbhHrcWe7fbbRwDGB7w,6717
|
6
|
+
AIEmailAutomationUtility/Email_Upload_Document.py,sha256=3bdkxfDlwoeRp-46KPw2Gs1dqBhEIoA1yE5GCudpdV8,1320
|
7
|
+
AIEmailAutomationUtility/Save_Transaction.py,sha256=Gg1w6hhzHmEFjsuzYvkq-3-EsWReetjLHsYSv5YIGgM,3816
|
8
|
+
AIEmailAutomationUtility/__init__.py,sha256=UzDkFSvLwwc0NLnvMiM0jNV5pIWUlM9p2zvpcrh9rkM,344
|
9
|
+
AIEmailAutomationUtility-0.0.4.dist-info/LICENCE.txt,sha256=2qX9IkEUBx0VJp1Vh9O2dsRwE-IpYId0lXDyn7OVsJ8,1073
|
10
|
+
AIEmailAutomationUtility-0.0.4.dist-info/METADATA,sha256=0nvJzPE0TMCF1bPLE2g18sOl2FHie0QZViXbm19_W7U,636
|
11
|
+
AIEmailAutomationUtility-0.0.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
12
|
+
AIEmailAutomationUtility-0.0.4.dist-info/top_level.txt,sha256=3jTWrTUblVkaP7mpwY2UBSnrlfot5Ykpfsehyke-Uzw,25
|
13
|
+
AIEmailAutomationUtility-0.0.4.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
AIEmailAutomationUtility
|