diff --git a/scrape.go b/scrape.go index 5109019..4d6eaa2 100755 --- a/scrape.go +++ b/scrape.go @@ -31,7 +31,7 @@ func Scrape(m *mail.Message, banks map[Bank]bool) ([]*Transaction, error) { func buildScraper(m *mail.Message, banks map[Bank]bool) (scraper, error) { subject := fmt.Sprint(m.Header["Subject"]) - if !containsAny(subject, "transaction", "report", "Transaction", "payment", "Payment", "Deposit", "Withdrawal", "balance is") { + if !containsAny(subject, "transaction", "report", "Transaction", "payment", "Payment", "Deposit", "Withdrawal", "balance is", "Balance") { return nil, errors.New("cannot build scraper for subject " + subject) } from := fmt.Sprint(m.Header["From"]) @@ -247,6 +247,9 @@ func (c *uccuScraper) scrape(m *mail.Message) ([]*Transaction, error) { func (c *fidelityScraper) scrape(m *mail.Message) ([]*Transaction, error) { subject := fmt.Sprint(m.Header["Subject"]) + if strings.Contains(subject, "Daily Balance") { + return c.scrapeBalance(m) + } if strings.Contains(subject, "Debit Withdrawal") { return c.scrapeWithdrawal(m) } @@ -256,6 +259,25 @@ func (c *fidelityScraper) scrape(m *mail.Message) ([]*Transaction, error) { panic(nil) } +func (c *fidelityScraper) scrapeBalance(m *mail.Message) ([]*Transaction, error) { + b, err := ioutil.ReadAll(m.Body) + if err != nil { + return nil, err + } + + fidelAcc, _ := findSubstringBetween(b, "Account: XXXXX", "\n") + fidelAmount, _ := findSubstringBetween(b, "Your Daily Balance is $", " for ") + + transaction := NewTransaction( + fmt.Sprintf("%s-%s", Fidelity, fidelAcc), + "="+strings.ReplaceAll(fidelAmount, ",", ""), + "*", + fmt.Sprint(m.Header["Date"]), + Fidelity, + ) + return []*Transaction{transaction}, nil +} + func (c *fidelityScraper) scrapeDeposit(m *mail.Message) ([]*Transaction, error) { b, err := ioutil.ReadAll(m.Body) if err != nil { diff --git a/scrape_test.go b/scrape_test.go index 833e75d..ad59c61 100644 --- a/scrape_test.go +++ b/scrape_test.go @@ -8,6 +8,39 @@ import ( "testing" ) +func TestScrapeFidelityBalance(t *testing.T) { + b, _ := os.ReadFile("testdata/fidelity.balance.txt") + + message := &mail.Message{ + Header: map[string][]string{ + "Subject": []string{"Fidelity Alerts: Daily Balance"}, + }, + Body: bytes.NewReader(b), + } + + fidelity := &fidelityScraper{} + + gots, err := fidelity.scrapeBalance(message) + if err != nil { + t.Fatal(err) + } + if len(gots) != 1 { + t.Fatal(gots) + } + got := gots[0] + + if got.Account != "Fidelity-5576" { + t.Fatalf("bad account: %v: %+v", got.Account, got) + } + if got.Amount != "=5525.52" { + t.Fatalf("bad amount: %v: %+v", got.Amount, got) + } + if got.Vendor != "*" { + t.Fatalf("bad vendor: %v: %+v", got.Vendor, got) + } + t.Logf("%+v", got) +} + func TestScrapeChase2025Balance(t *testing.T) { b, _ := os.ReadFile("testdata/chase.2025.balance.txt") diff --git a/testdata/fidelity.balance.txt b/testdata/fidelity.balance.txt new file mode 100644 index 0000000..38d4da3 --- /dev/null +++ b/testdata/fidelity.balance.txt @@ -0,0 +1,81 @@ +Delivered-To: breellocaldev@gmail.com +Received: by 2002:a05:6022:841f:b0:65:30d1:7dd8 with SMTP id cd31csp2236378lab; + Mon, 13 Jan 2025 23:12:32 -0800 (PST) +X-Google-Smtp-Source: AGHT+IGzcvzUeoSHy9weRVPEN80i7klhLA5WFNCceEUieFFQlZbSflLGUQjDFpsW5D9aHLZTkoKk +X-Received: by 2002:a05:620a:4494:b0:7b6:d710:2282 with SMTP id af79cd13be357-7bcd9764e4bmr4052577585a.49.1736838752584; + Mon, 13 Jan 2025 23:12:32 -0800 (PST) +ARC-Seal: i=1; a=rsa-sha256; t=1736838752; cv=none; + d=google.com; s=arc-20240605; + b=MFiRJLSDXgvdRnFQMIDv2plhY999E2EheBn6MnebrFQlYqgD1TLUKCYMU7eT1qJy/v + c0JAUxIfgbzFOpPIO7pHJDZP4WDoRhej8Gh3qSYcHXFLtZHWj0wbDQDoXK16wSgUsJWW + 7/dd5ozpiYuyaDI/1tA1OUSR062gYORAdr11zmOYt1Uc+tcjXKnzPA67XIwSFi+Az6am + qlrv8bg1Qn9b/xTHlQfVOFNIiSoyM3ftmitaGBfXJ1GunB/xA/gwEh36nbnTc/7r7Pp8 + 8or7OdOq9s0anbYTo7xkluQ+Hj9lE8w5B80ugiA1DuLrSY9+4RYmp4pJ5HzV3V+L5vBd + ZUjQ== +ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; + h=message-id:reply-to:subject:from:to:dkim-signature:date; + bh=IBSexcmXnA6APE8uxbjJm+VKBGCbhSeCXT8D1g6eZ1w=; + fh=lrt9myTdNYNEckm/XjduC9YuGfYr3v7r45vYTr/7vKM=; + b=XPb7aldXPvreTtcrvTw4vjE/In1umhczsRboGdb2vgrv37O8x+5VoE2AME6WtdLNHn + phmCPM8S9eW83lR17pk6T/jbWtrQUIcKmZhKXzLTVj1qofy2SP09kTF7p/1g/IChhmT3 + //gYi3MGISpt0sjFMaxFgN8gK5V2qTbcFiYBa3bkCgZYuvbc7N7TqPplKDMNIAq7qse+ + daSLuYoWMb83N1lSKO5njyFgWzWt6kLzfZVj+Mr05z5n45pWg4T82NcY2q2ZW0aTJI4t + 9tbNDXkpkG4/iboDAe1xdOXtSmay6trbzYzIsXBjDD6XBLmewPWZ4ek7wNzzoGhn4Rn5 + FW1w==; + dara=google.com +ARC-Authentication-Results: i=1; mx.google.com; + dkim=pass header.i=@fidelity.com header.s=2022-02-04 header.b=KVDNK8Zf; + spf=pass (google.com: domain of fidelity.alerts@fidelity.com designates 155.199.72.10 as permitted sender) smtp.mailfrom=Fidelity.Alerts@fidelity.com; + dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=fidelity.com +Return-Path: +Received: from ltm-fwomaa109-110-vlan230.fidelity.com (ltm-fwomaa109-110-vlan230.fidelity.com. [155.199.72.10]) + by mx.google.com with ESMTPS id af79cd13be357-7bce350af33si1300715285a.632.2025.01.13.23.12.32 + for + (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); + Mon, 13 Jan 2025 23:12:32 -0800 (PST) +Received-SPF: pass (google.com: domain of fidelity.alerts@fidelity.com designates 155.199.72.10 as permitted sender) client-ip=155.199.72.10; +Authentication-Results: mx.google.com; + dkim=pass header.i=@fidelity.com header.s=2022-02-04 header.b=KVDNK8Zf; + spf=pass (google.com: domain of fidelity.alerts@fidelity.com designates 155.199.72.10 as permitted sender) smtp.mailfrom=Fidelity.Alerts@fidelity.com; + dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=fidelity.com +Received: from pps.filterd (PFFWOMA2PVAPP.fmr.com [127.0.0.1]) by PFFWOMA2PVAPP.fmr.com (8.18.1.2/8.18.1.2) with ESMTP id 50E6w3CZ000756 for ; Tue, 14 Jan 2025 02:12:31 -0500 +Date: Tue, 14 Jan 2025 02:12:31 -0500 +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fidelity.com; h=from : message-id : reply-to : subject : to; s=2022-02-04; bh=IBSexcmXnA6APE8uxbjJm+VKBGCbhSeCXT8D1g6eZ1w=; b=KVDNK8ZfHhv9nj/4YxtK1lmyL8v+qKQdV/7j8iTegeFyxjXg55x9CSgbLane3jsjvI/+ P2PeaTZTNbNNTe864IcZNq2dNdneQC+G16sUX9lWd/YVnHP3EgVYUuhr2d/8DJLhmR/t g0VmQ4Fku359+fgeHAyzotPfbL7F9eVW5itr+oI9UHYGfbDeX9g46t8ZA8W0GYD/wbZY 6U0VFoO3GrkS1WKj2gUk0SPVu74JtyS3i6cmxDSy8BC2ohbtiichUXgGh2XtdS1WLGPh E7ZTXDiaeRUmaZ/CvptkhQf243V6kf1yu/Rwh5dk3r5+N3m3R1vJsOm0guYemThmOs4B 6g== +Received: from server (ltm-fwomaa109-110-vlan283.fmr.com [10.1.32.10]) by PFFWOMA2PVAPP.fmr.com (PPS) with ESMTP id 443mv0krnu-210 for ; Tue, 14 Jan 2025 02:12:31 -0500 +To: breellocaldev@gmail.com +From: Fidelity Investments +Subject: Fidelity Alerts: Daily Balance +Reply-To: Fidelity_Investments_Service_DoNotReply@fidelity.com +Message-ID: <443mv0krnu-210@PFFWOMA2PVAPP.fmr.com> +X-Proofpoint-ORIG-GUID: vODKJqJrtoA_MJzerqUlGVd6tvOLphKx +X-Proofpoint-GUID: vODKJqJrtoA_MJzerqUlGVd6tvOLphKx +X-Proofpoint-Virus-Version: vendor=nai engine=6700 definitions=11314 signatures=596817 +X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 malwarescore=0 mlxlogscore=715 adultscore=0 spamscore=0 mlxscore=0 bulkscore=0 suspectscore=0 phishscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2411120000 definitions=main-2501140058 + +Account: XXXXX5576 + +Your Daily Balance is $5,525.52 for 01/14/2025. + + +Important information: +Fidelity automatically emails certain alerts to customers who have provided +an email address. You can see the terms which govern these alerts at +https://www.fidelity.com/customer-service/alerts-agreement. If you would +prefer to not receive these alerts, please change your preferences at +https://scs.fidelity.com/customeronly/alerts.shtml. + +Review Fidelity's Terms of Use for Third Party Content and Research at +https://www.fidelity.com/terms-of-use#Third. + +If your email has changed, please update your email address +at https://alertable.fidelity.com/ftgw/alerts/GetUserDeliveryDevices to continue to +receive your alerts. + +Fidelity Brokerage Services LLC, Member NYSE, SIPC + +EMAIL REF# 537048 + +Copyright 2025 FMR LLC +All rights reserved. Important Legal Information +at http://www.fidelity.com/terms-of-use. +