def forward(self, to=[], cc=[], bcc=[], content=""): to = format_email_address(to) cc = format_email_address(cc) bcc = format_email_address(bcc) new_message_wrapper = self._create_message_instance( "Fwd: " + self.subject, to, cc, bcc, content) if not self._is_simulate: if new_message_wrapper: from engine.models.mailbox import MailBox # noqa: F401 ignore unused we use it for typing mailbox = MailBox(self._schema.imap_account, self._imap_client) mailbox._send_message( new_message_wrapper )
def reply(self, to=[], cc=[], bcc=[], content=""): # type: (t.Iterable[t.AnyStr], t.Iterable[t.AnyStr], t.Iterable[t.AnyStr], t.AnyStr) -> None """Reply to the sender of this message """ if not self._is_simulate: to_addr = "" if isinstance(to, list): to_addr = to.append(self._schema.from_) to = format_email_address(to_addr) else: to = format_email_address([self._schema.from_, to]) cc = format_email_address(cc) bcc = format_email_address(bcc) new_message_wrapper = self._create_message_instance( "Re: " + self.subject, to, cc, bcc, content) if new_message_wrapper: from engine.models.mailbox import MailBox # noqa: F401 ignore unused we use it for typing mailbox = MailBox(self._schema.imap_account, self._imap_client) mailbox._send_message( new_message_wrapper )
def apply_button_rule(user, email, er_id, msg_schema_id, kargs): res = {'status': False} try: logger.info("here") imapAccount = ImapAccount.objects.get(email=email) auth_res = authenticate(imapAccount) if not auth_res['status']: raise ValueError( 'Something went wrong during authentication. Refresh and try again!' ) imap = auth_res['imap'] # noqa: F841 ignore unused er = EmailRule.objects.get(id=er_id) # read from DB and convert to the type accordingly for key, value in kargs.iteritems(): er_arg = EmailRule_Args.objects.get(rule=er, name=key) # parse datetime if er_arg.type == "datetime": try: kargs[key] = datetime.datetime.strptime( value, '%Y-%m-%dT%H:%M') except Exception: res['code'] = key raise TypeError mailbox = MailBox(imapAccount, imap, is_simulate=False) res = interpret_bypass_queue(mailbox, extra_info={ "msg-id": msg_schema_id, "code": er.code, "shortcut": kargs, "rule_name": er.name }) logger.info(kargs) logger.info(er.code) logger.info(res) res['status'] = True except ImapAccount.DoesNotExist: res['code'] = "Error during deleting the mode. Please refresh the page." except MailbotMode.DoesNotExist: res['code'] = "Error during deleting the mode. Please refresh the page." except TypeError: res['code'] = "Datetime %s is in wrong format!" % res['code'] except Exception as e: logger.exception(e) res['code'] = msg_code['UNKNOWN_ERROR'] return res
def handle(self, *args, **options): # iterate over all the user accounts in the database imapAccounts = ImapAccount.objects.filter(is_initialized=False) for imapAccount in imapAccounts: if imapAccount.is_running: continue imapAccount.is_running = True imapAccount.save() res = {'status': False, 'imap_error': False} logger.info("run initial sync for email: %s" % imapAccount.email) # authenticate with the user's imap server auth_res = authenticate(imapAccount) # if authentication failed we can't run anything if not auth_res['status']: continue # get an imapclient which is authenticated imap = auth_res['imap'] try: # create the mailbox mailbox = MailBox(imapAccount, imap) # sync the mailbox with imap mailbox._sync() logger.info("Mailbox sync done") # after sync, logout to prevent multi-connection issue imap.logout() logger.info( "Mailbox logged out to prevent multi-connection issue") mailbox._run_user_code() except Exception: logger.exception("mailbox task running failed %s " % imapAccount.email) send_email("Your YoUPS account is ready!", "no-reply@" + BASE_URL, '*****@*****.**', "%s register inbox failed " % imapAccount.email) continue imapAccount.is_initialized = True imapAccount.is_running = False imapAccount.save() res['status'] = True
def register_inbox(): """Do the initial sync on an inbox. """ lockFile = 'register_inbox2.lock' with open(lockFile, 'w') as f: have_lock = get_lock(f) if not have_lock: logger.info('Lock already taken %s' % lockFile) return for imapAccount in ImapAccount.objects.filter(is_initialized=False): try: logger.info('registering inbox: %s', imapAccount.email) while True: try: # authenticate with the user's imap server auth_res = authenticate(imapAccount) # if authentication failed we can't run anything if not auth_res['status']: # Stop doing loop # TODO maybe we should email the user logger.critical('register authentication failed for %s', imapAccount.email) continue # get an imapclient which is authenticated imap = auth_res['imap'] # type: IMAPClient # create the mailbox mailbox = MailBox(imapAccount, imap) # TODO(lukemurray): remove this mailbox._log_message_ids() # sync the mailbox with imap done = mailbox._sync() if done: break # if we catch an EOF error we continue except imaplib.IMAP4.abort: logger.exception("Caught EOF error while syncing") try: imap.logout() except Exception: logger.exception("Failure while logging out due to EOF bug") continue # if we catch any other type of exception we abort to avoid infinite loop except Exception: logger.critical("Failure while initially syncing") logger.exception("Failure while initially syncing") raise logger.info("After sync, set up an exercise folder for the new user") try: imap.create_folder("_YouPS exercise") msg1 = mailbox._create_message_wrapper("Welcome to YouPS!", imapAccount.email, content="This is the test email from YouPS", content_html="This is the test email from YouPS") msg1["From"] = "*****@*****.**" imap.append("_YouPS exercise", str(msg1)) msg1 = mailbox._create_message_wrapper("[Example email] Follow up", imapAccount.email + ", [email protected]", cc="*****@*****.**", content="Hello! I just wanted to follow up regarding our last meeting! Let me know how you think!", content_html="Hello! I just wanted to follow up regarding our last meeting! Let me know how you think!") msg1["From"] = "*****@*****.**" imap.append("_YouPS exercise", str(msg1)) msg1 = mailbox._create_message_wrapper("[Example email] Blah blah", imapAccount.email + ", [email protected]", cc="*****@*****.**", content="Howdy y'all!", content_html="Howdy y'all!") msg1["From"] = "*****@*****.**" imap.append("_YouPS exercise", str(msg1)) except Exception as e: logger.exception(e) logger.info("Mailbox sync done: %s" % (imapAccount.email)) # after sync, logout to prevent multi-connection issue imap.logout() imapAccount.is_initialized = True imapAccount.save() site = Site.objects.get_current() # TODO(lukemurray): bring this back # send_email("Your YouPS account is ready!", # "no-reply@" + BASE_URL, # imapAccount.email, # "Start writing your automation rule here! %s://%s" % (PROTOCOL, site.domain)) # Create a default mode & email rule to demo logger.info( 'Register done for %s', imapAccount.email) except ImapAccount.DoesNotExist: imapAccount.is_initialized = False imapAccount.save() logger.exception( "syncing fails Remove periodic tasks. imap_account not exist %s" % (imapAccount.email)) except Exception as e: logger.exception( "User inbox syncing fails %s. Stop syncing %s" % (imapAccount.email, e))
def loop_sync_user_inbox(): lockFile = 'loop_sync_user_inbox2.lock' with open(lockFile, 'w') as f: have_lock = get_lock(f) if not have_lock: logger.info('Lock already taken %s' % lockFile) return imapAccounts = ImapAccount.objects.filter( is_initialized=True) # type: t.List[ImapAccount] for imapAccount in imapAccounts: # if imapAccount.email not in ["*****@*****.**", "*****@*****.**", "*****@*****.**"]: # continue # refresh from database imapAccount = ImapAccount.objects.get(id=imapAccount.id) if not imapAccount.is_initialized: continue imapAccount_email = imapAccount.email try: logger.info('Start syncing %s ', imapAccount_email) # authenticate with the user's imap server auth_res = authenticate(imapAccount) # if authentication failed we can't run anything if not auth_res['status']: # Stop doing loop # TODO maybe we should email the user logger.critical('authentication failed for %s' % imapAccount.email) continue # get an imapclient which is authenticated imap = auth_res['imap'] # create the mailbox try: mailbox = MailBox(imapAccount, imap) # TODO(lukemurray): remove this mailbox._log_message_ids() # sync the mailbox with imap mailbox._sync() logger.info(mailbox.event_data_list) except Exception: logger.exception("Mailbox sync failed") # TODO maybe we should email the user continue logger.debug("Mailbox sync done: %s" % (imapAccount_email)) try: # get scheduled tasks email_rules = EmailRule.objects.filter(mode=imapAccount.current_mode, type__startswith='new-message-') # type: t.List[EmailRule] for email_rule in email_rules: # Truncate millisec since mysql doesn't suport msec. now = timezone.now().replace(microsecond=0) + datetime.timedelta(seconds=1) mailbox._manage_task(email_rule, now) # mark timestamp to prevent running on certain message multiple times email_rule.executed_at = now + datetime.timedelta(seconds=1) email_rule.save() logger.info(mailbox.event_data_list) except Exception: logger.exception("Mailbox managing task failed") # TODO maybe we should email the user continue logger.debug("Mailbox managing task done: %s" % (imapAccount_email)) try: # get deadline tasks email_rules = EmailRule.objects.filter(mode=imapAccount.current_mode, type='deadline') # type: t.List[EmailRule] for email_rule in email_rules: # Truncate millisec since mysql doesn't suport msec. now = timezone.now().replace(microsecond=0) + datetime.timedelta(seconds=1) mailbox._get_due_messages(email_rule, now) # mark timestamp to prevent running on certain message multiple times email_rule.executed_at = now + datetime.timedelta(seconds=1) email_rule.save() logger.info(mailbox.event_data_list) except Exception: logger.exception("Mailbox managing task failed") # TODO maybe we should email the user continue try: res = mailbox._run_user_code() dump_execution_log(imapAccount, res['imap_log']) except Exception(): logger.exception("Mailbox run user code failed") # after sync, logout to prevent multi-connection issue imap.logout() logger.info( 'Sync done for %s', imapAccount_email) except ImapAccount.DoesNotExist: imapAccount.is_initialized = False imapAccount.save() logger.exception( "syncing fails Remove periodic tasks. imap_account not exist %s" % (imapAccount_email)) except Exception as e: logger.exception( "User inbox syncing fails %s. Stop syncing %s" % (imapAccount_email, e))
class Command(BaseCommand): args = '' help = 'Process email' # Auto-send messages to TEST_ACCOUNT_EMAIL and see the results match expected results by running multiple unit tests def handle(self, *args, **options): if len(args) == 0: print("option: send-test|run-test") print( "You should run `send-test` at least once prior to `run-test`") return test_emails = [ { 'subject': 'test email %s ' % str(datetime.datetime.now().strftime("%m/%d %H:%M:%S,%f")), 'from_addr': TEST_ACCOUNT_EMAIL, 'to': "*****@*****.**", 'cc': "", 'bcc': "", 'body_plain': 'hello world', 'body_html': 'hi' }, { 'subject': 'test email with emoji 🤷♀️ %s ' % str(datetime.datetime.now().strftime("%m/%d %H:%M:%S,%f")), 'from_addr': TEST_ACCOUNT_EMAIL, 'to': "[email protected], [email protected]", 'cc': "[email protected], [email protected]", 'bcc': "[email protected], [email protected], [email protected]", 'body_plain': '😎', 'body_html': '😎' }, ] imapAccount = None imap = None # Auth to test email accountss try: imapAccount = ImapAccount.objects.get(email=TEST_ACCOUNT_EMAIL) except ImapAccount.DoesNotExist: login_imap(TEST_ACCOUNT_EMAIL, TEST_ACCOUNT_PASSWORD, 'imap.gmail.com', is_oauth=False) print( "Just created a YouPS account for a test account. It will take couple minutes to set up" ) return try: auth_res = authenticate(imapAccount) if not auth_res['status']: raise ValueError( 'Something went wrong during authentication. Refresh and try again!' ) imap = auth_res['imap'] # noqa: F841 ignore unused except Exception, e: print("failed logging into imap", str(e)) return if args[0] == "send-test": mailbox = MailBox(imapAccount, imap, is_simulate=False) for i in range(len(test_emails)): test = test_emails[i] mailbox.send(subject="#%d " % i + test['subject'].decode('utf-8'), to=test['to'], cc=test['cc'], bcc=test['bcc'], \ body=test['body_plain'].decode('utf-8'), body_html=test['body_html'].decode('utf-8')) # TODO cc, bcc # index = 0 # for t in test_emails: # # # TODO send using django core # # send_mail("#%d " % index + t['subject'].decode('utf-8'), t['body_plain'], TEST_ACCOUNT_EMAIL, [TEST_ACCOUNT_EMAIL]) # send_email("#%d " % index + t['subject'].decode('utf-8'), t['from_addr'], TEST_ACCOUNT_EMAIL, t['body_plain'].decode('utf-8'), t['body_html'].decode('utf-8')) # index = index + 1 elif args[0] == "run-test": test_cases = [ [ # test cases for #0 email { 'code': 'print (my_message.from_)', 'expected': TEST_ACCOUNT_EMAIL }, { 'code': 'print("True" if "%s" == my_message.from_ else "") ' % TEST_ACCOUNT_EMAIL, 'expected': "True" }, { 'code': 'print("True" if "test email " in my_message.subject else "") ', 'expected': 'True' }, ], [{ 'code': 'print ("True" if "🤷♀️" in my_message.subject else "")', 'expected': 'True' }, { 'code': 'print ("True" if my_message.contains("😎") else "")', 'expected': 'True' }, { 'code': 'print ("*****@*****.**" in my_message.to)', 'expected': 'True' }, { 'code': 'print ("*****@*****.**" in my_message.cc)', 'expected': 'True' }, { 'code': 'print (len(my_message.cc) == 2)', 'expected': 'True' }, { 'code': """my_message.add_flags("test")\n\tprint (my_message.has_flag("test"))""", 'expected': 'True' }, { 'code': """my_message.remove_flags("test")\n\tprint (my_message.has_flag("test"))""", 'expected': 'False' }] ] # Run test try: folder_names = ["INBOX"] for msg_index in range(len(test_cases)): for folder_name in folder_names: # pick up recent messages message = MessageSchema.objects.filter( \ imap_account=imapAccount, folder__name=folder_name, base_message__subject__startswith='#%d ' % msg_index).order_by("-base_message__date") if not message.exists(): print( "Unable to load the corresponding message #%d %s" % (msg_index, test_emails[msg_index]['subject'])) continue message = message[0] assert isinstance(message, MessageSchema) mailbox = MailBox(imapAccount, imap, is_simulate=False) for test_per_message_index in range( len(test_cases[msg_index])): imap_res = interpret_bypass_queue(mailbox, extra_info={'code': "def on_message(my_message):\n\t" + \ test_cases[msg_index][test_per_message_index]['code'].decode("utf-8", "replace"), 'msg-id': message.id}) # print(imap_res) try: # print(imap_res['appended_log'][message.id]) result = imap_res['appended_log'][ message.id]['log'].rstrip("\n\r") assert result == test_cases[msg_index][ test_per_message_index]['expected'] except AssertionError: print ("CASE #%d-%d %s (expected %s)" % (msg_index, test_per_message_index, \ result, test_cases[msg_index][test_per_message_index]['expected'])) continue print("SUCCESS #%d-%d %s" % (msg_index, test_per_message_index, message.base_message.subject)) except Exception, e: print("failed while doing a user code run %s %s " % (e, traceback.format_exc())) finally:
except Exception, e: logger.exception("failed while logging into imap") res['code'] = "Fail to access your IMAP account" return try: res['messages'] = {} messages = MessageSchema.objects.filter( imap_account=imapAccount, folder_schema__name=folder_name).order_by("-date")[:N] imap.select_folder(folder_name) for message_schema in messages: imap_res = interpret(MailBox(imapAccount, imap), None, bypass_queue=True, is_simulate=True, extra_info={ 'code': code, 'msg-id': message_schema.id }) logger.info(imap_res) message = Message(message_schema, imap) from_field = None if message_schema.from_m: from_field = { "name": message.from_.name,
def register_inbox(): """Do the initial sync on an inbox. """ lockFile = 'register_inbox.lock' with open(lockFile, 'w') as f: have_lock = get_lock(f) if not have_lock: logger.info('Lock already taken %s' % lockFile) return for imapAccount in ImapAccount.objects.filter(is_initialized=False): try: logger.info('registering inbox: %s', imapAccount.email) while True: try: # authenticate with the user's imap server auth_res = authenticate(imapAccount) # if authentication failed we can't run anything if not auth_res['status']: # Stop doing loop # TODO maybe we should email the user logger.critical( 'register authentication failed for %s', imapAccount.email) continue # get an imapclient which is authenticated imap = auth_res['imap'] # type: IMAPClient # create the mailbox mailbox = MailBox(imapAccount, imap) # sync the mailbox with imap done = mailbox._sync() if done: break # if we catch an EOF error we continue except imaplib.IMAP4.abort: logger.exception("Caught EOF error while syncing") try: imap.logout() except Exception: logger.exception( "Failure while logging out due to EOF bug") continue # if we catch any other type of exception we abort to avoid infinite loop except Exception: logger.critical("Failure while initially syncing") logger.exception("Failure while initially syncing") raise logger.info("Mailbox sync done: %s" % (imapAccount.email)) # after sync, logout to prevent multi-connection issue imap.logout() imapAccount.is_initialized = True imapAccount.save() site = Site.objects.get_current() send_email( "Your YoUPS account is ready!", "no-reply@" + BASE_URL, imapAccount.email, "Start writing your automation rule here! %s://%s" % (PROTOCOL, site.domain)) logger.info('Register done for %s', imapAccount.email) except ImapAccount.DoesNotExist: imapAccount.is_initialized = False imapAccount.save() logger.exception( "syncing fails Remove periodic tasks. imap_account not exist %s" % (imapAccount.email)) except Exception as e: logger.exception( "User inbox syncing fails %s. Stop syncing %s" % (imapAccount.email, e))
except Exception, e: logger.exception("failed while logging into imap") res['code'] = "Fail to access your IMAP account" return try: res['messages'] = {} for folder_name in folder_names: messages = MessageSchema.objects.filter( imap_account=imapAccount, folder__name=folder_name).order_by("-base_message__date")[:N] for message_schema in messages: assert isinstance(message_schema, MessageSchema) mailbox = MailBox(imapAccount, imap, is_simulate=True) imap_res = interpret_bypass_queue(mailbox, None, extra_info={ 'code': code, 'msg-id': message_schema.id }) logger.debug(imap_res) message = Message(message_schema, imap) from_field = None if message.from_: from_field = { "name": message.from_.name,
def START(message, address=None, host=None): logger.info("Email to mailbot@") arrived_message = message name, addr = parseaddr(arrived_message['from'].lower()) site = Site.objects.get_current() auth_res = None # restart the db connection django.db.close_connection() try: addr = addr.strip() imapAccount = ImapAccount.objects.get(email=addr) er_to_execute = None ers = EmailRule.objects.filter(mode__imap_account=imapAccount, type='shortcut') for er in ers: if er.get_forward_addr().lower() == address.lower(): er_to_execute = er break if not er_to_execute: body_part = [] body = {} options = get_available_shortcut_link_text(imapAccount, site.domain) body["text"] = "You email to %s@%s but this shortcut does not exist. \n\n %s \n\n Link to YouPS: %s://%s" % (address, host, options["text"], PROTOCOL, site.domain) body["html"] = "You email to %s@%s but this shortcut does not exist. <br><br> %s <br><br> Link to YouPS: <a href='%s://%s'>%s://%s</a>" % (address, host, options["html"], PROTOCOL, site.domain, PROTOCOL, site.domain) new_message = create_response(arrived_message, addr,arrived_message["message-id"], body, host) relay.deliver(new_message) return logging.debug("mailbot %s" % addr) auth_res = authenticate( imapAccount ) if not auth_res['status']: raise ValueError('Something went wrong during authentication. Log in again at %s/editor' % host) imap = auth_res['imap'] mailbox = MailBox(imapAccount, imap) # local shortcut if arrived_message["In-Reply-To"]: # Get the original message original_message_schema = MessageSchema.objects.filter(imap_account=imapAccount, base_message__message_id=arrived_message["In-Reply-To"]) if original_message_schema.exists(): original_message_schema = original_message_schema[0] imap.select_folder(original_message_schema.folder.name) original_message = Message(original_message_schema, imap) else: # in case YouPS didn't register to DB yet, save the message to DB immediately mail_found_at = "" original_message_id = -1 for folder in mailbox._list_selectable_folders(): imap.select_folder(folder.name) original_message_id = imap.search(["HEADER", "Message-ID", arrived_message["In-Reply-To"]]) # original_message if original_message_id: mail_found_at = folder break if not mail_found_at: raise ValueError("Email does not exist. The message is deleted or YouPS can't detect the message.") else: # Save this message immediately. so it can be ran when it is registered to the database try: logger.critical("%s %s" %(imapAccount.email, mail_found_at)) folder = mail_found_at if original_message_id: folder._save_new_messages(original_message_id[0], urgent=True) original_message_schema = MessageSchema.objects.filter(imap_account=imapAccount, base_message__message_id=arrived_message["In-Reply-To"]) if not original_message_schema.exists(): raise imap.select_folder(original_message_schema.folder.name) original_message = Message(original_message_schema, imap) except FolderSchema.DoesNotExist, MessageSchema.DoesNotExist: raise ValueError("Email does not exist. The message is deleted or YouPS can't detect the message.") entire_message = message_from_string(str(arrived_message)) entire_body = get_body(entire_message) code_body = entire_body['plain'][:(-1)*len(original_message.content['text'])] gmail_header = "---------- Forwarded message ---------" if gmail_header in code_body: code_body = code_body.split(gmail_header)[0].strip() logging.debug(code_body) shortcuts = EmailRule.objects.filter(mode=imapAccount.current_mode, type="shortcut") if not imapAccount.current_mode or not shortcuts.exists(): body = "Your YouPS hasn't turned on or don't have email shortcuts yet! Define your shortcuts here %s://%s" % (PROTOCOL, site.domain) mail = MailResponse(From = WEBSITE+"@" + host, To = imapAccount.email, Subject = "Re: " + original_message.subject, Body = body) relay.deliver(mail) else: res, body = run_shortcut(shortcuts, mailbox, original_message_schema, code_body) # Go to sent folder and delete the sent function from user if imapAccount.is_gmail: imap.select_folder('[Gmail]/Sent Mail') else: import imapclient sent = imap.find_special_folder(imapclient.SENT) if sent is not None: imap.select_folder(sent) this_message = imap.search(["HEADER", "In-Reply-To", original_message_schema.message_id]) imap.delete_messages(this_message) # new_message.set_payload(content.encode('utf-8')) if "text" in body and "html" in body: body["text"] = "Your command: %s%sResult: %s" % (code_body, "\n\n", body["text"]) body["html"] = "Your command: %s%sResult: %s" % (code_body, "<br><br>", body["html"]) else: body["text"] = "Your command:%s%sResult:%s" % (code_body, "\n\n", body["text"]) new_message = create_response(arrived_message, addr, original_message_schema.message_id, body, host) try: new_msg = {} from_field = original_message._get_from_friendly() to_field = original_message._get_to_friendly() cc_field = original_message._get_cc_friendly() new_msg["timestamp"] = str(datetime.now().strftime("%m/%d %H:%M:%S,%f")) new_msg["type"] = "new_message" new_msg["from_"] = from_field new_msg["to"] = to_field new_msg["cc"] = cc_field new_msg["trigger"] = "shortcut" new_msg["log"] = body["text"] new_msg.update(original_message._get_meta_data_friendly()) except Exception: logger.critical("error adding logs") imap.append(original_message_schema.folder.name, str(new_message)) # instead of sending email, just replace the forwarded email to arrive on the inbox quietly # global shortcut else:
def run_simulate_on_messages(user, email, folder_names, N=3, code=''): """This function is called to evaluate user's code on messages Args: user (Model.UserProfile) email (string): user's email address folder_name (string): name of a folder to extract messages N (int): number of recent messages code (string): code to be simulated """ res = {'status': False, 'imap_error': False, 'imap_log': ""} logger = logging.getLogger('youps') # type: logging.Logger # this log is going to stdout but not going to the logging file # why are django settings not being picked up logger.info("user %s has requested simulation" % email) imapAccount = None imap = None try: imapAccount = ImapAccount.objects.get(email=email) auth_res = authenticate(imapAccount) if not auth_res['status']: raise ValueError( 'Something went wrong during authentication. Refresh and try again!' ) imap = auth_res['imap'] # noqa: F841 ignore unused except Exception as e: logger.exception("failed while logging into imap") res['code'] = "Fail to access your IMAP account" return try: res['messages'] = {} for folder_name in folder_names: messages = MessageSchema.objects.filter( imap_account=imapAccount, folder__name=folder_name).order_by("-base_message__date")[:N] for message_schema in messages: assert isinstance(message_schema, MessageSchema) mailbox = MailBox(imapAccount, imap, is_simulate=True) imap_res = interpret_bypass_queue(mailbox, extra_info={ 'code': code, 'msg-id': message_schema.id }) logger.debug(imap_res) message = Message(message_schema, imap) from_field = None if message.from_: from_field = { "name": message.from_.name, "email": message.from_.email, "organization": message.from_.organization, "geolocation": message.from_.geolocation } to_field = [{ "name": tt.name, "email": tt.email, "organization": tt.organization, "geolocation": tt.geolocation } for tt in message.to] cc_field = [{ "name": tt.name, "email": tt.email, "organization": tt.organization, "geolocation": tt.geolocation } for tt in message.cc] # TODO attach activated line # This is to log for users new_msg = { "timestamp": str(datetime.datetime.now().strftime("%m/%d %H:%M:%S,%f")), "type": "new_message", "folder": message.folder.name, "from_": from_field, "subject": message.subject, "to": to_field, "cc": cc_field, "flags": [f.encode('utf8', 'replace') for f in message.flags], "date": str(message.date), "deadline": str(message.deadline), "is_read": message.is_read, "is_deleted": message.is_deleted, "is_recent": message.is_recent, "log": imap_res['appended_log'][message_schema.id]['log'], "error": imap_res['appended_log'][message_schema.id]['error'] if 'error' in imap_res['appended_log'][message_schema.id] else False } res['messages'][message_schema.id] = new_msg res['status'] = True except ImapAccount.DoesNotExist: logger.exception("failed while doing a user code run") res['code'] = "Not logged into IMAP" except FolderSchema.DoesNotExist: logger.exception("failed while doing a user code run") logger.debug("Folder is not found, but it should exist!") except Exception as e: logger.exception("failed while doing a user code run %s %s " % (e, traceback.format_exc())) res['code'] = msg_code['UNKNOWN_ERROR'] finally: imap.logout() logging.debug(res) return res
def fetch_watch_message(user, email, watched_message): res = {'status': False, 'log': "", 'messages': {}} try: imapAccount = ImapAccount.objects.get(email=email) res['watch_status'] = True except ImapAccount.DoesNotExist: res['code'] = "Error during authentication. Please refresh" return try: imap = None auth_res = authenticate(imapAccount) if not auth_res['status']: raise ValueError( 'Something went wrong during authentication. Refresh and try again!' ) imap = auth_res['imap'] # noqa: F841 ignore unused except Exception as e: logger.exception("failed while logging into imap") res['code'] = "Fail to access your IMAP account" return try: if res['watch_status']: imapAccount.sync_paused = True mailbox = MailBox(imapAccount, imap, is_simulate=False) msgs = None cnt = 0 while True: for folder in mailbox._list_selectable_folders(): response = imap.select_folder(folder.name) highest_mod_seq = response.get('HIGHESTMODSEQ') logger.debug(highest_mod_seq) # this mailbox is using highest mod_seq and there is no update if highest_mod_seq is not None and folder._highest_mod_seq <= 0: continue logger.info("refresh flags") msgs = folder._refresh_flag_changes(highest_mod_seq) if msgs: for r in msgs: # if this message is already caught, skip to next to find another new msgs logger.debug(r.id) logger.debug(watched_message) if str(r.id) in watched_message: continue logger.info(r.base_message.subject) message = Message( r, imap_client="" ) # since we are only extracting metadata, no need to use imap_client res['message_schemaid'] = r.id res['message'] = message._get_meta_data_friendly() res['sender'] = message._get_from_friendly() try: message_arrival_time = dateutil.parser.parse( res["message"]["date"]) # if the message arrives today, send only hour and minute if message_arrival_time.date( ) == datetime.datetime.today().date(): res["message"]["date"] = "%d:%02d" % ( message_arrival_time.hour, message_arrival_time.minute) except: logger.exception( "parsing arrival date fail; skipping parsing" ) res['context'] = { 'sender': res['sender']["name"], "subject": res['message']['subject'], "date": res["message"]["date"], "message_id": res['message_schemaid'] } # if there is update, send it to the client immediatly if 'message' in res: res['status'] = True return res # r=requests.get(url, headers=headers) # if r.json()['deltas']: # break # # logger.info("finding delta..") if cnt == 10: break cnt = cnt + 1 sleep(0.01) # for d in r.json()['deltas']: # if d['object'] == "message" or d['object'] == "thread": # logger.info(d['attributes']['subject']) # res["log"] = d['attributes']['subject'] # if bc and bc.code == ButtonChannel.OK: # res['folder'] = bc.message.folder.name # res['uid'] = bc.message.id # message = Message(bc.message, imap_client="") # since we are only extracting metadata, no need to use imap_client # res['message'] = message._get_meta_data_friendly() # res['sender'] = message._get_from_friendly() # else: # # if something went wrong only return the log # logger.info(bc.code) # res["log"] = "%s - %s" % (bc.get_code_display(), bc.log) res['status'] = True except ButtonChannel.DoesNotExist: res['uid'] = 0 except Exception as e: logger.exception(e) res['code'] = msg_code['UNKNOWN_ERROR'] finally: logger.info("Finish watching cycle") imapAccount.sync_paused = False imap.logout() return res
def mailbot(arrived_message, address=None, host=None): # no public groups to list on squadbox. if WEBSITE == 'squadbox' or WEBSITE == 'murmur': logging.debug("Ignored message to all@%s, no public groups to list" % HOST) return else: logger.info("Email to mailbot@%s" % HOST) name, addr = parseaddr(arrived_message['from'].lower()) site = None # restart the db connection django.db.close_connection() try: site = Site.objects.get_current() addr = addr.strip() imapAccount = ImapAccount.objects.get(email=addr) logging.debug("mailbot %s" % addr) auth_res = authenticate(imapAccount) if not auth_res['status']: raise ValueError( 'Something went wrong during authentication. Log in again at %s/editor' % host) imap = auth_res['imap'] mailbox = MailBox(imapAccount, imap) # Get the original message original_message_schema = MessageSchema.objects.filter( imap_account=imapAccount, message_id=arrived_message["In-Reply-To"]) if original_message_schema.exists(): original_message_schema = original_message_schema[0] imap.select_folder(original_message_schema.folder.name) original_message = Message(original_message_schema, imap) else: # in case YoUPS didn't register to DB yet, save the message to DB immediately mail_found_at = "" original_message_id = -1 for folder in mailbox._list_selectable_folders(): imap.select_folder(folder.name) original_message_id = imap.search([ "HEADER", "Message-ID", arrived_message["In-Reply-To"] ]) # original_message if original_message_id: mail_found_at = folder break if not mail_found_at: raise ValueError( "Email does not exist. The message is deleted or YoUPS can't detect the message." ) else: # Save this message immediately. so it can be ran when it is registered to the database try: logger.critical("%s %s" % (imapAccount.email, mail_found_at)) folder = mail_found_at if original_message_id: folder._save_new_messages(original_message_id[0], urgent=True) original_message_schema = MessageSchema.objects.filter( imap_account=imapAccount, message_id=arrived_message["In-Reply-To"]) if not original_message_schema.exists(): raise imap.select_folder( original_message_schema.folder.name) original_message = Message(original_message_schema, imap) except FolderSchema.DoesNotExist, MessageSchema.DoesNotExist: raise ValueError( "Email does not exist. The message is deleted or YoUPS can't detect the message." ) entire_message = message_from_string(str(arrived_message)) entire_body = get_body(entire_message) code_body = entire_body['plain'][:( -1) * len(original_message.content['text'])] gmail_header = "---------- Forwarded message ---------" if gmail_header in code_body: code_body = code_body.split(gmail_header)[0].strip() logging.debug(code_body) shortcuts = EmailRule.objects.filter(mode=imapAccount.current_mode, type="shortcut") if not imapAccount.current_mode or not shortcuts.exists(): body = "Your YoUPS hasn't turned on or don't have email shortcuts yet! Define your shortcuts here %s://%s" % ( PROTOCOL, site.domain) mail = MailResponse(From=WEBSITE + "@" + host, To=imapAccount.email, Subject="Re: " + original_message.subject, Body=body) relay.deliver(mail) else: body = {"text": "", "html": ""} for shortcut in shortcuts: res = interpret_bypass_queue( mailbox, None, extra_info={ "msg-id": original_message_schema.id, "code": shortcut.code, "shortcut": code_body }) logging.debug(res) for key, value in res['appended_log'].iteritems(): if not value['error']: body[ "text"] = 'Your mail shortcut is successfully applied! \n' body[ "html"] = 'Your mail shortcut is successfully applied! <br>' else: body["text"] = 'Something went wrong! \n' body["html"] = 'Something went wrong! <br>' body["text"] = body["text"] + value['log'] body["html"] = body["html"] + value['log'] logger.debug(body) # Go to sent folder and delete the sent function from user if imapAccount.is_gmail: imap.select_folder('[Gmail]/Sent Mail') else: import imapclient sent = imap.find_special_folder(imapclient.SENT) if sent is not None: imap.select_folder(sent) this_message = imap.search([ "HEADER", "In-Reply-To", original_message_schema.message_id ]) imap.delete_messages(this_message) new_message = MIMEMultipart('alternative') new_message["Subject"] = "Re: " + arrived_message["subject"] new_message["From"] = WEBSITE + "@" + host new_message["In-Reply-To"] = original_message_schema.message_id try: new_msg = {} from_field = original_message._get_from_friendly() to_field = original_message._get_to_friendly() cc_field = original_message._get_cc_friendly() new_msg["timestamp"] = str( datetime.now().strftime("%m/%d %H:%M:%S,%f")) new_msg["type"] = "new_message" new_msg["from_"] = from_field new_msg["to"] = to_field new_msg["cc"] = cc_field new_msg["trigger"] = "shortcut" new_msg["log"] = body["text"] new_msg.update(original_message._get_meta_data_friendly()) log_decoded = json.loads(imapAccount.execution_log) if len( imapAccount.execution_log) else {} log_decoded[new_msg["timestamp"]] = new_msg imapAccount.execution_log = json.dumps(log_decoded) imapAccount.save() except Exception: logger.critical("error adding logs") # new_message.set_payload(content.encode('utf-8')) if "text" in body and "html" in body: body["text"] = "Your command: %s%sResult: %s" % ( code_body, "\n\n", body["text"]) body["html"] = "Your command: %s%sResult: %s" % ( code_body, "<br><br>", body["html"]) part1 = MIMEText(body["text"].encode('utf-8'), 'plain') part2 = MIMEText(body["html"].encode('utf-8'), 'html') new_message.attach(part1) new_message.attach(part2) else: body["text"] = "Your command:%s%sResult:%s" % ( code_body, "\n\n", body["text"]) part1 = MIMEText(body["text"].encode('utf-8'), 'plain') new_message.attach(part1) imap.append(original_message_schema.folder.name, str(new_message)) # instead of sending email, just replace the forwarded email to arrive on the inbox quietly except ImapAccount.DoesNotExist: subject = "YoUPS shortcuts Error" error_msg = 'Your email %s is not registered or stopped due to an error. Write down your own email rule at %s://%s' % ( addr, PROTOCOL, site.domain) mail = MailResponse(From=WEBSITE + "@" + host, To=arrived_message['From'], Subject=subject, Body=error_msg) relay.deliver(mail)