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 run_shortcut(shortcuts, mailbox, original_message_schema, code_body): body = {"text": "", "html": ""} res = None for shortcut in shortcuts: extra_info={"msg-id": original_message_schema.id, "code": shortcut.code, "shortcut": code_body} res = interpret_bypass_queue(mailbox, extra_info={"msg-id": original_message_schema.id, "code": shortcut.code, "shortcut": code_body}) logger.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) return res, body
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:
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, "email": message.from_.email, "organization": message.from_.organization, "geolocation": message.from_.geolocation }
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 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)