test new and old scrape for chase
parent
cea7a30884
commit
1ebaf9eac8
46
scrape.go
46
scrape.go
|
|
@ -67,27 +67,57 @@ func containsAny(a string, b ...string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *chaseScraper) scrape(m *mail.Message) ([]*Transaction, error) {
|
func (c *chaseScraper) scrape(m *mail.Message) ([]*Transaction, error) {
|
||||||
|
transactions, err := c.scrape2020(m)
|
||||||
|
if err == nil && len(transactions) > 0 {
|
||||||
|
return transactions, err
|
||||||
|
}
|
||||||
|
return c.scrape2021(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chaseScraper) scrape2021(m *mail.Message) ([]*Transaction, error) {
|
||||||
|
re := regexp.MustCompile(`^Your \$(?P<amount>[0-9\.]*) transaction with (?P<vendor>.*)$`)
|
||||||
|
matches := re.FindSubmatch([]byte(m.Header["Subject"][0]))
|
||||||
|
if len(matches) < 1 {
|
||||||
|
return nil, errors.New("no match subject search")
|
||||||
|
}
|
||||||
|
amount := string(matches[1])
|
||||||
|
vendor := string(matches[2])
|
||||||
|
|
||||||
|
b, _ := ioutil.ReadAll(m.Body)
|
||||||
|
re = regexp.MustCompile(`\(\.\.\.[0-9]{4}\)`)
|
||||||
|
match := re.Find(b)
|
||||||
|
re = regexp.MustCompile(`[0-9]{4}`)
|
||||||
|
account := string(re.Find(match))
|
||||||
|
|
||||||
|
return []*Transaction{NewTransaction(account, amount, vendor, fmt.Sprint(m.Header["Date"]), Chase)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chaseScraper) scrape2020(m *mail.Message) ([]*Transaction, error) {
|
||||||
b, err := ioutil.ReadAll(m.Body)
|
b, err := ioutil.ReadAll(m.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
regexp := regexp.MustCompile(`A charge of \([^)]*\) (?P<amount>[\d\.]+) at (?P<account>.*) has been authorized`)
|
re := regexp.MustCompile(`A charge of \([^)]*\) (?P<amount>[\d\.]+) at (?P<vendor>.*) has been authorized`)
|
||||||
matches := regexp.FindSubmatch(b)
|
matches := re.FindSubmatch(b)
|
||||||
if len(matches) < 2 {
|
if len(matches) < 2 {
|
||||||
return nil, fmt.Errorf("no full matches found")
|
return nil, fmt.Errorf("no full matches found")
|
||||||
}
|
}
|
||||||
results := make(map[string][]string)
|
results := make(map[string][]string)
|
||||||
for i, name := range regexp.SubexpNames() {
|
for i, name := range re.SubexpNames() {
|
||||||
if i != 0 && name != "" {
|
if i != 0 && name != "" {
|
||||||
results[name] = append(results[name], string(matches[i]))
|
results[name] = append(results[name], string(matches[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(results) != 2 || len(results["amount"]) != len(results["account"]) {
|
if len(results) != 2 || len(results["amount"]) != len(results["vendor"]) {
|
||||||
return nil, fmt.Errorf("unexpected matches found looking for transactions: %+v", results)
|
return nil, fmt.Errorf("unexpected matches found looking for transactions: %+v", results)
|
||||||
}
|
}
|
||||||
|
re = regexp.MustCompile(`account ending in (?P<account>[0-9]{4})\.`)
|
||||||
|
match := re.Find(b)
|
||||||
|
re = regexp.MustCompile(`[0-9]{4}`)
|
||||||
|
account := string(re.Find(match))
|
||||||
transactions := make([]*Transaction, len(results["amount"]))
|
transactions := make([]*Transaction, len(results["amount"]))
|
||||||
for i := range results["amount"] {
|
for i := range results["amount"] {
|
||||||
transactions[i] = NewTransaction(results["amount"][i], results["account"][i], fmt.Sprint(m.Header["Date"]), Chase)
|
transactions[i] = NewTransaction(account, results["amount"][i], results["vendor"][i], fmt.Sprint(m.Header["Date"]), Chase)
|
||||||
}
|
}
|
||||||
return transactions, nil
|
return transactions, nil
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +139,7 @@ func (c *citiScraper) scrape(m *mail.Message) ([]*Transaction, error) {
|
||||||
|
|
||||||
vendor := bytes.Split(bytes.Split(match, []byte(" on card ending in"))[0], []byte("transaction was made at "))[1]
|
vendor := bytes.Split(bytes.Split(match, []byte(" on card ending in"))[0], []byte("transaction was made at "))[1]
|
||||||
|
|
||||||
transaction := NewTransaction(string(price), string(vendor), date, Citi)
|
transaction := NewTransaction(Citi.String(), string(price), string(vendor), date, Citi)
|
||||||
|
|
||||||
return []*Transaction{transaction}, nil
|
return []*Transaction{transaction}, nil
|
||||||
//Citi Alert: A $598.14 transaction was made at REMIX MUSIC SPRINGDA on card ending in 3837
|
//Citi Alert: A $598.14 transaction was made at REMIX MUSIC SPRINGDA on card ending in 3837
|
||||||
|
|
@ -147,7 +177,7 @@ func (c *citiScraper) scrape(m *mail.Message) ([]*Transaction, error) {
|
||||||
|
|
||||||
transactions := make([]*Transaction, len(results["amount"]))
|
transactions := make([]*Transaction, len(results["amount"]))
|
||||||
for i := range results["amount"] {
|
for i := range results["amount"] {
|
||||||
transactions[i] = NewTransaction(results["amount"][i], results["account"][i], fmt.Sprint(m.Header["Date"]), Citi)
|
transactions[i] = NewTransaction(Citi.String(), results["amount"][i], results["account"][i], fmt.Sprint(m.Header["Date"]), Citi)
|
||||||
}
|
}
|
||||||
|
|
||||||
return transactions, nil
|
return transactions, nil
|
||||||
|
|
@ -173,6 +203,6 @@ func (c *uccuScraper) scrape(m *mail.Message) ([]*Transaction, error) {
|
||||||
if !bytes.Contains(b, []byte("credit")) {
|
if !bytes.Contains(b, []byte("credit")) {
|
||||||
f *= -1.0
|
f *= -1.0
|
||||||
}
|
}
|
||||||
transaction := NewTransaction(fmt.Sprintf("%.2f", f), "?", fmt.Sprint(m.Header["Date"]), UCCU)
|
transaction := NewTransaction(UCCU.String(), fmt.Sprintf("%.2f", f), "?", fmt.Sprint(m.Header["Date"]), UCCU)
|
||||||
return []*Transaction{transaction}, nil
|
return []*Transaction{transaction}, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/mail"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScrapeChase2021(t *testing.T) {
|
||||||
|
b, err := ioutil.ReadFile("./testdata/chase.2021.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
message := &mail.Message{
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Subject": []string{"Your $38.84 transaction with TARGET T-1754"},
|
||||||
|
},
|
||||||
|
Body: bytes.NewReader(b),
|
||||||
|
}
|
||||||
|
|
||||||
|
chase := &chaseScraper{}
|
||||||
|
|
||||||
|
gots, err := chase.scrape2021(message)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(gots) != 1 {
|
||||||
|
t.Fatal(gots)
|
||||||
|
}
|
||||||
|
got := gots[0]
|
||||||
|
|
||||||
|
if got.Account != "8824" {
|
||||||
|
t.Fatalf("bad account: %v: %+v", got.Account, got)
|
||||||
|
}
|
||||||
|
if got.Amount != "38.84" {
|
||||||
|
t.Fatalf("bad amount: %v: %+v", got.Amount, got)
|
||||||
|
}
|
||||||
|
if got.Vendor != "TARGET T-1754" {
|
||||||
|
t.Fatalf("bad vendor: %v: %+v", got.Vendor, got)
|
||||||
|
}
|
||||||
|
t.Logf("%+v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScrapeChase2020(t *testing.T) {
|
||||||
|
b, err := ioutil.ReadFile("./testdata/chase.2020.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
message := &mail.Message{
|
||||||
|
Body: bytes.NewReader(b),
|
||||||
|
}
|
||||||
|
|
||||||
|
chase := &chaseScraper{}
|
||||||
|
|
||||||
|
gots, err := chase.scrape2020(message)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(gots) != 1 {
|
||||||
|
t.Fatal(gots)
|
||||||
|
}
|
||||||
|
got := gots[0]
|
||||||
|
|
||||||
|
if got.Account != "8824" {
|
||||||
|
t.Fatalf("bad account: %v: %+v", got.Account, got)
|
||||||
|
}
|
||||||
|
if got.Amount != "16.08" {
|
||||||
|
t.Fatalf("bad amount: %v: %+v", got.Amount, got)
|
||||||
|
}
|
||||||
|
if got.Vendor != "PAYPAL *BLIZZARDENT" {
|
||||||
|
t.Fatalf("bad vendor: %q: %+v", got.Vendor, got)
|
||||||
|
}
|
||||||
|
t.Logf("%+v", got)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
This is an Alert to help you manage your credit card account ending in 8824.
|
||||||
|
|
||||||
|
As you requested, we are notifying you of any charges over the amount of ($USD) 0.00, as specified in your Alert settings.
|
||||||
|
A charge of ($USD) 16.08 at PAYPAL *BLIZZARDENT has been authorized on Jul 6, 2021 at 6:21 PM ET.
|
||||||
|
|
||||||
|
Do not reply to this Alert.
|
||||||
|
|
||||||
|
If you have questions, please call the number on the back of your credit card, or send a secure message from your Inbox on www.chase.com.
|
||||||
|
|
||||||
|
To see all of the Alerts available to you, or to manage your Alert settings, please log on to www.chase.com.
|
||||||
|
|
@ -0,0 +1,396 @@
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.=
|
||||||
|
w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns=3D"http://www.w3.org/1999/xhtml" lang=3D"en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3DUTF-8" />
|
||||||
|
<meta name=3D"viewport" content=3D"width=3Ddevice-width, initial-scale=3D1.=
|
||||||
|
0"/>
|
||||||
|
<title>This transaction is above the level you set, see more here.</title>
|
||||||
|
<style type=3D"text/css">
|
||||||
|
* {
|
||||||
|
=09line-height: normal !important;
|
||||||
|
}
|
||||||
|
strong {
|
||||||
|
=09font-weight: bold !important;
|
||||||
|
}
|
||||||
|
em {
|
||||||
|
=09font-style: italic !important;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
=09background-color: #d7dbe0 !important;
|
||||||
|
=09-webkit-text-size-adjust: none !important;
|
||||||
|
}
|
||||||
|
.ExternalClass * {
|
||||||
|
=09line-height: 112%
|
||||||
|
}
|
||||||
|
.ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass =
|
||||||
|
td {
|
||||||
|
=09line-height: 112%
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
=09-webkit-text-size-adjust: none;
|
||||||
|
}
|
||||||
|
a[href^=3Dtel] {
|
||||||
|
=09color: inherit;
|
||||||
|
=09text-decoration: none;
|
||||||
|
}
|
||||||
|
.applelinksgray41 a {
|
||||||
|
=09color: #414042 !important;
|
||||||
|
=09text-decoration: none;
|
||||||
|
}
|
||||||
|
.applelinksgray a {
|
||||||
|
=09color: #717171 !important;
|
||||||
|
=09text-decoration: none;
|
||||||
|
}
|
||||||
|
.wordBreak {
|
||||||
|
=09overflow-wrap: break-word;
|
||||||
|
=09word-wrap: break-word;
|
||||||
|
=09word-break: break-all;
|
||||||
|
=09word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
.fullWidth {
|
||||||
|
=09width: 100% !important;
|
||||||
|
=09min-width: 100% !important;
|
||||||
|
=09margin-left: auto !important;
|
||||||
|
=09margin-right: auto !important;
|
||||||
|
=09padding: 0px !important;
|
||||||
|
=09text-align: center !important;
|
||||||
|
}
|
||||||
|
.moPad {
|
||||||
|
=09padding-right: 20px !important;
|
||||||
|
=09padding-left: 20px !important;
|
||||||
|
}
|
||||||
|
.zeroPad {
|
||||||
|
=09padding-right: 0px !important;
|
||||||
|
=09padding-left: 0px !important;
|
||||||
|
}
|
||||||
|
.font14 {
|
||||||
|
=09font-size: 14px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print and (max-width: 800px) {
|
||||||
|
.fullWidth {
|
||||||
|
=09width: 100% !important;
|
||||||
|
=09min-width: 100% !important;
|
||||||
|
=09margin-left: auto !important;
|
||||||
|
=09margin-right: auto !important;
|
||||||
|
=09padding: 0px !important;
|
||||||
|
=09text-align: center !important;
|
||||||
|
}
|
||||||
|
.moPad {
|
||||||
|
=09padding-right: 20px !important;
|
||||||
|
=09padding-left: 20px !important;
|
||||||
|
}
|
||||||
|
.zeroPad {
|
||||||
|
=09padding-right: 0px !important;
|
||||||
|
=09padding-left: 0px !important;
|
||||||
|
}
|
||||||
|
.font14 {
|
||||||
|
=09font-size: 14px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style=3D"padding: 0px;margin: 0px; background-color:#d7dbe0;">
|
||||||
|
<table role=3D"presentation" align=3D"center" width=3D"100%" border=3D"0" c=
|
||||||
|
ellspacing=3D"0" cellpadding=3D"0" style=3D"min-width:800px; background-col=
|
||||||
|
or:#d7dbe0;" class=3D"fullWidth">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"center" style=3D"vertical-align:top; padding:0px 0px 20px =
|
||||||
|
0px; min-width:800px; background-color:#d7dbe0;" class=3D"fullWidth"><table=
|
||||||
|
role=3D"presentation" align=3D"center" width=3D"800" cellpadding=3D"0" cel=
|
||||||
|
lspacing=3D"0" border=3D"0" class=3D"fullWidth" style=3D"background-color:#=
|
||||||
|
FFFFFF;">
|
||||||
|
<!-- Start of Content -->
|
||||||
|
<tr>
|
||||||
|
<td align=3D"center" style=3D"vertical-align:top; padding: 23px 0=
|
||||||
|
px 0px;background-color: #005EB8;"><table role=3D"presentation" cellpadding=
|
||||||
|
=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"right" style=3D"vertical-align:bottom; padding=
|
||||||
|
:0px 0px; width:12px;"><img src=3D"https://www.chase.com/content/dam/email/=
|
||||||
|
images/blue-left.jpg" width=3D"12" height=3D"226" border=3D"0" style=3D"dis=
|
||||||
|
play:block;" alt=3D""/></td>
|
||||||
|
<td align=3D"center" style=3D"vertical-align:bottom; paddin=
|
||||||
|
g: 0px 0px 0px;width:616px; background-color: #FFFFFF;"><table role=3D"pres=
|
||||||
|
entation" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng: 0px 0px;"><table role=3D"presentation" width=3D"100%" cellpadding=3D"0"=
|
||||||
|
cellspacing=3D"0" border=3D"0">
|
||||||
|
<!-- Start hidden preview text -->
|
||||||
|
<div style=3D"display: none; max-height: 0px; ove=
|
||||||
|
rflow: hidden;">This transaction is above the level you set, see more here.=
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Insert ‌ after hidden preview tex=
|
||||||
|
t -->
|
||||||
|
|
||||||
|
<div style=3D"display: none; max-height: 0px; ove=
|
||||||
|
rflow: hidden;"> ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; </div>
|
||||||
|
<!-- End hidden preview text -->
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding-left: 30px;" class=3D"moPad"><table role=3D"presentation" width=3D"=
|
||||||
|
100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:bottom; padding:36px 0px 20px;"><img src=3D"https://www.chase.com/content=
|
||||||
|
/dam/email/images/chase-logo-h-rgb.png" width=3D"104" height=3D"20" border=
|
||||||
|
=3D"0" style=3D"display:block;" alt=3D"Chase Logo"/></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding: 20px 28px 0px;" class=3D"moPad"><table role=3D"presentation" alig=
|
||||||
|
n=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top;"><table role=3D"presentation" width=3D"100%" cellpadding=3D"0" cells=
|
||||||
|
pacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertica=
|
||||||
|
l-align:top; padding:5px 10px; font-family:Arial, Helvetica, sans-serif; fo=
|
||||||
|
nt-size:12px; font-weight:bold; color:#414042; background-color:#D7DBE0; bo=
|
||||||
|
rder-radius:20px; -moz-border-radius: 20px; -webkit-border-radius:20px; whi=
|
||||||
|
te-space: nowrap;" class=3D"font14">Transaction alert</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
"><table role=3D"presentation" width=3D"100%" cellpadding=3D"0" cellspacing=
|
||||||
|
=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top; padding: 20px 30px 28px;" class=3D"moPad"><table role=3D"presentatio=
|
||||||
|
n" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertica=
|
||||||
|
l-align:top; padding: 0px 20px 0px 0px;"><img src=3D"https://static.chasecd=
|
||||||
|
n.com/content/services/rendition/image.small.png/unified-assets/digital-car=
|
||||||
|
ds/aarp/41473417018.png" width=3D"57" height=3D"auto" alt=3D"" border=3D"0"=
|
||||||
|
style=3D"display:block;"/></td>
|
||||||
|
<td align=3D"left" style=3D"vertica=
|
||||||
|
l-align:top; padding:0px 50px 0px 0px; font-family:Arial, Helvetica, sans-s=
|
||||||
|
erif; font-size:30px; font-weight: bold; color:#414042;" class=3D"zeroPad">=
|
||||||
|
You made a $38.84 transaction</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:bottom; padding:=
|
||||||
|
0px 0px;width:12px; "><img src=3D"https://www.chase.com/content/dam/email/i=
|
||||||
|
mages/blue-right.jpg" width=3D"12" height=3D"226" border=3D"0" style=3D"dis=
|
||||||
|
play:block;" alt=3D""/></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"center" style=3D"vertical-align:top; padding: 0px 0p=
|
||||||
|
x 0px; background-color: #FFFFFF;"><table role=3D"presentation" cellpadding=
|
||||||
|
=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"right" style=3D"vertical-align:top; padding:0p=
|
||||||
|
x 0px; width:12px;"><img src=3D"https://www.chase.com/content/dam/email/ima=
|
||||||
|
ges/white-left.jpg" width=3D"12" height=3D"77" border=3D"0" style=3D"displa=
|
||||||
|
y:block;" alt=3D""/></td>
|
||||||
|
<td align=3D"center" style=3D"vertical-align:top; padding: =
|
||||||
|
0px 0px 0px;width:616px;"><table role=3D"presentation" width=3D"100%" cellp=
|
||||||
|
adding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng: 0px 150px 0px 30px;" class=3D"moPad"><table role=3D"presentation" width=
|
||||||
|
=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding: 10px 0px;border-bottom: solid 1px #414042;"><table role=3D"presen=
|
||||||
|
tation" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top; padding:0px 0px 0px 0px; font-family:Arial, Helvetica, sans-serif; f=
|
||||||
|
ont-size:16px; color:#414042;" class=3D"font14">Account</td>
|
||||||
|
<td align=3D"right" style=3D"vertical-ali=
|
||||||
|
gn:top; padding:0px 0px 0px 5px; font-family:Arial, Helvetica, sans-serif; =
|
||||||
|
font-size:16px; font-weight:bold; color:#414042;" class=3D"font14">AARP fro=
|
||||||
|
m Chase (...8824)</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding: 10px 0px;border-bottom: solid 1px #414042;"><table role=3D"presen=
|
||||||
|
tation" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top; padding:0px 0px 0px 0px; font-family:Arial, Helvetica, sans-serif; f=
|
||||||
|
ont-size:16px; color:#414042;" class=3D"font14">Date</td>
|
||||||
|
<td align=3D"right" style=3D"vertical-ali=
|
||||||
|
gn:top; padding:0px 0px 0px 5px; font-family:Arial, Helvetica, sans-serif; =
|
||||||
|
font-size:16px; font-weight:bold; color:#414042;" class=3D"font14"><span cl=
|
||||||
|
ass=3D"applelinksgray41"><a style=3D"color:#414042;text-decoration: none;">=
|
||||||
|
Jul 23, 2021 at 3:06 PM ET</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding: 10px 0px;border-bottom: solid 1px #414042;"><table role=3D"presen=
|
||||||
|
tation" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top; padding:0px 0px 0px 0px; font-family:Arial, Helvetica, sans-serif; f=
|
||||||
|
ont-size:16px; color:#414042;" class=3D"font14">Merchant</td>
|
||||||
|
<td align=3D"right" style=3D"vertical-ali=
|
||||||
|
gn:top; padding:0px 0px 0px 5px; font-family:Arial, Helvetica, sans-serif; =
|
||||||
|
font-size:16px; font-weight:bold; color:#414042;" class=3D"font14">TARGET T=
|
||||||
|
-1754</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding: 10px 0px;border-bottom: solid 1px #414042;"><table role=3D"presen=
|
||||||
|
tation" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top; padding:0px 0px 0px 0px; font-family:Arial, Helvetica, sans-serif; f=
|
||||||
|
ont-size:16px; color:#414042;" class=3D"font14">Amount</td>
|
||||||
|
<td align=3D"right" style=3D"vertical-ali=
|
||||||
|
gn:top; padding:0px 0px 0px 5px; font-family:Arial, Helvetica, sans-serif; =
|
||||||
|
font-size:16px; font-weight:bold; color:#414042;" class=3D"font14">$38.84</=
|
||||||
|
td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; pad=
|
||||||
|
ding:40px 150px 40px 30px; font-family:Arial, Helvetica, sans-serif; font-s=
|
||||||
|
ize:16px; color:#414042;" class=3D"moPad">You are receiving this alert beca=
|
||||||
|
use your transaction was more than the $0.00 level you set. You can visit o=
|
||||||
|
ur <a style=3D"text-decoration: underline; color:#0060F0;" href=3D"https://=
|
||||||
|
www.chase.com/personal/credit-cards/card-resource-center" rel=3D"noopener n=
|
||||||
|
oreferrer" target=3D"_blank">Resource Center</a> anytime to help answer yo=
|
||||||
|
ur questions or manage your account.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"padding:0px; vertical-ali=
|
||||||
|
gn:top; padding: 0px 0px 30px 30px;" class=3D"moPad"><table role=3D"present=
|
||||||
|
ation" align=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=3D"0" styl=
|
||||||
|
e=3D"vertical-align:top;">
|
||||||
|
<tr>
|
||||||
|
<td role=3D"button" align=3D"center" style=3D"b=
|
||||||
|
ackground-color:#0060f0; color: #fffffe; font-size: 16px; font-family: Aria=
|
||||||
|
l, Helvetica, sans-serif; padding: 10px 0px; border: 1px solid #0060f0; ver=
|
||||||
|
tical-align:top; border-radius:4px; -moz-border-radius: 4px; -webkit-border=
|
||||||
|
-radius:4px;width: 168px;"><a href=3D"https://secure.chase.com/web/auth/nav=
|
||||||
|
?navKey=3DrequestDashboard" target=3D"_blank" style=3D"color: #fffffe; text=
|
||||||
|
-decoration:none;">Review account</a></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng:0px 30px 20px; font-family:Arial, Helvetica, sans-serif; font-size:12px;=
|
||||||
|
color:#717171;" class=3D"moPad font14">Securely access your accounts with =
|
||||||
|
the <a style=3D"text-decoration: underline; color:#0060F0;" href=3D"https:/=
|
||||||
|
/www.chase.com/digital/mobile-banking" rel=3D"noopener noreferrer" target=
|
||||||
|
=3D"_blank">Chase Mobile<span style=3D"font-size:70%; line-height:0; v=
|
||||||
|
ertical-align:3px; text-decoration: none;">®</span> app</a> or <a style=
|
||||||
|
=3D"text-decoration: underline; color:#0060F0;" href=3D"https://secure.chas=
|
||||||
|
e.com/web/auth/nav?navKey=3DrequestDashboard" rel=3D"noopener noreferrer" =
|
||||||
|
target=3D"_blank">chase.com</a>. </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng: 0px 0px; background-color: #F6F6F6;"><table role=3D"presentation" width=
|
||||||
|
=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding:20px 30px 60px; font-family:Arial, Helvetica, sans-serif; font-siz=
|
||||||
|
e:12px; color:#717171;" class=3D"moPad font14"><span role=3D"heading" style=
|
||||||
|
=3D"text-transform: uppercase; font-weight: bold;">About this message</span=
|
||||||
|
><br />
|
||||||
|
<br />
|
||||||
|
Chase Mobile<span style=3D"font-size:70%=
|
||||||
|
; line-height:0; vertical-align:3px;">®</span>=C2=A0app is available f=
|
||||||
|
or select mobile devices. Message and data rates may apply.<br />
|
||||||
|
<br />
|
||||||
|
This service email was sent based on your ale=
|
||||||
|
rt settings. Use the Chase Mobile app or visit <a href=3D"https://www.=
|
||||||
|
chase.com/personal/mobile-online-banking/login-alerts" target=3D"_blank" st=
|
||||||
|
yle=3D"text-decoration: underline; color:#0060F0;" rel=3D"noopener noreferr=
|
||||||
|
er">chase.com/alerts</a> to view or manage your settings.<br />
|
||||||
|
<br />
|
||||||
|
Chase cannot guarantee the delivery of alerts=
|
||||||
|
and notifications.=C2=A0Wireless or internet service provider outages or o=
|
||||||
|
ther circumstances could delay them. You can always check <span class=3D"ap=
|
||||||
|
plelinksgray"><a style=3D"color:#717171;text-decoration: none;">chase.com</=
|
||||||
|
a></span> or the Chase Mobile=C2=A0app for the status of your accounts=
|
||||||
|
including your latest account balances and transaction details.=C2=A0<br /=
|
||||||
|
>
|
||||||
|
<br />
|
||||||
|
To protect your personal information, please =
|
||||||
|
don't reply to this message. Chase won't ask for confidential information i=
|
||||||
|
n an email. <br />
|
||||||
|
<br />
|
||||||
|
If you have concerns about the authenticity o=
|
||||||
|
f this message or have questions about your account visit <a style=3D"text-=
|
||||||
|
decoration: underline; color:#0060F0;" href=3D"https://www.chase.com/digita=
|
||||||
|
l/customer-service" target=3D"_blank" rel=3D"noopener noreferrer">chase.com=
|
||||||
|
/CustomerService</a> for ways to contact us.<br />
|
||||||
|
<br />
|
||||||
|
Your privacy is important to us. See our onli=
|
||||||
|
ne <a style=3D"text-decoration: underline; color:#0060F0;" href=3D"https://=
|
||||||
|
www.chase.com/digital/resources/privacy-security" target=3D"_blank" rel=3D"=
|
||||||
|
noopener noreferrer">Security Center</a> to learn how to protect your infor=
|
||||||
|
mation.<br />
|
||||||
|
<br />
|
||||||
|
© 2021 JPMorgan Chase & Co. </td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; padding:0px=
|
||||||
|
0px; width:12px;"><img src=3D"https://www.chase.com/content/dam/email/imag=
|
||||||
|
es/white-right.jpg" width=3D"12" height=3D"77" border=3D"0" style=3D"displa=
|
||||||
|
y:block;" alt=3D""/></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<!--End of Content -->
|
||||||
|
=20
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -9,28 +9,30 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
ID string
|
ID string
|
||||||
Bank Bank
|
Bank Bank
|
||||||
Amount string
|
Amount string
|
||||||
Vendor string
|
Vendor string
|
||||||
Date string
|
Date string
|
||||||
|
Account string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transaction) Format() string {
|
func (t *Transaction) Format() string {
|
||||||
return fmt.Sprintf("(%s) %v: %s @ %s", cleanDate(t.Date), t.Bank, t.Amount, t.Vendor)
|
return fmt.Sprintf("(%s) %v/%v: %s @ %s", cleanDate(t.Date), t.Account, t.Bank, t.Amount, t.Vendor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transaction) String() string {
|
func (t *Transaction) String() string {
|
||||||
return fmt.Sprint(*t)
|
return fmt.Sprint(*t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTransaction(amount, account, date string, bank Bank) *Transaction {
|
func NewTransaction(account, amount, vendor, date string, bank Bank) *Transaction {
|
||||||
regexp := regexp.MustCompile(`\s\s+`)
|
regexp := regexp.MustCompile(`\s\s+`)
|
||||||
t := &Transaction{
|
t := &Transaction{
|
||||||
Amount: regexp.ReplaceAllString(amount, " "),
|
Account: account,
|
||||||
Vendor: regexp.ReplaceAllString(account, " "),
|
Amount: regexp.ReplaceAllString(amount, " "),
|
||||||
Bank: bank,
|
Vendor: regexp.ReplaceAllString(vendor, " "),
|
||||||
Date: date,
|
Bank: bank,
|
||||||
|
Date: date,
|
||||||
}
|
}
|
||||||
t.ID = fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprint(t))))
|
t.ID = fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprint(t))))
|
||||||
return t
|
return t
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package main
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestTransactionFormat(t *testing.T) {
|
func TestTransactionFormat(t *testing.T) {
|
||||||
x := NewTransaction("12.34", "Amazon", "[Wed, 1 Apr 2020 10:14:11 -0400 (EDT)]", Chase)
|
x := NewTransaction("me", "12.34", "Amazon", "[Wed, 1 Apr 2020 10:14:11 -0400 (EDT)]", Chase)
|
||||||
t.Logf("%s", x.String())
|
t.Logf("%s", x.String())
|
||||||
t.Logf("%s", x.Format())
|
t.Logf("%s", x.Format())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue