Example #1
0
        /* TODO: Refactor to smaller functions
         * Checks if the email needs to be parsed for Door Dash or GrubHub,
         * and saves the order to file for reference
         */
        private static Order HandleMessage(string messageId)
        {
            Debug.WriteLine("Handling message: " + messageId);

            bool   isGrubHubOrder = false;
            string base64Input    = null; //the input to be converted to base64url encoding format
            string fileName       = null; //the file name without the complete path
            string storageDir     = null; //the file saving directory
            string filePath       = null; //the full path to the file

            var emailResponse = GetMessage(service, userId, messageId);

            if (emailResponse == null)
            {
                Debug.WriteLine("Message deleted, returning");
                return(null);
            }

            var headers = emailResponse.Payload.Headers;
            MessagePartHeader dateHeader = headers.FirstOrDefault(item => item.Name == "Received");
            MessagePartHeader fromHeader = headers.FirstOrDefault(item => item.Name == "From");

            DateTime dateTime      = ParseDateTime(dateHeader.Value);
            string   senderAddress = fromHeader.Value;

            //Check if the email is from GrubHub
            if (fromHeader.Value == "*****@*****.**")
            {
                Debug.WriteLine("Email Type: GrubHub");
                isGrubHubOrder = true;
                try {
                    var body = emailResponse.Payload.Body.Data;

                    base64Input = body;
                    fileName    = messageId + ".html";
                    storageDir  = GrubHubDir;
                } catch (Exception e) {
                    Debug.WriteLine(e.Message);
                }
                //Otherwise check if the email is from DoorDash
            }
            else if (fromHeader.Value == @"DoorDash <*****@*****.**>")
            {
                Debug.WriteLine("Email Type: DoorDash");
                try {
                    var attachId = emailResponse.Payload.Parts[1].Body.AttachmentId;

                    //Need to do another API call to get the actual attachment from the attachment id
                    MessagePartBody attachPart = GetAttachment(service, userId, messageId, attachId);

                    base64Input = attachPart.Data;
                    fileName    = messageId + ".pdf";
                    storageDir  = DoorDashDir;
                }catch (Exception e) {
                    Debug.WriteLine(e.Message);
                }
                //The email is refers to a cancelled GrubHub order, so set the associated OrderCons' status to cancelled
            }
            else if (fromHeader.Value == "*****@*****.**")
            {
                Debug.WriteLine("From [email protected]");
                MessagePartHeader subjectHeader = headers.FirstOrDefault(item => item.Name == "Subject");
                string            orderNum      = subjectHeader.Value.Split(' ')[1]; //gets orderNum from ("Order" {orderNum} "Cancelled")
                Debug.WriteLine("OrderNum: " + orderNum);
                if (form1.InvokeRequired)
                {
                    form1.Invoke((MethodInvoker) delegate { form1.SetOrderToCancelled(orderNum); });
                }
                else
                {
                    form1.SetOrderToCancelled(orderNum);
                }
                return(null);
                //The email is irrelevant
            }
            else
            {
                Debug.WriteLine("Not an order, returning");
                return(null);
            }

            byte[] data = FromBase64ForUrlString(base64Input);
            filePath = Path.Combine(storageDir, fileName);

            //Saves the order to file if it doesn't exist
            if (!File.Exists(filePath))
            {
                Debug.WriteLine("Writing new file: " + fileName);
                File.WriteAllBytes(filePath, data);
            }
            else
            {
                Debug.WriteLine("File already exists: " + fileName);
            }
            Debug.WriteLine("----------------------");

            if (isGrubHubOrder)
            {
                GrubHubParser grubHubParser = new GrubHubParser();
                string        decodedBody   = Encoding.UTF8.GetString(data);
                Order         order         = grubHubParser.ParseOrder(decodedBody, dateTime, messageId);

                if (DebugPrint)
                {
                    order.PrintOrder();
                }
                return(order);
            }
            else
            {
                DoorDashParser doorDashParser = new DoorDashParser();
                List <string>  lines          = doorDashParser.ExtractTextFromPDF(filePath, messageId);
                Order          order          = doorDashParser.ParseOrder(lines, dateTime, messageId);

                string s           = emailResponse.Payload.Parts[0].Parts[1].Body.Data;
                byte[] data2       = FromBase64ForUrlString(emailResponse.Payload.Parts[0].Parts[1].Body.Data);
                string decodedBody = Encoding.UTF8.GetString(data2);
                doorDashParser.ParseConfirmURL(order, decodedBody);

                if (DebugPrint)
                {
                    order.PrintOrder();
                }
                return(order);
            }
        }
        /* Extracts the order from an grubhub html file and returns an Order object */
        public Order ParseOrder(string html, DateTime dateTime, string messageId)
        {
            Order order = new Order();

            order.Service      = "GrubHub";
            order.TimeReceived = dateTime;
            order.MessageId    = messageId;

            var htmlDoc = new HtmlDocument();

            htmlDoc.LoadHtml(html);

            //We begin setting the possible locations of the relevant information, and then choose which one depending on the format on the html doc
            string metaInfoLoc1 = "//body/table/tbody/tr/td/table/tbody/tr/td/table[3]/tbody/tr/th[2]/table/tbody/tr/th/div/div[2]/div/div";
            string metaInfoLoc2 = "//body/table/tbody/tr/td/table/tbody/tr/td/table[4]/tbody/tr/th[2]/table/tbody/tr/th/div/div/div/div";
            string metaInfoLoc3 = "//body/table/tbody/tr/td/table/tbody/tr/td/table[5]/tbody/tr/th[2]/table/tbody/tr/th/div/div/div/div";

            string metaInfoBaseLoc1 = "//body/table/tbody/tr/td/table/tbody/tr/td/table[3]";
            string metaInfoBaseLoc2 = "//body/table/tbody/tr/td/table/tbody/tr/td/table[4]";
            string metaInfoBaseLoc3 = "//body/table/tbody/tr/td/table/tbody/tr/td/table[5]";

            string delivMethodLoc1 = @"/tbody/tr/th/table/tbody/tr/th/div/div[2]/div/span/span";
            string delivMethodLoc2 = @"/tbody/tr/th/table/tbody/tr/th/div/div/div/span/span";
            string delivMethodLoc3 = @"/tbody/tr/th/table/tbody/tr/th/div/div/div/span/span";

            string pickUpTimeLoc1 = @"/tbody/tr/th/table/tbody/tr/th/div/div[2]/div[2]/span";
            string pickUpTimeLoc2 = @"/tbody/tr/th/table/tbody/tr/th/div/div/div[2]/span";
            string pickUpTimeLoc3 = @"/tbody/tr/th/table/tbody/tr/th/div/div/div[2]/span";

            HtmlNodeCollection metaInfoNodes      = null;
            HtmlNode           deliveryMethodNode = null;
            HtmlNode           pickupTimeNode     = null;

            //If this is not null, the order is in standard format
            if (htmlDoc.DocumentNode.SelectNodes(metaInfoLoc1) != null)
            {
                //Debug.WriteLine("Using Standard format");
                metaInfoNodes      = htmlDoc.DocumentNode.SelectNodes(metaInfoLoc1);
                deliveryMethodNode = htmlDoc.DocumentNode.SelectSingleNode(metaInfoBaseLoc1 + delivMethodLoc1);
                pickupTimeNode     = htmlDoc.DocumentNode.SelectSingleNode(metaInfoBaseLoc1 + pickUpTimeLoc1);
            }
            //Otherwise the previous loc is null and there's an extra <table> "SCHEDULED ORDER" before the pickup/delivery <table>
            //so we use location 2
            else if (htmlDoc.DocumentNode.SelectNodes(metaInfoLoc2) != null)
            {
                //Debug.WriteLine("Using Scheduled Order format");
                metaInfoNodes      = htmlDoc.DocumentNode.SelectNodes(metaInfoLoc2);
                deliveryMethodNode = htmlDoc.DocumentNode.SelectSingleNode(metaInfoBaseLoc2 + delivMethodLoc2);
                pickupTimeNode     = htmlDoc.DocumentNode.SelectSingleNode(metaInfoBaseLoc2 + pickUpTimeLoc2);
            }
            //Otherwise the previous loc is null and there's also an extra <table> "ORDER ADJUSTMENT" before the relevant <table>
            //so we use location 3
            else if (htmlDoc.DocumentNode.SelectNodes(metaInfoLoc3) != null)
            {
                //Debug.WriteLine("Using Adjusted Order format");
                metaInfoNodes      = htmlDoc.DocumentNode.SelectNodes(metaInfoLoc3);
                deliveryMethodNode = htmlDoc.DocumentNode.SelectSingleNode(metaInfoBaseLoc3 + delivMethodLoc3);
                pickupTimeNode     = htmlDoc.DocumentNode.SelectSingleNode(metaInfoBaseLoc3 + pickUpTimeLoc3);
            }
            //If location 3 is still null, then the format is not recognized
            else
            {
                //Debug.WriteLine("Cannot parse order - format not recognized");
                return(null);
            }

            var orderNumberNode = htmlDoc.DocumentNode.SelectSingleNode("//body/table/tbody/tr/td/table/tbody/tr/td/table[2]/tbody/tr/th/table/tbody/tr/th/div/div[4]/span[2]");

            ParseOrderNumber(orderNumberNode, order);
            ParsePickUpTime(pickupTimeNode, order);

            int metaDivCount     = metaInfoNodes.Count; //the # of <div> elems
            int nonItemCount     = 5;                   //the last 5 <tr>'s of the <tbody> is meta information
            int lastAddressIndex = metaDivCount - 3;

            //We need to know whether it's DELIVERY or PICKUP because there's a difference in the html structure
            if (deliveryMethodNode.InnerHtml.Trim() == "DELIVERY")
            {
                nonItemCount = 6;
            }
            else
            {
                order.DeliveryMethod = "Pickup";
            }

            var pickupByNameNode = metaInfoNodes.ElementAt(1);

            ParsePickupName(pickupByNameNode, order);
            ParseContactNumber(metaInfoNodes.ElementAt(metaDivCount - 1), order);

            var orderContentNodes = htmlDoc.DocumentNode.SelectNodes("//td[@class='orderSummary__data']");
            //order.UniqueItemCount = orderContentNodes.Count - nonItemCount;

            //Loops through all the td nodes with class = "orderSummary__data"
            //Parses itemName from td's with inner structure <div>
            //Parses   addOns from td's with inner structure <div> <ul>
            //Does nothing for td's with no css child nodes
            //Stops parsing when hitting the td with inner node <span>, or when there are no more tdNodes to scan
            bool parsedItemName = false;
            Item item           = null;

            for (int i = 0; i < orderContentNodes.Count; i++)
            {
                var tdNode = orderContentNodes[i];

                string spanNodePath = tdNode.XPath + "/span";
                var    spanNode     = tdNode.SelectSingleNode(spanNodePath);

                if (spanNode != null)
                {
                    //Debug.WriteLine("Hit td with <span> - breaking");
                    break;
                }

                var divNode = tdNode.Element("div");

                if (divNode == null)
                {
                    //Debug.WriteLine("No childs nodes - no addons");
                    continue;
                }

                if (divNode.Element("ul") != null)
                {
                    //Debug.WriteLine("contains addOns");
                    ParseAddOns(divNode.Element("ul"), item);
                }
                else
                {
                    //Since Special Instructions and ItemNames have the same html strucuture,
                    //we need manually check the innerHTML to decide which it is
                    if (divNode.InnerHtml.Trim().StartsWith("Instructions"))
                    {
                        ParseSpecialInstruction(divNode, item);
                    }
                    else
                    {
                        //Debug.WriteLine("is an itemName");
                        if (item != null)
                        {
                            order.ItemList.Add(item); //add the previous item if it's not null, then handle the new item
                        }
                        item = new Item();
                        ParseItemName(divNode, item);
                        ParseQuantity(tdNode, item);
                        ParseItemType(divNode, item);
                    }
                }
                //SetItemCount(i + 1, order.UniqueItemCount, item);
            }
            order.ItemList.Add(item); //add the last item since it won't be added without a next item

            DoorDashParser.SetDrinkAndSnackCount(order);
            DoorDashParser.SetOrderSize(order); //Need to move function to be general
            ParseConfirmURL(order, htmlDoc);
            return(order);
        }