Compare commits

...

24 Commits

Author SHA1 Message Date
bel 5f37804f2b tag empty string then no tag 2025-12-16 16:39:30 -07:00
bel 8cfccf1eb0 emails upate subjects 2025-10-20 15:23:58 -06:00
Bel LaPointe b920ea80c8 plumas iff -banks=plumas 2025-10-20 15:11:47 -06:00
Bel LaPointe d794a4db52 scrape uccu bal 2025-10-19 21:19:59 -06:00
bel dee4a132f6 chase updated subjects 2025-06-07 09:21:07 -06:00
Bel LaPointe eed07ff89c newline 2025-06-05 12:00:20 -06:00
Bel LaPointe 4526a8f2cb ohhh 2025-06-05 11:59:01 -06:00
Bel LaPointe 6ff59511b2 gr 2025-06-05 11:56:30 -06:00
Bel LaPointe bbaad3c352 oop 2025-06-05 11:27:24 -06:00
Bel LaPointe e55f7c78aa amex ready 2025-06-05 11:22:08 -06:00
Bel LaPointe 95bab29a63 can read daily fidelity balance 2025-01-14 20:58:00 -07:00
Bel LaPointe b4950a9060 oop 2025-01-14 20:48:56 -07:00
Bel LaPointe b7557c5a20 can read chase balance report 2025-01-14 20:44:29 -07:00
bel 9dc2c7ecac hm 2025-01-14 20:38:59 -07:00
bel 48f49e9b3a accept bofa charge 2024 2024-11-25 08:07:36 -07:00
Bel LaPointe e3d821e219 upgrade to .todo. inbox 2023-11-09 08:24:08 -07:00
Bel LaPointe 6a70c6d2ac to gogs.inhome 2023-04-10 11:02:34 -06:00
Bel LaPointe 9adf5e57cf wip 2023-04-10 10:58:36 -06:00
bel dc0b0a64e2 transaction is only a dupe if value matches though this may cause other problems dupes are better than skips 2023-04-09 09:19:58 -06:00
Bel LaPointe 1e01058c7c add fidelity to config 2022-12-09 09:58:19 -07:00
Bel LaPointe 5c557ea713 impl fidel deposit 2022-12-09 09:52:22 -07:00
Bel LaPointe 8b226294a2 can read fidel withdrawal emails 2022-12-09 09:48:04 -07:00
Bel LaPointe f6fc366dd4 stub wip fidelity 2022-12-09 08:52:36 -07:00
Bel LaPointe 1139fef0ab tidy 2022-12-09 08:44:30 -07:00
19 changed files with 4973 additions and 215 deletions

17
bank.go
View File

@ -3,14 +3,19 @@ package main
type Bank int
const (
Chase Bank = iota + 1
Citi Bank = iota + 1
UCCU Bank = iota + 1
BankOfAmerica Bank = iota + 1
Chase Bank = iota + 1
Citi
UCCU
BankOfAmerica
Fidelity
Amex
Plumas
)
func (b Bank) String() string {
switch b {
case Fidelity:
return "Fidelity"
case BankOfAmerica:
return "BankOfAmerica"
case Chase:
@ -19,6 +24,10 @@ func (b Bank) String() string {
return "Citi"
case UCCU:
return "UCCU"
case Amex:
return "AmericanExpress"
case Plumas:
return "Plumas"
}
return "?"
}

100
config.go
View File

@ -1,27 +1,22 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"local/args"
"local/oauth2"
"local/storage"
"log"
"net/http"
"strings"
"gitea.inhome.blapointe.com/local/args"
"gitea.inhome.blapointe.com/local/storage"
)
type Uploader int
const (
UploaderTodo = Uploader(iota)
DeprecatedUploaderTodo = Uploader(iota)
UploaderLedger
UploaderPTTodo
)
var uploaders = map[string]Uploader{
"todo": UploaderTodo,
"ledger": UploaderLedger,
"pttodo": UploaderPTTodo,
}
@ -30,9 +25,8 @@ type Config struct {
EmailUser string
EmailPass string
EmailIMAP string
EmailLimit int
TodoAddr string
TodoToken string
TodoList string
TodoTag string
Uploader Uploader
Storage storage.DB
@ -49,16 +43,17 @@ func NewConfig() Config {
as.Append(args.STRING, "emailuser", "email username", "breellocaldev@gmail.com")
as.Append(args.STRING, "emailpass", "email password", "diblloewfncwssof")
as.Append(args.STRING, "emailimap", "email imap", "imap.gmail.com:993")
as.Append(args.INT, "emaillimit", "email limit", 0)
as.Append(args.STRING, "uploader", "todo, ledger, pttodo", "todo")
as.Append(args.STRING, "uploader", "ledger|pttodo", "ledger")
as.Append(args.STRING, "todoaddr", "todo addr", "https://todo-server.remote.blapointe.com")
as.Append(args.STRING, "todoaddr", "todo addr", "/tmp/email-xactions-to-todo.dat.txt")
as.Append(args.STRING, "todopass", "todo pass", "gJtEXbbLHLf54yS9EdujtVN2n6Y")
as.Append(args.STRING, "todotoken", "todo token", "")
as.Append(args.STRING, "todolist", "todo list", "")
as.Append(args.STRING, "todotag", "todo tag", "expense")
as.Append(args.STRING, "banks", "uccu,citi,chase,bankofamerica", "uccu,citi,chase,bankofamerica")
as.Append(args.STRING, "banks", "uccu,citi,chase,bankofamerica,fidelity,americanexpress", "uccu,citi,chase,bankofamerica,fidelity,americanexpress")
as.Append(args.STRING, "accounts", "regex to include filter accounts", ".*")
as.Append(args.STRING, "not-accounts", "regex to exclude filter accounts", "zzzzzz")
@ -87,6 +82,7 @@ func NewConfig() Config {
EmailUser: as.GetString("emailuser"),
EmailPass: as.GetString("emailpass"),
EmailIMAP: as.GetString("emailimap"),
EmailLimit: as.GetInt("emaillimit"),
TodoAddr: as.GetString("todoaddr"),
TodoTag: as.GetString("todotag"),
AccountsPattern: as.GetString("accounts"),
@ -98,86 +94,20 @@ func NewConfig() Config {
Chase: strings.Contains(strings.ToLower(as.GetString("banks")), strings.ToLower(Chase.String())),
Citi: strings.Contains(strings.ToLower(as.GetString("banks")), strings.ToLower(Citi.String())),
UCCU: strings.Contains(strings.ToLower(as.GetString("banks")), strings.ToLower(UCCU.String())),
Fidelity: strings.Contains(strings.ToLower(as.GetString("banks")), strings.ToLower(Fidelity.String())),
Amex: strings.Contains(strings.ToLower(as.GetString("banks")), strings.ToLower(Amex.String())),
Plumas: strings.Contains(strings.ToLower(as.GetString("banks")), strings.ToLower(Plumas.String())),
},
}
log.Printf("config: %+v", config)
if config.Uploader == UploaderTodo {
token := as.GetString("todotoken")
if len(token) == 0 {
token = getToken(as)
}
list := as.GetString("todolist")
if len(list) == 0 {
list = getList(as, token)
}
config.TodoToken = token
config.TodoList = list
}
return config
}
func getToken(as *args.ArgSet) string {
c := &http.Client{CheckRedirect: func(r *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}}
body := "username=" + as.GetString("todopass")
name := strings.Split(as.GetString("todoaddr"), ".")[0]
name = strings.TrimPrefix(name, "http://")
name = strings.TrimPrefix(name, "https://")
req, err := http.NewRequest("POST", as.GetString("authaddr")+"/authorize/"+name+"?"+oauth2.REDIRECT+"=127.0.0.1", strings.NewReader(body))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := c.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode > 399 {
panic("bad status getting token: " + resp.Status)
}
cookie := resp.Header.Get("Set-Cookie")
token := cookie[strings.Index(cookie, "=")+1:]
token = strings.Split(token, "; ")[0]
if len(token) == 0 {
panic(fmt.Sprintf("no token found: (%v) %v", resp.StatusCode, resp.Header))
}
return token
panic("DEAD")
}
func getList(as *args.ArgSet, token string) string {
req, err := http.NewRequest("GET", as.GetString("todoaddr")+"/ajax.php?loadLists", nil)
if err != nil {
panic(err)
}
req.Header.Set("Cookie", oauth2.COOKIE+"="+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var r struct {
List []struct {
ID string `json:"id"`
} `json:"list"`
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
if err := json.Unmarshal(b, &r); err != nil {
panic(fmt.Errorf("%v: %s", err, b))
}
if len(r.List) == 0 {
panic("no lists found")
}
list := r.List[0].ID
if len(list) == 0 {
panic("empty list found")
}
return list
panic("DEAD")
}

32
go.mod
View File

@ -1,25 +1,22 @@
module local/email-xactions-to-todo
module gitea.inhome.blapointe.com/local/email-xactions-to-todo
go 1.17
go 1.23.0
require (
gitea.inhome.blapointe.com/local-sandbox/contact v0.0.2-0.20231109150121-14036702ee2a
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56
gitea.inhome.blapointe.com/local/storage v0.0.0-20231109151605-736d446d407d
github.com/google/uuid v1.3.0
local/args v0.0.0-00010101000000-000000000000
local/oauth2 v0.0.0-00010101000000-000000000000
local/sandbox/contact/contact v0.0.0-00010101000000-000000000000
local/storage v0.0.0-00010101000000-000000000000
)
require (
cloud.google.com/go v0.33.1 // indirect
gitea.inhome.blapointe.com/local/logb v0.0.0-20231109150430-1221d87a6dbc // indirect
github.com/Unknwon/goconfig v0.0.0-20181105214110-56bd8ab18619 // indirect
github.com/abbot/go-http-auth v0.4.0 // indirect
github.com/aws/aws-sdk-go v1.15.81 // indirect
github.com/boltdb/bolt v1.3.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
github.com/buraksezer/consistent v0.9.0 // indirect
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/emersion/go-imap v1.2.0 // indirect
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
github.com/go-stack/stack v1.8.0 // indirect
@ -52,24 +49,11 @@ require (
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect
golang.org/x/oauth2 v0.0.0-20181120190819-8f65e3013eba // indirect
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2 // indirect
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect
google.golang.org/api v0.0.0-20181120235003-faade3cbb06a // indirect
google.golang.org/appengine v1.3.0 // indirect
gopkg.in/ini.v1 v1.42.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
local/logb v0.0.0-00010101000000-000000000000 // indirect
)
replace local/args => ../args
replace local/oauth2 => ../oauth2
replace local/sandbox/contact/contact => ../sandbox/contact/contact
replace local/storage => ../storage
replace local/router => ../router
replace local/logb => ../logb

24
go.sum
View File

@ -1,12 +1,18 @@
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
cloud.google.com/go v0.33.1 h1:fmJQWZ1w9PGkHR1YL/P7HloDvqlmKQ4Vpb7PC2e+aCk=
cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
gitea.inhome.blapointe.com/local-sandbox/contact v0.0.2-0.20231109150121-14036702ee2a h1:vDt7kgsUwsI6fq7ObPUuUQ2CiIts3RaEsvcuwruspEY=
gitea.inhome.blapointe.com/local-sandbox/contact v0.0.2-0.20231109150121-14036702ee2a/go.mod h1:3LBm8MXwR5D5Z8gwp5p39KmabJR/F7cxBSZwVHWTfQQ=
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56 h1:zTGGZ77KLFagqUvDSgTOnm0qF+iSLwQWiEtGjb2jjlY=
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56/go.mod h1:SqCOE3bE3wvrztVIQGHuyxHKfDjRKU9EWhBdkmkiwyc=
gitea.inhome.blapointe.com/local/logb v0.0.0-20231109150430-1221d87a6dbc h1:u3akQkq12V8xWXlcDgjZxIK6hqo6f1eHd9KOxAKMoKc=
gitea.inhome.blapointe.com/local/logb v0.0.0-20231109150430-1221d87a6dbc/go.mod h1:KwilawX4UgD4HxSJAVFEzkuckrnHeQrd49KwUX6GpYU=
gitea.inhome.blapointe.com/local/storage v0.0.0-20231109151605-736d446d407d h1:SQq4hWImnvtrRfpPgOW4go+sBjMluuhRL/43b8L0yB4=
gitea.inhome.blapointe.com/local/storage v0.0.0-20231109151605-736d446d407d/go.mod h1:TRK5z/XTT6jws++Q21Y8DQot+5vZGTNeHf+RjuY8aQk=
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-storage-blob-go v0.0.0-20181023070848-cf01652132cc/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Unknwon/goconfig v0.0.0-20181105214110-56bd8ab18619 h1:6X8iB881g299aNEv6KXrcjL31iLOH7yA6NXoQX+MbDg=
github.com/Unknwon/goconfig v0.0.0-20181105214110-56bd8ab18619/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw=
github.com/a8m/tree v0.0.0-20180321023834-3cf936ce15d6/go.mod h1:FSdwKX97koS5efgm8WevNf7XS3PqtyFkKDDXrz778cg=
@ -18,14 +24,8 @@ github.com/aws/aws-sdk-go v1.15.81/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3A
github.com/billziss-gh/cgofuse v1.1.0/go.mod h1:LJjoaUojlVjgo5GQoEJTcJNqZJeRU0nCR84CyxKt2YM=
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/buraksezer/consistent v0.9.0 h1:Zfs6bX62wbP3QlbPGKUhqDw7SmNkOzY5bHZIYXYpR5g=
github.com/buraksezer/consistent v0.9.0/go.mod h1:6BrVajWq7wbKZlTOUPs/XVfR8c0maujuPowduSpZqmw=
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e h1:mQTN05gz0rDZSABqKMzAPMb5ATWcvvdMljRzEh0LjBo=
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e/go.mod h1:alXX+s7a4cKaIprgjeEboqi4Tm7XR/HXEwUTxUV/ywU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/coreos/bbolt v0.0.0-20180318001526-af9db2027c98/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -172,8 +172,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@ -232,16 +230,16 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2 h1:T5DasATyLQfmbTpfEXx/IOL9vfjzW6up+ZDkmHvIf2s=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=

View File

@ -1,9 +1,10 @@
package main
import (
"local/sandbox/contact/contact"
"log"
"regexp"
"gitea.inhome.blapointe.com/local-sandbox/contact"
)
func main() {
@ -12,6 +13,7 @@ func main() {
IMAP: config.EmailIMAP,
From: config.EmailUser,
Password: config.EmailPass,
Limit: config.EmailLimit,
}
emails, err := emailer.Read()
if err != nil {
@ -33,7 +35,7 @@ func main() {
log.Printf("skipping match account antipattern %q vs %q", config.AccountsAntiPattern, transaction.Account)
continue
}
if _, err := config.Storage.Get(transaction.ID); err == nil {
if v, err := config.Storage.Get(transaction.ID); err == nil || string(v) == transaction.String() {
log.Println("skipping duplicate transaction:", transaction)
} else {
if err := Upload(config, transaction); err != nil {

370
scrape.go
View File

@ -4,9 +4,11 @@ import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"net/mail"
"regexp"
"slices"
"strconv"
"strings"
)
@ -15,10 +17,13 @@ type scraper interface {
scrape(*mail.Message) ([]*Transaction, error)
}
type fidelityScraper struct{}
type bankOfAmericaScraper struct{}
type chaseScraper struct{}
type citiScraper struct{}
type uccuScraper struct{}
type amexScraper struct{}
type plumasScraper struct{}
func Scrape(m *mail.Message, banks map[Bank]bool) ([]*Transaction, error) {
scraper, err := buildScraper(m, banks)
@ -30,13 +35,16 @@ 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") {
if !containsAny(subject, "transaction", "report", "Transaction", "payment", "Payment", "Deposit", "Withdrawal", "balance is", "Balance", "Large Purchase", "transaction with", "credit pending on your card", "Account Alert") {
return nil, errors.New("cannot build scraper for subject " + subject)
}
from := fmt.Sprint(m.Header["From"])
if strings.Contains(from, "Chase") && banks[Chase] {
return newChaseScraper(), nil
}
if strings.Contains(from, "Fidelity") && banks[Fidelity] {
return newFidelityScraper(), nil
}
if strings.Contains(from, "Bank of America") && banks[BankOfAmerica] {
return newBankOfAmericaScraper(), nil
}
@ -46,7 +54,17 @@ func buildScraper(m *mail.Message, banks map[Bank]bool) (scraper, error) {
if strings.Contains(from, "Notifications@uccu.com") && banks[UCCU] {
return newUCCUScraper(), nil
}
return nil, errors.New("unknown sender: " + from)
if strings.Contains(from, "Notifications@plumasbank.com") && banks[Plumas] {
return newPlumasScraper(), nil
}
if strings.Contains(strings.ToLower(from), strings.ToLower("AmericanExpress")) && banks[Amex] {
return newAmexScraper(), nil
}
return nil, fmt.Errorf("unknown sender: %q", from)
}
func newFidelityScraper() scraper {
return &fidelityScraper{}
}
func newBankOfAmericaScraper() scraper {
@ -65,6 +83,14 @@ func newCitiScraper() scraper {
return &citiScraper{}
}
func newAmexScraper() scraper {
return &amexScraper{}
}
func newPlumasScraper() scraper {
return &plumasScraper{}
}
func containsAny(a string, b ...string) bool {
for i := range b {
if strings.Contains(a, b[i]) {
@ -75,13 +101,100 @@ func containsAny(a string, b ...string) bool {
}
func (c *chaseScraper) scrape(m *mail.Message) ([]*Transaction, error) {
transactions, err := c.scrape2021(m)
if err == nil && len(transactions) > 0 {
if transactions, err := c.scrape202506(m); err == nil && len(transactions) > 0 {
return transactions, err
}
if transactions, err := c.scrape2025Balance(m); err == nil && len(transactions) > 0 {
return transactions, err
}
if transactions, err := c.scrape2021(m); err == nil && len(transactions) > 0 {
return transactions, err
}
return c.scrape2020(m)
}
func (c *chaseScraper) scrape202506(m *mail.Message) ([]*Transaction, error) {
if xactions, err := c.scrape202506Charge(m); err == nil && len(xactions) > 0 {
return xactions, nil
}
return c.scrape202506Credit(m)
}
func (c *chaseScraper) scrape202506Credit(m *mail.Message) ([]*Transaction, error) {
pattern := `^You have a \$([0-9][,0-9]*\.[0-9]{2}) credit pending on your credit card`
re := regexp.MustCompile(pattern)
matches := re.FindSubmatch([]byte(m.Header["Subject"][0]))
if len(matches) < 1 {
return nil, fmt.Errorf("no match subject search (%q vs %q)", pattern, m.Header["Subject"][0])
}
b, _ := io.ReadAll(m.Body)
amount := string(matches[1])
amount = strings.ReplaceAll(amount, ",", "")
vendor := "*"
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) scrape202506Charge(m *mail.Message) ([]*Transaction, error) {
pattern := `^(Your|You made an?) (\$(?P<amount>[,0-9\.]*)|online, phone, or mail) transaction with (?P<vendor>.*)$`
re := regexp.MustCompile(pattern)
matches := re.FindSubmatch([]byte(m.Header["Subject"][0]))
if len(matches) < 1 {
return nil, fmt.Errorf("no match subject search (%q vs %q)", pattern, m.Header["Subject"][0])
}
b, _ := io.ReadAll(m.Body)
amount := string(matches[3])
if amount == "" {
pattern := `\$([0-9][,0-9]*\.[0-9]{2})`
matches := regexp.MustCompile(pattern).FindStringSubmatch(string(b))
if len(matches) < 2 || len(matches[1]) == 0 {
return nil, fmt.Errorf("no $x,yyy,zz in body")
}
amount = matches[1]
}
amount = strings.ReplaceAll(amount, ",", "")
vendor := string(matches[4])
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) scrape2025Balance(m *mail.Message) ([]*Transaction, error) {
re := regexp.MustCompile(`^Your.*balance is \$[0-9,\.]*$`)
if !re.Match([]byte(m.Header["Subject"][0])) {
return nil, errors.New("no match subject search")
}
subject := m.Header["Subject"][0]
fields := strings.Fields(subject)
amount := fields[len(fields)-1]
amount = strings.TrimLeft(amount, "$")
amount = strings.ReplaceAll(amount, ",", "")
amount = fmt.Sprintf("=%s", amount)
b, err := ioutil.ReadAll(m.Body)
if err != nil {
return nil, err
}
re = regexp.MustCompile(`\(\.\.\.([0-9]{4})\)`)
submatches := re.FindSubmatch(b)
account := string(submatches[len(submatches)-1])
return []*Transaction{NewTransaction(account, amount, "*", fmt.Sprint(m.Header["Date"]), Chase)}, nil
}
func (c *chaseScraper) scrape2021(m *mail.Message) ([]*Transaction, error) {
if t, err := c.scrape2021Payment(m); err == nil {
return t, err
@ -192,6 +305,9 @@ func (c *uccuScraper) scrape(m *mail.Message) ([]*Transaction, error) {
if err != nil {
return nil, err
}
if bytes.Contains(b, []byte(`alance alert`)) {
return c.scrapeBalance(m, b)
}
regexp := regexp.MustCompile(`\$([0-9]+,?)+\.[0-9][0-9]`)
match := regexp.Find(b)
if len(match) == 0 {
@ -210,6 +326,147 @@ func (c *uccuScraper) scrape(m *mail.Message) ([]*Transaction, error) {
return []*Transaction{transaction}, nil
}
func (c *uccuScraper) scrapeBalance(m *mail.Message, b []byte) ([]*Transaction, error) {
re := regexp.MustCompile(`is \$([0-9]+,?)+\.[0-9][0-9]`)
match := re.Find(b)
if len(match) == 0 {
return nil, fmt.Errorf("no matches found")
}
match = match[4:]
match = bytes.ReplaceAll(match, []byte(","), []byte{})
f, err := strconv.ParseFloat(string(match), 10)
if err != nil {
return nil, err
}
acc := UCCU.String()
re = regexp.MustCompile(`ending in [0-9]*`)
match = re.Find(b)
if len(match) > 0 {
acc = fmt.Sprintf("%s-%s", acc, match[len(`ending in `):])
}
transaction := NewTransaction(acc, fmt.Sprintf("=%.2f", f), "*", fmt.Sprint(m.Header["Date"]), UCCU)
return []*Transaction{transaction}, nil
}
func (c *amexScraper) scrape(m *mail.Message) ([]*Transaction, error) {
b, err := ioutil.ReadAll(m.Body)
if err != nil {
return nil, err
}
b = bytes.ReplaceAll(b, []byte("=\n"), []byte(""))
matches := regexp.MustCompile(`\$([0-9]+,?)+\.[0-9][0-9]`).FindAll(b, -1)
matches = slices.DeleteFunc(matches, func(match []byte) bool {
return string(match) == "$1.00"
})
if len(matches) == 0 {
return nil, fmt.Errorf("no matches found")
}
match := matches[0]
match = match[1:]
match = bytes.ReplaceAll(match, []byte(","), []byte{})
f, err := strconv.ParseFloat(string(match), 10)
if err != nil {
return nil, err
}
f *= -1.0
vendors := regexp.MustCompile(`>[A-Z][A-Z ]*<`).FindAll(b, -1)
vendors = slices.DeleteFunc(vendors, func(b []byte) bool { return string(b) == ">BREE A LAPOINTE<" })
vendor := "*"
if len(vendors) > 0 {
vendor = string(vendors[0])
}
vendor = strings.TrimSpace(strings.Trim(strings.Trim(vendor, ">"), "<"))
vendor = strings.ReplaceAll(vendor, "\n", "")
accs := regexp.MustCompile(`Account Ending: [0-9]*([0-9]{4})[^0-9]`).FindSubmatch(b)
acc := "?"
if len(accs) > 1 {
acc = string(accs[1])
}
acc = strings.ReplaceAll(acc, "\n", "")
transaction := NewTransaction(
fmt.Sprintf("%s-%s", Amex.String(), acc),
fmt.Sprintf("%.2f", f),
vendor,
fmt.Sprint(m.Header["Date"]),
Amex,
)
return []*Transaction{transaction}, nil
}
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)
}
if strings.Contains(subject, "Deposit Received") {
return c.scrapeDeposit(m)
}
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 {
return nil, err
}
fidelAcc, _ := findSubstringBetween(b, "Account: XXXXX", "\n")
transaction := NewTransaction(
fmt.Sprintf("%s-%s", Fidelity, fidelAcc),
"?.??",
"misc",
fmt.Sprint(m.Header["Date"]),
Fidelity,
)
return []*Transaction{transaction}, nil
}
func (c *fidelityScraper) scrapeWithdrawal(m *mail.Message) ([]*Transaction, error) {
b, err := ioutil.ReadAll(m.Body)
if err != nil {
return nil, err
}
amount, amountOk := findSubstringBetween(b, "in the amount of $", " ")
fidelAcc, fidelAccOk := findSubstringBetween(b, "For account ending in ", ":")
acc, accOk := findSubstringBetween(b, "in the amount of $"+amount+" by ", ".")
if amount == "" || acc == "" {
return nil, fmt.Errorf("no amount/account found: fidelAcc=%v,fidelAccOk=%v, acc=%v,accOk=%v, amount=%v,amountOk=%v", fidelAcc, fidelAccOk, acc, accOk, amount, amountOk)
}
transaction := NewTransaction(fmt.Sprintf("%s-%s", Fidelity, fidelAcc), amount, acc, fmt.Sprint(m.Header["Date"]), Fidelity)
return []*Transaction{transaction}, nil
}
func (c *bankOfAmericaScraper) scrape(m *mail.Message) ([]*Transaction, error) {
subject := fmt.Sprint(m.Header["Subject"])
if strings.Contains(subject, "Credit card transaction") {
@ -226,9 +483,40 @@ func (c *bankOfAmericaScraper) scrapeCharge(m *mail.Message) ([]*Transaction, er
if err != nil {
return nil, err
}
if v, err := c.scrapeCharge2023(m, b); err == nil {
return v, nil
}
return c.scrapeCharge2024(m, b)
}
amount := c.findFloatAfter(b, "Amount: $")
acc := string(c.findLineAfter(b, "Where: "))
func (c *bankOfAmericaScraper) scrapeCharge2023(m *mail.Message, b []byte) ([]*Transaction, error) {
amount := findFloatAfter(b, "Amount: $")
acc := string(findLineAfter(b, "Where: "))
if amount == "" || acc == "" {
return nil, errors.New("no amount/account found")
}
transaction := NewTransaction(BankOfAmerica.String(), amount, acc, fmt.Sprint(m.Header["Date"]), BankOfAmerica)
return []*Transaction{transaction}, nil
}
func (c *bankOfAmericaScraper) scrapeCharge2024(m *mail.Message, b []byte) ([]*Transaction, error) {
amountPattern := `Amount:[^\$]*\$([0-9]*\.[0-9]*)`
amountMatches := regexp.MustCompile(amountPattern).FindSubmatch(bytes.ReplaceAll(b, []byte("\n"), []byte(" ")))
if len(amountMatches) < 2 {
return nil, fmt.Errorf("email does not match amount %q: %+v", amountPattern, amountMatches)
}
amount := string(amountMatches[1])
b2 := bytes.Split(b, []byte("Where:"))[1]
b3 := bytes.Split(b2, []byte("</tr>"))[0]
b4 := bytes.ReplaceAll(b3, []byte("\n"), []byte(" "))
accPattern := `<b>(.*)</b>`
accMatches := regexp.MustCompile(accPattern).FindSubmatch(b4)
if len(accMatches) < 2 {
return nil, fmt.Errorf("email does not match acc %q", accPattern)
}
acc := string(accMatches[1])
if amount == "" || acc == "" {
return nil, errors.New("no amount/account found")
@ -242,7 +530,7 @@ func (c *bankOfAmericaScraper) scrapePayment(m *mail.Message) ([]*Transaction, e
if err != nil {
return nil, err
}
amount := "-" + c.findFloatAfter(b, "Payment: $")
amount := "-" + findFloatAfter(b, "Payment: $")
acc := "Payment"
if amount == "" || acc == "" {
return nil, errors.New("no amount/account found")
@ -251,8 +539,70 @@ func (c *bankOfAmericaScraper) scrapePayment(m *mail.Message) ([]*Transaction, e
return []*Transaction{transaction}, nil
}
func (c *bankOfAmericaScraper) findFloatAfter(b []byte, prefix string) string {
amount := string(c.findLineAfter(b, prefix))
func (c *plumasScraper) scrape(m *mail.Message) ([]*Transaction, error) {
b, err := ioutil.ReadAll(m.Body)
if err != nil {
return nil, err
}
if bytes.Contains(b, []byte(`alance alert`)) {
return c.scrapeBalance(m, b)
}
regexp := regexp.MustCompile(`\$([0-9]+,?)+\.[0-9][0-9]`)
match := regexp.Find(b)
if len(match) == 0 {
return nil, fmt.Errorf("no matches found")
}
match = match[1:]
match = bytes.ReplaceAll(match, []byte(","), []byte{})
f, err := strconv.ParseFloat(string(match), 10)
if err != nil {
return nil, err
}
if !bytes.Contains(b, []byte("credit")) {
f *= -1.0
}
transaction := NewTransaction(Plumas.String(), fmt.Sprintf("%.2f", f), "?", fmt.Sprint(m.Header["Date"]), Plumas)
return []*Transaction{transaction}, nil
}
func (c *plumasScraper) scrapeBalance(m *mail.Message, b []byte) ([]*Transaction, error) {
re := regexp.MustCompile(`is \$([0-9]+,?)+\.[0-9][0-9]`)
match := re.Find(b)
if len(match) == 0 {
return nil, fmt.Errorf("no matches found")
}
match = match[4:]
match = bytes.ReplaceAll(match, []byte(","), []byte{})
f, err := strconv.ParseFloat(string(match), 10)
if err != nil {
return nil, err
}
acc := Plumas.String()
re = regexp.MustCompile(`ending in [0-9]*`)
match = re.Find(b)
if len(match) > 0 {
acc = fmt.Sprintf("%s-%s", acc, match[len(`ending in `):])
}
transaction := NewTransaction(acc, fmt.Sprintf("=%.2f", f), "*", fmt.Sprint(m.Header["Date"]), Plumas)
return []*Transaction{transaction}, nil
}
func findSubstringBetween(b []byte, prefix, suffix string) (string, bool) {
byPre := bytes.Split(b, []byte(prefix))
if len(byPre) < 2 {
return "", false
}
bySuff := bytes.Split(byPre[1], []byte(suffix))
if len(bySuff) < 2 {
return "", false
}
return string(bySuff[0]), true
}
func findFloatAfter(b []byte, prefix string) string {
amount := string(findLineAfter(b, prefix))
words := strings.Split(amount, " ")
lastword := words[len(words)-1]
escapedfloat := strings.TrimPrefix(lastword, "$")
@ -261,7 +611,7 @@ func (c *bankOfAmericaScraper) findFloatAfter(b []byte, prefix string) string {
return amount
}
func (c *bankOfAmericaScraper) findLineAfter(b []byte, prefix string) []byte {
func findLineAfter(b []byte, prefix string) []byte {
for _, line := range bytes.Split(b, []byte("\n")) {
if bytes.HasPrefix(line, []byte(prefix)) {
return bytes.TrimSpace(bytes.TrimPrefix(line, []byte(prefix)))

View File

@ -4,9 +4,141 @@ import (
"bytes"
"io/ioutil"
"net/mail"
"os"
"testing"
)
func TestScrapeAmex(t *testing.T) {
b, _ := os.ReadFile("testdata/amex.txt")
message := &mail.Message{
Header: map[string][]string{
"Subject": []string{"Large Purchase Approved"},
},
Body: bytes.NewReader(b),
}
amex := &amexScraper{}
gots, err := amex.scrape(message)
if err != nil {
t.Fatal(err)
}
if len(gots) != 1 {
t.Fatal(gots)
}
got := gots[0]
if got.Account != "AmericanExpress-2003" {
t.Fatalf("bad account: %v: %+v", got.Account, got)
}
if got.Amount != "-30.00" {
t.Fatalf("bad amount: %v: %+v", got.Amount, got)
}
if got.Vendor != "CRAWFORD LEISHMAN DENTAL" {
t.Fatalf("bad vendor: %v: %+v", got.Vendor, got)
}
t.Logf("%+v", got)
}
func TestScrapeUCCUBalance(t *testing.T) {
b, _ := os.ReadFile("testdata/uccu.balance.txt")
message := &mail.Message{
Header: map[string][]string{
"Subject": []string{"UCCU Account Alert Notification"},
},
}
uccu := &uccuScraper{}
gots, err := uccu.scrapeBalance(message, b)
if err != nil {
t.Fatal(err)
}
if len(gots) != 1 {
t.Fatal(gots)
}
got := gots[0]
if got.Account != "UCCU-33350" {
t.Fatalf("bad account: %v: %+v", got.Account, got)
}
if got.Amount != "=231.20" {
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 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")
message := &mail.Message{
Header: map[string][]string{
"Subject": []string{"Your Chase Freedom Unlimited balance is $1,029.08"},
},
Body: bytes.NewReader(b),
}
chase := &chaseScraper{}
gots, err := chase.scrape2025Balance(message)
if err != nil {
t.Fatal(err)
}
if len(gots) != 1 {
t.Fatal(gots)
}
got := gots[0]
if got.Account != "5876" {
t.Fatalf("bad account: %v: %+v", got.Account, got)
}
if got.Amount != "=1029.08" {
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 TestScrapeChase202112Payment(t *testing.T) {
b, err := ioutil.ReadFile("./testdata/chase.2021.12.payment.txt")
if err != nil {
@ -77,6 +209,111 @@ func TestScrapeChase2021Payment(t *testing.T) {
t.Logf("%+v", got)
}
func TestScrapeChase202506Credit(t *testing.T) {
b, err := ioutil.ReadFile("./testdata/chase.202506.credit.txt")
if err != nil {
t.Fatal(err)
}
message := &mail.Message{
Header: map[string][]string{
"Subject": []string{"You have a $394.96 credit pending on your credit card"},
},
Body: bytes.NewReader(b),
}
chase := &chaseScraper{}
gots, err := chase.scrape202506(message)
if err != nil {
t.Fatal(err)
}
if len(gots) != 1 {
t.Fatal(gots)
}
got := gots[0]
if got.Account != "5876" {
t.Fatalf("bad account: %v: %+v", got.Account, got)
}
if got.Amount != "394.96" {
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 TestScrapeChase202506(t *testing.T) {
b, err := ioutil.ReadFile("./testdata/chase.202506.txt")
if err != nil {
t.Fatal(err)
}
message := &mail.Message{
Header: map[string][]string{
"Subject": []string{"You made a $3.42 transaction with Nintendo CB141137080"},
},
Body: bytes.NewReader(b),
}
chase := &chaseScraper{}
gots, err := chase.scrape202506(message)
if err != nil {
t.Fatal(err)
}
if len(gots) != 1 {
t.Fatal(gots)
}
got := gots[0]
if got.Account != "5876" {
t.Fatalf("bad account: %v: %+v", got.Account, got)
}
if got.Amount != "3.42" {
t.Fatalf("bad amount: %v: %+v", got.Amount, got)
}
if got.Vendor != "Nintendo CB141137080" {
t.Fatalf("bad vendor: %v: %+v", got.Vendor, got)
}
t.Logf("%+v", got)
}
func TestScrapeChase202506Online(t *testing.T) {
b, err := ioutil.ReadFile("./testdata/chase.202506.online.txt")
if err != nil {
t.Fatal(err)
}
message := &mail.Message{
Header: map[string][]string{
"Subject": []string{"You made an online, phone, or mail transaction with DD *KLUCKSKRISPYCHIC"},
},
Body: bytes.NewReader(b),
}
chase := &chaseScraper{}
gots, err := chase.scrape202506(message)
if err != nil {
t.Fatal(err)
}
if len(gots) != 1 {
t.Fatal(gots)
}
got := gots[0]
if got.Account != "5876" {
t.Fatalf("bad account: %v: %+v", got.Account, got)
}
if got.Amount != "18.17" {
t.Fatalf("bad amount: %v: %+v", got.Amount, got)
}
if got.Vendor != "DD *KLUCKSKRISPYCHIC" {
t.Fatalf("bad vendor: %v: %+v", got.Vendor, got)
}
t.Logf("%+v", got)
}
func TestScrapeChase2021(t *testing.T) {
b, err := ioutil.ReadFile("./testdata/chase.2021.txt")
if err != nil {
@ -179,6 +416,111 @@ func TestScrapeBofAPayment(t *testing.T) {
}
}
func TestScrapeFidelityDeposit(t *testing.T) {
b, err := ioutil.ReadFile("./testdata/fidelity.deposit.txt")
if err != nil {
t.Fatal(err)
}
message := &mail.Message{
Header: map[string][]string{
"Subject": []string{"Fidelity Alerts: Deposit Received"},
},
Body: bytes.NewReader(b),
}
fidelity := &fidelityScraper{}
gots, err := fidelity.scrape(message)
if err != nil {
t.Fatal(err)
}
if len(gots) != 1 {
t.Fatal(len(gots))
}
got := gots[0]
want := Transaction{
ID: got.ID,
Bank: Fidelity,
Amount: "?.??",
Vendor: "misc",
Date: "[]",
Account: Fidelity.String() + "-5576",
}
if *got != want {
t.Fatalf("want:\n\t%+v, got\n\t%+v", want, *got)
}
}
func TestScrapeFidelityWithdrawal(t *testing.T) {
b, err := ioutil.ReadFile("./testdata/fidelity.withdrawal.txt")
if err != nil {
t.Fatal(err)
}
message := &mail.Message{
Header: map[string][]string{
"Subject": []string{"Fidelity Alerts - Direct Debit Withdrawal"},
},
Body: bytes.NewReader(b),
}
fidelity := &fidelityScraper{}
gots, err := fidelity.scrape(message)
if err != nil {
t.Fatal(err)
}
if len(gots) != 1 {
t.Fatal(len(gots))
}
got := gots[0]
want := Transaction{
ID: got.ID,
Bank: Fidelity,
Amount: "1.00",
Vendor: "CHASE CREDIT CRD",
Date: "[]",
Account: Fidelity.String() + "-5576",
}
if *got != want {
t.Fatalf("want:\n\t%+v, got\n\t%+v", want, *got)
}
}
func TestScrapeBofACharge2024(t *testing.T) {
b, err := ioutil.ReadFile("./testdata/bofa.charge.2024.txt")
if err != nil {
t.Fatal(err)
}
message := &mail.Message{
Header: map[string][]string{
"Subject": []string{"Credit card transaction exceeds alert limit you set"},
},
Body: bytes.NewReader(b),
}
bofa := &bankOfAmericaScraper{}
gots, err := bofa.scrape(message)
if err != nil {
t.Fatal(err)
}
if len(gots) != 1 {
t.Fatal(len(gots))
}
got := gots[0]
want := Transaction{
ID: got.ID,
Bank: BankOfAmerica,
Amount: "21.48",
Vendor: "PP SPOTIFYUSAI",
Date: "[]",
Account: BankOfAmerica.String(),
}
if *got != want {
t.Fatalf("want:\n\t%+v, got\n\t%+v", want, *got)
}
}
func TestScrapeBofACharge(t *testing.T) {
b, err := ioutil.ReadFile("./testdata/bofa.charge.txt")
if err != nil {

1407
testdata/amex.txt vendored Normal file

File diff suppressed because it is too large Load Diff

623
testdata/bofa.charge.2024.txt vendored Normal file
View File

@ -0,0 +1,623 @@
Delivered-To: breellocaldev@gmail.com
Received: by 2002:a05:6e04:410a:b0:337:e209:a6dd with SMTP id ab10csp997577imd;
Fri, 22 Nov 2024 03:21:45 -0800 (PST)
X-Google-Smtp-Source: AGHT+IF+g6ycRXBD1NCmTgCmYsdR7EaUiTrr6m4TjY81halMm8PMqQPaIVPGdqeuLn6MScBOpgIL
X-Received: by 2002:a05:6214:d6e:b0:6d4:2b2:61f9 with SMTP id 6a1803df08f44-6d442336b62mr127197466d6.5.1732274505767;
Fri, 22 Nov 2024 03:21:45 -0800 (PST)
ARC-Seal: i=1; a=rsa-sha256; t=1732274505; cv=none;
d=google.com; s=arc-20240605;
b=DOWQaaaBxJOxTAbGlmwXcxHxVYcT2JoZRrYDY1ybcxQmvEhuXA2mDLI4sbePTHa1X+
NLEh/hvBkwfwQe0b7554tIaTF3Q/7MBsZKRttc41JcgnbsApiznFn5ncGJ5/vuPTMAG9
8/oBvgNLjgJ8R7yUBSYNR0zlPeaN69okV59DJ2FYjIxvKCbuaM/aeJ7qQtrrvIcG0Oyi
YRRcw8l4pkenP9fzHo9MmupYS3YJP5YVE0DaIFxA9y1jFkpxN4rGGHl2VPrGUezWBIGJ
owxLJNzKwHjzq2cfKvko3BftaBdDVVWetHsHUd5TISPnLYQkfdvctN4SOH9q9JXl0l9T
7xTQ==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605;
h=content-transfer-encoding:feedback-id:message-id:reply-to
:mime-version:list-help:date:subject:to:from:dkim-signature
:dkim-signature;
bh=Kjcf8l3mYIkPtkdpXpj0inhpY0YdNBqkJohGWf3viHk=;
fh=3JUHTSQLTIIxQd8AswL6O34TjQIBrZ4ZoXgEKtTs4Cc=;
b=k9x6eOqDsHMK/3BHdWV57glqBDel/EtRixV/w6OsUGH0p4Q9Uhc9TFsHFn1vdChlcr
bQnuBKWU8/TM6Z8fb3VdjMX6kQ9dtuFJbGlBdDX5iUuUNGwLytLeyRiQEscMGESlRkQe
gmRexmvVRzxE4BlKQqmoJYgq0oNG+pxs8YGgaIBkySe9yU9xw+kdr0fcWkh1Y7C1ILmo
eFVH1zRqAHaKe03iXqBb4aLNrJ+mF77CtGi0wU3ZzPz02OI4MGSMFkmJfONE2nE0ZXOD
NRcV5My0xaWPs/6vfvBZ+Ffdtjwsqcqyqe7/B3SO0AKZ/iwN1+5uzR81yff1lzFsZc5N
/vVw==;
dara=google.com
ARC-Authentication-Results: i=1; mx.google.com;
dkim=pass header.i=@ealerts.bankofamerica.com header.s=200608 header.b=bBfeKaM5;
dkim=pass header.i=@s5.y.mc.salesforce.com header.s=fbldkim5 header.b=I8atuJ7U;
spf=pass (google.com: domain of bounce-145816_text-1457994210-6989359-522000109-197442@bounce.ealerts.bankofamerica.com designates 68.232.194.86 as permitted sender) smtp.mailfrom=bounce-145816_TEXT-1457994210-6989359-522000109-197442@bounce.ealerts.bankofamerica.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=bankofamerica.com
Return-Path: <bounce-145816_TEXT-1457994210-6989359-522000109-197442@bounce.ealerts.bankofamerica.com>
Received: from mta.appointment.bankofamerica.com (mta.appointment.bankofamerica.com. [68.232.194.86])
by mx.google.com with ESMTPS id 6a1803df08f44-6d451b8d6e9si17689236d6.558.2024.11.22.03.21.45
for <breellocaldev@gmail.com>
(version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128);
Fri, 22 Nov 2024 03:21:45 -0800 (PST)
Received-SPF: pass (google.com: domain of bounce-145816_text-1457994210-6989359-522000109-197442@bounce.ealerts.bankofamerica.com designates 68.232.194.86 as permitted sender) client-ip=68.232.194.86;
Authentication-Results: mx.google.com;
dkim=pass header.i=@ealerts.bankofamerica.com header.s=200608 header.b=bBfeKaM5;
dkim=pass header.i=@s5.y.mc.salesforce.com header.s=fbldkim5 header.b=I8atuJ7U;
spf=pass (google.com: domain of bounce-145816_text-1457994210-6989359-522000109-197442@bounce.ealerts.bankofamerica.com designates 68.232.194.86 as permitted sender) smtp.mailfrom=bounce-145816_TEXT-1457994210-6989359-522000109-197442@bounce.ealerts.bankofamerica.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=bankofamerica.com
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; s=200608; d=ealerts.bankofamerica.com; h=From:To:Subject:Date:List-Help:MIME-Version:Reply-To:Message-ID: Content-Type:Content-Transfer-Encoding; i=onlinebanking@ealerts.bankofamerica.com; bh=Kjcf8l3mYIkPtkdpXpj0inhpY0YdNBqkJohGWf3viHk=; b=bBfeKaM5RO/rMVToMyColBLLRtSjYNT3TGvWj49o32q2tfyrsE7wX9Yq7EaMYTCCMzoTWacjdDiF
UcfZoBtLt9twX1FMPGFPMJ75J9FJhrEPFhvakDou2fWrwFlV+RMhE9DWOq2eFug4vgpSqGNi/jfF
SS9H481F8tG3XpkpuDw=
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; s=fbldkim5; d=s5.y.mc.salesforce.com; h=From:To:Subject:Date:List-Help:MIME-Version:Reply-To:Message-ID: Content-Type:Content-Transfer-Encoding; bh=Kjcf8l3mYIkPtkdpXpj0inhpY0YdNBqkJohGWf3viHk=; b=I8atuJ7U2Q5P1QwvqEFf5/btRqH4ha8RSoyCenSOtaSf216FNv+Lg443P7yLzghV5bNjEMI/C6b/
r9+sUJB09dvKUbxV5qAhQr7a8bbvoKII3mk80NwdZf+4gNigTQFOF2y7rpi1kFZzmYKgvzY/EecN
AKfVmwpc+mirDuQi0lcIYAIDbaSsh20DchPAwJN+sw+f6kw+8c61s8PkDIxTFkbu3rTNexMgBEub
9zvvxBYVt+yzQqx6NyB5XTSXhpzILBV28SKD2rf0stAe+E9aie5JeSEkdYIceFkEF8RkNmMWq0cj
7Esz0wuM90nIy/vUUWEdLKvdVNCAvk5GnA0y5g==
Received: by mta.appointment.bankofamerica.com id h81kki2fmd4o for <breellocaldev@gmail.com>; Fri, 22 Nov 2024 11:21:43 +0000 (envelope-from <bounce-145816_TEXT-1457994210-6989359-522000109-197442@bounce.ealerts.bankofamerica.com>)
From: Bank of America <onlinebanking@ealerts.bankofamerica.com>
To: <breellocaldev@gmail.com>
Subject: Credit card transaction exceeds alert limit you set
Date: Fri, 22 Nov 2024 05:21:43 -0600
List-Help: <http://click.%%_DomainName%%/subscription_center.aspx?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtaWQiOiI1MjIwMDAxMDkiLCJzIjoiMTQ1Nzk5NDIxMCIsImxpZCI6IjE0NTgxNiIsImoiOiI2OTg5MzU5IiwiamIiOiIxOTc0NDIiLCJkIjoiNTAxMTEifQ.VIN-6jZTHGjKCpFQLN6_MwHb5PM931J2PWkK0V62I6o>
MIME-Version: 1.0
Reply-To: Bank of America <reply-fe7e12797d6d07787c-145816_TEXT-1457994210-522000109-197442@ealerts.bankofamerica.com>
X-SFMC-Stack: 5
x-job: 522000109_6989359
Message-ID: <063d3237-4661-4553-ac40-a4dabde00bad@las1s05mta5.xt.local>
Feedback-ID: 522000109:6989359:68.232.194.86:sfmktgcld
Content-Type: text/html; charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<style type="text/css">
/* CLIENT-SPECIFIC STYLES */
#outlook a{padding:0;} /* Force Outlook to provide a "view in browser" message */
.ReadMsgBody{width:100%;} .ExternalClass{width:100%;} /* Force Hotmail to display emails at full width */
.ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;} /* Force Hotmail to display normal line spacing */
body, table, td, a{-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%;} /* Prevent WebKit and Windows mobile changing default text sizes */
table, td{mso-table-lspace:0pt; mso-table-rspace:0pt;} /* Remove spacing between tables in Outlook 2007 and up */
img{-ms-interpolation-mode:bicubic;} /* Allow smoother rendering of resized image in Internet Explorer */
/* RESET STYLES */
body{margin:0; padding:0;}
img{border:0; height:auto; line-height:100%; outline:none; text-decoration:none;}
table{border-collapse:collapse !important;}
body{height:100% !important; margin:0; padding:0; width:100% !important;}
/* iOS BLUE LINKS */
.appleBody a {color:#68440a; text-decoration: none;}
.appleFooter a {color:#999999; text-decoration: none;}
ReadMsgBody{ width: 100%;}
.ExternalClass {width: 100%;}
.ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;}
body {-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%;margin:0 !important; padding:0 !important;}
p { margin: 1em 0;}
table td { border-collapse: collapse;}
img {outline:0;}
a img {border:none;}
p {margin: 1em 0;}
@-ms-viewport{ width: device-width;}
a[x-apple-data-detectors] {color: inherit !important;text-decoration: none !important;font-size: inherit !important;font-family: inherit !important; font-weight: inherit !important;line-height: inherit !important;}
@media only screen and (max-width: 480px) {
body[yahoo] .tbContainer { width:100% !important; }
body[yahoo] .tdMobHeaderImage { padding-top:30px !important;padding-bottom:0px !important; }
body[yahoo] .imgMobSize163x21 { max-width:163px !important; max-height:21px !important;}
body[yahoo] .imgMobSize280x58 { max-width:280px !important; max-height:58px !important;}
body[yahoo] .BofA_2019_228x23 { max-width:228px !important; max-height:23px !important; padding-left:84px; padding-right:64px}
body[yahoo] .imgMobSize70x70 { width:70px !important; height:70px !important;}
body[yahoo] .tdMobAlertImage { padding-top:30px !important; padding-bottom:0px !important; }
body[yahoo] .tdMobZ1tbl { width:335px !important; display: block !important;}
body[yahoo] .tdMobZ1tbl1 { width:335px !important; display: block !important;padding-bottom:30px !important;}
body[yahoo] .tdMobZ1tbl2 { width:335px !important; display: block !important;padding-bottom:0px !important;}
body[yahoo] .tdMobTitle { display: block !important;font-size:28px !important;padding-bottom:30px !important;}
body[yahoo] .tdMobZ1BottomPadding30px { display: block !important;font-size:24px !important;padding-bottom:30px !important; padding-top:30px !important; padding-left:20px !important;padding-right:20px !important}
body[yahoo] .tdMobZ1 { display: block !important;font-size:24px !important;padding-bottom:0px !important; padding-top:30px !important; padding-left:20px !important;padding-right:20px !important}
body[yahoo] .tdMobZ1BottomNew { display: block !important;font-size:24px !important;padding-top:30px !important; padding-left:20px !important;padding-right:20px !important}
body[yahoo] .tdMobZ2tbl { width:335px !important; align:center !important;}
body[yahoo] .tdMobZ2 { display: block !important; padding-left:45px !important;padding-right:20px !important}
body[yahoo] .tdMobZ2TopPadding18px { display: block !important;padding-top:18px !important;padding-left:45px !important;padding-right:20px !important}
body[yahoo] .tdMobZ2TopPadding30px { display: block !important;padding-top:30px !important;padding-left:45px !important;padding-right:20px !important}
body[yahoo] .tdMobTopPadding0px { display: block !important;padding-top:0px !important;padding-left:45px !important;padding-right:20px !important}
body[yahoo] .tdMobZ2tbllabel { display: block !important; font-size:16px !important; padding-bottom:12px !important; line-height:20px !important; text-align:left !important;}
body[yahoo] .tdMobZ2tbllabelinline { font-size:16px !important; padding-bottom:12px !important; padding-right:5px !important; line-height:20px !important; text-align:left !important;}
body[yahoo] .tdMobZ2tbllabel1 { display: block !important; font-size:16px !important; padding-bottom:12px !important; line-height:20px !important; text-align:left !important; margin-left:10px !important;}
body[yahoo] .tdMobZ2tbllabel2 { display: block !important; font-size:16px !important; padding-bottom:0px !important; line-height:20px !important; text-align:left !important;}
body[yahoo] .tdMobZ2tbllabel3 { display: block !important; font-size:16px !important; padding-bottom:0px !important; line-height:20px !important; text-align:left !important; width:100% !important;}
body[yahoo] .tdMobZ2tbllabel3a { display: block !important; font-size:16px !important; padding-bottom:0px !important; line-height:20px !important; text-align:left !important; width:100% !important; padding-bottom:12px}
body[yahoo] .tdMobZ2tbllabel3a1 { display: block !important; font-size:16px !important; padding-bottom:0px !important; line-height:20px !important; text-align:left !important; width:100% !important; padding-bottom:30px}
body[yahoo] .tdMobZ2tblData { font-size:16px !important;line-height:20px !important; text-align:left !important;}
body[yahoo] .tdMobZ2tblData1 { font-size:16px !important;line-height:24px !important; text-align:left !important; padding-bottom:12px !important;}
body[yahoo] .tdMobZ2tblData2 { font-size:16px !important;line-height:24px !important; text-align:left !important; width:175px !important;}
body[yahoo] .tdMobZ2atblData2 { font-size:16px !important;line-height:24px !important; text-align:right !important; width:175px !important;}
body[yahoo] .tdMobZ2atblData2a { font-size:16px !important;line-height:24px !important; text-align:right !important; width:175px !important; padding-bottom:12px}
body[yahoo] .tdMobZ2atblData2a1 { font-size:16px !important;line-height:24px !important; text-align:right !important; width:175px !important; padding-bottom:30px}
body[yahoo] .tdMobContentZ2Font { display: block !important;font-size:18px !important;}
body[yahoo] .tdMobTitleDetails { display: block !important;font-size:18px !important; padding-bottom:30px !important;}
body[yahoo] .zone3-message-img1 { display:block !important; width: 30px !important; height:30px !important;padding-top:0px !important;padding-bottom: 25px !important;padding-left:48px !important;}
body[yahoo] .zone3-message-body2 { display:block !important; padding-top:0px !important;padding-bottom: 30px !important;padding-left:15px !important;font-size:14px !important;line-height:18px !important;}
body[yahoo] .tblMobZ5Content4new1 { width:335px !important; text-align:left !important;align:center !important; padding-bottom:0px !important; padding-top:20px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content4border { width:300px !important; text-align:left !important;border: 1px solid #979797 !important; align:center !important; padding-bottom:0px !important; font-size:16px !important; padding-right:10px; !important;line-height:20px !important;}
body[yahoo] .zone3-message-body22 { width:335px !important; text-align:center !important;align:center !important; padding-bottom:0px !important; padding-top:20px !important; padding-left:0px !important; padding-right:0px !important;margin:0px !important;font-size:16px !important; line-height:20px !important;}
body[yahoo] .zone3-message-body23 { width:335px !important; text-align:center !important;align:center !important; padding-bottom:20px !important; padding-top:0px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .zone3-message-img11 { display:block !important; width: 17px !important; height:30px !important;padding-top:60px !important;padding-bottom: 25px !important;padding-left:18px !important;}
body[yahoo] .tblMobZ5new { width:335px !important;height:114px !important; text-align:center !important;align:center !important; padding-bottom:0px !important;}
body[yahoo] .tdMobAlertImage33 { padding-top:60px !important; padding-bottom:0px !important;padding-left:30px !important; }
body[yahoo] .zone4-message-body1 { display:block !important; padding-top:0px !important;padding-bottom: 18px !important;padding-left:18px !important;font-size:16px !important;line-height:20px !important;padding-right:18px !important;}
body[yahoo] .zone3-message-bodyNew { display:block !important; padding-top:0px !important;padding-bottom: 0px !important;padding-left:15px !important;font-size:14px !important;line-height:18px !important;}
body[yahoo] .tblMobZ3 { width:335px !important;text-align:center !important;align:center !important; }
body[yahoo] .tdMobZ3TopPadding18px { display: block !important;padding-top:18px !important;}
body[yahoo] .tdMobZ3TopPadding30px { display: block !important;padding-top:30px !important;}
body[yahoo] .tdMobZ3 { display: block !important;padding-top:0px !important;}
body[yahoo] .tdMobBottomPadding30px { display: block !important;padding-bottom:30px !important;}
body[yahoo] .tblMobZ3Content1 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:20px !important; }
body[yahoo] .tblMobZ3Content1New { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-top:20px !important; }
body[yahoo] .tblMobZ3Content2 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:15px !important;padding-top:0px !important;}
body[yahoo] .tblMobZ3Content3 { display: list-item !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:15px !important;}
body[yahoo] .tblMobZ3Content31 { display: list-item !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:12px !important;}
body[yahoo] .tblMobZ3Content6 { display: list-item !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:15px !important; padding-top:15px !important;}
body[yahoo] .tblMobZ3Content4 { display:list-item !important; font-size:16px !important; line-height:20px !important; text-align:left !important; }
body[yahoo] .tblMobZ3Content4New { display:list-item !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-top:12px !important;}
body[yahoo] .tblMobZ3Content5 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:0px !important; }
body[yahoo] .tblMobZ3Content8 { display:list-item !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:20px !important;}
body[yahoo] .tblMobZ3Content7 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:20px !important;}
body[yahoo] .tblMobZ3Content9 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:30px !important;}
body[yahoo] .tblMobZ3Content9New4 { display: block !important; font-size:12px !important; line-height:16px !important; text-align:left !important; padding-bottom:0px !important;}
body[yahoo] .tblMobZ3Content13 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:0px !important; padding-top:30px !important;}
body[yahoo] .tblMobZ3Content14 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-top:30px !important; padding-bottom:12px !important; }
body[yahoo] .tblMobZ3Content9New1 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-top:30px !important;}
body[yahoo] .tblMobZ3Content9New2 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-top:30px !important; padding-bottom:30px !important;}
body[yahoo] .tblMobZ3Content9New3 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:center !important; padding-top:30px !important; padding-bottom:30px!important;}
body[yahoo] .tblMobZ3Content9center { display: block !important; font-size:16px !important; line-height:20px !important; text-align:center !important; padding-bottom:30px !important;}
body[yahoo] .tblMobZ3Content9Bottom { display: block !important; font-size:16px !important; line-height:22px !important; text-align:center !important; padding-bottom:10px !important;}
body[yahoo] .tblMobZ3Content9CenterNoPadding { display: block !important; font-size:16px !important; line-height:22px !important; text-align:center !important; padding-bottom:0px !important;}
body[yahoo] .tblMobZ3Content10 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:12px !important; }
body[yahoo] .tblMobZ3Content11 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-top:15px !important; padding-bottom:0px !important;}
body[yahoo] .tblMobZ3Content51 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:12px !important; padding-left:25px !important; }
body[yahoo] .tblMobZ3Content21 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:20px !important; padding-top:0px !important;}
body[yahoo] .tblMobZ3Content21New{ display: block !important; font-size:12px !important; line-height:16px !important; text-align:left !important; padding-bottom:20px !important; padding-top:0px !important;}
body[yahoo] .tblMobZ3Content52 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:0px !important; padding-left:25px !important; }
body[yahoo] .bulletTopPadding { display:list-item !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:0px !important; padding-top:12px !important;}
body[yahoo] .tblMobZ3Bulletpadding { display: block !important; padding-bottom:15px !important; colspan:2 !important;}
body[yahoo] .tblMobZ4 { width:335px !important;text-align:center !important;align:center !important;}
body[yahoo] .tblMobZ4ButtonPadding { padding-bottom:30px !important; padding-top:30px !important;}
body[yahoo] .tblMobZ4ButtonPadding1 { padding-bottom:0px !important; padding-top:18px !important;}
body[yahoo] .tblMobZ4ButtonPadding2 { padding-bottom:0px !important; padding-top:30px !important;}
body[yahoo] .tdMobButtonZ4 { font-size:20px !important; line-height:45px !important; width:335px !important; border-left:0px solid #0052C2 !important; border-right:0px solid #0052C2 !important; border-top:0px solid #0052C2 !important; border-bottom:0px solid #0052C2 !important; text-align:center !important; align:center !important;}
body[yahoo] .tblMobZ5 { width:335px !important;text-align:center !important;align:center !important; padding-bottom:0px !important;}
body[yahoo] .tblMobZ5Content { width:335px !important; text-align:left !important;align:center !important; padding-bottom:20px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content1 { width:335px !important; text-align:left !important;align:center !important; padding-bottom:10px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content2 { width:335px !important; text-align:left !important;align:center !important; padding-bottom:50px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content3 { width:335px !important; text-align:left !important;align:center !important; padding-bottom:30px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content4 { width:335px !important; text-align:left !important;align:center !important; padding-bottom:0px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content5 { width:335px !important; text-align:left !important;align:center !important; padding-bottom:40px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content6 { width:335px !important; text-align:left !important;align:center !important; padding-bottom:30px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content7 { width:335px !important; text-align:left !important;align:center !important; padding-bottom:40px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content8 { width:335px !important; text-align:left !important;align:center !important; padding-bottom:15px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tdMobAltButtonZ4 { width:335px !important; text-align:left !important;align:center !important; font-size:16px !important; line-height:20px !important; padding-bottom:0px !important; padding-top:20px !important; font-family: Connections Light, Arial !important;}
body[yahoo] .tblMobFootnoteZ7 { display: block !important; width:335px !important;text-align:center !important;align:center !important;}
body[yahoo] .tdMobFootnoteZ7 { padding-top:20px !important;width:335px !important;font-size:11px !important;}
body[yahoo] .tdMobFootnoteZ72 { padding-top:30px !important; padding-bottom:0px !important; width:335px !important;font-size:11px !important; line-height:13px !important;}
body[yahoo] .tdMobFootnoteZ72Left { padding-top:30px !important; padding-bottom:0px !important; width:335px !important;font-size:11px !important; line-height:13px !important; text-align:left !important; margin:0px !important}
body[yahoo] .tdMobFootnoteZ73 { padding-top:20px !important; padding-bottom:0px !important; width:335px !important;font-size:11px !important; line-height:13px !important;}
body[yahoo] .tdMobFootnoteZ74 { padding-top:0px !important; padding-bottom:0px !important; width:335px !important;font-size:11px !important; line-height:13px !important;}
body[yahoo] .tdMobFootnoteZ75 { padding-top:15px !important; padding-bottom:0px !important; width:335px !important;font-size:11px !important; line-height:13px !important;}
body[yahoo] .tdMobFootnoteZ76 { padding-top:15px !important; Margin-left:0px !important; padding-bottom:0px !important; max-width:300px !important;font-size:11px !important; line-height:13px !important;}
body[yahoo] .tdMobFootnoteZ77 { padding-top:0px !important; padding-bottom:30px !important; width:335px !important;font-size:11px !important; line-height:13px !important;}
body[yahoo] .tdMobFootnotetopbottompadding0{ display: block !important; width:335px !important; font-size:16px !important; line-height:20px !important; text-align:center !important; padding-bottom:0px !important; padding-top:0px !important;}
body[yahoo] .tdMobFooter { font-size:11px !important;line-height:13px !important;}
body[yahoo] .tdMobFooter1 { font-size:11px !important;}
body[yahoo] .tdMobFooter2 { font-size:11px !important;line-height:16px !important;}
body[yahoo] .tdMobFooter3 { font-size:11px !important;line-height:13px !important; width:335px !important;}
body[yahoo] .tdMobFooter4 { font-size:11px !important; padding-bottom:30px !important;}
body[yahoo] .tdMobFooterTitle { font-size:15px !important; width:100% !important; max-width:100% !important; padding-bottom:20px !important;}
body[yahoo] .tdMobFooterContent { font-size:15px !important; width:100% !important; max-width:100% !important; padding-bottom:20px !important; padding-left:55px !important;}
body[yahoo] .tdMobFooterContent2 { font-size:15px !important; width:100% !important; max-width:100% !important; padding-left:55px !important;}
body[yahoo] .tdMobFooterContent3 { font-size:15px !important; width:100% !important; max-width:100% !important; padding-bottom:20px !important; padding-left:75px !important;}
body[yahoo] .tdMobFooterContent3New { font-size:15px !important; width:100% !important; max-width:100% !important; padding-bottom:20px !important; padding-left:55px !important;}
body[yahoo] .tdMobFooterContent4 { font-size:15px !important; width:100% !important; max-width:100% !important; padding-left:75px !important;}
body[yahoo] .tdMobFooterContent4New { font-size:15px !important; width:100% !important; max-width:100% !important; padding-left:55px !important;}
body[yahoo] .imgMobContainerZ6 { width:40px !important; height:40px !important; padding-right:15px !important; }
body[yahoo] .imgMobContainerZ6New { width:40px !important; height:40px !important; padding-right:15px !important; margin-top:0px !important;}
body[yahoo] .imgMobContainerZ7 { width:30px !important; height:30px !important; padding-right:15px !important; }
body[yahoo] .tdMobHr { display: block !important;width:335px !important; }
body[yahoo] .tdMobZ6 { display: block !important; width:100% !important;text-align:center !important;align:center !important; float:center !important;}
body[yahoo] .tblMobContainerTableZ6 { width:100% !important; text-align:center !important;align:center !important;}
body[yahoo] .tdMobZ62 { display: block !important; width:335px !important;text-align:center !important;align:center !important; float:center !important;}
body[yahoo] .tblMobContainerTableZ62 { width:335px !important; text-align:center !important;align:center !important;}
body[yahoo] .tdMobTopPadding40px { padding-top:40px !important;}
body[yahoo] .tdMobContentHr { display: block !important;}
body[yahoo] .tdDeskHr { display: none !important; }
body[yahoo] .tdDeskContentHr { display: none !important; }
body[yahoo] .tdMobContainerZ6description1 { width:100% !important; max-width:100% !important; }
body[yahoo] .tdMobContainerZ6description2 { width:100% !important; max-width:100% !important; }
body[yahoo] .tdMobZ3 { width:335px !important; max-width:100% !important; }
body[yahoo] .tdMobButtonZ3 { font-size:20px !important; line-height:45px !important; width:320px !important; border-left:0px solid #0052C2 !important; border-right:0px solid #0052C2 !important; border-top:0px solid #0052C2 !important; border-bottom:0px solid #0052C2 !important; text-align:center !important; align:center !important;}
body[yahoo] .imgDeskDisp { display:none !important; }
body[yahoo] .title-spacer { width:10px !important; align:top !important;}
body[yahoo] .footer-class { padding-left:45px !important; padding-right:45px !important; padding-top:40px !important;}
body[yahoo] .footer-class1 { padding-left:45px !important; padding-right:45px !important; padding-top: 50px !important;}
body[yahoo] .footer-class2 { padding-left:65px !important; padding-right:65px !important;}
body[yahoo] .bullet-list { padding-left:18px !important;}
body[yahoo] .zone2-data-table-Bamd18 table { width:56px !important;}
body[yahoo] .zone2-data-table-Bamd19 { width:56px !important; }
body[yahoo] .zone2-data-table-Bamd22{ padding-left:20px !important;}
body[yahoo] .zone2-data-table-Bamd29a table { width:42px !important;}
body[yahoo] .zone2-data-table-Bamd30 table { width:7px !important; height:14px !important;}
body[yahoo] .zone2-data-table-Bamd31 { width:42px !important; height:14px !important; font-size:11px !important; line-height:14px !important;}
body[yahoo] .zone2-data-table-Bamd1 { width:07px !important; height:7px !important;}
body[yahoo] .zone2-data-table-Bamd1a { width:03px !important; height:7px !important;}
body[yahoo] .zone2-data-table-Bamd1b img{ width:03px !important; height:7px !important;}
body[yahoo] .zone2-data-table-Bamd2 { width:49px !important; height:6px !important;}
body[yahoo] .zone2-data-table-Bamd2a img{ width:47px !important; height:6px !important;}
body[yahoo] .zone2-data-table-Bamd3a { width:14px !important; height:14px !important;}
body[yahoo] .zone2-data-table-Bamd4 { width:07px !important; height:42px !important;}
body[yahoo] .zone2-data-table-Bamd4a { width:03px !important; height:42px !important;}
body[yahoo] .zone2-data-table-Bamd4c img{ width:07px !important; height:42px !important;}
body[yahoo] .zone2-data-table-Bamd4b img{ width:03px !important; height:42px !important;}
body[yahoo] .zone2-data-table-Bamd5a { width:42px !important; height:42px !important;}
body[yahoo] .zone2-data-table-Bamd5 img { width:100% !important; height:100% !important;}
body[yahoo] .zone2-data-table-Bamd12 { width:7px !important; height:14px !important;}
body[yahoo] .zone2-data-table-Bamd12a img { width:7px !important; height:14px !important;}
body[yahoo] .zone2-data-table-Bamd14 { width:3px !important; height:07px !important;}
body[yahoo] .zone2-data-table-Bamd14a img{ width:3px !important; height:07px !important;}
body[yahoo] .zone2-data-table-Bamd17 { width:3px !important; height:72px !important;}
body[yahoo] .zone2-data-table-Bamd17a img { width:1px !important; height:72px !important;}
body[yahoo] .zone2-data-table-Bamd21 { width:3px !important; height:14px !important;}
body[yahoo] .zone2-data-table-Bamd21a img { width:3px !important; height:14px !important;}
body[yahoo] .zone2-data-table-Bamd24 { width:7px !important; height:07px !important;}
body[yahoo] .zone2-data-table-Bamd24a img{ width:7px !important; height:07px !important;}
body[yahoo] .zone2-data-table-Bamd28 { width:42px !important; height:07px !important;}
body[yahoo] .zone2-data-table-Bamd28a img{ width:42px !important; height:07px !important;}
body[yahoo] .zone2-data-table-Bamd32a img{ width:3px !important; height:09px !important;}
body[yahoo] .zone2-data-table-Bamd32 { width:3px !important; height:09px !important;}
body[yahoo] .zone2-data-table-Bamd35 { width:56px !important; height:09px !important;}
body[yahoo] .zone2-data-table-Bamd35a img{ width:56px !important; height:09px !important;}
body[yahoo] .zone2-data-table-Bamd36 { width:7px !important; height:09px !important;}
body[yahoo] .zone2-data-table-Bamd36a img{ width:7px !important; height:09px !important;}
body[yahoo] .tblMobZ2Content { width:335px !important; text-align:left !important;align:center !important; padding-bottom:30px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tdMobZ5tblData-BA360 { font-size:16px !important;line-height:20px !important; text-align:left !important;}
body[yahoo] .tblMobZ5Content-BA360 { text-align:left !important;align:center !important; padding-bottom:12px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content-BA360-zone1fontsize { text-align:left !important;align:center !important; padding-bottom:12px !important; font-size:24px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content-BA360-1 { text-align:left !important;align:center !important; padding-bottom:10px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5Content-BA360-2 { text-align:left !important;align:center !important; padding-bottom:20px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5aContent-BA360 { text-align:right !important;align:center !important; padding-bottom:12px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5aContent-BA360-1 { text-align:right !important;align:center !important; padding-bottom:10px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .tblMobZ5TopPadding-BA360 { padding-top:20px !important;}
body[yahoo] .tblMobZ3ButtonPadding-BA360 { padding-bottom:10px !important;}
body[yahoo] .zone1padding1 { display: block !important;font-size:24px !important;padding-bottom:30px !important; padding-top:0px !important; padding-left:0px !important;padding-right:0px !important;line-height:28px !important;}
body[yahoo] .zone1padding2 {display: block !important;font-size:24px !important;padding-bottom:0px !important; padding-top:30px !important; padding-left:0px !important;padding-right:0px !important;line-height:28px !important;}
body[yahoo] .tblMobZ3Content12 { display: list-item !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:12px !important;}
body[yahoo] .tblMobZ3Content12a { display: list-item !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:0px !important;}
body[yahoo] .tdMobz1width { font-size:20px !important; height:30px !important; width:320px !important; border-left:0px solid #FFFFFF !important; border-right:0px solid #FFFFFF !important; border-top:0px solid #FFFFFF !important; border-bottom:0px solid #FFFFFF !important; text-align:center !important; align:center !important;}
body[yahoo] .tdMobTitleDetailsBottom0px { display: block !important;font-size:18px !important; padding-bottom:0px !important;}body[yahoo] .tdMobTitleDetailsBottom0px { display: block !important;font-size:18px !important; padding-bottom:0px !important;}
body[yahoo] .tdMobAlertIcon { display: block !important;width:335 !important; padding-top:30px !important; padding-bottom:0px !important; }
body[yahoo] .dataTableColumn1 { font-size:13px !important;padding-top:10px !important; padding-bottom:10px !important;}
body[yahoo] .dataTableColumn2 { font-size:16px !important;padding-bottom:10px !important;}
body[yahoo] .dataTableColumn2OneValue { font-size:16px !important;padding-top:10px !important; padding-bottom:10px !important;}
body[yahoo] .dataTableTotalVerbiage { padding-top:10px !important; padding-bottom:10px !important;padding-left:10px !important;}
body[yahoo] .dataTableTotalValue { padding-top:10px !important; padding-bottom:10px !important;padding-right:10px !important;}
body[yahoo] .tblMobZ2Content1 { width:335px !important; text-align:left !important;align:center !important; padding-bottom:0px !important; font-size:16px !important; line-height:20px !important;}
body[yahoo] .BofA_2019_70x70 { max-width:70px !important; align:center !important; max-height:70px !important; padding-left:133px; padding-right:132px}
body[yahoo] .tblMobZ1topbottompadding30{ display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:30px !important; padding-top:30px !important;}
body[yahoo] .tblMobZ2Contenttoppadding0 { display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:30px !important;padding-top:0px !important; }
body[yahoo] .tblMobZ1topbottompadding0{ display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:0px !important; padding-top:0px !important;}
body[yahoo] .drop-down-data { display:block !important; width: 100% !important; float:left; clear:both;}
body[yahoo] .tblMobBrandingZ5 { display: block !important; text-align:center !important;align:center !important;padding-bottom:0px !important;}
body[yahoo] .imgMobBrandingZ5img { width:189px !important;height:14px !important;align:center !important;padding-bottom:50px !important;}
body[yahoo] .imgMobBrandingZ5 { max-width:189px !important; max-height:14px !important; padding-bottom:0px !important; padding-top:0px !important;}
body[yahoo] .title-spacer1 { width:95px !important; align:right !important; }
body[yahoo] .tblMobZ1zone1padding{ display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:15px !important; padding-top:30px !important;}
body[yahoo] .tblMobZ1zone1padding1{ display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:15px !important; padding-top:15px !important;}
body[yahoo] .tblMobZ1zone1padding2{ display: block !important; font-size:16px !important; line-height:20px !important; text-align:left !important; padding-bottom:15px !important; padding-top:0px !important;}
body[yahoo] .tdMobFootnoteZ72Left { padding-top:30px !important; padding-bottom:0px !important; width:335px !important;font-size:11px !important; line-height:13px !important; text-align:left !important;}
body[yahoo] .tdMobFootnoteZ72Left2 { padding-top:30px !important; padding-bottom:10px !important; width:335px !important;font-size:11px !important; line-height:13px !important; text-align:left !important;}
body[yahoo] .zone3-message-body3 { display:block !important; padding-top:0px !important;padding-bottom: 0px !important;padding-left:15px !important;font-size:14px !important;line-height:18px !important;}
body[yahoo] .bamd-spacer { width:4px !important; height:119px !important;}
body[yahoo] .bamd-spacerimg img { width:4px !important; height:119px !important;}
body[yahoo] .tblMobZ3Content9Right { font-size:16px !important;line-height:24px !important; text-align:right !important; padding-bottom:30px !important;}
body[yahoo] .tdMobZ2tblDataRight { font-size:16px !important;line-height:20px !important; text-align:right !important;}
body[yahoo] .zone3-message-body3 { display:block !important; padding-top:0px !important;padding-bottom: 10px !important;padding-left:15px !important;font-size:14px !important;line-height:18px !important;}
body[yahoo] .tdMobBottomPadding40px { padding-bottom:40px !important;}
body[yahoo] .tdMobTopPadding0px { padding-top:0px !important;}
body[yahoo] .tdMobZ2tbl2 { width:275px !important; align:center !important;}
body[yahoo] .tdwidth { width:125px !important; }
}
@media only screen and (max-width: 640px) {
body[yahoo] .tbContainer { width:100% !important; }
}
@media only screen and (min-width: 481px) {
body[yahoo] .imgMobDisp { display:none !important; }
}
</style>
</head>
<body bgcolor="#ffffff" text="#000000" style="background-color: #FFFFFF; color: #000000; margin: 0px; padding:0px; -webkit-text-size-adjust:none;" yahoo="fix">
<table width="100%" border="0" cellpadding="0" cellspacing="0" align="center">
<tr>
<td align="center">
<table cellspacing="0" cellpadding="0" border="0" width="600" class="tbContainer" align="center">
<tr>
<td>
<table cellspacing="0" cellpadding="0" border="0" bordercolor="#000000" bgcolor="#ffffff" width="100%">
<!-- Zone 00 - Bofa Image Header - Begins -->
<tr>
<td align="center" valign="top">
<table cellpadding="0" cellspacing="0" border="0" mcdisabled-bordercolor="" width="100%" mcdisabled-bgcolor="">
<tr>
<td valign="top" align="center">
<table width="100%" bgcolor="#ffffff" border="0" mcdisabled-bordercolor="" cellpadding="0" cellspacing="0">
<tr>
<td class="tdMobHeaderImage" style="text-align:center;padding-top:30px;padding-bottom:0px;padding-left:10px;margin:0px;valign:top;">
<img class="BofA_2019_228x23" id="image-top-bofa" src="https://www.bankofamerica.com/content/images/ContextualSiteGraphics/Logos/en_US/BofA_2019_307x31.png" border="0" alt="Bank of America." height="31px" width="307px">
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!-- Zone 00 - Bofa Image Header - Ends-->
<tr>
<td align="center" valign="top">
<table cellpadding="0" cellspacing="0" width="100%" role="presentation" style="min-width: 100%; " class="stylingblock-content-wrapper"><tr><td class="stylingblock-content-wrapper camarker-inner"><table cellpadding="0" cellspacing="0" width="100%" role="presentation" style="min-width: 100%; " class="stylingblock-content-wrapper"><tr><td class="stylingblock-content-wrapper camarker-inner"><table cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td align="center" valign="top">
<table cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
</tr></table><table class="tdMobZ1tbl2" align="center" bgcolor="#ffffff" border="0" bordercolor="" cellpadding="0" cellspacing="0">
<!-- Zone1 - Begins-->
<tr>
<td class="tdMobAlertImage" style="text-align:center;padding-top:40px;padding-bottom:0px;margin:0px;valign:top;">
<img class="imgMobSize70x70" id="image-Zone1" src="https://www.bankofamerica.com/content/images/ContextualSiteGraphics/Merchant/awareness_red_105x105.png" border="0" alt="" height="105px" width="105px">
</td>
</tr>
<tr>
<!-- tdMobZ1BottomNew-->
<td class="tdMobZ1BottomPadding30px" valign="top" align="top" style="font-size:36px; font-family: Connections, arial; color:#000000; padding-bottom:40px;padding-top:40px; float:center; text-align:center; valign:top;">
Credit card transaction <br>exceeds alert limit you set
</td>
</tr>
<!--horizontal line begins mob view-->
<tr>
<td class="tdMobHr" style="display:none; mso-table-lspace:0pt; mso-table-rspace:0pt;mso-hide:all; padding-top:0px; padding-bottom:0px;">
<table cellpadding="0" cellspacing="0" border="0" style="background-color:#FFFFFF; mso-table-lspace:0pt; mso-table-rspace:0pt;mso-hide:all;">
<tr>
<td class="tdMobContentHr" width="335px" style="display:none; font-size:1px; height:1px; mso-table-lspace:0pt; mso-table-rspace:0pt;mso-hide:all;" colspan="2" bgcolor="#D3D3D3" height="1"></td>
</tr>
</table>
</td>
</tr>
<!--horizontal line mob view ends-->
<!--horizontal line begins desktop view-->
<tr>
<td>
</td></tr><tr>
<td class="tdDeskHr" style="display:block; padding-top:0px; padding-bottom:0px; mso-table-lspace:0pt; mso-table-rspace:0pt;mso-hide:all;" colspan="2"></td>
</tr>
<tr>
<td class="tdDeskContentHr" width="600" style="display:block; font-size:1px; height:1px; mso-table-lspace:0pt; mso-table-rspace:0pt;mso-hide:all;" colspan="2" bgcolor="#D3D3D3" height="1"></td>
</tr>
<!--horizontal line desktop view ends-->
<!-- Zone1 - Ends-->
</table>
</td></tr>
<tr>
<td style="padding-top:0px;">
<!-- Zone2 Begins -->
<table class="tdMobZ2tbl" width="100%" valign="bottom" cellpadding="0" cellspacing="0" border="0" bgcolor="#FFFFFF" align="center">
<tr>
<td class="tdMobZ2TopPadding30px" style="padding-bottom:0px; padding-top:40px">
<!-- Zone 2 Data table - Begins -->
<table class="tdMobZ2tbl" style="margin-right:25px" valign="bottom" cellpadding="0" cellspacing="0" border="0" bgcolor="#FFFFFF" align="Left">
<tr>
<td class="tblMobZ3Content5" style="line-height:32px; mso-line-height-rule: exactly;padding-bottom:40px; padding-right:0px;font-family: Connections Light, arial; font-weight:normal; text-align:left; font-size:22px; color:#000000; " valign="top">
National Education Association World Mas<b> ending in 7522</b>
</td>
</tr>
</table>
<!-- Zone 2 Data table - Ends -->
</td>
</tr>
<tr>
<td class="tdMobZ2TopPadding30px" style="padding-bottom:0px; padding-top:0px">
<!-- Zone 2 Data table - Begins -->
<table class="tdMobZ2tbl" style="margin-right:25px" valign="bottom" cellpadding="0" cellspacing="0" border="0" bgcolor="#FFFFFF" align="Left">
<tr>
<td class="tdMobZ2tbllabel2" style="line-height:32px; mso-line-height-rule: exactly;padding-bottom:0px; padding-right:0px;font-family: Connections Light, arial; font-weight:normal; text-align:left; font-size:22px; color:#000000; " valign="top">
Amount:
</td>
<td class="tdMobZ2tblData" style="mso-line-height-rule: exactly;line-height:32px; padding-bottom:0px; padding-left:20px; font-family: Connections Light, arial; font-weight:normal; text-align:left; font-size:22px; color:#000000;" valign="top">
<b>$21.48</b>
</td>
</tr>
<tr>
<td class="tdMobZ2tbllabel2" style="line-height:32px; mso-line-height-rule: exactly;padding-bottom:0px; padding-right:0px;font-family: Connections Light, arial; font-weight:normal; text-align:left; font-size:22px; color:#000000; " valign="top">
Date:
</td>
<td class="tdMobZ2tblData" style="mso-line-height-rule: exactly;line-height:32px; padding-bottom:0px; padding-left:20px; font-family: Connections Light, arial; font-weight:normal; text-align:left; font-size:22px; color:#000000;" valign="top">
<b>November 22, 2024</b>
</td>
</tr>
<tr>
<td class="tdMobZ2tbllabel2" style="line-height:32px; mso-line-height-rule: exactly;padding-bottom:0px; padding-right:0px;font-family: Connections Light, arial; font-weight:normal; text-align:left; font-size:22px; color:#000000; " valign="top">
Where:
</td>
<td class="tdMobZ2tblData" style="mso-line-height-rule: exactly;line-height:32px; padding-bottom:0px; padding-left:20px; font-family: Connections Light, arial; font-weight:normal; text-align:left; font-size:22px; color:#000000;" valign="top">
<b>PP SPOTIFYUSAI</b>
</td>
</tr>
</table>
<!-- Zone 2 Data table - Ends -->
</td>
</tr>
<!-- Zone2 - Ends-->
<tr><td style="padding-top:0px;">
<!-- Zone 3 Table - Begins -->
<table class="tblMobZ3" width="100%" valign="bottom" cellpadding="0" cellspacing="0" border="0" bgcolor="#FFFFFF" align="center">
<tr>
<td class="tdMobHeaderImage" style="padding-bottom:0px;padding-top:40px" align="center">
<table>
<!-- Primary CTA Button - Begins-->
<tr>
<td class="tdMobButtonZ4" align="center" bgcolor="#0052C2" style="mso-padding-alt: 10px 40px;">
<a class="tdMobButtonZ4" href="https://www.bankofamerica.com/deeplink/redirect.go?target=bofasignin&screen=Accounts:Home&version=7.0.0" style="font-family: Connections,arial;font-size:30px; color:#FFFFFF; display: inline-block;border-left:0px solid #0052C2; border-right:0px solid #0052C2; border-top:0px solid #0052C2; border-bottom: 0px solid #0052C2; background-color:#0052C2; line-height:68px; width:240px; text-decoration:none;-webkit-text-size-adjust:none;" aria-label="View details">
View details</a>
</td>
<!-- Primary CTA Button - Ends -->
</tr>
</table>
</td>
</tr>
</table>
<!-- Zone 3 Table - Ends -->
</td>
</tr><tr>
<td>
<table class="tblMobZ3" width="100%" valign="bottom" cellpadding="0" cellspacing="0" border="0" bgcolor="#FFFFFF" align="center">
<!-- Zone4 - Begins -->
<tr>
<td class="tdMobZ3TopPadding30px tblMobZ3Content5" style="font-family: Connections, arial; font-weight:normal;text-align:left;padding-top:40px; padding-bottom:0px; padding-left: 0px; font-size:22px; line-height:28px; color:#000000;" valign="bottom" align="left">
If you made this purchase or payment but don't recognize the amount, wait until the final purchase amount has posted before filing a dispute claim.
</td>
</tr>
<!-- Zone4 - Ends -->
</table>
</td>
</tr>
<tr>
<td>
<table class="tblMobZ3" width="100%" valign="bottom" cellpadding="0" cellspacing="0" border="0" bgcolor="#FFFFFF" align="center">
<!-- Zone5 - Begins -->
<tr>
<td class="tblMobZ1topbottompadding30" style="font-family: Connections, arial; font-weight:normal;text-align:left;padding-top:40px; padding-bottom:0px; padding-left: 0px; font-size:22px; line-height:28px; color:#000000;" valign="bottom" align="left">
If you don't recognize this activity, contact us at the number on the back of your card.
</td>
</tr>
<!-- Zone5 - Ends -->
</table>
</td>
</tr>
<tr>
<td>
<table class="tblMobZ3" width="100%" valign="bottom" cellpadding="0" cellspacing="0" border="0" bgcolor="#FFFFFF" align="center">
<!-- Zone6 - Begins -->
<!-- Zone6 - Ends -->
</table>
</td>
</tr>
<!-- footer - Begins -->
<tr>
<td>
<table class="tblMobZ3" width="100%" valign="bottom" cellpadding="0" cellspacing="0" border="0" bgcolor="#FFFFFF" align="center">
<tr>
<td class="" colspan="2" style="padding-bottom:0px; padding-top:0px; font-family: Connections, arial; font-weight:normal; text-align:left; font-size:22px; line-height:28px; color:#000000;" valign="bottom" align="left">
<footer>
<table cellpadding="0" cellspacing="0" width="100%" role="presentation" style="min-width: 100%; " class="stylingblock-content-wrapper"><tr><td class="stylingblock-content-wrapper camarker-inner"><table class="footer-class" align="center" border="0" width="100%">
<tr>
<td style="padding:40px 55px 0px 55px">
<table align="center" border="0" width="100%">
<tr>
<td class="tdMobFooter" width="" valign="bottom" align="top" style="font-size:13px; font-family: Connections, arial; font-weight:Regular; color:#666666;padding-left:0px; padding-right:0px; padding-bottom:8px; padding-top:0px; line-height:18px;float:center; text-align:center; valign:top;">
We'll never ask for your personal information such as SSN or ATM PIN in email messages. If you get an email that looks suspicious or you are not the intended recipient of this email, don't click on any links. Instead, forward to <a href="#" aria-label="abuse@bankofamerica.com" style="pointer-events: none; cursor: default;color: #666666; text-decoration: none; ">abuse@bankofamerica.com</a> then delete it.
</td>
</tr>
<tr>
<td class="tdMobFooter1" width="" valign="bottom" align="top" style="font-size:13px; font-family: Connections, arial; font-weight:Regular; color:#666666;padding-left:0px; padding-right:0px; padding-bottom:8px; padding-top:0px; float:center; text-align:center; valign:top;">
Please don't reply to this automatically generated service email.
</td>
</tr>
<tr>
<td>
<table align="center" border="0">
<tr>
<td class="tdMobFooter2New" style="font-family: Connections, arial; font-weight:Regular; text-align:left; font-size:13px; color:#0052C2; padding-bottom:8px; line-height:18px; padding-left:20px; padding-right:20px;">
<a href="https://www.bankofamerica.com/privacy/consumer-privacy-notice.go" name="privacy_notice" style="text-decoration:none;color:#0052C2" aria-label="Privacy Notice">Privacy Notice</a><span style="color:#000000;"></span>
</td>
<td class="tdMobFooter2New" style="font-family: Connections, arial; font-weight:Regular; text-align:left; font-size:13px; color:#0052C2; padding-bottom:8px; line-height:18px; padding-left:20px; padding-right:20px;">
<a href="https://www.bankofamerica.com/help/equalhousing.cfm" name="equal_housing_lender" style="text-decoration:none;color:#0052C2" aria-label="Equal Housing Lender">Equal Housing Lender <img src="https://www.bankofamerica.com/content/images/ContextualSiteGraphics/alerts/desktop_icon_equalhousing.png" style="border:none"></a><br>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table width="300" align="center" border="0">
<tr>
<td class="tdMobFooter2" width="" valign="bottom" align="top" style="line-height:16px; font-size:13px; font-family: Connections, arial; font-weight:Regular; color:#666666;padding-left:0px; padding-right:0px; padding-bottom:0px; padding-top:0px; float:center; text-align:center; valign:top;">
Bank of America, N.A. Member FDIC.
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table width="300" align="center" border="0">
<tr>
<td class="tdMobFooter2" width="" valign="bottom" align="top" style="line-height:16px; font-size:13px; font-family: Connections, arial; font-weight:Regular; color:#666666;padding-left:0px; padding-right:0px; padding-bottom:20px; padding-top:0px; float:center; text-align:center; valign:top;">
&copy; 2024 Bank of America Corporation.
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table></td></tr></table>
</footer>
</td>
</tr>
</table>
</td>
</tr>
<!-- footer - Ends -->
</table>
</td>
</tr>
</table></td></tr></table>
</td></tr></table></td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
<img src="http://click.ealerts.bankofamerica.com/open.aspx?ffcb10-fe7e12797d6d07787c-fdf41571736402787c117570-fe3c11727764047d741479-fec61574706c057b-fdf5157470630d7471167175-fec415797260007f&bmt=0" width="1" height="1" alt="">
</body>
</html>

450
testdata/chase.2025.balance.txt vendored Normal file
View File

@ -0,0 +1,450 @@
Delivered-To: breellocaldev@gmail.com
Received: by 2002:a05:6022:841f:b0:65:30d1:7dd8 with SMTP id cd31csp2448994lab;
Tue, 14 Jan 2025 06:30:20 -0800 (PST)
X-Google-Smtp-Source: AGHT+IHYZt9A+xv7R+D4Y7HTjuMOF9AEKaiIlF4EdjFtsSfmydmud4yvbL76Eu3hDdG3gPzkKq0q
X-Received: by 2002:a05:6102:26c8:b0:4b2:ad82:133a with SMTP id ada2fe7eead31-4b3d0eb943emr21123581137.25.1736865019937;
Tue, 14 Jan 2025 06:30:19 -0800 (PST)
ARC-Seal: i=1; a=rsa-sha256; t=1736865019; cv=none;
d=google.com; s=arc-20240605;
b=Rc1IYtgLaCuSXjmliSBjzCic5e4nKmKGznZ2oE2tH/uDoOMup1O1tttRQlC9cOctpQ
bnab4p6dFpkgMbRJQ5bkUq8hZBufvpTISsrBVrl1QeHuX+IpOCAJSVWAhq/4DlFZYsOg
DJz4bmNsRXBI3/sY5s6F6lBovD5TyJhrxrIOR9BucgdcF1MpggUIa7BUDFjUHMmnRJbl
w9aFWQUzts6j/EbqCrzfcG8VwSqNI+92NacyHUZBQW3FkGFOL6awi+mFWB54aE2pbMfd
gs70fluhGntfisIoN+FLhbSh3tDYJx+s2jradTChgYHSDzbS+d6OLLtlmbQLw8XDwMZU
PzOg==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605;
h=retry-count:notification-id:content-transfer-encoding:mime-version
:subject:message-id:to:from:date:dkim-signature;
bh=3jCY5JM6cbpoPMj0taKBjFy0yqql6DMNeO04KlgY7p8=;
fh=lrt9myTdNYNEckm/XjduC9YuGfYr3v7r45vYTr/7vKM=;
b=eKMw8xKjUKtT6k6DyEh0UDuPooWFu1Si3rlu8EVh+kAGTUorD+6X8psBD/n15j+msg
+07h4M8xk0FSAMUcRsVENUp9ptnGW6Mt7gBO0CrxdspczNz1ZcKvC61YVevslrqN535F
ndp54648k1fLGbFVQwsRViJkHGne9v8nHw6jYyVC8aU2dwKxTNJBznzgnkyppF6Se1ti
htPjPmc+wVUL0uoz+OhVDCoKhDn+PfyjS5dDdb1kk9EM1WgxW66CrGxGC78pJstZJv9i
tH7ImIDKoyPCRrr57faT+tN5JoA9uR+ANX9jXeFspgyfwuPIXvXzZmfRlwRch4IYp/Vj
Cdlw==;
dara=google.com
ARC-Authentication-Results: i=1; mx.google.com;
dkim=pass header.i=@chase.com header.s=d4815 header.b=Sn1+UmH9;
spf=pass (google.com: domain of no.reply.alerts.14@chase.com designates 146.143.72.23 as permitted sender) smtp.mailfrom=no.reply.alerts.14@chase.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=chase.com
Return-Path: <no.reply.alerts.14@chase.com>
Received: from vsin80p3166.jpmchase.com (vsin80p3166.jpmchase.com. [146.143.72.23])
by mx.google.com with ESMTPS id ada2fe7eead31-4b60929a598si3998057137.312.2025.01.14.06.30.19
for <breellocaldev@gmail.com>
(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
Tue, 14 Jan 2025 06:30:19 -0800 (PST)
Received-SPF: pass (google.com: domain of no.reply.alerts.14@chase.com designates 146.143.72.23 as permitted sender) client-ip=146.143.72.23;
Authentication-Results: mx.google.com;
dkim=pass header.i=@chase.com header.s=d4815 header.b=Sn1+UmH9;
spf=pass (google.com: domain of no.reply.alerts.14@chase.com designates 146.143.72.23 as permitted sender) smtp.mailfrom=no.reply.alerts.14@chase.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=chase.com
Received: from pps.filterd (vsin80p3166.dmz.us.jpmchase.net [127.0.0.1]) by vsin80p3166.jpmchase.com (8.18.1.2/8.18.1.2) with ESMTP id 50EDZ96f024767 for <breellocaldev@gmail.com>; Tue, 14 Jan 2025 14:30:18 GMT
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chase.com; h=content-transfer-encoding : content-type : date : from : message-id : mime-version : subject : to; s=d4815; bh=3jCY5JM6cbpoPMj0taKBjFy0yqql6DMNeO04KlgY7p8=; b=Sn1+UmH9sAIKrcSjgZt+WYnhb6bWokFKDnKcueoi51YEx1PhxifjbnkH5vC7IxwUTzGA isW1vjStyAi0mF64goItdf4GQBbD2hajG8GhPsDDlssHM5mwLi8Yz07B/p1mrHp8wCqf PRn278RbCw/hP7x2enyrd4WKASn3b/TLQId35h+j8OJk8cDOS/zw2iF/SA7C51hSTcEj Pnm84TIJXer81VZzGC4ReReIpWQbN5Is/A/25h1vLu21Ahb8aIIQ3QHI1D2B2jshpO17 6CzXpxUsBWscUXJWNvrjgqeSlLJW3MPD9bb//uKb+qLr3oGQQ7uD+KOnSG8iqmQPpXHZ lg==
Received: from vsin68p8372.nane.jpmchase.net ([169.103.129.216]) by vsin80p3166.dmz.us.jpmchase.net (PPS) with ESMTPS id 4443sjtnqe-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for <breellocaldev@gmail.com>; Tue, 14 Jan 2025 14:30:18 +0000
Received: from iaasn00692074.nane.jpmchase.net (iaasn00692074.nane.jpmchase.net [169.103.127.222])
by vsin68p8372.nane.jpmchase.net
with ESMTP id 50EEUHGS011857
for <breellocaldev@gmail.com>; Tue, 14 Jan 2025 14:30:17 GMT
Date: Tue, 14 Jan 2025 14:30:17 +0000 (UTC)
From: Chase <no.reply.alerts@chase.com>
To: breellocaldev@gmail.com
Message-ID: <128726875.10629657.1736865017764.JavaMail.webuser@iaasn00692074>
Subject: Your Chase Freedom Unlimited balance is $1,029.08
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
NOTIFICATION-ID: CREDIT_BATCH_BALANCE-28a12d61790b7acb6dc0d2075b17f7c0380224e9a180581da96fa1a87b454084-ver4-31-941c9e20-41c8-3b3c-aefd-f27755b09008
RETRY-COUNT: 0
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://ww=
w.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=3D=
1.0"/>
<title>See more details about your card balance.</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, .ExternalClas=
s 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;
}
.hero {
=09width: 100% !important;
=09height: auto !important;
}
.moPad {
=09padding-right: 20px !important;
=09padding-left: 20px !important;
}
.zeroPad {
=09padding-right: 0px !important;
=09padding-left: 0px !important;
}
.font14 {
=09font-size: 14px !important;
}
.font24 {
=09font-size: 24px !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;
}
.hero {
=09width: 100% !important;
=09height: auto !important;
}
.moPad {
=09padding-right: 20px !important;
=09padding-left: 20px !important;
}
.zeroPad {
=09padding-right: 0px !important;
=09padding-left: 0px !important;
}
.font14 {
=09font-size: 14px !important;
}
.font24 {
=09font-size: 24px !important;
}
}
</style>
</head>
<body style=3D"padding: 0px;margin: 0px; background-color:#d7dbe0;">
<table align=3D"center" width=3D"100%" border=3D"0" cellspacing=3D"0" cel=
lpadding=3D"0" style=3D"min-width:800px; background-color:#d7dbe0;" class=
=3D"fullWidth">
<tr>
<td align=3D"center" style=3D"vertical-align:top; padding:0px 0px 20p=
x 0px; min-width:800px; background-color:#d7dbe0;" class=3D"fullWidth"><tab=
le align=3D"center" width=3D"800" cellpadding=3D"0" cellspacing=3D"0" borde=
r=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=
0px 0px;background-color: #005EB8;"><table cellpadding=3D"0" cellspacing=
=3D"0" border=3D"0">
<tr>
<td align=3D"right" style=3D"vertical-align:bottom; paddi=
ng:0px 0px; width:12px;"><img src=3D"https://www.chase.com/content/dam/emai=
l/images/blue-left.jpg" width=3D"12" height=3D"226" border=3D"0" style=3D"d=
isplay:block;" alt=3D""/></td>
<td align=3D"center" style=3D"vertical-align:bottom; padd=
ing: 0px 0px 0px;width:616px; background-color: #FFFFFF;"><table width=3D"1=
00%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-align:top; pad=
ding: 0px 0px; background-color: #ffffff;"><table width=3D"100%" cellpaddin=
g=3D"0" cellspacing=3D"0" border=3D"0">
<!-- Start hidden preview text -->
<div style=3D"display: none; max-height: 0px; o=
verflow: hidden;">See more details about your card balance.</div>
<!-- Insert &zwnj;&nbsp; after hidden preview t=
ext -->
<div style=3D"display: none; max-height: 0px; o=
verflow: hidden;"> &nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&z=
wnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj=
;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&z=
wnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj=
;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&n=
bsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp=
;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&z=
wnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj=
;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&n=
bsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp=
;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&nbsp;&zwnj;&n=
bsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp=
;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&z=
wnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj=
;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&n=
bsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp=
;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&z=
wnj;&nbsp;</div>
<!-- End hidden preview text -->
<tr>
<td align=3D"left" style=3D"vertical-align:to=
p;padding-left: 30px; background-color: #ffffff;" class=3D"moPad"><table wi=
dth=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-al=
ign:bottom; padding:36px 0px 20px;"><img src=3D"https://www.chase.com/conte=
nt/dam/email/images/chase-logo-h-rgb.png" width=3D"104" height=3D"20" borde=
r=3D"0" style=3D"display:block;" alt=3D"Chase Logo"/></td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:to=
p; padding: 20px 28px 0px; background-color: #ffffff;" class=3D"moPad"><tab=
le align=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-al=
ign:top;"><table width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=
=3D"0">
<tr>
<td align=3D"left" style=3D"verti=
cal-align:top; padding:5px 10px; font-family:Arial, Helvetica, sans-serif; =
font-size:12px; font-weight:bold; color:#414042; background-color:#D7DBE0; =
border-radius:20px; -moz-border-radius: 20px; -webkit-border-radius:20px; w=
hite-space: nowrap;" class=3D"font14">Account Alert</td>
</tr>
</table></td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:to=
p; background-color: #ffffff;"><table width=3D"100%" cellpadding=3D"0" cell=
spacing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-al=
ign:top; padding: 20px 30px 28px;" class=3D"moPad"><table width=3D"100%" ce=
llpadding=3D"0" cellspacing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"verti=
cal-align:top; padding: 0px 20px 0px 0px;"><img src=3D"https://static.chase=
cdn.com/content/services/rendition/image.small.png/unified-assets/digital-c=
ards/chase-freedom/freedom-unlimited/41473417013.png" width=3D"57" height=
=3D"auto" alt=3D"" border=3D"0" style=3D"display:block;"/></td>
<td align=3D"left" style=3D"verti=
cal-align:top; padding:0px 50px 0px 0px; font-family:Arial, Helvetica, sans=
-serif; font-size:30px; font-weight: bold; color:#414042;" class=3D"zeroPad=
">Your balance of $1,029.08 is over the level you set</td>
</tr>
</table></td>
</tr>
</table></td>
</tr>
</table></td>
</tr>
</table></td>
<td align=3D"left" style=3D"vertical-align:bottom; paddin=
g:0px 0px;width:12px; "><img src=3D"https://www.chase.com/content/dam/email=
/images/blue-right.jpg " width=3D"12" height=3D"226" border=3D"0" style=3D"=
display:block;" alt=3D""/></td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"center" style=3D"vertical-align:top; padding: 0px =
0px 0px; background-color: #FFFFFF;"><table cellpadding=3D"0" cellspacing=
=3D"0" border=3D"0">
<tr>
<td align=3D"right" style=3D"vertical-align:top; padding:=
0px 0px; width:12px;"><img src=3D"https://www.chase.com/content/dam/email/i=
mages/white-left.jpg" width=3D"12" height=3D"77" border=3D"0" style=3D"disp=
lay:block;" alt=3D""/></td>
<td align=3D"center" style=3D"vertical-align:top; padding=
: 0px 0px 0px;width:616px;"><table width=3D"100%" cellpadding=3D"0" cellspa=
cing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-align:top; pad=
ding: 0px 150px 0px 30px;" class=3D"moPad"><table width=3D"100%" cellpaddin=
g=3D"0" cellspacing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-align:to=
p; padding: 10px 0px;border-bottom: solid 1px #414042;"><table width=3D"100=
%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-al=
ign:top; padding:0px 0px 0px 0px; font-family:Arial, Helvetica, sans-serif;=
font-size:16px; color:#414042;" class=3D"font14">Account</td>
<td align=3D"right" style=3D"vertical-a=
lign:top; padding:0px 0px 0px 5px; font-family:Arial, Helvetica, sans-serif=
; font-size:16px; font-weight:bold; color:#414042;" class=3D"font14">Chase =
Freedom Unlimited (...5876)</td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:to=
p; padding: 10px 0px;border-bottom: solid 1px #414042;"><table width=3D"100=
%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-al=
ign:top; padding:0px 0px 0px 0px; font-family:Arial, Helvetica, sans-serif;=
font-size:16px; color:#414042;" class=3D"font14">Balance</td>
<td align=3D"right" style=3D"vertical-a=
lign:top; padding:0px 0px 0px 5px; font-family:Arial, Helvetica, sans-serif=
; font-size:16px; font-weight:bold; color:#414042;" class=3D"font14">$1,029=
.08</td>
</tr>
</table></td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top; pad=
ding:40px 180px 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 balance has reached the $0.00 level you set.<br /> <br /> You can =
visit our <a style=3D"text-decoration: underline; color:#0060F0;" href=3D"h=
ttps://www.chase.com/personal/credit-cards/card-resource-center" rel=3D"noo=
pener noreferrer" target=3D"_blank">Resource Center</a> anytime to help an=
swer your questions or manage your account.</td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top;"><t=
able width=3D"100%" align=3D"left" cellpadding=3D"0" cellspacing=3D"0" bord=
er=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-align:to=
p;"><table width=3D"200" align=3D"left" cellpadding=3D"0" cellspacing=3D"0"=
border=3D"0" class=3D"fullWidth">
<tr>
<td align=3D"left" style=3D"padding:0px=
; vertical-align:top; padding: 0px 0px 30px 30px;" class=3D"moPad"><table a=
lign=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=3D"0" style=3D"ver=
tical-align:top;">
<tr>
<td role=3D"button" align=3D"cent=
er" style=3D"background-color:#0060f0; color: #fffffe; font-size: 16px; fon=
t-family: Arial, Helvetica, sans-serif; padding: 10px 0px; border: 1px soli=
d #0060f0; vertical-align:top; border-radius:4px; -moz-border-radius: 4px; =
-webkit-border-radius:4px;width: 168px;"><a href=3D"https://secure.chase.co=
m/web/auth/nav?navKey=3DrequestDashboard" target=3D"_blank" style=3D"color:=
#fffffe; text-decoration:none;">Review account</a></td>
</tr>
</table></td>
</tr>
</table>
</td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top; pad=
ding:0px 30px 20px; font-family:Arial, Helvetica, sans-serif; font-size:12p=
x; color:#717171;" class=3D"moPad font14">Securely access your accounts wit=
h the <a style=3D"text-decoration: underline; color:#0060F0;" href=3D"https=
://www.chase.com/digital/mobile-banking" rel=3D"noopener noreferrer" targe=
t=3D"_blank">Chase&nbsp;Mobile<span style=3D"font-size:70%; line-height:0; =
vertical-align:3px; text-decoration: none;">&reg;</span> app</a> or <a styl=
e=3D"text-decoration: underline; color:#0060F0;" href=3D"https://secure.cha=
se.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; pad=
ding: 0px 0px; background-color: #F6F6F6;"><table width=3D"100%" cellpaddin=
g=3D"0" cellspacing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-align:to=
p; padding:20px 30px 60px; font-family:Arial, Helvetica, sans-serif; font-s=
ize:12px; color:#717171;" class=3D"moPad font14"><span role=3D"heading" sty=
le=3D"text-transform: uppercase; font-weight: bold;">About this message</sp=
an><br />
<br />
Chase&nbsp;Mobile<span style=3D"font-size:7=
0%; line-height:0; vertical-align:3px;">&reg;</span> app is available for s=
elect mobile devices. Message and data rates may apply.<br />
<br />
This service email was sent based on your a=
lert settings. Use the Chase&nbsp;Mobile app or visit <a href=3D"https://ww=
w.chase.com/personal/mobile-online-banking/login-alerts" target=3D"_blank" =
style=3D"text-decoration: underline; color:#0060F0;" rel=3D"noopener norefe=
rrer">chase.com/alerts</a> to view or manage your settings.<br />
<br />
Chase cannot guarantee the delivery of aler=
ts and notifications. Wireless or internet service provider outages or othe=
r circumstances could delay them. You can always check <span class=3D"apple=
linksgray"><a style=3D"color:#717171;text-decoration: none;">chase.com</a><=
/span> or the Chase&nbsp;Mobile app for the status of your accounts includi=
ng your latest account balances and transaction details.<br />
<br />
To protect your personal information, pleas=
e don't reply to this message. Chase won't ask for confidential information=
in an email. <br />
<br />
If you have concerns about the authenticity=
of this message or have questions about your account visit <a style=3D"tex=
t-decoration: underline; color:#0060F0;" href=3D"https://www.chase.com/digi=
tal/customer-service" target=3D"_blank" rel=3D"noopener noreferrer">chase.c=
om/CustomerService</a> for ways to contact us.<br />
<br />
Your privacy is important to us. See our on=
line <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 i=
nformation.<br />
<br />
&copy; 2025 JPMorgan Chase &amp; Co. </td>
</tr>
</table></td>
</tr>
</table></td>
<td align=3D"left" style=3D"vertical-align:top; padding:0=
px 0px; width:12px;"><img src=3D"https://www.chase.com/content/dam/email/im=
ages/white-right.jpg" width=3D"12" height=3D"77" border=3D"0" style=3D"disp=
lay:block;" alt=3D""/></td>
</tr>
</table></td>
</tr>
<!--End of Content -->
</table></td>
</tr>
</table>
</body>
</html>
</html>

477
testdata/chase.202506.credit.txt vendored Normal file
View File

@ -0,0 +1,477 @@
Delivered-To: breellocaldev@gmail.com
Received: by 2002:a05:6022:5675:b0:72:1e7d:3888 with SMTP id er21csp4584200lab;
Wed, 4 Jun 2025 14:21:55 -0700 (PDT)
X-Google-Smtp-Source: AGHT+IEHEZFpfFptRb8iKNwmfilCWAGJ701ZkjFidhuUYVUH39CGReGxuKMxY2anJJipep1sauOZ
X-Received: by 2002:a05:6602:b89:b0:86c:f30b:1f53 with SMTP id ca18e2360f4ac-8731c618143mr564722439f.13.1749072114993;
Wed, 04 Jun 2025 14:21:54 -0700 (PDT)
ARC-Seal: i=1; a=rsa-sha256; t=1749072114; cv=none;
d=google.com; s=arc-20240605;
b=WL5tcjRmDMa4djUmb4JWFIrG+HkINxUSguY0lXuzX4mUn1Ign88n5KZhXa9DLgKVjn
OOD0pxiJwikBLWeF0BAIH9ZLE7oUj0WVerOqw2TMRPUxGhJlPLs3bZvVukZnCUpGY88v
Igw8fo4r7ulJtxbi4rZ9zA0SJJ19hLJL5M1yLq5d9S2J4t+ZNlDro5VUkaxofJKvirRS
oue1WDn4sORjY2foIwI262UaD7Jbb1xCt806w22Ce5IAy+hH+Oy2woByR34cnFHtY8N5
Wy4XSpuvIxwS5yCNIs+NZnQ+3h7st03BOloIpNjG97nIwu4nIHuLc0XGvpxPkshBlkzT
XAyg==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605;
h=retry-count:notification-id:content-transfer-encoding:mime-version
:subject:message-id:to:from:date:dkim-signature;
bh=s8C47OdoV1gXfqN9qIkir+Y8P9kkuDnmIZQEA9WBZbw=;
fh=lrt9myTdNYNEckm/XjduC9YuGfYr3v7r45vYTr/7vKM=;
b=kqPsUii3FbWObwAWdD8O1DA0jpayOshHNJbSBHS6aNDpkwgXmB6GqaA0W+99hSLdhD
xWVDHHkbyfpAWTh5s7jKs97jT56IZwl4TTL2HGimXqpwKvNf4yLy+nPthZ6R8Yi+ufn6
XjA2dCSn+87/RNfHE82sE7RFLuGD3LzXKyArQ7EDSo/8cV4bqq0zl1rot3vmm1rEs5P5
A4P1QJUVXxfGsJ1oXXOfW7L3MOk6Uzb0YwirfnxIb2oDQai+RvYMgCQ/avrYQpIm09BM
vgNZqVMtD3Vcig9bxuhmbQ++gdqk55MVyTRXlVLr3CWPOWfRehdtagiZ9cRQD/yjEVkq
NRXQ==;
dara=google.com
ARC-Authentication-Results: i=1; mx.google.com;
dkim=pass header.i=@chase.com header.s=d4815 header.b=iVm+FQf7;
spf=pass (google.com: domain of no.reply.alerts.08@chase.com designates 146.143.72.27 as permitted sender) smtp.mailfrom=no.reply.alerts.08@chase.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=chase.com
Return-Path: <no.reply.alerts.08@chase.com>
Received: from vsin80p3239.jpmchase.com (vsin80p3239.jpmchase.com. [146.143.72.27])
by mx.google.com with ESMTPS id ca18e2360f4ac-86cf5e5248esi1579461739f.41.2025.06.04.14.21.54
for <breellocaldev@gmail.com>
(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
Wed, 04 Jun 2025 14:21:54 -0700 (PDT)
Received-SPF: pass (google.com: domain of no.reply.alerts.08@chase.com designates 146.143.72.27 as permitted sender) client-ip=146.143.72.27;
Authentication-Results: mx.google.com;
dkim=pass header.i=@chase.com header.s=d4815 header.b=iVm+FQf7;
spf=pass (google.com: domain of no.reply.alerts.08@chase.com designates 146.143.72.27 as permitted sender) smtp.mailfrom=no.reply.alerts.08@chase.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=chase.com
Received: from pps.filterd (vsin80p3239.dmz.us.jpmchase.net [127.0.0.1]) by vsin80p3239.jpmchase.com (8.18.1.2/8.18.1.2) with ESMTP id 554Kr2up017789 for <breellocaldev@gmail.com>; Wed, 4 Jun 2025 21:21:54 GMT
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chase.com; h=content-transfer-encoding : content-type : date : from : message-id : mime-version : subject : to; s=d4815; bh=s8C47OdoV1gXfqN9qIkir+Y8P9kkuDnmIZQEA9WBZbw=; b=iVm+FQf7A/D88nX3I1dK8HOPR4FMeFF5OqHUJRKLUlqxP6Hj9/yrDf+dwrjS+lcHkS1Y nNGNhMyBy1jQMwfw5B6Wa49VrcD637A0zu9iHwVVW5dvIwbrkl0zC18PQDo85UdyunpY qIwrNapM9aX0cYqJZ/E12S0YaTt/3xyGdQe8eBb1iB7xDzNZTAakHiYV1JsmzL+phQfi C8r1OkSJEAY0lxMY2ksxgvW5gKx0ltYX4PKi6wV6g5vV4lc1IBhZz/4VpjWNba77SeH4 DByZmhDWFkL9qRSy5OPigrow5eJ6Xj/1bFO1P+OiPZ6Mz2dvByQ5K2Qv7G5xOz4A8LRE mQ==
Received: from vsin53p7503.nanw.jpmchase.net ([169.72.103.155]) by vsin80p3239.dmz.us.jpmchase.net (PPS) with ESMTPS id 470djp00eg-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for <breellocaldev@gmail.com>; Wed, 04 Jun 2025 21:21:54 +0000
Received: from iaasn00692069.nane.jpmchase.net (iaasn00692069.nane.jpmchase.net [169.101.108.220])
by vsin53p7503.nanw.jpmchase.net
with ESMTP id 554LLrV6018616
for <breellocaldev@gmail.com>; Wed, 4 Jun 2025 21:21:54 GMT
Date: Wed, 4 Jun 2025 21:21:53 +0000 (UTC)
From: Chase <no.reply.alerts@chase.com>
To: breellocaldev@gmail.com
Message-ID: <1514303812.1327452.1749072113609.JavaMail.webuser@iaasn00692069>
Subject: You have a $394.96 credit pending on your credit card
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
NOTIFICATION-ID: CREDIT_REALTIME_AUTH-f49cf41b1787628a22bb0429e97ec4a845fde585fdfb593548c46498773b47a2-ver3-3187-941c9e20-41c8-3b3c-aefd-f27755b09008
RETRY-COUNT: 0
<!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>See more details 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;
}
.hero {
=09width: 100% !important;
=09height: auto !important;
}
.moPad {
=09padding-right: 20px !important;
=09padding-left: 20px !important;
}
.zeroPad {
=09padding-right: 0px !important;
=09padding-left: 0px !important;
}
.font14 {
=09font-size: 14px !important;
}
.font24 {
=09font-size: 24px !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;
}
.hero {
=09width: 100% !important;
=09height: auto !important;
}
.moPad {
=09padding-right: 20px !important;
=09padding-left: 20px !important;
}
.zeroPad {
=09padding-right: 0px !important;
=09padding-left: 0px !important;
}
.font14 {
=09font-size: 14px !important;
}
.font24 {
=09font-size: 24px !important;
}
}
</style>
</head>
<body style=3D"padding: 0px;margin: 0px; background-color:#d7dbe0;">
<table align=3D"center" width=3D"100%" border=3D"0" cellspacing=3D"0" cellp=
adding=3D"0" style=3D"min-width:800px; background-color:#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=
align=3D"center" width=3D"800" cellpadding=3D"0" cellspacing=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 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 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; background-color: #ffffff;"><table 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;">See more details here.</div>
<!-- Insert &zwnj;&nbsp; after hidden preview tex=
t -->
<div style=3D"display: none; max-height: 0px; ove=
rflow: hidden;"> &nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;</div>
<!-- End hidden preview text -->
<tr>
<td align=3D"left" style=3D"vertical-align:top;=
padding-left: 30px;" class=3D"moPad"><table 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; background-color: #ffffff;" class=3D"moPad"><table=
align=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-alig=
n:top;"><table width=3D"100%" cellpadding=3D"0" cellspacing=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 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 width=3D"100%" cell=
padding=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/chase-freedom/freedom-unlimited/41473417013.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 have a $394.96 credit pending on your credit card</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"di=
splay: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 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 width=3D"100%" cellpadding=3D"0" cellspaci=
ng=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 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 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">Chase Fr=
eedom Unlimited Visa (...5876)</td>
</tr>
</table></td>
</tr>
=09=09=09=09=09=09 <tr>
<td align=3D"left" style=3D"vertical-align:top;=
padding: 10px 0px;border-bottom: solid 1px #414042;"><table 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;">=
Jun 4, 2025 at 5:21 PM ET</a></span></td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top;=
padding: 10px 0px;border-bottom: solid 1px #414042;"><table 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">AMAZON M=
KTPLACE PMTS</td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top;=
padding: 10px 0px;border-bottom: solid 1px #414042;"><table 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">Credit 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">$394.96<=
/td>
</tr>
</table></td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top; paddi=
ng:40px 180px 40px 30px; font-family:Arial, Helvetica, sans-serif; font-siz=
e:16px; color:#414042;" class=3D"moPad">You can visit our <a style=3D"text-=
decoration: underline; color:#0060F0;" href=3D"https://www.chase.com/person=
al/credit-cards/card-resource-center" rel=3D"noopener noreferrer" target=
=3D"_blank">Resource Center</a> anytime to help answer your questions or ma=
nage your account.</td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top;"><tab=
le width=3D"100%" align=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=
=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-align:top;=
"><table width=3D"200" align=3D"left" cellpadding=3D"0" cellspacing=3D"0" b=
order=3D"0" class=3D"fullWidth">
<tr>
<td align=3D"left" style=3D"padding:0px; =
vertical-align:top; padding: 0px 0px 30px 30px;" class=3D"moPad"><table ali=
gn=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=3D"0" style=3D"verti=
cal-align:top;">
<tr>
<td role=3D"button" align=3D"center=
" style=3D"background-color:#0060f0; color: #fffffe; font-size: 16px; font-=
family: Arial, Helvetica, sans-serif; padding: 10px 0px; border: 1px solid =
#0060f0; vertical-align:top; border-radius:4px; -moz-border-radius: 4px; -w=
ebkit-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>
</table></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&nbsp;Mobile<span style=3D"font-size:70%; line-height:0; v=
ertical-align:3px; text-decoration: none;">&reg;</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 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&nbsp;Mobile<span style=3D"font-size:70%=
; line-height:0; vertical-align:3px;">&reg;</span> app is available for se=
lect mobile devices. Message and data rates may apply.<br />
<br />
This service email was sent based on your ale=
rt settings. Use the Chase&nbsp;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. Wireless or internet service provider outages or other =
circumstances could delay them. You can always check <span class=3D"appleli=
nksgray"><a style=3D"color:#717171;text-decoration: none;">chase.com</a></s=
pan> or the Chase&nbsp;Mobile app for the status of your accounts including=
your latest account balances and transaction details.<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 />
&copy; 2025 JPMorgan Chase &amp; 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 -->
</table></td>
</tr>
</table>
</body>
</html>

480
testdata/chase.202506.online.txt vendored Normal file
View File

@ -0,0 +1,480 @@
Delivered-To: breellocaldev@gmail.com
Received: by 2002:a05:6022:5675:b0:72:1e7d:3888 with SMTP id er21csp4539254lab;
Wed, 4 Jun 2025 12:43:12 -0700 (PDT)
X-Google-Smtp-Source: AGHT+IHLeG/uqg3e2qFuWubH6OD72TD4QmoFcQ31qKCueuVIZoGu49++XdkNYyPDDDjXxgHpTYGz
X-Received: by 2002:a05:622a:17c9:b0:494:adc6:239f with SMTP id d75a77b69052e-4a5a573b3ffmr56590531cf.10.1749066192430;
Wed, 04 Jun 2025 12:43:12 -0700 (PDT)
ARC-Seal: i=1; a=rsa-sha256; t=1749066192; cv=none;
d=google.com; s=arc-20240605;
b=UW84WKYFh9Li7n5AqU3nJhcA0/kJkrmUDuwjsdJ+F/1rCW6wyGavwX03AaHscl8Pob
mSnqS5M/E4dn6KxlA7F/qEfBwKLUiY+2F3IDZE+/2a0JB10vXf/zlibv0YW9+wGZ/IGo
I5CASyfhZE4v46TPcmEf0oFPynqdmLbXCaTe4C6B8cpeQoIxIIBAQSKUlqezPc0QTFfg
cI9DXK2MlS4LVV96avu/riqyjlF2C2DmrJCYVoAo3f+ZX3ZZdpbjdeFPQamu/lwVq21y
yM2IXu9AGzLATGwZSXne9eCpA9mq/77o2jBpZq4mpcmIPGwKd/Eh8+pV1pVJ5y8ADX5L
+ptw==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605;
h=retry-count:notification-id:content-transfer-encoding:mime-version
:subject:message-id:to:from:date:dkim-signature;
bh=rF8v8eIeIYAhxeF3fale6PXDnNUzP2v6MrVz5J+kDO4=;
fh=lrt9myTdNYNEckm/XjduC9YuGfYr3v7r45vYTr/7vKM=;
b=EVrCwozzyia+LzWOT/2KZDSib6ZYshhBgwQLH/OtCZj802Mwz+AaV+RISBdrvrHAWK
FvVe7oFJTQ6yjpQgQtCwlwyFleGPugdlZXQyuDu7kvNM55t4RBFf3MvJxgHzrTQpjqj+
7pQIav6n/wq4lY30mIbYWxrki/c+oBMvqRLhXw921d30l6FVAW3xxAdHD4ivLGWahAej
BKFvzyGSeNq+z7BnlLXInNoPj7Il2PBtEz6w0dgQV++3DMmvbaprCnCQUaSyDeOAwLwR
l+dzEhXz9Oc65b58D9+IQvRk1QQTMMPA6QEQYYS8zqPzMJr2u9LaOO9Z/cSw0DdKDxKp
tA4g==;
dara=google.com
ARC-Authentication-Results: i=1; mx.google.com;
dkim=pass header.i=@chase.com header.s=d4815 header.b=gHcZ2j7g;
spf=pass (google.com: domain of no.reply.alerts.09@chase.com designates 146.143.72.25 as permitted sender) smtp.mailfrom=no.reply.alerts.09@chase.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=chase.com
Return-Path: <no.reply.alerts.09@chase.com>
Received: from vsin80p3234.jpmchase.com (vsin80p3234.jpmchase.com. [146.143.72.25])
by mx.google.com with ESMTPS id d75a77b69052e-4a435886637si159823641cf.98.2025.06.04.12.43.12
for <breellocaldev@gmail.com>
(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
Wed, 04 Jun 2025 12:43:12 -0700 (PDT)
Received-SPF: pass (google.com: domain of no.reply.alerts.09@chase.com designates 146.143.72.25 as permitted sender) client-ip=146.143.72.25;
Authentication-Results: mx.google.com;
dkim=pass header.i=@chase.com header.s=d4815 header.b=gHcZ2j7g;
spf=pass (google.com: domain of no.reply.alerts.09@chase.com designates 146.143.72.25 as permitted sender) smtp.mailfrom=no.reply.alerts.09@chase.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=chase.com
Received: from pps.filterd (vsin80p3234.dmz.us.jpmchase.net [127.0.0.1]) by vsin80p3234.jpmchase.com (8.18.1.2/8.18.1.2) with ESMTP id 554JehTC009711 for <breellocaldev@gmail.com>; Wed, 4 Jun 2025 19:43:11 GMT
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chase.com; h=content-transfer-encoding : content-type : date : from : message-id : mime-version : subject : to; s=d4815; bh=rF8v8eIeIYAhxeF3fale6PXDnNUzP2v6MrVz5J+kDO4=; b=gHcZ2j7g3sPVIuyjtV46AuRgSrHoMkqrGuqNs8KbFuiLqjnHFvArirYP4wyLDfjjbuva 3/UHHLjS9KJPMBMnpheaduJpqpISIzTPWZR+t0sOWtnYHBBryuQFqnAlwyyVt8Iu3/lw z4loKQLPd/t2pD4p1qiZLRwftLpA/mKnMAcl2khr7GTHIo8/KP3B0r29yh/w+KNs4IKC a8hQ92Chtq2Sa+naj5ucg+rWsQWrPvb/Mxl8JOrC/uHJ7NQqmeTWxsLkKYI4LdiUCTKn 8PO+EzFJPLxoY+0cbeAzsrjPok6vyjoVB/K2WVaHUKwkloEw2KX3whb+VJoH7aRQbjhH 6w==
Received: from vsin53p7503.nanw.jpmchase.net ([169.72.103.155]) by vsin80p3234.dmz.us.jpmchase.net (PPS) with ESMTPS id 472efftmp5-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for <breellocaldev@gmail.com>; Wed, 04 Jun 2025 19:43:11 +0000
Received: from iaasn00692069.nane.jpmchase.net (iaasn00692069.nane.jpmchase.net [169.101.108.220])
by vsin53p7503.nanw.jpmchase.net
with ESMTP id 554JhA4n017167
for <breellocaldev@gmail.com>; Wed, 4 Jun 2025 19:43:11 GMT
Date: Wed, 4 Jun 2025 19:43:10 +0000 (UTC)
From: Chase <no.reply.alerts@chase.com>
To: breellocaldev@gmail.com
Message-ID: <1107486429.1219861.1749066190799.JavaMail.webuser@iaasn00692069>
Subject: You made an online, phone, or mail transaction with DD *KLUCKSKRISPYCHIC
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
NOTIFICATION-ID: CREDIT_REALTIME_AUTH-48e9c860b8465de975777f4fb83dd401b6344dbc8bda5e349afcf1b90dcfaca7-ver3-95-941c9e20-41c8-3b3c-aefd-f27755b09008
RETRY-COUNT: 0
<!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>See more details about your transaction.</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;
}
.hero {
=09width: 100% !important;
=09height: auto !important;
}
.moPad {
=09padding-right: 20px !important;
=09padding-left: 20px !important;
}
.zeroPad {
=09padding-right: 0px !important;
=09padding-left: 0px !important;
}
.font14 {
=09font-size: 14px !important;
}
.font24 {
=09font-size: 24px !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;
}
.hero {
=09width: 100% !important;
=09height: auto !important;
}
.moPad {
=09padding-right: 20px !important;
=09padding-left: 20px !important;
}
.zeroPad {
=09padding-right: 0px !important;
=09padding-left: 0px !important;
}
.font14 {
=09font-size: 14px !important;
}
.font24 {
=09font-size: 24px !important;
}
}
</style>
</head>
<body style=3D"padding: 0px;margin: 0px; background-color:#d7dbe0;">
<table align=3D"center" width=3D"100%" border=3D"0" cellspacing=3D"0" cellp=
adding=3D"0" style=3D"min-width:800px; background-color:#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=
align=3D"center" width=3D"800" cellpadding=3D"0" cellspacing=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 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 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; background-color: #ffffff;"><table 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;">See more details about your transaction.</div>
<!-- Insert &zwnj;&nbsp; after hidden preview tex=
t -->
<div style=3D"display: none; max-height: 0px; ove=
rflow: hidden;"> &nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;</div>
<!-- End hidden preview text -->
<tr>
<td align=3D"left" style=3D"vertical-align:top;=
padding-left: 30px;" class=3D"moPad"><table 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; background-color: #ffffff;" class=3D"moPad"><table=
align=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-alig=
n:top;"><table width=3D"100%" cellpadding=3D"0" cellspacing=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 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 width=3D"100%" cell=
padding=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/chase-freedom/freedom-unlimited/41473417013.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 an online, phone, or mail 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 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 width=3D"100%" cellpadding=3D"0" cellspaci=
ng=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 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 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">Chase Fr=
eedom Unlimited Visa (...5876)</td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top;=
padding: 10px 0px;border-bottom: solid 1px #414042;"><table 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;">=
Jun 4, 2025 at 3:43 PM ET</a></span></td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top;=
padding: 10px 0px;border-bottom: solid 1px #414042;"><table 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">DD *KLUC=
KSKRISPYCHIC</td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top;=
padding: 10px 0px;border-bottom: solid 1px #414042;"><table 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"><span cl=
ass=3D"applelinksgray41"><a style=3D"color:#414042;text-decoration: none;">=
$18.17</a></span></td>
</tr>
</table></td>
</tr>
</table></td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top; p=
adding:40px 180px 40px 30px; font-family:Arial, Helvetica, sans-serif; font=
-size:16px; color:#414042;" class=3D"moPad">Let us monitor where you saved =
your card so you don't have to
with this free, easy and secure tool. <a style=
=3D"text-decoration: underline; color:#0060F0;" href=3D"https://secure.chas=
e.com/web/auth/nav?navKey=3DrequestBusinessCardOnFile" rel=3D"noopener nore=
ferrer" target=3D"_blank">Get started</a>.</td>
</tr>
<tr>
<td align=3D"left" style=3D"vertical-align:top;"><tab=
le width=3D"100%" align=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=
=3D"0">
<tr>
<td align=3D"left" style=3D"vertical-align:top;=
"><table width=3D"200" align=3D"left" cellpadding=3D"0" cellspacing=3D"0" b=
order=3D"0" class=3D"fullWidth">
<tr>
<td align=3D"left" style=3D"padding:0px; =
vertical-align:top; padding: 0px 0px 30px 30px;" class=3D"moPad"><table ali=
gn=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=3D"0" style=3D"verti=
cal-align:top;">
<tr>
<td role=3D"button" align=3D"center=
" style=3D"background-color:#0060f0; color: #fffffe; font-size: 16px; font-=
family: Arial, Helvetica, sans-serif; padding: 10px 0px; border: 1px solid =
#0060f0; vertical-align:top; border-radius:4px; -moz-border-radius: 4px; -w=
ebkit-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>
</table></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&nbsp;Mobile<span style=3D"font-size:70%; line-height:0; v=
ertical-align:3px; text-decoration: none;">&reg;</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 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&nbsp;Mobile<span style=3D"font-size:70%=
; line-height:0; vertical-align:3px;">&reg;</span> app is available for se=
lect mobile devices. Message and data rates may apply.<br />
<br />
This service email was sent based on your ale=
rt settings. Use the Chase&nbsp;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. Wireless or internet service provider outages or other =
circumstances could delay them. You can always check <span class=3D"appleli=
nksgray"><a style=3D"color:#717171;text-decoration: none;">chase.com</a></s=
pan> or the Chase&nbsp;Mobile app for the status of your accounts including=
your latest account balances and transaction details.<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 />
&copy; 2025 JPMorgan Chase &amp; 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 -->
</table></td>
</tr>
</table>
</body>
</html>

460
testdata/chase.202506.txt vendored Normal file
View File

@ -0,0 +1,460 @@
Delivered-To: breellocaldev@gmail.com
Received: by 2002:a05:6022:9201:b0:72:1e7d:3888 with SMTP id da1csp605545lab;
Thu, 5 Jun 2025 19:07:07 -0700 (PDT)
X-Google-Smtp-Source: AGHT+IE+moNfFhXJTJvvdmPs/w/Vts7d+VkwUju2TbMUnkpSCDhDLZxYSC6SVeKH9qQ26rMWYzr4
X-Received: by 2002:a05:620a:1a23:b0:7d0:9ffd:4240 with SMTP id af79cd13be357-7d229933921mr325162385a.55.1749175627255;
Thu, 05 Jun 2025 19:07:07 -0700 (PDT)
ARC-Seal: i=1; a=rsa-sha256; t=1749175627; cv=none;
d=google.com; s=arc-20240605;
b=SfoZtzKcm9L5kaOTQKOF8wEN8vAPSa+H38CyuvZJYmO4kzMB1yRKQOAilXThduLFbT
9NrbZjM+0mIbHVQm/lF0NSKV04UBMFkxAUBATsXNO69RLCxtO5RrxvZYn2L49thzuhbT
usSuj7Im6vZ+tz5ze4WJGGHawDcnqnKgEVnm848sKo8woBtXElDMo6nryqN7CuuovvWW
MptLzwgU612lBpahTHfNYNqws7PsTqbC4wfhMoqnkcxfjrC9m47MgzywL3QRlLhPX2kV
i8wOpbS+K+erFfNlzZV0FiUbiY8A/K6o2fcBaiNkXvuLximJiOAHTmxws24i4TMl2SJi
bGIw==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605;
h=retry-count:notification-id:content-transfer-encoding:mime-version
:subject:message-id:to:from:date:dkim-signature;
bh=lnABD7nCflWxnAXhTOyo6zRvYYLTdV0caaqTQPlvs7A=;
fh=lrt9myTdNYNEckm/XjduC9YuGfYr3v7r45vYTr/7vKM=;
b=f5s9zvAULbp6Qu4JmCkGMNAAeSRlFlj+jLMCr1q8H2XbW/fh8MYdmocgy19QABq9BY
kBJHlV2UA1bbodmtJlBWmFymJo8FAzm7eUoepJ4CWWctm17xhxbJ6uO6judb9xPKxA1I
57XXLQMSighE3qYBN9vnPyk0lgkN7zP/Cu/TV4fr6zXzdIBQKBv5tr93EvUdlt9S/MbL
rU1xeiFenksejp9Bhfd5h1IzYDb8y4/rB7558x1TCxo6MaIfZpR94f554H8/kjivTQMV
d/oPCSKsqcpEfaCRbOSm1V6e4G9HOzmOpTrGL8GFUF1/df2Z2gmSPYUnC5gJljSBiQVz
UR/Q==;
dara=google.com
ARC-Authentication-Results: i=1; mx.google.com;
dkim=pass header.i=@chase.com header.s=d4815 header.b=LdKPsfJ6;
spf=pass (google.com: domain of no.reply.alerts.11@chase.com designates 146.143.136.25 as permitted sender) smtp.mailfrom=no.reply.alerts.11@chase.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=chase.com
Return-Path: <no.reply.alerts.11@chase.com>
Received: from vsin80p3228.jpmchase.com (vsin80p3228.jpmchase.com. [146.143.136.25])
by mx.google.com with ESMTPS id af79cd13be357-7d25a5f0fa7si79799685a.266.2025.06.05.19.07.07
for <breellocaldev@gmail.com>
(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
Thu, 05 Jun 2025 19:07:07 -0700 (PDT)
Received-SPF: pass (google.com: domain of no.reply.alerts.11@chase.com designates 146.143.136.25 as permitted sender) client-ip=146.143.136.25;
Authentication-Results: mx.google.com;
dkim=pass header.i=@chase.com header.s=d4815 header.b=LdKPsfJ6;
spf=pass (google.com: domain of no.reply.alerts.11@chase.com designates 146.143.136.25 as permitted sender) smtp.mailfrom=no.reply.alerts.11@chase.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=chase.com
Received: from pps.filterd (vsin80p3228.dmz.us.jpmchase.net [127.0.0.1]) by vsin80p3228.jpmchase.com (8.18.1.2/8.18.1.2) with ESMTP id 555Nje8s003429 for <breellocaldev@gmail.com>; Fri, 6 Jun 2025 02:07:06 GMT
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chase.com; h=content-transfer-encoding : content-type : date : from : message-id : mime-version : subject : to; s=d4815; bh=lnABD7nCflWxnAXhTOyo6zRvYYLTdV0caaqTQPlvs7A=; b=LdKPsfJ6BvS0A55LRUSug3cAoDccuMxnqIdO4fmxSvJYXpgmhAEbyOHwrrq0OZOFr8XC TeMVIfDxg1P3pfUZHvtgm2QKERByVDGfSv+kiII9yIj0bpbt+6//REssh5RyjxKbCaU/ iL8XHeRmetjBbiqhoj8A4b9NN3wgt25QtFRzemuiGUa78CtX+fqTL6KJOlygtXv7otNB Ci3X1JWdv/hb7t0PVplmKmABVPuVAxdiUeZWCiBbgxq8xCYSK5YTZasqF4uoro0u1q8K BhqZsx/Yd8HVO9Y1PstG+04XGhz1dbJk2WXKEfa7IhLU9eHrov3nqdukTxH6kACj9DAs 1A==
Received: from vsin52p7465.nanw.jpmchase.net ([169.74.48.181]) by vsin80p3228.dmz.us.jpmchase.net (PPS) with ESMTPS id 470cwruea9-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for <breellocaldev@gmail.com>; Fri, 06 Jun 2025 02:07:06 +0000
Received: from iaasn00692068.nane.jpmchase.net (iaasn00692068.nane.jpmchase.net [169.101.122.220])
by vsin52p7465.nanw.jpmchase.net
with ESMTP id 5562753I004398
for <breellocaldev@gmail.com>; Fri, 6 Jun 2025 02:07:05 GMT
Date: Fri, 6 Jun 2025 02:07:05 +0000 (UTC)
From: Chase <no.reply.alerts@chase.com>
To: breellocaldev@gmail.com
Message-ID: <1155623531.3989104.1749175625501.JavaMail.webuser@iaasn00692068>
Subject: You made a $3.42 transaction with Nintendo CB141137080
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
NOTIFICATION-ID: CREDIT_REALTIME_AUTH-770c6a3a398615aa3e43ca933e45e0d7505968eebcc33b814c913513bf149e99-ver3-3-941c9e20-41c8-3b3c-aefd-f27755b09008
RETRY-COUNT: 0
<!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 &zwnj;&nbsp; after hidden preview tex=
t -->
<div style=3D"display: none; max-height: 0px; ove=
rflow: hidden;"> &nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&=
nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbs=
p;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&=
zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwn=
j;&nbsp;</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>
<!-- End category pill -->
<!-- Start Headline -->
<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/chase-freedom/freedom-unlimited/41473417013.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 $3.42 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">Chase Fr=
eedom Unlimited Visa (...5876)</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;">=
Jun 5, 2025 at 10:07 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">Nintendo=
CB141137080</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">$3.42</t=
d>
</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&nbsp;Mobile<span style=3D"font-size:70%; line-height:0; v=
ertical-align:3px; text-decoration: none;">&reg;</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&nbsp;Mobile<span style=3D"font-size:70%=
; line-height:0; vertical-align:3px;">&reg;</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&nbsp;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&nbsp;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 />
&copy; 2025 JPMorgan Chase &amp; 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>

81
testdata/fidelity.balance.txt vendored Normal file
View File

@ -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: <Fidelity.Alerts@fidelity.com>
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 <breellocaldev@gmail.com>
(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 <breellocaldev@gmail.com>; 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 <breellocaldev@gmail.com>; Tue, 14 Jan 2025 02:12:31 -0500
To: breellocaldev@gmail.com
From: Fidelity Investments <Fidelity.Alerts@fidelity.com>
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.

29
testdata/fidelity.deposit.txt vendored Normal file
View File

@ -0,0 +1,29 @@
Account: XXXXX5576
A deposit to your account was received on 12/08/2022.
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 2022 FMR LLC
All rights reserved. Important Legal Information
at http://www.fidelity.com/terms-of-use.

49
testdata/fidelity.withdrawal.txt vendored Normal file
View File

@ -0,0 +1,49 @@
For account ending in 5576:
Money was withdrawn from your account through a direct debit in the amount of $1.00 by CHASE CREDIT CRD.
If you authorized this transaction, no action is needed.
If you did not authorize this transaction, please contact us immediately at 800-343-3548.
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://alertable.fidelity.com/alerts/help/agreement.html. If you would
prefer to not receive these alerts, please change your preferences at
https://scs.fidelity.com/customeronly/alerts.shtml.
This new alerts service will not affect delivery of paper communications
you are scheduled to receive.
To stop receipt of alerts, modify your preferences on the Existing Alerts page
at https://scs.fidelity.com/customeronly/fens.shtml, or temporarily
stop/restart alerts at
https://scs.fidelity.com/customeronly/fens_alertstatus.shtml.
Fidelity offers access to a broader range of third-party research at
http://personal.fidelity.com/research/stocks/content/stocksindex.shtml.
Fidelity is not recommending or endorsing any third-party research by making it
available to its customers or by notifying customers of its availability.
Review Fidelity's Terms of Use for Third Party Content and Research at
http://activequote.fidelity.com/rtrnews/terms.html.
If your email has changed, please update your email address
at https://scs.fidelity.com/customeronly/fens_profile.shtml to continue to
receive your alerts.
Read Fidelity's Commitment to Privacy
at http://personal.fidelity.com/global/search/content/privacy.html.tvsr.
Visit Fidelity's Home Page
http://www.fidelity.com/
Fidelity Brokerage Services LLC, Member NYSE, SIPC, 900 Salem Street, Smithfield, RI 02917
EMAIL REF# 537048
Copyright 2022 FMR LLC
All rights reserved. Important Legal Information
at http://personal.fidelity.com/misc/legal/legal.html.tvsr.

113
testdata/uccu.balance.txt vendored Normal file
View File

@ -0,0 +1,113 @@
Delivered-To: breellocaldev@gmail.com
Received: by 2002:ac0:d587:0:b0:36b:891d:d518 with SMTP id f7csp8195321imh;
Sun, 19 Oct 2025 20:12:12 -0700 (PDT)
X-Google-Smtp-Source: AGHT+IFUgpNCx0iWHjiZ2ACXymtPllyGTHFvg3AZWvOx3YrM5uMz8x77KC3VIs4hxjkggj+ZNRuB
X-Received: by 2002:a05:6214:1c43:b0:87c:836:8f21 with SMTP id 6a1803df08f44-87c20594815mr163096026d6.21.1760929932283;
Sun, 19 Oct 2025 20:12:12 -0700 (PDT)
ARC-Seal: i=1; a=rsa-sha256; t=1760929932; cv=none;
d=google.com; s=arc-20240605;
b=kApQs1Ymlz+s8G0nYujuPNSJ3cs97QI0fmmz5g3n2bcpxmONxDWSUFjDTYoAJeW+15
i/KuwuU1VusVWtJvg7KKJWwkmp7qeYVlkQmxq/v3IaZAM5p+OtJx8WmTqKePpCK5ZuiR
qrKdGfZAfnKMvN12F59N0WgHV0+wEMvi6AS2yKzZsVdY7UEq4o//ve3INHO4OeJjo2Uy
aXaUxAXGQZeDlv/v4mSSE8I0utowTDiGpEyoSnFUhJk7hmiG26JRF4YHQA/vjAc32mUK
jrKNjc15y4vw8LOT2ATm/jgeaZIQ0/8Z0ymQG8sLoQVhWe3WkxUK7Y9MxzLyeWCyCvRV
qBhA==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605;
h=to:message-id:date:subject:from:mime-version:dkim-signature
:dkim-signature;
bh=k7jGHJ2ljFiolAPU5T6OwM+LnPG+3HzBJixYSB6L+E8=;
fh=lrt9myTdNYNEckm/XjduC9YuGfYr3v7r45vYTr/7vKM=;
b=iJeycLvOEG6ySvr9T2fxpvTqIGeYBeDcVxW5nrqqdKELVdqBMgHL9axXUn8RdUP8pW
RoZSRuNlIOaqCYGtGPjGGD0pWl/wexytBnV6yoHNNwTExfVBr6XvTQ+JPjyTu/L11O09
9x2oaN3Dc/JGjKVMMGVcD/AEXrhSjjsQYpNqyq+DADqssWiVgioB4jKCInnjPIgWkqnK
i820QlMqRmzZzIi4kR4LFL5uM0MyNwc1SU/l0EUYDBF3BMdOCQ1i2JrAw2hWD28OHjTZ
ORHBrQY2THy573M0gL1j83XZz5ph3wBLnHXkYpQYOL/kv7tKdIhIs1aKYobFL7evEKTt
zyDw==;
dara=google.com
ARC-Authentication-Results: i=1; mx.google.com;
dkim=pass header.i=@uccu.com header.s=s1 header.b=ICj11Mxp;
dkim=pass header.i=@sendgrid.info header.s=smtpapi header.b=v2gr3NCY;
spf=pass (google.com: domain of bounces+14101083-c28a-breellocaldev=gmail.com@sg.uccu.com designates 149.72.182.202 as permitted sender) smtp.mailfrom="bounces+14101083-c28a-breellocaldev=gmail.com@sg.uccu.com";
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=uccu.com
Return-Path: <bounces+14101083-c28a-breellocaldev=gmail.com@sg.uccu.com>
Received: from o1.em.sg.q2ebanking.com (o1.em.sg.q2ebanking.com. [149.72.182.202])
by mx.google.com with ESMTPS id 6a1803df08f44-87d02d89698si21675916d6.731.2025.10.19.20.12.12
for <breellocaldev@gmail.com>
(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);
Sun, 19 Oct 2025 20:12:12 -0700 (PDT)
Received-SPF: pass (google.com: domain of bounces+14101083-c28a-breellocaldev=gmail.com@sg.uccu.com designates 149.72.182.202 as permitted sender) client-ip=149.72.182.202;
Authentication-Results: mx.google.com;
dkim=pass header.i=@uccu.com header.s=s1 header.b=ICj11Mxp;
dkim=pass header.i=@sendgrid.info header.s=smtpapi header.b=v2gr3NCY;
spf=pass (google.com: domain of bounces+14101083-c28a-breellocaldev=gmail.com@sg.uccu.com designates 149.72.182.202 as permitted sender) smtp.mailfrom="bounces+14101083-c28a-breellocaldev=gmail.com@sg.uccu.com";
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=uccu.com
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uccu.com; h=mime-version:from:subject:content-type:date:to:cc:content-type:date: feedback-id:from:subject:to; s=s1; bh=k7jGHJ2ljFiolAPU5T6OwM+LnPG+3HzBJixYSB6L+E8=; b=ICj11Mxph/4neYlK/LfDIN1I/A/STnJE9NBuDTPPuIU5SrWgvbrcHxdSRshjnygf1s6u RLkV/0jB1sMd/5hcmIQu0VLkJLbfpgLt9VAbP4+nNjVLn+YFNYWdgy8Y1eDsA8otUY5MPa E6Ojm1Dx1Z9AHZM/YS4cG5aK+090dDESsMnzpNRhGgLmnVhzR6J9rQj25u0G/g3trM0pNL mdadIuRde77nr/5OszQHLVNr1HRg6kbf4duco9blG05qWMpdbO9HZW83iPKZ5vLsnuTy9i tPxn+Hg6BNPHzI1PysBxgWsbV33VXt1v+J+PTQ5f/Bu4BSvF+fvtwK8Lo5UNRhAQ==
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sendgrid.info; h=mime-version:from:subject:content-type:date:to:cc:content-type:date: feedback-id:from:subject:to; s=smtpapi; bh=k7jGHJ2ljFiolAPU5T6OwM+LnPG+3HzBJixYSB6L+E8=; b=v2gr3NCY2aTS6WJkQB+uhaChiF3jnvSZDZpZrF4al1Fbm75q0wzWL4kt+m8jZyHzFflr +WpE8JcjyWo0jvhS6VjPUxQzIl2ZbOt0laHQMyQZEZbZQQCjVY8BGBx8wWJKfwRn9569wH +FuyXlox0CHwFCxqfeIVKKBeiIPrtMDsw=
Received: by recvd-7f4fb8995-jgz2d with SMTP id recvd-7f4fb8995-jgz2d-1-68F5A88B-E 2025-10-20 03:12:11.990860065 +0000 UTC m=+11874446.569028758
Received: from use1-postfix-shs-prd-001a.q2.internal (unknown) by geopod-ismtpd-26 (SG) with ESMTP id 9Jzm7g1dQxW6QvcyQoTs6A for <breellocaldev@gmail.com>; Mon, 20 Oct 2025 03:12:11.966 +0000 (UTC)
Received: from localhost (ip-10-233-152-95.ec2.internal [10.233.152.95]) by use1-postfix-shs-prd-001a.q2.internal (Postfix) with UTF8SMTP id C4917B4727 for <breellocaldev@gmail.com>; Mon, 20 Oct 2025 03:12:11 +0000 (UTC)
MIME-Version: 1.0
From: Notifications@uccu.com
Subject: UCCU Account Alert Notification
Content-Type: multipart/alternative; boundary=1c6f2f280d6200ff5d69ad23e6f5273369aefc9f9ac30cf0c16c4288a093
Date: Mon, 20 Oct 2025 03:12:12 +0000 (UTC)
Message-ID: <9Jzm7g1dQxW6QvcyQoTs6A@geopod-ismtpd-26>
X-SG-EID: u001.g3f8YwzV+uExvNyHXs20qs20kPT4Xr5yKfd6Jo1DS06qVgodwVcYFczS2kDS38t+kK3dSvo5nZC1MPxUDC3ru0wgAcAHPFVQv8BD/b/gkmc5AuFBZcSIQClE4ZT0W+naP4PHr7dyrI5P8IsN5P6ujZD0kASrUjlUoRuemNpmREOkP4aiIOtJ2tuZrJAeh2feDTZY4aT4tGpvaYcbz3KKFg==
To: breellocaldev@gmail.com
X-Entity-ID: u001.dyI+c5ymAHdeWQ9Y++I+vA==
--1c6f2f280d6200ff5d69ad23e6f5273369aefc9f9ac30cf0c16c4288a093
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=us-ascii
This is your requested account alert notification.
Additional details are available in your online secure mailbox.
As this is an automated reminder, please do not reply to this email.
--1c6f2f280d6200ff5d69ad23e6f5273369aefc9f9ac30cf0c16c4288a093
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=us-ascii
<!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"><head><meta http-equiv=3D"Content-Type" content=3D"text/htm=
l; charset=3Dutf-8" /><title>Secure Banking: Requested Information</title><=
/head><body><table width=3D"600" border=3D"0" cellspacing=3D"0" cellpadding=
=3D"0" align=3D"center" bgcolor=3D"#ffffff"><tr><td style=3D"line-height:0p=
x"><img src=3D"https://cdn1.onlineaccess1.com/cdn/wedge/5138/bank/images/em=
aillogo.PNG" width=3D"150" height=3D"47" alt=3D"Utah Community Credit Union=
" border=3D"0" style=3D"display:block;" /></td></tr><tr><td style=3D"line-h=
eight:0px"><img src=3D"https://cdn1.onlineaccess1.com/cdn/wedge/5138/bank/i=
mages/sp.png" width=3D"1" height=3D"40" border=3D"0" style=3D"display:block=
;" /></td></tr><tr><td><table width=3D"600" border=3D"0" cellspacing=3D"0" =
cellpadding=3D"0"><tr><td width=3D"15"> </td><td width=3D"570" align=3D"lef=
t" style=3D"font-family:Arial, Helvetica, sans-serif; color:#000000; font-s=
ize:13px; font-weight:bold;">This is your requested Current Balance alert n=
otification. Your Current Balance is greater than your specified alert valu=
e of 0.000 for Checking ending in 33350. The new Current Balance is $231.20=
.<br/><br/>Additional details are available in your online secure mailbox.<=
br/><br/><br/>As this is an automated reminder, please do not reply to this=
email.<br/><br/></td><td width=3D"15"> </td></tr></table></td></tr><tr><td=
style=3D"line-height:0px"><img src=3D"https://cdn1.onlineaccess1.com/cdn/w=
edge/5138/bank/images/sp.png" width=3D"1" height=3D"40" border=3D"0" style=
=3D"display:block;" /></td></tr><tr><td align=3D"center" style=3D"font-fami=
ly:Arial, Helvetica, sans-serif; color:#000000; font-size:10px;">This email=
has been automatically generated by request. This electronic transmission =
is confidential and is intended for designated recipients only. If you have=
received this message in error, please delete the original and all copies =
and notify the sender immediately. To ensure that you continue receiving ou=
r emails, please add us to your address book or safe list.<br/></td></tr></=
table><img src=3D"https://u14101083.ct.sendgrid.net/wf/open?upn=3Du001.2WNV=
s8xmQ7XpjpOSgNIvAfchoCyYJSuzcXKuDlb-2FPKacqwLzmLuntClQxOirDnsaiA6Z0cicvkwOH=
ph7YcSEi1O5404wbtVB7zLxCqDKkBYnT8HmklJoBCym4NlBju2mEhxg1bf71cLWWJZwGtWyaQKd=
x4H0TqqBHskCVE5JG7mrNU10L-2BYehg5Yx3gg-2FdNYkjUEa5H0GD62X3WQwvewRg-3D-3D" a=
lt=3D"" width=3D"1" height=3D"1" border=3D"0" style=3D"height:1px !importan=
t;width:1px !important;border-width:0 !important;margin-top:0 !important;ma=
rgin-bottom:0 !important;margin-right:0 !important;margin-left:0 !important=
;padding-top:0 !important;padding-bottom:0 !important;padding-right:0 !impo=
rtant;padding-left:0 !important;"/></body></html>
--1c6f2f280d6200ff5d69ad23e6f5273369aefc9f9ac30cf0c16c4288a093--

View File

@ -1,27 +1,20 @@
package main
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"local/oauth2"
"net/http"
"net/url"
"os"
"path"
"regexp"
"strconv"
"strings"
"time"
yaml "gopkg.in/yaml.v2"
"github.com/google/uuid"
)
func Upload(config Config, transaction *Transaction) error {
switch config.Uploader {
case UploaderTodo:
return uploadTodo(config, transaction)
case DeprecatedUploaderTodo:
panic("DEAD")
case UploaderLedger:
return uploadLedger(config, transaction)
case UploaderPTTodo:
@ -31,67 +24,18 @@ func Upload(config Config, transaction *Transaction) error {
}
}
func uploadTodo(config Config, transaction *Transaction) error {
params := url.Values{
"list": {config.TodoList},
"title": {transaction.Format()},
"tag": {config.TodoTag},
}
req, err := http.NewRequest("POST", config.TodoAddr+"/ajax.php?newTask", strings.NewReader(params.Encode()))
if err != nil {
return err
}
req.Header.Set("Cookie", oauth2.COOKIE+"="+config.TodoToken)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
b, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("bad status from todo: %v: %s", resp.StatusCode, b)
}
return nil
}
func uploadPTTodo(config Config, transaction *Transaction) error {
b, err := ioutil.ReadFile(config.TodoAddr)
if os.IsNotExist(err) {
b = []byte("todo:\n")
} else if err != nil {
return err
} else if len(b) == 0 {
b = []byte("todo:\n")
}
f, err := ioutil.TempFile(path.Dir(config.TodoAddr), path.Base("."+config.TodoAddr))
f, err := os.Create(fmt.Sprintf("%s.todo.%s", config.TodoAddr, uuid.New().String()))
if err != nil {
return err
}
defer f.Close()
sep := []byte{'\n'}
seek := []byte("todo:")
for len(b) > 0 {
idx := bytes.Index(b, sep)
if idx == -1 {
idx = len(b) - 1
}
fmt.Fprintf(f, "%s\n", b[:idx])
if bytes.Equal(bytes.TrimSpace(b[:idx]), seek) {
fmt.Fprintf(f, `- {"todo":%q, "tags":%q}%s`, transaction.Format(), config.TodoTag, "\n")
}
b = b[idx+1:]
if config.TodoTag == "" {
fmt.Fprintf(f, `- {"todo":%q}%s`, transaction.Format(), "\n")
} else {
fmt.Fprintf(f, `- {"todo":%q, "tags":%q}%s`, transaction.Format(), config.TodoTag, "\n")
}
f.Close()
var v interface{}
b, err = ioutil.ReadFile(f.Name())
if err != nil {
return err
}
if err := yaml.Unmarshal(b, &v); err != nil {
return err
}
return os.Rename(f.Name(), config.TodoAddr)
return f.Close()
}
func uploadLedger(config Config, transaction *Transaction) error {

View File

@ -3,17 +3,30 @@ package main
import (
"bytes"
"io/ioutil"
"local/storage"
"os"
"path"
"path/filepath"
"testing"
"gitea.inhome.blapointe.com/local/storage"
"github.com/google/uuid"
)
func TestUploadPTTodo(t *testing.T) {
addr := path.Join(t.TempDir(), "test.upload.pttodo")
config := Config{TodoAddr: addr, TodoTag: "expense"}
reset := func(t *testing.T) {
files, err := filepath.Glob(addr + "*")
if err != nil {
t.Fatal(err)
}
for _, f := range files {
if f != addr {
os.Remove(f)
}
}
}
xaction := func() *Transaction {
return &Transaction{
ID: "id",
@ -24,6 +37,7 @@ func TestUploadPTTodo(t *testing.T) {
}
}
t.Run("full file", func(t *testing.T) {
defer reset(t)
if err := ioutil.WriteFile(addr, []byte(`
todo:
- first
@ -37,18 +51,18 @@ done: []
if err != nil {
t.Error(err)
}
b, err := ioutil.ReadFile(addr)
files, err := filepath.Glob(addr + ".todo.*")
if err != nil {
t.Error(err)
}
if bytes.Compare(bytes.TrimSpace(b), bytes.TrimSpace([]byte(`
todo:
- {"todo":"(today) /UCCU: 1.23 @ vendor vendor", "tags":"expense"}
- first
- todo: second
scheduled: []
done: []
`))) != 0 {
if len(files) != 1 {
t.Fatal(files)
}
b, err := ioutil.ReadFile(files[0])
if err != nil {
t.Error(err)
}
if bytes.Compare(bytes.TrimSpace(b), bytes.TrimSpace([]byte(`- {"todo":"(today) /UCCU: 1.23 @ vendor vendor", "tags":"expense"}`))) != 0 {
t.Errorf("full file came out wrong: got %s", b)
}
if !bytes.Contains(b, []byte(xaction().Format())) {
@ -57,12 +71,20 @@ done: []
t.Logf("%s", b)
})
t.Run("no file", func(t *testing.T) {
defer reset(t)
os.Remove(addr)
err := uploadPTTodo(config, xaction())
if err != nil {
t.Error(err)
}
b, err := ioutil.ReadFile(addr)
files, err := filepath.Glob(addr + ".todo.*")
if err != nil {
t.Error(err)
}
if len(files) != 1 {
t.Fatal(files)
}
b, err := ioutil.ReadFile(files[0])
if err != nil {
t.Error(err)
}
@ -72,6 +94,7 @@ done: []
t.Logf("%s", b)
})
t.Run("empty file", func(t *testing.T) {
defer reset(t)
if err := ioutil.WriteFile(addr, []byte{}, os.ModePerm); err != nil {
t.Fatal(err)
}
@ -79,7 +102,14 @@ done: []
if err != nil {
t.Error(err)
}
b, err := ioutil.ReadFile(addr)
files, err := filepath.Glob(addr + ".todo.*")
if err != nil {
t.Error(err)
}
if len(files) != 1 {
t.Fatal(files)
}
b, err := ioutil.ReadFile(files[0])
if err != nil {
t.Error(err)
}