AIEmailAutomationUtility 0.0.21__py3-none-any.whl → 0.0.23__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.
@@ -1,25 +1,25 @@
1
1
  import imaplib
2
2
  import email
3
- import os
4
- import json
5
3
  import time
6
4
  import loggerutility as logger
7
- from flask import request
8
- import traceback
9
- from fpdf import FPDF
10
- from openai import OpenAI
11
- import requests
12
- import openai
13
- import google.generativeai as genai
14
5
  from datetime import datetime
15
6
 
7
+ import threading
8
+ import traceback
9
+ import json
10
+ import os
11
+ import csv
12
+
16
13
  from .Save_Transaction import Save_Transaction
17
- from .Email_Upload_Document import Email_Upload_Document
18
14
  from .Email_Classification import Email_Classification
19
15
  from .EmailReplyAssistant import EmailReplyAssistant
20
- from .Email_Draft import Email_Draft
21
- from .Email_DocumentUploader import Email_DocumentUploader
22
- import sqlite3
16
+ from .Process_Category import Process_Category
17
+
18
+ import re
19
+ from email.utils import parseaddr
20
+ from email.header import decode_header
21
+
22
+ shared_status = True
23
23
 
24
24
  class Email_Read:
25
25
  def read_email(self, email_config):
@@ -50,7 +50,6 @@ class Email_Read:
50
50
  if status == 'OK':
51
51
  raw_email = data[0][1]
52
52
  msg = email.message_from_bytes(raw_email)
53
-
54
53
  sender_email = msg['From']
55
54
  cc_email = msg['CC']
56
55
  subject = msg['Subject']
@@ -88,10 +87,8 @@ class Email_Read:
88
87
  except Exception as close_error:
89
88
  logger.log(f"Error during mail close/logout: {str(close_error)}")
90
89
 
91
- def read_email_automation(self, email_config):
92
- # try:
90
+ def read_email_automation(self, email_config,user_id):
93
91
  logger.log(f"inside read_email_automation")
94
- # logger.log(f"email_config ::: {email_config}")
95
92
  LABEL = "Unprocessed_Email"
96
93
  file_JsonArray = []
97
94
  templateName = "ai_email_automation.json"
@@ -103,177 +100,132 @@ class Email_Read:
103
100
  host = email_config.get('host', '')
104
101
  port = email_config.get('port', '')
105
102
 
106
- mail = imaplib.IMAP4_SSL(host, port)
107
- mail.login(reciever_email_addr, receiver_email_pwd)
108
- logger.log("login successfully")
109
- mail.select('inbox')
110
-
111
- file_JsonArray, categories = self.read_JSON_File(templateName)
103
+ try:
104
+ mail = imaplib.IMAP4_SSL(host, port)
105
+ mail.login(reciever_email_addr, receiver_email_pwd)
106
+ logger.log("login successfully")
107
+ login_status = "Success"
108
+ mail.select('inbox')
109
+
110
+ file_JsonArray, categories = self.read_JSON_File(templateName)
111
+
112
+ except Exception as e:
113
+ logger.log(f"Login failed: {e}")
114
+
115
+ # Log the result
116
+ self.log_email_login(user_id, reciever_email_addr, Model_Name, login_status)
112
117
 
113
118
  while True:
114
- status, email_ids = mail.search(None, 'UNSEEN')
115
- emails = []
116
-
117
- if status == 'OK':
118
- email_ids = email_ids[0].split()
119
+ shared_status = self.read_status()
119
120
 
120
- if not email_ids:
121
- logger.log("Email not found, going to check new mail")
122
- logger.log("Email not found,\ngoing to check new mail \n")
123
- else:
121
+ if shared_status:
122
+ status, email_ids = mail.search(None, 'UNSEEN')
123
+ emails = []
124
124
 
125
- for email_id in email_ids:
126
- email_body = ""
127
- attachments = []
128
- status, data = mail.fetch(email_id, '(RFC822)')
129
-
130
- if status == 'OK':
131
- raw_email = data[0][1]
132
- msg = email.message_from_bytes(raw_email)
125
+ if status == 'OK':
126
+ email_ids = email_ids[0].split()
127
+
128
+ if not email_ids:
129
+ logger.log("Email not found, going to check new mail")
130
+ logger.log("Email not found,\ngoing to check new mail \n")
131
+ else:
132
+
133
+ for email_id in email_ids:
134
+ email_body = ""
135
+ attachments = []
136
+ # status, data = mail.fetch(email_id, '(RFC822)')
137
+ status, data = mail.fetch(email_id, '(RFC822 UID)') # Fetch UID as well
138
+ emailCategory = "Not Classified"
133
139
 
134
- subject = msg['Subject']
135
- sender_email_addr = msg['From']
136
- cc_email_addr = msg['CC']
137
- subject = msg['Subject']
140
+ if status == 'OK':
141
+ raw_email = data[0][1]
142
+ msg = email.message_from_bytes(raw_email)
138
143
 
139
- if msg.is_multipart():
140
- for part in msg.walk():
141
- content_type = part.get_content_type()
142
- if content_type == "text/plain":
143
- email_body += part.get_payload(decode=True).decode('utf-8', errors='replace')
144
- else:
145
- email_body = msg.get_payload(decode=True).decode('utf-8', errors='replace')
146
-
147
- openai_Process_Input = email_body
148
- logger.log(f"\nEmail Subject::: {subject}")
149
- logger.log(f"\nEmail body::: {openai_Process_Input}")
144
+ subject = msg['Subject']
145
+ sender_email_addr = msg['From']
146
+ cc_email_addr = msg['CC']
147
+ subject = msg['Subject']
148
+ to_email_addr = msg.get('To', '')
149
+
150
150
 
151
- openai_api_key = email_config.get('openai_api_key', '')
152
- geminiAI_APIKey = email_config.get('gemini_api_key', '')
153
- signature = email_config.get('signature', '')
154
- localAIURL = email_config.get('local_ai_url', '')
155
-
156
- if len(str(openai_Process_Input)) > 0 :
157
- email_cat_data = {
158
- "model_type" : Model_Name,
159
- "openai_api_key" : openai_api_key,
160
- "categories" : categories,
161
- "email_body" : email_body,
162
- "gemini_api_key" : geminiAI_APIKey,
163
- "signature" : signature,
164
- "local_ai_url" : localAIURL,
165
- }
166
- # logger.log(f"\nemail_cat_data ::: {email_cat_data}")
167
- email_classification = Email_Classification()
168
- emailCategory = email_classification.detect_category(email_cat_data)
169
- emailCategory = emailCategory['message']
170
- logger.log(f"\nDetected Email category ::: {emailCategory}")
151
+ # Extract UID
152
+ print(f" the data -----{data[0][0]}")
153
+ raw_uid = data[0][0].decode() if isinstance(data[0][0], bytes) else data[0][0]
154
+ print(f"the raw uid is ------- {raw_uid}")
155
+ uid_match = re.search(r'UID (\d+)', raw_uid)
156
+ uid = uid_match.group(1) if uid_match else "N/A"
157
+
158
+ is_html = False # Initialize is_html
159
+
160
+ if msg.is_multipart():
161
+ for part in msg.walk():
162
+ content_type = part.get_content_type()
163
+ if content_type == "text/html" and not is_html:
164
+ is_html = True # Set flag if HTML part is found
165
+
166
+ if content_type == "text/plain":
167
+ email_body += part.get_payload(decode=True).decode('utf-8', errors='replace')
168
+
169
+ else:
170
+ email_body = msg.get_payload(decode=True).decode('utf-8', errors='replace')
171
+ content_type = msg.get_content_type()
172
+ is_html = (content_type == "text/html") # Set is_html based on single-part type
171
173
 
172
- if emailCategory == 'Others':
173
- logger.log(f"Marking email as UNREAD. ")
174
- mail.store(email_id, '-FLAGS', '\\Seen')
175
-
176
- mail.create(LABEL)
177
- mail.copy(email_id, LABEL)
178
- mail.store(email_id, '+FLAGS', '\\Deleted') # Mark for deletion
179
- mail.expunge()
180
- logger.log(f"Mail removed from inbox and added to '{LABEL}' label.")
174
+ openai_Process_Input = email_body
175
+
176
+ logger.log(f"\nEmail Subject::: {subject}")
177
+ logger.log(f"\nEmail body::: {openai_Process_Input}")
178
+
179
+ openai_api_key = email_config.get('openai_api_key', '')
180
+ geminiAI_APIKey = email_config.get('gemini_api_key', '')
181
+ signature = email_config.get('signature', '')
182
+ localAIURL = email_config.get('local_ai_url', '')
181
183
 
182
- elif emailCategory == "Product Enquiry":
183
-
184
- if Model_Name == "OpenAI":
185
- responseMethod, parameters = self.get_JsonArray_values(emailCategory, file_JsonArray)
186
- if responseMethod == "Reply_Email_Ai_Assistant" :
187
-
188
- emailreplyassistant = EmailReplyAssistant()
189
- openai_Response = emailreplyassistant.Reply_Email_Ai_Assistant(openai_api_key, parameters["Assistant_Id"], openai_Process_Input, subject)
190
-
191
- logger.log(f"Process openai_Response ::: {openai_Response['message']}\n")
192
- email_details = {"sender":sender_email_addr, "cc":cc_email_addr, "subject":subject, "body": email_body}
193
-
194
- email_draft = Email_Draft()
195
- status, response = email_draft.draft_email(email_config, email_details, openai_Response['message'])
196
- logger.log(f"status ::: {status}")
197
- else :
198
- message = f"Invalid response method received '{responseMethod}' for category : '{emailCategory}'"
199
- raise ValueError(message)
200
- elif Model_Name == "LocalAI":
201
- logger.log("localAI")
202
- Detect_Email_category = False
203
- LocalAI_Response = emailCategory
204
- logger.log(f"Process LocalAI_Response ::: {LocalAI_Response}\n")
205
- email_details = {"sender":sender_email_addr, "cc":cc_email_addr, "subject":subject, "body": email_body}
206
-
207
- email_draft = Email_Draft()
208
- status, response = email_draft.draft_email(email_config, email_details, LocalAI_Response)
209
- logger.log(f"status ::: {status}")
210
- elif Model_Name == "GeminiAI":
211
- logger.log("GeminiAI")
212
- Detect_Email_category = False
213
- GeminiAI_Response = emailCategory
214
- logger.log(f"Process GeminiAI_Response ::: {GeminiAI_Response}\n")
215
- email_details = {"sender":sender_email_addr, "cc":cc_email_addr, "subject":subject, "body": email_body}
216
-
217
- email_draft = Email_Draft()
218
- status, response = email_draft.draft_email(email_config, email_details, GeminiAI_Response)
219
- logger.log(f"status ::: {status}")
220
- else:
221
- raise ValueError(f"Invalid Model Name provided : '{Model_Name}'")
222
-
223
- elif emailCategory == "Purchase Order":
224
- responseMethod, parameters = self.get_JsonArray_values(emailCategory, file_JsonArray)
225
- logger.log(f"responseMethod ::: {responseMethod}")
226
- logger.log(f"parameters ::: {parameters}")
227
-
228
- # Download the attachment
229
- fileName = self.download_attachment(msg)
230
-
231
- # Get today's date folder path
232
- today_date = datetime.today().strftime('%Y-%m-%d')
233
- order_folder = os.path.join("ORDERS", today_date)
234
-
235
- if responseMethod == "Upload_Document":
236
- if len(fileName) != 0:
237
- email_upload_document = Email_DocumentUploader()
238
- file_path = os.path.join(order_folder, fileName) # Correct file path
239
-
240
- with open(file_path, "rb") as file:
241
- response_status, restAPI_Result = email_upload_document.email_document_upload(file, parameters)
242
- logger.log(f"email_upload_document_response ::: {restAPI_Result}")
243
- else:
244
- new_fileName = self.create_file_from_emailBody(email_body, sender_email_addr, parameters)
245
- new_file_path = os.path.join(order_folder, new_fileName)
246
-
247
- with open(new_file_path, "rb") as file:
248
- response_status, restAPI_Result = email_upload_document.email_document_upload(file, parameters)
249
- logger.log(f"email_upload_document_response ::: {restAPI_Result}")
250
-
251
- if response_status == "200":
252
- logger.log(f"Attachment uploaded successfully against Document ID: '{restAPI_Result}'.")
253
- else:
254
- logger.log(restAPI_Result)
255
-
256
- else :
257
- message = f"Invalid response method received '{responseMethod}' for category : '{emailCategory}'"
258
- raise ValueError(message)
259
- else:
260
- message = f"Detected Email category not found : '{emailCategory}'"
261
- raise ValueError(message)
184
+ if len(str(openai_Process_Input)) > 0 :
185
+ email_cat_data = {
186
+ "model_type" : Model_Name,
187
+ "openai_api_key" : openai_api_key,
188
+ "categories" : categories,
189
+ "email_body" : email_body,
190
+ "gemini_api_key" : geminiAI_APIKey,
191
+ "signature" : signature,
192
+ "local_ai_url" : localAIURL,
193
+ }
194
+ # logger.log(f"\nemail_cat_data ::: {email_cat_data}")
195
+ email_classification = Email_Classification()
196
+ emailCategory = email_classification.detect_category(email_cat_data)
197
+ emailCategory = emailCategory['message']
198
+ logger.log(f"\nDetected Email category ::: {emailCategory}")
199
+
200
+ dataValues = {
201
+ 'Model_Name': Model_Name,
202
+ 'file_JsonArray': file_JsonArray,
203
+ 'openai_api_key': openai_api_key,
204
+ 'openai_Process_Input': openai_Process_Input,
205
+ 'subject': subject,
206
+ 'sender_email_addr': sender_email_addr,
207
+ 'cc_email_addr': cc_email_addr,
208
+ 'email_body': email_body,
209
+ 'email_config': email_config,
210
+ 'msg': msg,
211
+ 'geminiAI_APIKey': geminiAI_APIKey,
212
+ 'localAIURL': localAIURL,
213
+ 'signature': signature,
214
+ 'LABEL': LABEL,
215
+ 'mail': mail,
216
+ 'email_id': email_id,
217
+ "uid": uid,
218
+ "to_email_addr": to_email_addr,
219
+ "user_id": user_id,
220
+ "is_html": is_html
221
+ }
222
+ processcategory = Process_Category()
223
+ processcategory.process_cat(emailCategory, dataValues)
224
+
262
225
  time.sleep(10)
263
-
264
- # except Exception as e:
265
- # return {"status": "Failed", "message": f"Error reading emails: {str(e)}"}
266
- # finally:
267
- # try:
268
- # mail.close()
269
- # mail.logout()
270
- # except Exception as close_error:
271
- # logger.log(f"Error during mail close/logout: {str(close_error)}")
272
- # return {"status": "Failed", "message": f"Error reading emails: {str(close_error)}"}
273
-
274
- def read_email_quotation(self, email_config):
226
+
227
+ def read_email_quotation(self, email_config,user_id):
275
228
  # try:
276
- logger.log(f"inside read_email_automation")
277
229
  LABEL = "Unprocessed_Email"
278
230
  file_JsonArray = []
279
231
  templateName = "ai_email_automation.json"
@@ -285,12 +237,20 @@ class Email_Read:
285
237
  host = email_config.get('host', '')
286
238
  port = email_config.get('port', '')
287
239
 
288
- mail = imaplib.IMAP4_SSL(host, port)
289
- mail.login(reciever_email_addr, receiver_email_pwd)
290
- logger.log("login successfully")
291
- mail.select('inbox')
292
-
293
- file_JsonArray, categories = self.read_JSON_File(templateName)
240
+ try:
241
+ mail = imaplib.IMAP4_SSL(host, port)
242
+ mail.login(reciever_email_addr, receiver_email_pwd)
243
+ logger.log("login successfully")
244
+ login_status = "Success"
245
+ mail.select('inbox')
246
+
247
+ file_JsonArray, categories = self.read_JSON_File(templateName)
248
+
249
+ except Exception as e:
250
+ logger.log(f"Login failed: {e}")
251
+
252
+ # Log the result
253
+ self.log_email_login(user_id, reciever_email_addr, Model_Name, login_status)
294
254
 
295
255
  while True:
296
256
  status, email_ids = mail.search(None, 'UNSEEN')
@@ -307,7 +267,7 @@ class Email_Read:
307
267
  for email_id in email_ids:
308
268
  email_body = ""
309
269
  attachments = []
310
- status, data = mail.fetch(email_id, '(RFC822)')
270
+ status, data = mail.fetch(email_id, '(RFC822 UID)')
311
271
 
312
272
  if status == 'OK':
313
273
  raw_email = data[0][1]
@@ -317,14 +277,28 @@ class Email_Read:
317
277
  sender_email_addr = msg['From']
318
278
  cc_email_addr = msg['CC']
319
279
  subject = msg['Subject']
280
+ to_email_addr = msg.get('To', '')
281
+
282
+ # Extract UID
283
+ raw_uid = data[0][0].decode() if isinstance(data[0][0], bytes) else data[0][0]
284
+ uid_match = re.search(r'UID (\d+)', raw_uid)
285
+ uid = uid_match.group(1) if uid_match else "N/A"
286
+
287
+ is_html = False # Initialize is_html
320
288
 
321
289
  if msg.is_multipart():
322
290
  for part in msg.walk():
323
291
  content_type = part.get_content_type()
292
+ if content_type == "text/html" and not is_html:
293
+ is_html = True # Set flag if HTML part is found
294
+
324
295
  if content_type == "text/plain":
325
296
  email_body += part.get_payload(decode=True).decode('utf-8', errors='replace')
297
+
326
298
  else:
327
299
  email_body = msg.get_payload(decode=True).decode('utf-8', errors='replace')
300
+ content_type = msg.get_content_type()
301
+ is_html = (content_type == "text/html") # Set is_html based on single-part type
328
302
 
329
303
  openai_Process_Input = email_body
330
304
  logger.log(f"\nEmail Subject::: {subject}")
@@ -354,379 +328,33 @@ class Email_Read:
354
328
  emailCategory = emailCategory['message']
355
329
  logger.log(f"\nDetected Email category ::: {emailCategory}")
356
330
 
357
- if emailCategory == "Quotation":
358
- responseMethod, parameters = self.get_JsonArray_values(emailCategory, file_JsonArray)
359
-
360
- logger.log(f"Inside Quotation")
361
- # Step 4: Identify customer from email using AI
362
- customer_data = self.identify_customer(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Customer_Assistant_Id"])
363
- logger.log(f"Identified customer: {customer_data}")
364
-
365
- # Step 5: Identify product from email using AI
366
- products = self.identify_products(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
367
-
368
- for product in products:
369
- db_connection = sqlite3.connect('price_list.db')
370
- cursor = db_connection.cursor()
371
-
372
- # Get rate from SQLite database
373
- query = f'SELECT Price FROM price_list_table WHERE "Item No." = "{product.get("item_no", "")}";'
374
- cursor.execute(query)
375
- result = cursor.fetchone()
376
-
377
- if result:
378
- rate = result[0]
379
- product["rate"] = rate
380
- else:
381
- product["rate"] = None
382
-
383
- logger.log(f"Identified products: {products}")
384
- logger.log(f"Identified products length: {len(products)}")
385
- quotation_draft = self.generate_quotation_draft(
386
- customer_data,
387
- products,
388
- Model_Name,
389
- openai_api_key,
390
- geminiAI_APIKey,
391
- localAIURL,
392
- parameters["Customer_Assistant_Id"],
393
- email_body,
394
- subject,
395
- signature
396
- )
397
- logger.log(f"quotation_draft ::: {quotation_draft}")
398
-
399
- # Step 8: Send draft quotation email
400
- email_details = {"sender":sender_email_addr, "cc":cc_email_addr, "subject":subject, "body": email_body}
401
- email_draft = Email_Draft()
402
- status, response = email_draft.quotation_draft_email(email_config, email_details, quotation_draft)
403
- logger.log(f"status ::: {status}")
404
-
405
- logger.log(f"Quotation email sent to {sender_email_addr}")
406
-
407
- else:
408
- logger.log(f"Marking email as UNREAD. ")
409
- mail.store(email_id, '-FLAGS', '\\Seen')
410
-
411
- mail.create(LABEL)
412
- mail.copy(email_id, LABEL)
413
- mail.store(email_id, '+FLAGS', '\\Deleted') # Mark for deletion
414
- mail.expunge()
415
- logger.log(f"Mail removed from inbox and added to '{LABEL}' label.")
331
+ dataValues = {
332
+ 'Model_Name': Model_Name,
333
+ 'file_JsonArray': file_JsonArray,
334
+ 'openai_api_key': openai_api_key,
335
+ 'openai_Process_Input': openai_Process_Input,
336
+ 'subject': subject,
337
+ 'sender_email_addr': sender_email_addr,
338
+ 'cc_email_addr': cc_email_addr,
339
+ 'email_body': email_body,
340
+ 'email_config': email_config,
341
+ 'msg': msg,
342
+ 'geminiAI_APIKey': geminiAI_APIKey,
343
+ 'localAIURL': localAIURL,
344
+ 'signature': signature,
345
+ 'LABEL': LABEL,
346
+ 'mail': mail,
347
+ 'email_id': email_id,
348
+ "uid": uid,
349
+ "to_email_addr": to_email_addr,
350
+ "user_id": user_id,
351
+ "is_html": is_html
352
+ }
353
+ processcategory = Process_Category()
354
+ processcategory.process_cat(emailCategory, dataValues)
416
355
 
417
356
  time.sleep(10)
418
357
 
419
- def identify_customer(self, email_body, subject, model_type, openai_api_key, gemini_api_key, local_ai_url, assistant_id):
420
- logger.log("Inside identify_customer")
421
-
422
- if model_type == "OpenAI":
423
- prompt = f"""Identify the customer code, customer name in json format from the following email {email_body} and received from {subject}. Do not include any instruction as the output will be directly in a program."""
424
- emailreplyassistant = EmailReplyAssistant()
425
- ai_result = emailreplyassistant.identify_customer_product_reply_assitant(openai_api_key, assistant_id, email_body, subject, prompt)
426
-
427
- elif model_type == "GeminiAI":
428
- prompt = f"""Identify the customer code, customer name in json format from the following email {email_body} and received from {subject}. Do not include any instruction as the output will be directly in a program."""
429
- ai_result = self.identify_customer_product_GeminiAI(gemini_api_key, email_body, prompt)
430
-
431
- elif model_type == "LocalAI":
432
- prompt = f"""Identify the customer code and customer name in JSON format from the following email: {email_body}, received from {subject}.
433
- If no customer details are found, return:{{"customer_name": "","customer_code": ""}}Only return the JSON object. No explanations, no additional text."""
434
- ai_result = self.identify_customer_product_LocalAI(openai_api_key, email_body, local_ai_url, prompt)
435
-
436
- else:
437
- ai_result = "{}"
438
-
439
- customer_data = {}
440
- if ai_result["status"] == "Success":
441
- customer_data = json.loads(ai_result["message"])
442
- else:
443
- customer_data = {
444
- "customer_name": "",
445
- "customer_code": ""
446
- }
447
- return customer_data
448
-
449
- def identify_products(self, email_body, subject, model_type, openai_api_key, gemini_api_key, local_ai_url, assistant_id):
450
- logger.log("Inside identify_products")
451
-
452
- if model_type == "OpenAI":
453
- prompt = f"""
454
- Can you give me price information of all products in following format requested_description, item_no, make, description, price, price unit, inventory unit for following items in strictly in JSON String format {email_body}.
455
- If there is one product or multiple should return in list.
456
- Do not include any instruction as the output will be directly in a program.
457
- """
458
- emailreplyassistant = EmailReplyAssistant()
459
- ai_result = emailreplyassistant.identify_customer_product_reply_assitant(openai_api_key, assistant_id, email_body, subject, prompt)
460
-
461
- elif model_type == "GeminiAI":
462
- prompt = f"""
463
- Can you give me price information of all products in following format requested_description, item_no, make, description, price, price unit, inventory unit for following items in strictly in JSON String format {email_body}.
464
- If there is one product or multiple should return in list.
465
- Do not include any instruction as the output will be directly in a program.
466
- """
467
- ai_result = self.identify_customer_product_GeminiAI(gemini_api_key, email_body, prompt)
468
-
469
- elif model_type == "LocalAI":
470
- prompt = f"""Can you give me price information in following format requested_description, item_no, make, description, price, price unit, inventory unit for following items it strictly in json format which loads directly in json {email_body}. If there is one product or multiple should return in list.
471
- If no product details are found, return:[] Only return the JSON object. No explanations, no additional text."""
472
- ai_result = self.identify_customer_product_LocalAI(openai_api_key, email_body, local_ai_url, prompt)
473
-
474
- else:
475
- ai_result = "{}"
476
-
477
- product_data = {}
478
- if ai_result["status"] == "Success":
479
- logger.log(f"ai_result ::: {ai_result}")
480
- product_data = json.loads(ai_result["message"])
481
- else:
482
- product_data = []
483
- return product_data
484
-
485
- def generate_quotation_draft(self, customer_data, products, model_type, openai_api_key, gemini_api_key, local_ai_url, assistant_id, email_body, subject, signature):
486
- logger.log("Inside generate_quotation_draft")
487
-
488
- customer = customer_data
489
-
490
- product_table = "Products:\n"
491
- for product in products:
492
- product_table += f'- {product.get("requested_description")} (Code: {product.get("item_no")}) = ${product.get("rate")}\n'
493
-
494
- if model_type == "OpenAI":
495
- prompt = f"""
496
- Generate product information in HTML tabular format with line separators for rows and columns in a draft reply based on the following information:
497
-
498
- Customer: {customer.get('customer_name', '')}
499
- Customer Code: {customer.get('customer_code', '')}
500
-
501
- {product_table}
502
- product_table must contain only price column even if it is none(set it as -).
503
- Original Email Subject: {subject}
504
-
505
- Return only the following JSON String format:
506
- {{
507
- "email_body": {{
508
- "body": "Draft email body proper response, It should not be same like mail content and does not having any signature part like Best regards.",
509
- "table_html": "Table Details with Sr. No. in HTML",
510
- "signature": "{signature}"
511
- }}
512
- }}
513
-
514
- Do not include signature in body and any instructions, explanations, or additional text—only the JSON object.
515
- """
516
- logger.log(f"Quotation draft ::: {prompt}")
517
- emailreplyassistant = EmailReplyAssistant()
518
- ai_result = emailreplyassistant.create_quotation_draft(openai_api_key, assistant_id, email_body, subject, prompt)
519
-
520
- elif model_type == "GeminiAI":
521
- prompt = f"""
522
- Create an HTML product information email draft with the following details:
523
-
524
- Customer Name: {customer.get('customer_name', '')}
525
- Customer Code: {customer.get('customer_code', '')}
526
-
527
- Product Information:
528
- {product_table}
529
- Note: Include price column with a value of "-" if price is not available.
530
-
531
- Email Subject Reference: {subject}
532
-
533
- Please format the response as a valid JSON string with these fields:
534
- {{
535
- "email_body": {{
536
- "body": "Professional email content that summarizes the product information without being identical to the input data. Do not include signature here.",
537
- "table_": "HTML table with SR. No. column and product details",
538
- "signature": "{signature}"
539
- }}
540
- }}
541
-
542
- Ensure the JSON is properly formatted with escaped newlines (\\n) and no trailing commas. Return only the valid JSON string without additional explanations or instructions.
543
- """
544
- logger.log(f"Quotation draft ::: {prompt}")
545
- ai_result = self.create_quotation_draft_GeminiAI(gemini_api_key, email_body, prompt)
546
-
547
- elif model_type == "LocalAI":
548
- prompt = f"""
549
- Generate product information in HTML tabular format with line separators for rows and columns in a draft reply based on the following information:
550
-
551
- Customer: {customer.get('customer_name', '')}
552
- Customer Code: {customer.get('customer_code', '')}
553
-
554
- {product_table}
555
- - The table must contain the **Price** column, even if it is empty (set it as `-` if None).
556
- - The table should include **Sr. No.** as the first column.
557
- - Format the table with `<table>`, `<tr>`, `<th>`, and `<td>` tags with some border to table.
558
-
559
- Original Email Subject: {subject}
560
-
561
- Return **strictly** in the following JSON String format:
562
- - All keys must be: `body`, `table_`, and `signature` inside the `email_body` JSON.
563
- - **Do not include** `\n`, `\`, `\\`, or any unnecessary escape characters.
564
- - Do not include instructions, explanations, or additional text—only the JSON object.
565
-
566
- Format:
567
- {{
568
- "email_body": {{
569
- "body": "Draft email body proper response, It should not contain the table or signature.",
570
- "table_": "Table Details with Sr. No. in HTML only",
571
- "signature": "{signature}"
572
- }}
573
- }}
574
- """
575
- logger.log(f"Quotation draft ::: {prompt}")
576
- ai_result = self.create_quotation_draft_LocalAI(openai_api_key, email_body, local_ai_url, prompt)
577
-
578
- else:
579
- ai_result = "Error: Unable to generate quotation draft. Please check the configuration."
580
-
581
- logger.log(f"Quotation draft ai_result::: {ai_result}")
582
- quotation_draft_data = None
583
- if ai_result != None:
584
- quotation_draft_data = json.loads(ai_result)["email_body"]
585
- return quotation_draft_data
586
-
587
- def identify_customer_product_LocalAI(self, openai_api_key, email_body, local_ai_url, prompt):
588
- logger.log("Inside identify_customer_product_LocalAI")
589
- try:
590
- message = [{
591
- "role": "user",
592
- "content": f"{prompt}"
593
- }]
594
-
595
- logger.log(f"Final Local AI message for detecting category::: {message}")
596
- openai.api_key = openai_api_key
597
- client = OpenAI(base_url=local_ai_url, api_key="lm-studio")
598
- completion = client.chat.completions.create(
599
- model="mistral",
600
- messages=message,
601
- temperature=0,
602
- stream=False,
603
- max_tokens=4096
604
- )
605
-
606
- final_result = str(completion.choices[0].message.content)
607
- final_result = final_result.replace("\n```", "").replace("```", "").replace("json","").replace("JSON","").replace("csv","").replace("CSV","").replace("html","")
608
- logger.log(f"finalResult:520 {final_result}")
609
- return {"status": "Success", "message": final_result}
610
-
611
- except Exception as e:
612
- logger.log(f"Error with LocalAI detection/generation: {str(e)}")
613
- return {"success": "Failed", "message": f"Error with LocalAI detection/generation: {str(e)}"}
614
-
615
- def create_quotation_draft_LocalAI(self, openai_api_key, email_body, local_ai_url, prompt):
616
- logger.log("Inside create_quotation_draft_LocalAI")
617
- try:
618
- message = [{
619
- "role": "user",
620
- "content": f"{prompt}"
621
- }]
622
-
623
- logger.log(f"Final Local AI message for detecting category::: {message}")
624
- openai.api_key = openai_api_key
625
- client = OpenAI(base_url=local_ai_url, api_key="lm-studio")
626
- completion = client.chat.completions.create(
627
- model="mistral",
628
- messages=message,
629
- temperature=0,
630
- stream=False,
631
- max_tokens=4096
632
- )
633
-
634
- final_result = str(completion.choices[0].message.content)
635
- final_result = final_result.replace("\n```", "").replace("```", "").replace("json","").replace("JSON","").replace("csv","").replace("CSV","").replace("html","")
636
- logger.log(f"finalResult:520 {final_result}")
637
- return final_result
638
-
639
- except Exception as e:
640
- logger.log(f"Error with LocalAI detection/generation: {str(e)}")
641
- return str(e)
642
-
643
- def identify_customer_product_GeminiAI(self, gemini_api_key, email_body, prompt):
644
- logger.log("Inside identify_customer_product_GeminiAI")
645
- try:
646
- message = [{
647
- "role": "user",
648
- "content": f"{prompt}"
649
- }]
650
-
651
- logger.log(f"Final Gemini AI message for detecting category::: {message}")
652
- message_list = str(message)
653
-
654
- genai.configure(api_key=gemini_api_key)
655
- # model = genai.GenerativeModel('gemini-1.0-pro')
656
- model = genai.GenerativeModel('gemini-1.5-pro-latest')
657
- response = model.generate_content(message_list)
658
-
659
- final_result = ""
660
- for part in response:
661
- final_result = part.text
662
- logger.log(f"response::: {final_result}")
663
- if final_result:
664
- try:
665
- final_result = final_result.replace("\\", "").replace('```', '').replace('json', '')
666
- if final_result.startswith("{{") and final_result.endswith("}}"):
667
- final_result = final_result[1:-1]
668
- except json.JSONDecodeError:
669
- logger.log(f"Exception : Invalid JSON Response GEMINI 1.5: {final_result} {type(final_result)}")
670
-
671
- logger.log(f"finalResult::: {final_result}")
672
- return {"status": "Success", "message": final_result}
673
-
674
- except Exception as e:
675
- logger.log(f"Error with Gemini AI detection/generation: {str(e)}")
676
- return {"success": "Failed", "message": f"Error with Gemini AI detection/generation: {str(e)}"}
677
-
678
- def create_quotation_draft_GeminiAI(self, gemini_api_key, email_body, prompt):
679
- logger.log("Inside identify_customer_product_GeminiAI")
680
- try:
681
- message = [{
682
- "role": "user",
683
- "content": f"{prompt}"
684
- }]
685
-
686
- logger.log(f"Final Gemini AI message for detecting category::: {message}")
687
- message_list = str(message)
688
-
689
- genai.configure(api_key=gemini_api_key)
690
- # model = genai.GenerativeModel('gemini-1.0-pro')
691
- model = genai.GenerativeModel('gemini-1.5-pro-latest')
692
- response = model.generate_content(message_list)
693
-
694
- final_result = ""
695
- for part in response:
696
- final_result = part.text
697
- logger.log(f"response::: {final_result}")
698
- if final_result:
699
- try:
700
- final_result = final_result.replace('```', '').replace('json', '')
701
- if final_result.startswith("{{") and final_result.endswith("}}"):
702
- final_result = final_result[1:-1]
703
- except json.JSONDecodeError:
704
- logger.log(f"Exception : Invalid JSON Response GEMINI 1.5: {final_result} {type(final_result)}")
705
-
706
- logger.log(f"finalResult::: {final_result}")
707
- return final_result
708
-
709
- except Exception as e:
710
- logger.log(f"Error with Gemini AI detection/generation: {str(e)}")
711
- return {"success": "Failed", "message": f"Error with Gemini AI detection/generation: {str(e)}"}
712
-
713
- def save_attachment(self, part, download_dir):
714
- try:
715
- filename = part.get_filename()
716
- if filename:
717
- # Create the directory if it doesn't exist
718
- if not os.path.exists(download_dir):
719
- os.makedirs(download_dir)
720
-
721
- file_path = os.path.join(download_dir, filename)
722
- with open(file_path, 'wb') as f:
723
- f.write(part.get_payload(decode=True))
724
-
725
- logger.log(f"Attachment saved: {file_path}")
726
- return file_path
727
- except Exception as e:
728
- return {"success": "Failed", "message": f"Error saving attachment: {str(e)}"}
729
-
730
358
  def Read_Email(self, data):
731
359
  try:
732
360
 
@@ -761,33 +389,144 @@ class Email_Read:
761
389
 
762
390
  except Exception as e:
763
391
  logger.log(f"Error in Read_Email: {str(e)}")
392
+
393
+ def extract_all_email_info(self, eml_content):
394
+ # Parse the email content
395
+ msg = email.message_from_string(eml_content)
396
+ extracted_info = {}
397
+
398
+ # Extracting To, From, and CC
399
+ extracted_info['to'] = msg.get('To')
400
+ extracted_info['from'] = msg.get('From')
401
+ extracted_info['cc'] = msg.get('Cc')
402
+ print(f"To: {extracted_info['to']}, From: {extracted_info['from']}, CC: {extracted_info['cc']}")
403
+
404
+ # Extracting subject and decoding it if necessary
405
+ subject = decode_header(msg.get('Subject', ''))[0][0]
406
+ if decode_header(msg.get('Subject', ''))[0][1]:
407
+ subject = subject.decode()
408
+ extracted_info['subject'] = subject
409
+ print(f"Subject: {extracted_info['subject']}")
410
+
411
+ # Extracting the body content (text or HTML)
412
+ text_body = None
413
+ html_body = None
414
+ if msg.is_multipart():
415
+ print("Multipart email detected.")
416
+ for part in msg.walk():
417
+ content_type = part.get_content_type()
418
+ content_disposition = str(part.get("Content-Disposition"))
419
+ print(f"Part content type: {content_type}, Content-Disposition: {content_disposition}")
420
+
421
+ if content_type == "text/plain" and "attachment" not in content_disposition:
422
+ text_body = part.get_payload(decode=True).decode()
423
+ print("Text body extracted.")
424
+ elif content_type == "text/html" and "attachment" not in content_disposition:
425
+ html_body = part.get_payload(decode=True).decode()
426
+ print("HTML body extracted.")
427
+ else:
428
+ if msg.get_content_type() == "text/plain":
429
+ text_body = msg.get_payload(decode=True).decode()
430
+ print("Text body extracted (non-multipart) .")
431
+ elif msg.get_content_type() == "text/html":
432
+ html_body = msg.get_payload(decode=True).decode()
433
+ print("HTML body extracted (non-multipart).")
434
+
435
+ extracted_info['email_body'] = text_body if text_body else html_body if html_body else None
436
+ extracted_info['is_html'] = bool(html_body)
437
+
438
+ # Extracting the date and converting it to ISO format
439
+ date_tuple = email.utils.parsedate_tz(msg.get('Date'))
440
+ print(f"date tuple is {date_tuple}")
441
+ if date_tuple:
442
+ local_date = datetime.fromtimestamp(email.utils.mktime_tz(date_tuple))
443
+ extracted_info['date'] = local_date.isoformat()
444
+ print(f"Date: {extracted_info['date']}")
445
+ else:
446
+ extracted_info['date'] = None
447
+ print("No date found.")
764
448
 
765
- def download_attachment(self, msg):
766
- base_folder = "ORDERS" # Main folder for storing orders
767
- today_date = datetime.today().strftime('%Y-%m-%d') # Format: YYYY-MM-DD
768
- date_folder = os.path.join(base_folder, today_date) # Path: ORDERS/YYYY-MM-DD
769
-
770
- # Ensure folders exist
771
- os.makedirs(date_folder, exist_ok=True)
449
+ # Extracting the unique ID (Message-ID)
450
+ extracted_info['unique_id'] = msg.get('Message-ID')
451
+ print(f"Unique ID: {extracted_info['unique_id']}")
452
+ print(f"-------------------------------The extracted info is --------------------------{extracted_info}")
772
453
 
773
- filename = ""
454
+ return extracted_info,msg
455
+
456
+ def process_eml_files(self, user_id, eml_content,mail,Model_Name,email_config):
457
+ LABEL = "Unprocessed_Email"
458
+ file_JsonArray = []
459
+ templateName = "ai_email_automation.json"
460
+ fileName = ""
461
+
462
+ file_JsonArray, categories = self.read_JSON_File(templateName)
463
+ # Call the `extract_all_email_info` method to extract details from the eml content
464
+ extracted_info,msg = self.extract_all_email_info(eml_content)
465
+
466
+ # Extract the details from `extracted_info`
467
+ subject = extracted_info.get('subject', '')
468
+ sender_email_addr = extracted_info.get('from', '')
469
+ cc_email_addr = extracted_info.get('cc', '')
470
+ to_email_addr = extracted_info.get('to', '')
471
+ date = extracted_info.get('date', '')
472
+ email_body = extracted_info.get('email_body', '')
473
+ msg_id = extracted_info.get('unique_id', '')
474
+ is_html = extracted_info.get('is_html', False)
475
+
476
+ uid = re.sub(r'[<>]|\@.*|\+', '', msg_id)
477
+ logger.log(f"\nEmail Subject::: {subject}")
478
+ logger.log(f"\nEmail body::: {email_body}")
479
+
480
+ openai_Process_Input = email_body
481
+
482
+ openai_api_key = email_config.get('openai_api_key', '')
483
+ geminiAI_APIKey = email_config.get('gemini_api_key', '')
484
+ signature = email_config.get('signature', '')
485
+ localAIURL = email_config.get('local_ai_url', '')
486
+
487
+ if len(str(openai_Process_Input)) > 0:
488
+ email_cat_data = {
489
+ "model_type": Model_Name,
490
+ "openai_api_key": openai_api_key,
491
+ "categories": categories,
492
+ "email_body": email_body,
493
+ "gemini_api_key": geminiAI_APIKey,
494
+ "signature": signature,
495
+ "local_ai_url": localAIURL,
496
+ }
497
+ email_classification = Email_Classification()
498
+ emailCategory = email_classification.detect_category(email_cat_data)
499
+ emailCategory = emailCategory['message']
500
+ logger.log(f"\nDetected Email category ::: {emailCategory}")
501
+
502
+ dataValues = {
503
+ 'Model_Name': Model_Name,
504
+ 'file_JsonArray': file_JsonArray,
505
+ 'openai_api_key': openai_api_key,
506
+ 'openai_Process_Input': openai_Process_Input,
507
+ 'subject': subject,
508
+ 'sender_email_addr': sender_email_addr,
509
+ 'cc_email_addr': cc_email_addr,
510
+ 'email_body': email_body,
511
+ 'email_config': email_config,
512
+ 'msg': msg,
513
+ 'geminiAI_APIKey': geminiAI_APIKey,
514
+ 'localAIURL': localAIURL,
515
+ 'signature': signature,
516
+ 'LABEL': LABEL,
517
+ 'mail': mail,
518
+ 'email_id': msg_id,
519
+ "uid": uid,
520
+ "to_email_addr": to_email_addr,
521
+ "user_id": user_id,
522
+ "is_html": is_html,
523
+ "import_file": True
524
+ }
525
+ processcategory = Process_Category()
526
+ processcategory.process_cat(emailCategory, dataValues)
774
527
 
775
- for part in msg.walk():
776
- if part.get_content_maintype() == 'multipart':
777
- continue
778
- if part.get('Content-Disposition') is None:
779
- continue
780
- filename = part.get_filename()
781
- if filename:
782
- filepath = os.path.join(date_folder, filename) # Save inside date-wise folder
528
+ return "success"
783
529
 
784
- with open(filepath, 'wb') as f:
785
- f.write(part.get_payload(decode=True))
786
- logger.log(f"\nAttachment saved: '{filepath}'")
787
- else:
788
- logger.log("\nNo Attachment found.")
789
- return filename
790
-
791
530
  def read_JSON_File(self, json_fileName):
792
531
  category_list = []
793
532
  categories = ""
@@ -812,56 +551,26 @@ class Email_Read:
812
551
  trace = traceback.format_exc()
813
552
  logger.log(f"Exception in writeJsonFile: {msg} \n {trace} \n DataType ::: {type(msg)}")
814
553
  raise Exception(msg)
815
-
816
- def get_JsonArray_values(self, category, jsonArray):
817
- responseMethod = ""
818
- parameters = ""
819
-
820
- for eachJson in jsonArray :
821
- for key, value in eachJson.items():
822
- if value == category:
823
- responseMethod = eachJson["Response_Method"]
824
- parameters = eachJson["Parameters"]
825
-
826
- return responseMethod, parameters
827
-
828
- def create_file_from_emailBody(self, text, sender_email_addr, parameters):
829
- base_folder = "ORDERS"
830
- today_date = datetime.today().strftime('%Y-%m-%d') # Format: YYYY-MM-DD
831
- order_folder = os.path.join(base_folder, today_date)
832
554
 
833
- # Ensure the date-wise folder exists
834
- os.makedirs(order_folder, exist_ok=True)
555
+ def log_email_login(self, user_id, email, model_name, login_status, base_dir="EMail_log"):
556
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
557
+ log_dir = os.path.join(base_dir, user_id)
558
+ os.makedirs(log_dir, exist_ok=True)
559
+ log_file_path = os.path.join(log_dir, f"{user_id}.csv")
835
560
 
836
- # Generate filename from sender's email
837
- fileName = sender_email_addr[sender_email_addr.find("<")+1:sender_email_addr.find("@")].strip().replace(".","_")
838
-
839
- if parameters["FILE_TYPE"] == "pdf":
840
- fileName = fileName + ".pdf"
841
- filePath = os.path.join(order_folder, fileName)
842
-
843
- pdf = FPDF()
844
- pdf.add_page()
845
- pdf.set_font("Arial", size=12)
846
- pdf.multi_cell(0, 10, text)
847
- pdf.output(filePath)
848
- logger.log(f"New PDF file created from email body and stored in '{filePath}'")
849
-
850
- elif parameters["FILE_TYPE"] == "txt":
851
- fileName = fileName + ".txt"
852
- filePath = os.path.join(order_folder, fileName)
853
-
854
- with open(filePath, "w") as file:
855
- file.write(text)
856
- logger.log(f"New TXT file created from email body and stored in '{filePath}'")
857
- else:
858
- message = f"Invalid File Type received."
859
- self.send_response(200)
860
- self.send_header('Content-type', 'text/html')
861
- self.end_headers()
862
- self.wfile.write(message.encode('utf-8'))
561
+ log_exists = os.path.isfile(log_file_path)
562
+ with open(log_file_path, mode='a', newline='') as file:
563
+ writer = csv.writer(file)
564
+ if not log_exists:
565
+ writer.writerow(["timestamp", "user_id", "email", "Model_Name", "login_status"])
566
+ writer.writerow([timestamp, user_id, email, model_name, login_status])
863
567
 
864
- return fileName
568
+ def update_status(self):
569
+ global shared_status
570
+ shared_status = False
865
571
 
866
-
572
+ def read_status(self):
573
+ global shared_status
574
+ return shared_status
867
575
 
576
+