Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc95242c94 | ||
|
|
19aa5f82fb | ||
|
|
f0f7c1d3f0 | ||
|
|
a5927b0485 | ||
|
|
c6d88f6abe | ||
|
|
0449e7bdaa | ||
|
|
10bc441e1e | ||
|
|
6f3bf1f6a4 | ||
|
|
09f14ec44c | ||
|
|
3077343c89 | ||
|
|
24b66d1c46 | ||
|
|
a6c5121d54 | ||
|
|
1ebaf9eac8 | ||
|
|
cea7a30884 | ||
|
|
c5c77a2b9b | ||
|
|
bbd51ea9c5 | ||
|
|
f32fb5aad1 |
6
bank.go
6
bank.go
@@ -5,14 +5,20 @@ type Bank int
|
|||||||
const (
|
const (
|
||||||
Chase Bank = iota + 1
|
Chase Bank = iota + 1
|
||||||
Citi Bank = iota + 1
|
Citi Bank = iota + 1
|
||||||
|
UCCU Bank = iota + 1
|
||||||
|
BankOfAmerica Bank = iota + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b Bank) String() string {
|
func (b Bank) String() string {
|
||||||
switch b {
|
switch b {
|
||||||
|
case BankOfAmerica:
|
||||||
|
return "BankOfAmerica"
|
||||||
case Chase:
|
case Chase:
|
||||||
return "Chase"
|
return "Chase"
|
||||||
case Citi:
|
case Citi:
|
||||||
return "Citi"
|
return "Citi"
|
||||||
|
case UCCU:
|
||||||
|
return "UCCU"
|
||||||
}
|
}
|
||||||
return "?"
|
return "?"
|
||||||
}
|
}
|
||||||
|
|||||||
67
config.go
67
config.go
@@ -7,10 +7,25 @@ import (
|
|||||||
"local/args"
|
"local/args"
|
||||||
"local/oauth2"
|
"local/oauth2"
|
||||||
"local/storage"
|
"local/storage"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Uploader int
|
||||||
|
|
||||||
|
const (
|
||||||
|
UploaderTodo = Uploader(iota)
|
||||||
|
UploaderLedger
|
||||||
|
UploaderPTTodo
|
||||||
|
)
|
||||||
|
|
||||||
|
var uploaders = map[string]Uploader{
|
||||||
|
"todo": UploaderTodo,
|
||||||
|
"ledger": UploaderLedger,
|
||||||
|
"pttodo": UploaderPTTodo,
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
EmailUser string
|
EmailUser string
|
||||||
EmailPass string
|
EmailPass string
|
||||||
@@ -19,7 +34,11 @@ type Config struct {
|
|||||||
TodoToken string
|
TodoToken string
|
||||||
TodoList string
|
TodoList string
|
||||||
TodoTag string
|
TodoTag string
|
||||||
|
Uploader Uploader
|
||||||
Storage storage.DB
|
Storage storage.DB
|
||||||
|
Banks map[Bank]bool
|
||||||
|
AccountsPattern string
|
||||||
|
AccountsAntiPattern string
|
||||||
}
|
}
|
||||||
|
|
||||||
var config Config
|
var config Config
|
||||||
@@ -28,14 +47,21 @@ func NewConfig() Config {
|
|||||||
as := args.NewArgSet()
|
as := args.NewArgSet()
|
||||||
|
|
||||||
as.Append(args.STRING, "emailuser", "email username", "breellocaldev@gmail.com")
|
as.Append(args.STRING, "emailuser", "email username", "breellocaldev@gmail.com")
|
||||||
as.Append(args.STRING, "emailpass", "email password", "ML3WQRFSqe9rQ8qNkm")
|
as.Append(args.STRING, "emailpass", "email password", "diblloewfncwssof")
|
||||||
as.Append(args.STRING, "emailimap", "email imap", "imap.gmail.com:993")
|
as.Append(args.STRING, "emailimap", "email imap", "imap.gmail.com:993")
|
||||||
|
|
||||||
|
as.Append(args.STRING, "uploader", "todo, ledger, pttodo", "todo")
|
||||||
|
|
||||||
as.Append(args.STRING, "todoaddr", "todo addr", "https://todo-server.remote.blapointe.com")
|
as.Append(args.STRING, "todoaddr", "todo addr", "https://todo-server.remote.blapointe.com")
|
||||||
as.Append(args.STRING, "todopass", "todo pass", "gJtEXbbLHLf54yS9EdujtVN2n6Y")
|
as.Append(args.STRING, "todopass", "todo pass", "gJtEXbbLHLf54yS9EdujtVN2n6Y")
|
||||||
as.Append(args.STRING, "todotoken", "todo token", "")
|
as.Append(args.STRING, "todotoken", "todo token", "")
|
||||||
as.Append(args.STRING, "todolist", "todo list", "")
|
as.Append(args.STRING, "todolist", "todo list", "")
|
||||||
as.Append(args.STRING, "todotag", "todo tag", "expense")
|
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, "accounts", "regex to include filter accounts", ".*")
|
||||||
|
as.Append(args.STRING, "not-accounts", "regex to exclude filter accounts", "zzzzzz")
|
||||||
|
|
||||||
as.Append(args.STRING, "authaddr", "auth addr", "https://auth.remote.blapointe.com")
|
as.Append(args.STRING, "authaddr", "auth addr", "https://auth.remote.blapointe.com")
|
||||||
as.Append(args.STRING, "store", "store type", "map")
|
as.Append(args.STRING, "store", "store type", "map")
|
||||||
as.Append(args.STRING, "storeaddr", "store addr", "/tmp/store")
|
as.Append(args.STRING, "storeaddr", "store addr", "/tmp/store")
|
||||||
@@ -46,14 +72,10 @@ func NewConfig() Config {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
token := as.GetString("todotoken")
|
uploader := as.GetString("uploader")
|
||||||
if len(token) == 0 {
|
ul, ok := uploaders[uploader]
|
||||||
token = getToken(as)
|
if !ok {
|
||||||
}
|
panic("invalid uploader: " + uploader)
|
||||||
|
|
||||||
list := as.GetString("todolist")
|
|
||||||
if len(list) == 0 {
|
|
||||||
list = getList(as, token)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
storage, err := storage.New(storage.TypeFromString(as.GetString("store")), as.GetString("storeaddr"), as.GetString("storeuser"), as.GetString("storepass"))
|
storage, err := storage.New(storage.TypeFromString(as.GetString("store")), as.GetString("storeaddr"), as.GetString("storeuser"), as.GetString("storepass"))
|
||||||
@@ -66,11 +88,34 @@ func NewConfig() Config {
|
|||||||
EmailPass: as.GetString("emailpass"),
|
EmailPass: as.GetString("emailpass"),
|
||||||
EmailIMAP: as.GetString("emailimap"),
|
EmailIMAP: as.GetString("emailimap"),
|
||||||
TodoAddr: as.GetString("todoaddr"),
|
TodoAddr: as.GetString("todoaddr"),
|
||||||
TodoToken: token,
|
|
||||||
TodoList: list,
|
|
||||||
TodoTag: as.GetString("todotag"),
|
TodoTag: as.GetString("todotag"),
|
||||||
|
AccountsPattern: as.GetString("accounts"),
|
||||||
|
AccountsAntiPattern: as.GetString("not-accounts"),
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
|
Uploader: ul,
|
||||||
|
Banks: map[Bank]bool{
|
||||||
|
BankOfAmerica: strings.Contains(strings.ToLower(as.GetString("banks")), strings.ToLower(BankOfAmerica.String())),
|
||||||
|
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())),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
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
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
75
go.mod
Normal file
75
go.mod
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
module local/email-xactions-to-todo
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require (
|
||||||
|
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
|
||||||
|
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
|
||||||
|
github.com/golang/protobuf v1.2.0 // indirect
|
||||||
|
github.com/golang/snappy v0.0.1 // indirect
|
||||||
|
github.com/gomodule/redigo v1.8.5 // indirect
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
|
||||||
|
github.com/json-iterator/go v1.1.9 // indirect
|
||||||
|
github.com/klauspost/compress v1.9.5 // indirect
|
||||||
|
github.com/klauspost/cpuid v1.2.3 // indirect
|
||||||
|
github.com/minio/md5-simd v1.1.0 // indirect
|
||||||
|
github.com/minio/minio-go/v6 v6.0.57 // indirect
|
||||||
|
github.com/minio/sha256-simd v0.1.1 // indirect
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
|
||||||
|
github.com/ncw/rclone v1.46.0 // indirect
|
||||||
|
github.com/ncw/swift v1.0.44 // indirect
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/rfjakob/eme v0.0.0-20171028163933-2222dbd4ba46 // indirect
|
||||||
|
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c // indirect
|
||||||
|
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
|
github.com/xdg-go/scram v1.0.2 // indirect
|
||||||
|
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||||
|
go.mongodb.org/mongo-driver v1.7.2 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 // indirect
|
||||||
|
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/text v0.3.7 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // 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
|
||||||
274
go.sum
Normal file
274
go.sum
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
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=
|
||||||
|
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=
|
||||||
|
github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0=
|
||||||
|
github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM=
|
||||||
|
github.com/anacrolix/dms v0.0.0-20180117034613-8af4925bffb5/go.mod h1:DGqLjaZ3ziKKNRt+U5Q9PLWJ52Q/4rxfaaH/b3QYKaE=
|
||||||
|
github.com/aws/aws-sdk-go v1.15.81 h1:va7uoFaV9uKAtZ6BTmp1u7paoMsizYRRLvRuoC07nQ8=
|
||||||
|
github.com/aws/aws-sdk-go v1.15.81/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||||
|
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=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/djherbis/times v1.1.0/go.mod h1:CGMZlo255K5r4Yw0b9RRfFQpM2y7uOmxg4jm9HsaVf8=
|
||||||
|
github.com/dropbox/dropbox-sdk-go-unofficial v5.4.0+incompatible/go.mod h1:lr+LhMM3F6Y3lW1T9j2U5l7QeuWm87N9+PPXo3yH4qY=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/emersion/go-imap v1.2.0 h1:lyUQ3+EVM21/qbWE/4Ya5UG9r5+usDxlg4yfp3TgHFA=
|
||||||
|
github.com/emersion/go-imap v1.2.0/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
|
||||||
|
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
|
||||||
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
|
||||||
|
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
|
||||||
|
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
|
||||||
|
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||||
|
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||||
|
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
|
||||||
|
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||||
|
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||||
|
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
|
||||||
|
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
|
||||||
|
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
|
||||||
|
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
|
||||||
|
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
|
||||||
|
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
|
||||||
|
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
|
||||||
|
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
|
||||||
|
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
|
||||||
|
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
|
||||||
|
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
|
||||||
|
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
|
||||||
|
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
|
||||||
|
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
|
||||||
|
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
||||||
|
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
||||||
|
github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9/go.mod h1:GpOj6zuVBG3Inr9qjEnuVTgBlk2lZ1S9DcoFiXWyKss=
|
||||||
|
github.com/goftp/server v0.0.0-20190111142836-88de73f463af/go.mod h1:k/SS6VWkxY7dHPhoMQ8IdRu8L4lQtmGbhyXGg+vCnXE=
|
||||||
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
|
||||||
|
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||||
|
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jlaffaye/ftp v0.0.0-20181101112434-47f21d10f0ee/go.mod h1:lli8NYPQOFy3O++YmYbqVgOcQ1JPCwdOy+5zSjKJ9qY=
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
|
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||||
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||||
|
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
|
||||||
|
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||||
|
github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
|
||||||
|
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
|
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
|
||||||
|
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
||||||
|
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
|
||||||
|
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
|
||||||
|
github.com/minio/minio-go/v6 v6.0.57 h1:ixPkbKkyD7IhnluRgQpGSpHdpvNVaW6OD5R9IAO/9Tw=
|
||||||
|
github.com/minio/minio-go/v6 v6.0.57/go.mod h1:5+R/nM9Pwrh0vqF+HbYYDQ84wdUFPyXHkrdT4AIkifM=
|
||||||
|
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
|
||||||
|
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
|
github.com/ncw/go-acd v0.0.0-20171120105400-887eb06ab6a2/go.mod h1:MLIrzg7gp/kzVBxRE1olT7CWYMCklcUWU+ekoxOD9x0=
|
||||||
|
github.com/ncw/rclone v1.46.0 h1:5SY9lB6LIIXqwOaCWp5twUqNWgJVZMW+0D4rX90A2+E=
|
||||||
|
github.com/ncw/rclone v1.46.0/go.mod h1:+uFY4HNpat/yXXIEin5ETWXxIwEplC+eDe/vT8vlk1w=
|
||||||
|
github.com/ncw/swift v1.0.44 h1:EKvOTvUxElbpDWqxsyVaVGvc2IfuOqQnRmjnR2AGhQ4=
|
||||||
|
github.com/ncw/swift v1.0.44/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||||
|
github.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
|
||||||
|
github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd/go.mod h1:4soZNh0zW0LtYGdQ416i0jO0EIqMGcbtaspRS4BDvRQ=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||||
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||||
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
|
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||||
|
github.com/pengsrc/go-shared v0.2.0/go.mod h1:jVblp62SafmidSkvWrXyxAme3gaTfEtWwRPGz5cpvHg=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/sftp v1.8.3/go.mod h1:NxmoDg/QLVWluQDUYG7XBZTLUpKeFa8e3aMf1BfjyHk=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rfjakob/eme v0.0.0-20171028163933-2222dbd4ba46 h1:w2CpS5muK+jyydnmlkqpAhzKmHmMBzBkfYUDjQNS1Dk=
|
||||||
|
github.com/rfjakob/eme v0.0.0-20171028163933-2222dbd4ba46/go.mod h1:U2bmx0hDj8EyDdcxmD5t3XHDnBFnyNNc22n1R4008eM=
|
||||||
|
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
|
github.com/sevlyar/go-daemon v0.1.4/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE=
|
||||||
|
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||||
|
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||||
|
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
|
||||||
|
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c h1:fyKiXKO1/I/B6Y2U8T7WdQGWzwehOuGIrljPtt7YTTI=
|
||||||
|
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
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=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||||
|
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||||
|
github.com/t3rm1n4l/go-mega v0.0.0-20190205172012-55a226cf41da/go.mod h1:XWL4vDyd3JKmJx+hZWUVgCNmmhZ2dTBcaNDcxH465s0=
|
||||||
|
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||||
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
|
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
|
||||||
|
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||||
|
github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
|
||||||
|
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
|
github.com/yunify/qingstor-sdk-go v2.2.15+incompatible/go.mod h1:w6wqLDQ5bBTzxGJ55581UrSwLrsTAsdo9N6yX/8d9RY=
|
||||||
|
go.mongodb.org/mongo-driver v1.7.2 h1:pFttQyIiJUHEn50YfZgC9ECjITMT44oiN36uArf/OFg=
|
||||||
|
go.mongodb.org/mongo-driver v1.7.2/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
||||||
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20181120190819-8f65e3013eba h1:YDkOrzGLLYybtuP6ZgebnO4OWYEYVMFSniazXsxrFN8=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20181120190819-8f65e3013eba/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
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/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/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=
|
||||||
|
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.0.0-20181120235003-faade3cbb06a h1:yMfgT1baklxtECXVk3UtZBELVXtVhDbK3/7xLFkFypw=
|
||||||
|
google.golang.org/api v0.0.0-20181120235003-faade3cbb06a/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
|
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
|
||||||
|
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||||
|
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
13
main.go
13
main.go
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"local/sandbox/contact/contact"
|
"local/sandbox/contact/contact"
|
||||||
"log"
|
"log"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -16,12 +17,22 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
patterns := regexp.MustCompile(config.AccountsPattern)
|
||||||
|
antipatterns := regexp.MustCompile(config.AccountsAntiPattern)
|
||||||
for email := range emails {
|
for email := range emails {
|
||||||
transactions, err := Scrape(email)
|
transactions, err := Scrape(email, config.Banks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("failed to scrape email:", err)
|
log.Println("failed to scrape email:", err)
|
||||||
}
|
}
|
||||||
for _, transaction := range transactions {
|
for _, transaction := range transactions {
|
||||||
|
if !patterns.MatchString(transaction.Account) {
|
||||||
|
log.Printf("skipping unmatching account pattern %q vs %q", config.AccountsPattern, transaction.Account)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if antipatterns.MatchString(transaction.Account) {
|
||||||
|
log.Printf("skipping match account antipattern %q vs %q", config.AccountsAntiPattern, transaction.Account)
|
||||||
|
continue
|
||||||
|
}
|
||||||
if _, err := config.Storage.Get(transaction.ID); err == nil {
|
if _, err := config.Storage.Get(transaction.ID); err == nil {
|
||||||
log.Println("skipping duplicate transaction:", transaction)
|
log.Println("skipping duplicate transaction:", transaction)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
209
scrape.go
209
scrape.go
@@ -7,6 +7,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,36 +15,52 @@ type scraper interface {
|
|||||||
scrape(*mail.Message) ([]*Transaction, error)
|
scrape(*mail.Message) ([]*Transaction, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type bankOfAmericaScraper struct{}
|
||||||
type chaseScraper struct{}
|
type chaseScraper struct{}
|
||||||
type citiScraper struct{}
|
type citiScraper struct{}
|
||||||
|
type uccuScraper struct{}
|
||||||
|
|
||||||
func Scrape(m *mail.Message) ([]*Transaction, error) {
|
func Scrape(m *mail.Message, banks map[Bank]bool) ([]*Transaction, error) {
|
||||||
scraper, err := buildScraper(m)
|
scraper, err := buildScraper(m, banks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return scraper.scrape(m)
|
return scraper.scrape(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildScraper(m *mail.Message) (scraper, error) {
|
func buildScraper(m *mail.Message, banks map[Bank]bool) (scraper, error) {
|
||||||
subject := fmt.Sprint(m.Header["Subject"])
|
subject := fmt.Sprint(m.Header["Subject"])
|
||||||
if !containsAny(subject, "transaction", "report", "Transaction") {
|
if !containsAny(subject, "transaction", "report", "Transaction", "payment", "Payment") {
|
||||||
return nil, errors.New("cannot build scraper for subject " + subject)
|
return nil, errors.New("cannot build scraper for subject " + subject)
|
||||||
}
|
}
|
||||||
from := fmt.Sprint(m.Header["From"])
|
from := fmt.Sprint(m.Header["From"])
|
||||||
if strings.Contains(from, "Chase") {
|
if strings.Contains(from, "Chase") && banks[Chase] {
|
||||||
return newChaseScraper(), nil
|
return newChaseScraper(), nil
|
||||||
}
|
}
|
||||||
if strings.Contains(from, "Citi") {
|
if strings.Contains(from, "Bank of America") && banks[BankOfAmerica] {
|
||||||
|
return newBankOfAmericaScraper(), nil
|
||||||
|
}
|
||||||
|
if strings.Contains(from, "Citi") && banks[Citi] {
|
||||||
return newCitiScraper(), nil
|
return newCitiScraper(), nil
|
||||||
}
|
}
|
||||||
|
if strings.Contains(from, "Notifications@uccu.com") && banks[UCCU] {
|
||||||
|
return newUCCUScraper(), nil
|
||||||
|
}
|
||||||
return nil, errors.New("unknown sender: " + from)
|
return nil, errors.New("unknown sender: " + from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newBankOfAmericaScraper() scraper {
|
||||||
|
return &bankOfAmericaScraper{}
|
||||||
|
}
|
||||||
|
|
||||||
func newChaseScraper() scraper {
|
func newChaseScraper() scraper {
|
||||||
return &chaseScraper{}
|
return &chaseScraper{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newUCCUScraper() scraper {
|
||||||
|
return &uccuScraper{}
|
||||||
|
}
|
||||||
|
|
||||||
func newCitiScraper() scraper {
|
func newCitiScraper() scraper {
|
||||||
return &citiScraper{}
|
return &citiScraper{}
|
||||||
}
|
}
|
||||||
@@ -58,27 +75,91 @@ func containsAny(a string, b ...string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *chaseScraper) scrape(m *mail.Message) ([]*Transaction, error) {
|
func (c *chaseScraper) scrape(m *mail.Message) ([]*Transaction, error) {
|
||||||
|
transactions, err := c.scrape2021(m)
|
||||||
|
if err == nil && len(transactions) > 0 {
|
||||||
|
return transactions, err
|
||||||
|
}
|
||||||
|
return c.scrape2020(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chaseScraper) scrape2021(m *mail.Message) ([]*Transaction, error) {
|
||||||
|
if t, err := c.scrape2021Payment(m); err == nil {
|
||||||
|
return t, err
|
||||||
|
}
|
||||||
|
return c.scrape2021Charge(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chaseScraper) scrape2021Payment(m *mail.Message) ([]*Transaction, error) {
|
||||||
|
re := regexp.MustCompile(`^We've received your .* payment$`)
|
||||||
|
if !re.Match([]byte(m.Header["Subject"][0])) {
|
||||||
|
return nil, errors.New("no match subject search")
|
||||||
|
}
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(m.Body)
|
b, err := ioutil.ReadAll(m.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
regexp := regexp.MustCompile(`A charge of \([^)]*\) (?P<amount>[\d\.]+) at (?P<account>.*) has been authorized`)
|
|
||||||
matches := regexp.FindSubmatch(b)
|
re = regexp.MustCompile(`\$[0-9,]+\.[0-9]{2}`)
|
||||||
|
amount := "-" + strings.TrimLeft(string(re.Find(b)), "$")
|
||||||
|
amount = strings.TrimLeft(string(re.Find(b)), "$")
|
||||||
|
amount = strings.ReplaceAll(amount, ",", "")
|
||||||
|
|
||||||
|
vendor := "Payment"
|
||||||
|
|
||||||
|
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) scrape2021Charge(m *mail.Message) ([]*Transaction, error) {
|
||||||
|
re := regexp.MustCompile(`^Your \$(?P<amount>[,0-9\.]*) transaction with (?P<vendor>.*)$`)
|
||||||
|
matches := re.FindSubmatch([]byte(m.Header["Subject"][0]))
|
||||||
|
if len(matches) < 1 {
|
||||||
|
return nil, errors.New("no match subject search")
|
||||||
|
}
|
||||||
|
amount := string(matches[1])
|
||||||
|
amount = strings.ReplaceAll(amount, ",", "")
|
||||||
|
vendor := string(matches[2])
|
||||||
|
|
||||||
|
b, _ := ioutil.ReadAll(m.Body)
|
||||||
|
re = regexp.MustCompile(`\(\.\.\.[0-9]{4}\)`)
|
||||||
|
match := re.Find(b)
|
||||||
|
re = regexp.MustCompile(`[0-9]{4}`)
|
||||||
|
account := string(re.Find(match))
|
||||||
|
|
||||||
|
return []*Transaction{NewTransaction(account, amount, vendor, fmt.Sprint(m.Header["Date"]), Chase)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chaseScraper) scrape2020(m *mail.Message) ([]*Transaction, error) {
|
||||||
|
b, err := ioutil.ReadAll(m.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
re := regexp.MustCompile(`A charge of \([^)]*\) (?P<amount>[\d\.]+) at (?P<vendor>.*) has been authorized`)
|
||||||
|
matches := re.FindSubmatch(b)
|
||||||
if len(matches) < 2 {
|
if len(matches) < 2 {
|
||||||
return nil, fmt.Errorf("no full matches found")
|
return nil, fmt.Errorf("no full matches found")
|
||||||
}
|
}
|
||||||
results := make(map[string][]string)
|
results := make(map[string][]string)
|
||||||
for i, name := range regexp.SubexpNames() {
|
for i, name := range re.SubexpNames() {
|
||||||
if i != 0 && name != "" {
|
if i != 0 && name != "" {
|
||||||
results[name] = append(results[name], string(matches[i]))
|
results[name] = append(results[name], string(matches[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(results) != 2 || len(results["amount"]) != len(results["account"]) {
|
if len(results) != 2 || len(results["amount"]) != len(results["vendor"]) {
|
||||||
return nil, fmt.Errorf("unexpected matches found looking for transactions: %+v", results)
|
return nil, fmt.Errorf("unexpected matches found looking for transactions: %+v", results)
|
||||||
}
|
}
|
||||||
|
re = regexp.MustCompile(`account ending in (?P<account>[0-9]{4})\.`)
|
||||||
|
match := re.Find(b)
|
||||||
|
re = regexp.MustCompile(`[0-9]{4}`)
|
||||||
|
account := string(re.Find(match))
|
||||||
transactions := make([]*Transaction, len(results["amount"]))
|
transactions := make([]*Transaction, len(results["amount"]))
|
||||||
for i := range results["amount"] {
|
for i := range results["amount"] {
|
||||||
transactions[i] = NewTransaction(results["amount"][i], results["account"][i], fmt.Sprint(m.Header["Date"]), Chase)
|
transactions[i] = NewTransaction(account, results["amount"][i], results["vendor"][i], fmt.Sprint(m.Header["Date"]), Chase)
|
||||||
}
|
}
|
||||||
return transactions, nil
|
return transactions, nil
|
||||||
}
|
}
|
||||||
@@ -89,58 +170,102 @@ func (c *citiScraper) scrape(m *mail.Message) ([]*Transaction, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
re := regexp.MustCompile(`Citi Alert: A \$[0-9][0-9]*\.[0-9][0-9] transaction was made at .* on card ending in`)
|
re := regexp.MustCompile(`Citi Alert: A \$[,0-9][,0-9]*\.[0-9][0-9] transaction was made at .* on card ending in`)
|
||||||
match := re.Find(b)
|
match := re.Find(b)
|
||||||
if len(match) == 0 {
|
if len(match) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rePrice := regexp.MustCompile(`[0-9][0-9]*\.[0-9][0-9]`)
|
rePrice := regexp.MustCompile(`[0-9][0-9,]*\.[0-9][0-9]`)
|
||||||
price := rePrice.Find(match)
|
price := rePrice.Find(match)
|
||||||
|
price = []byte(strings.ReplaceAll(string(price), ",", ""))
|
||||||
|
|
||||||
vendor := bytes.Split(bytes.Split(match, []byte(" on card ending in"))[0], []byte("transaction was made at "))[1]
|
vendor := bytes.Split(bytes.Split(match, []byte(" on card ending in"))[0], []byte("transaction was made at "))[1]
|
||||||
|
|
||||||
transaction := NewTransaction(string(price), string(vendor), date, Citi)
|
transaction := NewTransaction(Citi.String(), string(price), string(vendor), date, Citi)
|
||||||
|
|
||||||
return []*Transaction{transaction}, nil
|
return []*Transaction{transaction}, nil
|
||||||
//Citi Alert: A $598.14 transaction was made at REMIX MUSIC SPRINGDA on card ending in 3837
|
}
|
||||||
/*
|
|
||||||
|
func (c *uccuScraper) scrape(m *mail.Message) ([]*Transaction, error) {
|
||||||
b, err := ioutil.ReadAll(m.Body)
|
b, err := ioutil.ReadAll(m.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
targetLineRegexp := regexp.MustCompile(`Account #: XXXX[0-9]{4} .*`)
|
regexp := regexp.MustCompile(`\$([0-9]+,?)+\.[0-9][0-9]`)
|
||||||
targetMatches := targetLineRegexp.FindAll(b, -1)
|
match := regexp.Find(b)
|
||||||
if len(targetMatches) == 0 {
|
if len(match) == 0 {
|
||||||
return nil, errors.New("no lines with transactions found")
|
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(UCCU.String(), fmt.Sprintf("%.2f", f), "?", fmt.Sprint(m.Header["Date"]), UCCU)
|
||||||
|
return []*Transaction{transaction}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
results := make(map[string][]string)
|
func (c *bankOfAmericaScraper) scrape(m *mail.Message) ([]*Transaction, error) {
|
||||||
for _, b := range targetMatches {
|
subject := fmt.Sprint(m.Header["Subject"])
|
||||||
// Account #: XXXX3837 $137.87 at AMZN Mktp US Amzn.com/bill WA on 04/03/2020, 09:05 PM ET
|
if strings.Contains(subject, "Credit card transaction") {
|
||||||
regexp := regexp.MustCompile(`Account #: XXXX[0-9]{4} \$(?P<amount>[0-9]+\.[0-9]*) at (?P<account>[^,]*)`)
|
return c.scrapeCharge(m)
|
||||||
matches := regexp.FindSubmatch(b)
|
|
||||||
if len(matches) < 2 {
|
|
||||||
return nil, fmt.Errorf("no full matches found: %s", b)
|
|
||||||
}
|
}
|
||||||
for i, name := range regexp.SubexpNames() {
|
if strings.Contains(subject, "Credit Card Payment") {
|
||||||
if i != 0 && name != "" {
|
return c.scrapePayment(m)
|
||||||
if name == "account" {
|
|
||||||
matches[i] = bytes.Split(matches[i], []byte(" on "))[0]
|
|
||||||
}
|
|
||||||
results[name] = append(results[name], string(matches[i]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(results) != 2 || len(results["amount"]) != len(results["account"]) {
|
|
||||||
return nil, fmt.Errorf("unexpected matches found looking for transactions: %+v", results)
|
|
||||||
}
|
}
|
||||||
|
return nil, errors.New("not impl")
|
||||||
}
|
}
|
||||||
|
|
||||||
transactions := make([]*Transaction, len(results["amount"]))
|
func (c *bankOfAmericaScraper) scrapeCharge(m *mail.Message) ([]*Transaction, error) {
|
||||||
for i := range results["amount"] {
|
b, err := ioutil.ReadAll(m.Body)
|
||||||
transactions[i] = NewTransaction(results["amount"][i], results["account"][i], fmt.Sprint(m.Header["Date"]), Citi)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return transactions, nil
|
amount := c.findFloatAfter(b, "Amount: $")
|
||||||
*/
|
acc := string(c.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) scrapePayment(m *mail.Message) ([]*Transaction, error) {
|
||||||
|
b, err := ioutil.ReadAll(m.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
amount := "-" + c.findFloatAfter(b, "Payment: $")
|
||||||
|
acc := "Payment"
|
||||||
|
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) findFloatAfter(b []byte, prefix string) string {
|
||||||
|
amount := string(c.findLineAfter(b, prefix))
|
||||||
|
words := strings.Split(amount, " ")
|
||||||
|
lastword := words[len(words)-1]
|
||||||
|
escapedfloat := strings.TrimPrefix(lastword, "$")
|
||||||
|
fixEscape := strings.ReplaceAll(escapedfloat, "=2E", ".")
|
||||||
|
amount = fixEscape
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bankOfAmericaScraper) 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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
215
scrape_test.go
Normal file
215
scrape_test.go
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/mail"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScrapeChase202112Payment(t *testing.T) {
|
||||||
|
b, err := ioutil.ReadFile("./testdata/chase.2021.12.payment.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
message := &mail.Message{
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Subject": []string{"We've received your Chase Freedom Unlimited payment"},
|
||||||
|
},
|
||||||
|
Body: bytes.NewReader(b),
|
||||||
|
}
|
||||||
|
|
||||||
|
chase := &chaseScraper{}
|
||||||
|
|
||||||
|
gots, err := chase.scrape2021(message)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(gots) != 1 {
|
||||||
|
t.Fatal(gots)
|
||||||
|
}
|
||||||
|
got := gots[0]
|
||||||
|
|
||||||
|
if got.Account != "1049" {
|
||||||
|
t.Fatalf("bad account: %v: %+v", got.Account, got)
|
||||||
|
}
|
||||||
|
if got.Amount != "1750.00" {
|
||||||
|
t.Fatalf("bad amount: %v: %+v", got.Amount, got)
|
||||||
|
}
|
||||||
|
if got.Vendor != "Payment" {
|
||||||
|
t.Fatalf("bad vendor: %v: %+v", got.Vendor, got)
|
||||||
|
}
|
||||||
|
t.Logf("%+v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScrapeChase2021Payment(t *testing.T) {
|
||||||
|
b, err := ioutil.ReadFile("./testdata/chase.2021.payment.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
message := &mail.Message{
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Subject": []string{"We've received your AARP from Chase payment"},
|
||||||
|
},
|
||||||
|
Body: bytes.NewReader(b),
|
||||||
|
}
|
||||||
|
|
||||||
|
chase := &chaseScraper{}
|
||||||
|
|
||||||
|
gots, err := chase.scrape2021(message)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(gots) != 1 {
|
||||||
|
t.Fatal(gots)
|
||||||
|
}
|
||||||
|
got := gots[0]
|
||||||
|
|
||||||
|
if got.Account != "8824" {
|
||||||
|
t.Fatalf("bad account: %v: %+v", got.Account, got)
|
||||||
|
}
|
||||||
|
if got.Amount != "100.00" {
|
||||||
|
t.Fatalf("bad amount: %v: %+v", got.Amount, got)
|
||||||
|
}
|
||||||
|
if got.Vendor != "Payment" {
|
||||||
|
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 {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
message := &mail.Message{
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Subject": []string{"Your $38.84 transaction with TARGET T-1754"},
|
||||||
|
},
|
||||||
|
Body: bytes.NewReader(b),
|
||||||
|
}
|
||||||
|
|
||||||
|
chase := &chaseScraper{}
|
||||||
|
|
||||||
|
gots, err := chase.scrape2021(message)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(gots) != 1 {
|
||||||
|
t.Fatal(gots)
|
||||||
|
}
|
||||||
|
got := gots[0]
|
||||||
|
|
||||||
|
if got.Account != "8824" {
|
||||||
|
t.Fatalf("bad account: %v: %+v", got.Account, got)
|
||||||
|
}
|
||||||
|
if got.Amount != "38.84" {
|
||||||
|
t.Fatalf("bad amount: %v: %+v", got.Amount, got)
|
||||||
|
}
|
||||||
|
if got.Vendor != "TARGET T-1754" {
|
||||||
|
t.Fatalf("bad vendor: %v: %+v", got.Vendor, got)
|
||||||
|
}
|
||||||
|
t.Logf("%+v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScrapeChase2020(t *testing.T) {
|
||||||
|
b, err := ioutil.ReadFile("./testdata/chase.2020.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
message := &mail.Message{
|
||||||
|
Body: bytes.NewReader(b),
|
||||||
|
}
|
||||||
|
|
||||||
|
chase := &chaseScraper{}
|
||||||
|
|
||||||
|
gots, err := chase.scrape2020(message)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(gots) != 1 {
|
||||||
|
t.Fatal(gots)
|
||||||
|
}
|
||||||
|
got := gots[0]
|
||||||
|
|
||||||
|
if got.Account != "8824" {
|
||||||
|
t.Fatalf("bad account: %v: %+v", got.Account, got)
|
||||||
|
}
|
||||||
|
if got.Amount != "16.08" {
|
||||||
|
t.Fatalf("bad amount: %v: %+v", got.Amount, got)
|
||||||
|
}
|
||||||
|
if got.Vendor != "PAYPAL *BLIZZARDENT" {
|
||||||
|
t.Fatalf("bad vendor: %q: %+v", got.Vendor, got)
|
||||||
|
}
|
||||||
|
t.Logf("%+v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScrapeBofAPayment(t *testing.T) {
|
||||||
|
b, err := ioutil.ReadFile("./testdata/bofa.payment.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
message := &mail.Message{
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Subject": []string{"Confirmation: Thanks for Your Credit Card Payment"},
|
||||||
|
},
|
||||||
|
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: "-251.74",
|
||||||
|
Vendor: "Payment",
|
||||||
|
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 {
|
||||||
|
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: "75.08",
|
||||||
|
Vendor: "PAYPAL GIBBDOGENTE MA",
|
||||||
|
Date: "[]",
|
||||||
|
Account: BankOfAmerica.String(),
|
||||||
|
}
|
||||||
|
if *got != want {
|
||||||
|
t.Fatalf("want:\n\t%+v, got\n\t%+v", want, *got)
|
||||||
|
}
|
||||||
|
}
|
||||||
46
testdata/bofa.charge.txt
vendored
Normal file
46
testdata/bofa.charge.txt
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
|
||||||
|
Credit card transaction exceeds alert limit you set
|
||||||
|
|
||||||
|
National Education Association World Mas ending in 7522
|
||||||
|
|
||||||
|
Amount: $75=2E08
|
||||||
|
Date: September 05, 2021
|
||||||
|
Where: PAYPAL GIBBDOGENTE MA
|
||||||
|
|
||||||
|
View details by going to
|
||||||
|
https://www=2Ebankofamerica=2Ecom/deeplink/redirect=2Ego?target=3Dbofasigni=
|
||||||
|
n&screen=3DAccounts:Home&version=3D7=2E0=2E0
|
||||||
|
|
||||||
|
If you made this purchase or payment but don=27t recognize the amount,
|
||||||
|
wait until the final purchase amount has posted before filing a dispute
|
||||||
|
claim=2E
|
||||||
|
|
||||||
|
If you don=27t recognize this activity, please contact us at the number
|
||||||
|
on the back of your card=2E
|
||||||
|
|
||||||
|
Did you know?
|
||||||
|
You can choose how you get alerts from us including text messages and
|
||||||
|
mobile notifications=2E Go to Alert Settings at
|
||||||
|
https://www=2Ebankofamerica=2Ecom/deeplink/redirect=2Ego?target=3Dalerts_se=
|
||||||
|
ttings&screen=3DAlerts:Home&gotoSetting=3Dtrue&version=3D7=2E1=2E0
|
||||||
|
|
||||||
|
|
||||||
|
We'll never ask for your personal information such as SSN or ATM PIN in
|
||||||
|
email messages=2E If you get an email that looks suspicious or you are not =
|
||||||
|
|
||||||
|
the intended recipient of this email, don't click on any links=2E Instead, =
|
||||||
|
|
||||||
|
forward to abuse@bankofamerica=2Ecom then delete it=2E
|
||||||
|
|
||||||
|
Please don't reply to this automatically generated service email=2E
|
||||||
|
Read our Privacy Notice https://www=2Ebankofamerica=2Ecom/privacy/consumer-=
|
||||||
|
privacy-notice=2Ego
|
||||||
|
Equal Housing Lender: https://www=2Ebankofamerica=2Ecom/help/equalhousing=
|
||||||
|
=2Ecfm
|
||||||
|
Bank of America, N=2EA=2E Member FDIC
|
||||||
|
(C) 2021 Bank of America Corporation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=20
|
||||||
88
testdata/bofa.payment.txt
vendored
Normal file
88
testdata/bofa.payment.txt
vendored
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
Delivered-To: breellocaldev@gmail.com
|
||||||
|
Received: by 2002:a4f:f556:0:0:0:0:0 with SMTP id s22csp88584ivo;
|
||||||
|
Fri, 10 Sep 2021 03:09:37 -0700 (PDT)
|
||||||
|
X-Google-Smtp-Source: ABdhPJy5KOCCQILLhifnSPNnjMikzSGgZX0rSLKqSzdRkpjWyZAZB7Ml4gWSWxuiMPuJMUFQZPnF
|
||||||
|
X-Received: by 2002:a05:620a:15e8:: with SMTP id p8mr6940748qkm.27.1631268577237;
|
||||||
|
Fri, 10 Sep 2021 03:09:37 -0700 (PDT)
|
||||||
|
ARC-Seal: i=1; a=rsa-sha256; t=1631268577; cv=none;
|
||||||
|
d=google.com; s=arc-20160816;
|
||||||
|
b=fRiwZLXmORGlNgDHdYZ3g7DbcggjP3zVkUX1gIVHo3z/c4SLgmwu1FVu4qiUr7M2+6
|
||||||
|
9Ez7xjq0rG3JCLUk77q4I2MJW9pWL5LZdcMtoP9bbu5KYoZ0JwLQldFuzUOFp1qyLICc
|
||||||
|
pegPsozU1lTG3WSr2fxAi4kGgvr1PQUGd5EaeztK+u7I9SNyyOdXsgavbx0Dr+XLFAyG
|
||||||
|
eGo1WzDGy7NG8TMstFxQu+cfZiWKKtEeTFUGEjcXAUxCm/jvqK8MT1fPTwac9c66cCls
|
||||||
|
7bvBpXlmoSEmTz6NseH0DblgWZsdmGgkYZhIUS2cJaqIhGJUFxNqbMQswEXT29LrmnbG
|
||||||
|
zz0w==
|
||||||
|
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
|
||||||
|
h=content-transfer-encoding:feedback-id:message-id:list-id:reply-to
|
||||||
|
:mime-version:date:subject:to:from:dkim-signature;
|
||||||
|
bh=RNAxijcTyDxYmdzbEHMEKXkbCbc/Wnnsez0HenNHbUA=;
|
||||||
|
b=KtJdTZ9LFxfJTwq0gldceho7ktEybby+DLrKgjgjI2yUlaS4u0IJC2nDvkA21HjV1w
|
||||||
|
R2HMT4UITrQVoi9xa/fTsbdVIfEDjBl2rdbvO+gOthaonsCvxAsiQGFRPhmKHlbb1IiE
|
||||||
|
9GbgjYaf4qEZCO4nQUnMKTQPK+TalO1pX3UNPHf2/KTeAuXCUrySVKgierhZIxnkS3WQ
|
||||||
|
/GUsV4gDHMhmRKEQF8yxgLv3podfCm63iOBgOZ/CCITcKkQTFUByLQ2HdAmLo9TUXHNM
|
||||||
|
sJKv6pK7e05Dxp4ZeNKlm15c5xSo3OXoRqupvsXYCbzjvR2moBrVRSB7iwZHGL45zQXQ
|
||||||
|
OVug==
|
||||||
|
ARC-Authentication-Results: i=1; mx.google.com;
|
||||||
|
dkim=pass header.i=@ealerts.bankofamerica.com header.s=200608 header.b=niVgyX92;
|
||||||
|
spf=pass (google.com: domain of bounce-29_html-819616257-1667962-73720-2833596@bounce.ealerts.bankofamerica.com designates 68.232.194.2 as permitted sender) smtp.mailfrom=bounce-29_HTML-819616257-1667962-73720-2833596@bounce.ealerts.bankofamerica.com;
|
||||||
|
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=bankofamerica.com
|
||||||
|
Return-Path: <bounce-29_HTML-819616257-1667962-73720-2833596@bounce.ealerts.bankofamerica.com>
|
||||||
|
Received: from mta5.ealerts.bankofamerica.com (mta5.ealerts.bankofamerica.com. [68.232.194.2])
|
||||||
|
by mx.google.com with ESMTPS id a7si3031949qtn.85.2021.09.10.03.09.36
|
||||||
|
for <breellocaldev@gmail.com>
|
||||||
|
(version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128);
|
||||||
|
Fri, 10 Sep 2021 03:09:37 -0700 (PDT)
|
||||||
|
Received-SPF: pass (google.com: domain of bounce-29_html-819616257-1667962-73720-2833596@bounce.ealerts.bankofamerica.com designates 68.232.194.2 as permitted sender) client-ip=68.232.194.2;
|
||||||
|
Authentication-Results: mx.google.com;
|
||||||
|
dkim=pass header.i=@ealerts.bankofamerica.com header.s=200608 header.b=niVgyX92;
|
||||||
|
spf=pass (google.com: domain of bounce-29_html-819616257-1667962-73720-2833596@bounce.ealerts.bankofamerica.com designates 68.232.194.2 as permitted sender) smtp.mailfrom=bounce-29_HTML-819616257-1667962-73720-2833596@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:MIME-Version:Reply-To:List-ID:X-CSA-Complaints:
|
||||||
|
Message-ID:Content-Type:Content-Transfer-Encoding;
|
||||||
|
i=onlinebanking@ealerts.bankofamerica.com;
|
||||||
|
bh=RNAxijcTyDxYmdzbEHMEKXkbCbc/Wnnsez0HenNHbUA=;
|
||||||
|
b=niVgyX923ETmQwhHEaUcs91DEv/nznIH0c7CyqIgwu0h5KtgJZIKbkIw3inZNwLL9hF+/7lfV57q
|
||||||
|
ZYXmHQVV1aXIqJLQDD5RlAq2YZvghgLdglRBbq5N9cCDTsKIA3VlrKicwN+sAwDq2JlfBv4I8rzw
|
||||||
|
Vcmfup5eqf0vJnn6k9c=
|
||||||
|
Received: by mta5.ealerts.bankofamerica.com id h7cne22fmd4j for <breellocaldev@gmail.com>; Fri, 10 Sep 2021 10:09:35 +0000 (envelope-from <bounce-29_HTML-819616257-1667962-73720-2833596@bounce.ealerts.bankofamerica.com>)
|
||||||
|
From: "Bank of America" <onlinebanking@ealerts.bankofamerica.com>
|
||||||
|
To: <breellocaldev@gmail.com>
|
||||||
|
Subject: Confirmation: Thanks for Your Credit Card Payment
|
||||||
|
Date: Fri, 10 Sep 2021 04:09:32 -0600
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Reply-To: "Bank of America" <reply-fe8a157673630d7b77-29_HTML-819616257-73720-2833596@ealerts.bankofamerica.com>
|
||||||
|
List-ID: <71108.xt.local>
|
||||||
|
X-CSA-Complaints: whitelistcomplaints@eco.de
|
||||||
|
x-job: 73720_1667962
|
||||||
|
Message-ID: <3fb2377b-699b-411c-9ea5-a3b8817aa853@las1s04mta1081.xt.local>
|
||||||
|
Feedback-ID: 73720:1667962:68.232.194.2:sfmktgcld
|
||||||
|
Content-Type: text/plain;
|
||||||
|
charset="iso-8859-1"
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
|
||||||
|
Hi, BEL, we've received your credit card payment
|
||||||
|
|
||||||
|
Payment: $251.74
|
||||||
|
To: National Education Association World Mas ending in - 7522
|
||||||
|
Date posted: September 09, 2021
|
||||||
|
|
||||||
|
Sign in to bankofamerica.com to view your account details.
|
||||||
|
|
||||||
|
Thank you for being our customer.
|
||||||
|
|
||||||
|
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 abuse@bankofamerica.com then delete it.
|
||||||
|
|
||||||
|
Please don't reply to this automatically generated service email.
|
||||||
|
Read our Privacy Notice https://www.bankofamerica.com/privacy/consumer-privacy-notice.go
|
||||||
|
Equal Housing Lender: https://www.bankofamerica.com/help/equalhousing.cfm
|
||||||
|
Bank of America, N.A. Member FDIC
|
||||||
|
(C) 2019 Bank of America Corporation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
10
testdata/chase.2020.txt
vendored
Normal file
10
testdata/chase.2020.txt
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
This is an Alert to help you manage your credit card account ending in 8824.
|
||||||
|
|
||||||
|
As you requested, we are notifying you of any charges over the amount of ($USD) 0.00, as specified in your Alert settings.
|
||||||
|
A charge of ($USD) 16.08 at PAYPAL *BLIZZARDENT has been authorized on Jul 6, 2021 at 6:21 PM ET.
|
||||||
|
|
||||||
|
Do not reply to this Alert.
|
||||||
|
|
||||||
|
If you have questions, please call the number on the back of your credit card, or send a secure message from your Inbox on www.chase.com.
|
||||||
|
|
||||||
|
To see all of the Alerts available to you, or to manage your Alert settings, please log on to www.chase.com.
|
||||||
408
testdata/chase.2021.12.payment.txt
vendored
Normal file
408
testdata/chase.2021.12.payment.txt
vendored
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
<!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 payment has been applied to your account.</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;">This payment has been applied to your account.</div>
|
||||||
|
|
||||||
|
<!-- Insert ‌ after hidden preview tex=
|
||||||
|
t -->
|
||||||
|
|
||||||
|
<div style=3D"display: none; max-height: 0px; ove=
|
||||||
|
rflow: hidden;"> ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; </div>
|
||||||
|
<!-- End hidden preview text -->
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding-left: 30px; background-color: #ffffff;" class=3D"moPad"><table widt=
|
||||||
|
h=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 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:#000000; background-color:#24e16b; bo=
|
||||||
|
rder-radius:20px; -moz-border-radius: 20px; -webkit-border-radius:20px; whi=
|
||||||
|
te-space: nowrap;" class=3D"font14">Payment received</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
background-color: #ffffff;"><table width=3D"100%" cellpadding=3D"0" cellsp=
|
||||||
|
acing=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/41473417013.png" width=3D"57" height=3D"auto" alt=3D"" bo=
|
||||||
|
rder=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">=
|
||||||
|
We've received your credit card payment</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 20px 30px; font-family:Arial, Helvetica, sans-serif; font-size=
|
||||||
|
:16px; color:#414042;" class=3D"moPad">This payment has been applied to you=
|
||||||
|
r account.</td>
|
||||||
|
</tr>
|
||||||
|
<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 (...1049)</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">Posted 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">Dec 9, 2=
|
||||||
|
021</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">Payment 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;">=
|
||||||
|
$1,750.00</a></span></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng:40px 150px 40px 30px; font-family:Arial, Helvetica, sans-serif; font-siz=
|
||||||
|
e:16px; color:#414042;" class=3D"moPad">Find <a style=3D"text-decoration: u=
|
||||||
|
nderline; color:#0060F0;" href=3D"https://www.chase.com/personal/credit-car=
|
||||||
|
ds/login-epay" rel=3D"noopener noreferrer" target=3D"_blank">more informat=
|
||||||
|
ion</a> about the credit card payments process.</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"100%" 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 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: 200px;"><a href=3D"https://www.chase.com/per=
|
||||||
|
sonal/mobile-online-banking/payment-activity" target=3D"_blank" style=3D"co=
|
||||||
|
lor: #fffffe; text-decoration:none;">See payment activity</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 Mobile<span style=3D"font-size:70%; line-height:0; v=
|
||||||
|
ertical-align:3px; text-decoration: none;">®</span> app</a> or <a style=
|
||||||
|
=3D"text-decoration: underline; color:#0060F0;" href=3D"https://secure.chas=
|
||||||
|
e.com/web/auth/nav?navKey=3DrequestDashboard" rel=3D"noopener noreferrer" =
|
||||||
|
target=3D"_blank">chase.com</a>. </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng: 0px 0px; background-color: #F6F6F6;"><table width=3D"100%" cellpadding=
|
||||||
|
=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding:20px 30px 60px; font-family:Arial, Helvetica, sans-serif; font-siz=
|
||||||
|
e:12px; color:#717171;" class=3D"moPad font14"><span role=3D"heading" style=
|
||||||
|
=3D"text-transform: uppercase; font-weight: bold;">About this message</span=
|
||||||
|
><br />
|
||||||
|
<br />
|
||||||
|
Chase Mobile<span style=3D"font-size:70%=
|
||||||
|
; line-height:0; vertical-align:3px;">®</span> 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 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 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 />
|
||||||
|
© 2021 JPMorgan Chase & Co. </td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; padding:0px=
|
||||||
|
0px; width:12px;"><img src=3D"https://www.chase.com/content/dam/email/imag=
|
||||||
|
es/white-right.jpg" width=3D"12" height=3D"77" border=3D"0" style=3D"displa=
|
||||||
|
y:block;" alt=3D""/></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<!--End of Content -->
|
||||||
|
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
310
testdata/chase.2021.payment.txt
vendored
Normal file
310
testdata/chase.2021.payment.txt
vendored
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!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 payment has been applied to your account.</title>
|
||||||
|
</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;">This payment has been applied to your account.</div>
|
||||||
|
|
||||||
|
<!-- Insert ‌ after hidden preview tex=
|
||||||
|
t -->
|
||||||
|
|
||||||
|
<div style=3D"display: none; max-height: 0px; ove=
|
||||||
|
rflow: hidden;"> ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; </div>
|
||||||
|
<!-- End hidden preview text -->
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding-left: 30px; background-color: #ffffff;" class=3D"moPad"><table widt=
|
||||||
|
h=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 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:#000000; background-color:#24e16b; bo=
|
||||||
|
rder-radius:20px; -moz-border-radius: 20px; -webkit-border-radius:20px; whi=
|
||||||
|
te-space: nowrap;" class=3D"font14">Payment received</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
background-color: #ffffff;"><table width=3D"100%" cellpadding=3D"0" cellsp=
|
||||||
|
acing=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/aarp/41473417018.png" width=3D"57" height=3D"auto" alt=3D"" border=3D"0=
|
||||||
|
" style=3D"display:block;"/></td>
|
||||||
|
<td align=3D"left" style=3D"vertica=
|
||||||
|
l-align:top; padding:0px 50px 0px 0px; font-family:Arial, Helvetica, sans-s=
|
||||||
|
erif; font-size:30px; font-weight: bold; color:#414042;" class=3D"zeroPad">=
|
||||||
|
We've received your credit card payment</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 20px 30px; font-family:Arial, Helvetica, sans-serif; font-size=
|
||||||
|
:16px; color:#414042;" class=3D"moPad">This payment has been applied to you=
|
||||||
|
r account.</td>
|
||||||
|
</tr>
|
||||||
|
<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">AARP fro=
|
||||||
|
m Chase (...8824)</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding: 10px 0px;border-bottom: solid 1px #414042;"><table 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">Posted 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">Jul 30, =
|
||||||
|
2021</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">Payment 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;">=
|
||||||
|
$100.00</a></span></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng:40px 150px 40px 30px; font-family:Arial, Helvetica, sans-serif; font-siz=
|
||||||
|
e:16px; color:#414042;" class=3D"moPad">Find <a style=3D"text-decoration: u=
|
||||||
|
nderline; color:#0060F0;" href=3D"https://www.chase.com/personal/credit-car=
|
||||||
|
ds/login-epay" rel=3D"noopener noreferrer" target=3D"_blank">more informat=
|
||||||
|
ion</a> about the credit card payments process.</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"100%" 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 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: 200px;"><a href=3D"https://www.chase.com/per=
|
||||||
|
sonal/mobile-online-banking/payment-activity" target=3D"_blank" style=3D"co=
|
||||||
|
lor: #fffffe; text-decoration:none;">See payment activity</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 Mobile<span style=3D"font-size:70%; line-height:0; v=
|
||||||
|
ertical-align:3px; text-decoration: none;">®</span> app</a> or <a style=
|
||||||
|
=3D"text-decoration: underline; color:#0060F0;" href=3D"https://secure.chas=
|
||||||
|
e.com/web/auth/nav?navKey=3DrequestDashboard" rel=3D"noopener noreferrer" =
|
||||||
|
target=3D"_blank">chase.com</a>. </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng: 0px 0px; background-color: #F6F6F6;"><table width=3D"100%" cellpadding=
|
||||||
|
=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding:20px 30px 60px; font-family:Arial, Helvetica, sans-serif; font-siz=
|
||||||
|
e:12px; color:#717171;" class=3D"moPad font14"><span role=3D"heading" style=
|
||||||
|
=3D"text-transform: uppercase; font-weight: bold;">About this message</span=
|
||||||
|
><br />
|
||||||
|
<br />
|
||||||
|
Chase Mobile<span style=3D"font-size:70%=
|
||||||
|
; line-height:0; vertical-align:3px;">®</span> 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 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 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 />
|
||||||
|
© 2021 JPMorgan Chase & Co. </td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; padding:0px=
|
||||||
|
0px; width:12px;"><img src=3D"https://www.chase.com/content/dam/email/imag=
|
||||||
|
es/white-right.jpg" width=3D"12" height=3D"77" border=3D"0" style=3D"displa=
|
||||||
|
y:block;" alt=3D""/></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<!--End of Content -->
|
||||||
|
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
396
testdata/chase.2021.txt
vendored
Normal file
396
testdata/chase.2021.txt
vendored
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.=
|
||||||
|
w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns=3D"http://www.w3.org/1999/xhtml" lang=3D"en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3DUTF-8" />
|
||||||
|
<meta name=3D"viewport" content=3D"width=3Ddevice-width, initial-scale=3D1.=
|
||||||
|
0"/>
|
||||||
|
<title>This transaction is above the level you set, see more here.</title>
|
||||||
|
<style type=3D"text/css">
|
||||||
|
* {
|
||||||
|
=09line-height: normal !important;
|
||||||
|
}
|
||||||
|
strong {
|
||||||
|
=09font-weight: bold !important;
|
||||||
|
}
|
||||||
|
em {
|
||||||
|
=09font-style: italic !important;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
=09background-color: #d7dbe0 !important;
|
||||||
|
=09-webkit-text-size-adjust: none !important;
|
||||||
|
}
|
||||||
|
.ExternalClass * {
|
||||||
|
=09line-height: 112%
|
||||||
|
}
|
||||||
|
.ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass =
|
||||||
|
td {
|
||||||
|
=09line-height: 112%
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
=09-webkit-text-size-adjust: none;
|
||||||
|
}
|
||||||
|
a[href^=3Dtel] {
|
||||||
|
=09color: inherit;
|
||||||
|
=09text-decoration: none;
|
||||||
|
}
|
||||||
|
.applelinksgray41 a {
|
||||||
|
=09color: #414042 !important;
|
||||||
|
=09text-decoration: none;
|
||||||
|
}
|
||||||
|
.applelinksgray a {
|
||||||
|
=09color: #717171 !important;
|
||||||
|
=09text-decoration: none;
|
||||||
|
}
|
||||||
|
.wordBreak {
|
||||||
|
=09overflow-wrap: break-word;
|
||||||
|
=09word-wrap: break-word;
|
||||||
|
=09word-break: break-all;
|
||||||
|
=09word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
.fullWidth {
|
||||||
|
=09width: 100% !important;
|
||||||
|
=09min-width: 100% !important;
|
||||||
|
=09margin-left: auto !important;
|
||||||
|
=09margin-right: auto !important;
|
||||||
|
=09padding: 0px !important;
|
||||||
|
=09text-align: center !important;
|
||||||
|
}
|
||||||
|
.moPad {
|
||||||
|
=09padding-right: 20px !important;
|
||||||
|
=09padding-left: 20px !important;
|
||||||
|
}
|
||||||
|
.zeroPad {
|
||||||
|
=09padding-right: 0px !important;
|
||||||
|
=09padding-left: 0px !important;
|
||||||
|
}
|
||||||
|
.font14 {
|
||||||
|
=09font-size: 14px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print and (max-width: 800px) {
|
||||||
|
.fullWidth {
|
||||||
|
=09width: 100% !important;
|
||||||
|
=09min-width: 100% !important;
|
||||||
|
=09margin-left: auto !important;
|
||||||
|
=09margin-right: auto !important;
|
||||||
|
=09padding: 0px !important;
|
||||||
|
=09text-align: center !important;
|
||||||
|
}
|
||||||
|
.moPad {
|
||||||
|
=09padding-right: 20px !important;
|
||||||
|
=09padding-left: 20px !important;
|
||||||
|
}
|
||||||
|
.zeroPad {
|
||||||
|
=09padding-right: 0px !important;
|
||||||
|
=09padding-left: 0px !important;
|
||||||
|
}
|
||||||
|
.font14 {
|
||||||
|
=09font-size: 14px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style=3D"padding: 0px;margin: 0px; background-color:#d7dbe0;">
|
||||||
|
<table role=3D"presentation" align=3D"center" width=3D"100%" border=3D"0" c=
|
||||||
|
ellspacing=3D"0" cellpadding=3D"0" style=3D"min-width:800px; background-col=
|
||||||
|
or:#d7dbe0;" class=3D"fullWidth">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"center" style=3D"vertical-align:top; padding:0px 0px 20px =
|
||||||
|
0px; min-width:800px; background-color:#d7dbe0;" class=3D"fullWidth"><table=
|
||||||
|
role=3D"presentation" align=3D"center" width=3D"800" cellpadding=3D"0" cel=
|
||||||
|
lspacing=3D"0" border=3D"0" class=3D"fullWidth" style=3D"background-color:#=
|
||||||
|
FFFFFF;">
|
||||||
|
<!-- Start of Content -->
|
||||||
|
<tr>
|
||||||
|
<td align=3D"center" style=3D"vertical-align:top; padding: 23px 0=
|
||||||
|
px 0px;background-color: #005EB8;"><table role=3D"presentation" cellpadding=
|
||||||
|
=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"right" style=3D"vertical-align:bottom; padding=
|
||||||
|
:0px 0px; width:12px;"><img src=3D"https://www.chase.com/content/dam/email/=
|
||||||
|
images/blue-left.jpg" width=3D"12" height=3D"226" border=3D"0" style=3D"dis=
|
||||||
|
play:block;" alt=3D""/></td>
|
||||||
|
<td align=3D"center" style=3D"vertical-align:bottom; paddin=
|
||||||
|
g: 0px 0px 0px;width:616px; background-color: #FFFFFF;"><table role=3D"pres=
|
||||||
|
entation" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng: 0px 0px;"><table role=3D"presentation" width=3D"100%" cellpadding=3D"0"=
|
||||||
|
cellspacing=3D"0" border=3D"0">
|
||||||
|
<!-- Start hidden preview text -->
|
||||||
|
<div style=3D"display: none; max-height: 0px; ove=
|
||||||
|
rflow: hidden;">This transaction is above the level you set, see more here.=
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Insert ‌ after hidden preview tex=
|
||||||
|
t -->
|
||||||
|
|
||||||
|
<div style=3D"display: none; max-height: 0px; ove=
|
||||||
|
rflow: hidden;"> ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; ‌ ‌ ‌ ‌ ‌ ‌&=
|
||||||
|
nbsp;‌ ‌ ‌ ‌ ‌ ‌&nbs=
|
||||||
|
p;‌ ‌ ‌ ‌ ‌ ‌ &=
|
||||||
|
zwnj; ‌ ‌ ‌ ‌ ‌ &zwn=
|
||||||
|
j; </div>
|
||||||
|
<!-- End hidden preview text -->
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding-left: 30px;" class=3D"moPad"><table role=3D"presentation" width=3D"=
|
||||||
|
100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:bottom; padding:36px 0px 20px;"><img src=3D"https://www.chase.com/content=
|
||||||
|
/dam/email/images/chase-logo-h-rgb.png" width=3D"104" height=3D"20" border=
|
||||||
|
=3D"0" style=3D"display:block;" alt=3D"Chase Logo"/></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding: 20px 28px 0px;" class=3D"moPad"><table role=3D"presentation" alig=
|
||||||
|
n=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top;"><table role=3D"presentation" width=3D"100%" cellpadding=3D"0" cells=
|
||||||
|
pacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertica=
|
||||||
|
l-align:top; padding:5px 10px; font-family:Arial, Helvetica, sans-serif; fo=
|
||||||
|
nt-size:12px; font-weight:bold; color:#414042; background-color:#D7DBE0; bo=
|
||||||
|
rder-radius:20px; -moz-border-radius: 20px; -webkit-border-radius:20px; whi=
|
||||||
|
te-space: nowrap;" class=3D"font14">Transaction alert</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
"><table role=3D"presentation" width=3D"100%" cellpadding=3D"0" cellspacing=
|
||||||
|
=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top; padding: 20px 30px 28px;" class=3D"moPad"><table role=3D"presentatio=
|
||||||
|
n" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertica=
|
||||||
|
l-align:top; padding: 0px 20px 0px 0px;"><img src=3D"https://static.chasecd=
|
||||||
|
n.com/content/services/rendition/image.small.png/unified-assets/digital-car=
|
||||||
|
ds/aarp/41473417018.png" width=3D"57" height=3D"auto" alt=3D"" border=3D"0"=
|
||||||
|
style=3D"display:block;"/></td>
|
||||||
|
<td align=3D"left" style=3D"vertica=
|
||||||
|
l-align:top; padding:0px 50px 0px 0px; font-family:Arial, Helvetica, sans-s=
|
||||||
|
erif; font-size:30px; font-weight: bold; color:#414042;" class=3D"zeroPad">=
|
||||||
|
You made a $38.84 transaction</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:bottom; padding:=
|
||||||
|
0px 0px;width:12px; "><img src=3D"https://www.chase.com/content/dam/email/i=
|
||||||
|
mages/blue-right.jpg" width=3D"12" height=3D"226" border=3D"0" style=3D"dis=
|
||||||
|
play:block;" alt=3D""/></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"center" style=3D"vertical-align:top; padding: 0px 0p=
|
||||||
|
x 0px; background-color: #FFFFFF;"><table role=3D"presentation" cellpadding=
|
||||||
|
=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"right" style=3D"vertical-align:top; padding:0p=
|
||||||
|
x 0px; width:12px;"><img src=3D"https://www.chase.com/content/dam/email/ima=
|
||||||
|
ges/white-left.jpg" width=3D"12" height=3D"77" border=3D"0" style=3D"displa=
|
||||||
|
y:block;" alt=3D""/></td>
|
||||||
|
<td align=3D"center" style=3D"vertical-align:top; padding: =
|
||||||
|
0px 0px 0px;width:616px;"><table role=3D"presentation" width=3D"100%" cellp=
|
||||||
|
adding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng: 0px 150px 0px 30px;" class=3D"moPad"><table role=3D"presentation" width=
|
||||||
|
=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding: 10px 0px;border-bottom: solid 1px #414042;"><table role=3D"presen=
|
||||||
|
tation" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top; padding:0px 0px 0px 0px; font-family:Arial, Helvetica, sans-serif; f=
|
||||||
|
ont-size:16px; color:#414042;" class=3D"font14">Account</td>
|
||||||
|
<td align=3D"right" style=3D"vertical-ali=
|
||||||
|
gn:top; padding:0px 0px 0px 5px; font-family:Arial, Helvetica, sans-serif; =
|
||||||
|
font-size:16px; font-weight:bold; color:#414042;" class=3D"font14">AARP fro=
|
||||||
|
m Chase (...8824)</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding: 10px 0px;border-bottom: solid 1px #414042;"><table role=3D"presen=
|
||||||
|
tation" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top; padding:0px 0px 0px 0px; font-family:Arial, Helvetica, sans-serif; f=
|
||||||
|
ont-size:16px; color:#414042;" class=3D"font14">Date</td>
|
||||||
|
<td align=3D"right" style=3D"vertical-ali=
|
||||||
|
gn:top; padding:0px 0px 0px 5px; font-family:Arial, Helvetica, sans-serif; =
|
||||||
|
font-size:16px; font-weight:bold; color:#414042;" class=3D"font14"><span cl=
|
||||||
|
ass=3D"applelinksgray41"><a style=3D"color:#414042;text-decoration: none;">=
|
||||||
|
Jul 23, 2021 at 3:06 PM ET</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding: 10px 0px;border-bottom: solid 1px #414042;"><table role=3D"presen=
|
||||||
|
tation" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top; padding:0px 0px 0px 0px; font-family:Arial, Helvetica, sans-serif; f=
|
||||||
|
ont-size:16px; color:#414042;" class=3D"font14">Merchant</td>
|
||||||
|
<td align=3D"right" style=3D"vertical-ali=
|
||||||
|
gn:top; padding:0px 0px 0px 5px; font-family:Arial, Helvetica, sans-serif; =
|
||||||
|
font-size:16px; font-weight:bold; color:#414042;" class=3D"font14">TARGET T=
|
||||||
|
-1754</td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding: 10px 0px;border-bottom: solid 1px #414042;"><table role=3D"presen=
|
||||||
|
tation" width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-alig=
|
||||||
|
n:top; padding:0px 0px 0px 0px; font-family:Arial, Helvetica, sans-serif; f=
|
||||||
|
ont-size:16px; color:#414042;" class=3D"font14">Amount</td>
|
||||||
|
<td align=3D"right" style=3D"vertical-ali=
|
||||||
|
gn:top; padding:0px 0px 0px 5px; font-family:Arial, Helvetica, sans-serif; =
|
||||||
|
font-size:16px; font-weight:bold; color:#414042;" class=3D"font14">$38.84</=
|
||||||
|
td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; pad=
|
||||||
|
ding:40px 150px 40px 30px; font-family:Arial, Helvetica, sans-serif; font-s=
|
||||||
|
ize:16px; color:#414042;" class=3D"moPad">You are receiving this alert beca=
|
||||||
|
use your transaction was more than the $0.00 level you set. You can visit o=
|
||||||
|
ur <a style=3D"text-decoration: underline; color:#0060F0;" href=3D"https://=
|
||||||
|
www.chase.com/personal/credit-cards/card-resource-center" rel=3D"noopener n=
|
||||||
|
oreferrer" target=3D"_blank">Resource Center</a> anytime to help answer yo=
|
||||||
|
ur questions or manage your account.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"padding:0px; vertical-ali=
|
||||||
|
gn:top; padding: 0px 0px 30px 30px;" class=3D"moPad"><table role=3D"present=
|
||||||
|
ation" align=3D"left" cellpadding=3D"0" cellspacing=3D"0" border=3D"0" styl=
|
||||||
|
e=3D"vertical-align:top;">
|
||||||
|
<tr>
|
||||||
|
<td role=3D"button" align=3D"center" style=3D"b=
|
||||||
|
ackground-color:#0060f0; color: #fffffe; font-size: 16px; font-family: Aria=
|
||||||
|
l, Helvetica, sans-serif; padding: 10px 0px; border: 1px solid #0060f0; ver=
|
||||||
|
tical-align:top; border-radius:4px; -moz-border-radius: 4px; -webkit-border=
|
||||||
|
-radius:4px;width: 168px;"><a href=3D"https://secure.chase.com/web/auth/nav=
|
||||||
|
?navKey=3DrequestDashboard" target=3D"_blank" style=3D"color: #fffffe; text=
|
||||||
|
-decoration:none;">Review account</a></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng:0px 30px 20px; font-family:Arial, Helvetica, sans-serif; font-size:12px;=
|
||||||
|
color:#717171;" class=3D"moPad font14">Securely access your accounts with =
|
||||||
|
the <a style=3D"text-decoration: underline; color:#0060F0;" href=3D"https:/=
|
||||||
|
/www.chase.com/digital/mobile-banking" rel=3D"noopener noreferrer" target=
|
||||||
|
=3D"_blank">Chase Mobile<span style=3D"font-size:70%; line-height:0; v=
|
||||||
|
ertical-align:3px; text-decoration: none;">®</span> app</a> or <a style=
|
||||||
|
=3D"text-decoration: underline; color:#0060F0;" href=3D"https://secure.chas=
|
||||||
|
e.com/web/auth/nav?navKey=3DrequestDashboard" rel=3D"noopener noreferrer" =
|
||||||
|
target=3D"_blank">chase.com</a>. </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; paddi=
|
||||||
|
ng: 0px 0px; background-color: #F6F6F6;"><table role=3D"presentation" width=
|
||||||
|
=3D"100%" cellpadding=3D"0" cellspacing=3D"0" border=3D"0">
|
||||||
|
<tr>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top;=
|
||||||
|
padding:20px 30px 60px; font-family:Arial, Helvetica, sans-serif; font-siz=
|
||||||
|
e:12px; color:#717171;" class=3D"moPad font14"><span role=3D"heading" style=
|
||||||
|
=3D"text-transform: uppercase; font-weight: bold;">About this message</span=
|
||||||
|
><br />
|
||||||
|
<br />
|
||||||
|
Chase Mobile<span style=3D"font-size:70%=
|
||||||
|
; line-height:0; vertical-align:3px;">®</span>=C2=A0app is available f=
|
||||||
|
or select mobile devices. Message and data rates may apply.<br />
|
||||||
|
<br />
|
||||||
|
This service email was sent based on your ale=
|
||||||
|
rt settings. Use the Chase Mobile app or visit <a href=3D"https://www.=
|
||||||
|
chase.com/personal/mobile-online-banking/login-alerts" target=3D"_blank" st=
|
||||||
|
yle=3D"text-decoration: underline; color:#0060F0;" rel=3D"noopener noreferr=
|
||||||
|
er">chase.com/alerts</a> to view or manage your settings.<br />
|
||||||
|
<br />
|
||||||
|
Chase cannot guarantee the delivery of alerts=
|
||||||
|
and notifications.=C2=A0Wireless or internet service provider outages or o=
|
||||||
|
ther circumstances could delay them. You can always check <span class=3D"ap=
|
||||||
|
plelinksgray"><a style=3D"color:#717171;text-decoration: none;">chase.com</=
|
||||||
|
a></span> or the Chase Mobile=C2=A0app for the status of your accounts=
|
||||||
|
including your latest account balances and transaction details.=C2=A0<br /=
|
||||||
|
>
|
||||||
|
<br />
|
||||||
|
To protect your personal information, please =
|
||||||
|
don't reply to this message. Chase won't ask for confidential information i=
|
||||||
|
n an email. <br />
|
||||||
|
<br />
|
||||||
|
If you have concerns about the authenticity o=
|
||||||
|
f this message or have questions about your account visit <a style=3D"text-=
|
||||||
|
decoration: underline; color:#0060F0;" href=3D"https://www.chase.com/digita=
|
||||||
|
l/customer-service" target=3D"_blank" rel=3D"noopener noreferrer">chase.com=
|
||||||
|
/CustomerService</a> for ways to contact us.<br />
|
||||||
|
<br />
|
||||||
|
Your privacy is important to us. See our onli=
|
||||||
|
ne <a style=3D"text-decoration: underline; color:#0060F0;" href=3D"https://=
|
||||||
|
www.chase.com/digital/resources/privacy-security" target=3D"_blank" rel=3D"=
|
||||||
|
noopener noreferrer">Security Center</a> to learn how to protect your infor=
|
||||||
|
mation.<br />
|
||||||
|
<br />
|
||||||
|
© 2021 JPMorgan Chase & Co. </td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
<td align=3D"left" style=3D"vertical-align:top; padding:0px=
|
||||||
|
0px; width:12px;"><img src=3D"https://www.chase.com/content/dam/email/imag=
|
||||||
|
es/white-right.jpg" width=3D"12" height=3D"77" border=3D"0" style=3D"displa=
|
||||||
|
y:block;" alt=3D""/></td>
|
||||||
|
</tr>
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
<!--End of Content -->
|
||||||
|
=20
|
||||||
|
</table></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -12,23 +12,25 @@ type Transaction struct {
|
|||||||
ID string
|
ID string
|
||||||
Bank Bank
|
Bank Bank
|
||||||
Amount string
|
Amount string
|
||||||
Account string
|
Vendor string
|
||||||
Date string
|
Date string
|
||||||
|
Account string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transaction) Format() string {
|
func (t *Transaction) Format() string {
|
||||||
return fmt.Sprintf("(%s) %v: %s @ %s", cleanDate(t.Date), t.Bank, t.Amount, t.Account)
|
return fmt.Sprintf("(%s) %v/%v: %s @ %s", cleanDate(t.Date), t.Account, t.Bank, t.Amount, t.Vendor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transaction) String() string {
|
func (t *Transaction) String() string {
|
||||||
return fmt.Sprint(*t)
|
return fmt.Sprintf("%+v", *t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTransaction(amount, account, date string, bank Bank) *Transaction {
|
func NewTransaction(account, amount, vendor, date string, bank Bank) *Transaction {
|
||||||
regexp := regexp.MustCompile(`\s\s+`)
|
regexp := regexp.MustCompile(`\s\s+`)
|
||||||
t := &Transaction{
|
t := &Transaction{
|
||||||
|
Account: account,
|
||||||
Amount: regexp.ReplaceAllString(amount, " "),
|
Amount: regexp.ReplaceAllString(amount, " "),
|
||||||
Account: regexp.ReplaceAllString(account, " "),
|
Vendor: regexp.ReplaceAllString(vendor, " "),
|
||||||
Bank: bank,
|
Bank: bank,
|
||||||
Date: date,
|
Date: date,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package main
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestTransactionFormat(t *testing.T) {
|
func TestTransactionFormat(t *testing.T) {
|
||||||
x := NewTransaction("12.34", "Amazon", "[Wed, 1 Apr 2020 10:14:11 -0400 (EDT)]", Chase)
|
x := NewTransaction("me", "12.34", "Amazon", "[Wed, 1 Apr 2020 10:14:11 -0400 (EDT)]", Chase)
|
||||||
t.Logf("%s", x.String())
|
t.Logf("%s", x.String())
|
||||||
t.Logf("%s", x.Format())
|
t.Logf("%s", x.Format())
|
||||||
}
|
}
|
||||||
|
|||||||
94
upload.go
94
upload.go
@@ -1,15 +1,37 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"local/oauth2"
|
"local/oauth2"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Upload(config Config, transaction *Transaction) error {
|
func Upload(config Config, transaction *Transaction) error {
|
||||||
|
switch config.Uploader {
|
||||||
|
case UploaderTodo:
|
||||||
|
return uploadTodo(config, transaction)
|
||||||
|
case UploaderLedger:
|
||||||
|
return uploadLedger(config, transaction)
|
||||||
|
case UploaderPTTodo:
|
||||||
|
return uploadPTTodo(config, transaction)
|
||||||
|
default:
|
||||||
|
return errors.New("not impl: uploader")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func uploadTodo(config Config, transaction *Transaction) error {
|
||||||
params := url.Values{
|
params := url.Values{
|
||||||
"list": {config.TodoList},
|
"list": {config.TodoList},
|
||||||
"title": {transaction.Format()},
|
"title": {transaction.Format()},
|
||||||
@@ -32,3 +54,75 @@ func Upload(config Config, transaction *Transaction) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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(os.TempDir(), path.Base(config.TodoAddr))
|
||||||
|
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:]
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
var v interface{}
|
||||||
|
if b, err := ioutil.ReadFile(f.Name()); err != nil {
|
||||||
|
return err
|
||||||
|
} else if err := yaml.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Rename(f.Name(), config.TodoAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func uploadLedger(config Config, transaction *Transaction) error {
|
||||||
|
f, err := os.OpenFile(config.TodoAddr, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
amount, _ := strconv.ParseFloat(transaction.Amount, 32)
|
||||||
|
amount *= -1
|
||||||
|
remote := "Withdrawal:"
|
||||||
|
if amount > 0 {
|
||||||
|
remote = "Deposit:"
|
||||||
|
}
|
||||||
|
regexp := regexp.MustCompile(`[0-9a-zA-Z]`)
|
||||||
|
for _, substr := range regexp.FindAllString(transaction.Vendor, -1) {
|
||||||
|
remote += substr
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%-50s%-s\n", formatGMailDate(transaction.Date), transaction.Vendor)
|
||||||
|
fmt.Fprintf(f, "%-50s%-50s$%.2f\n", "", "AssetAccount:"+transaction.Bank.String()+":"+transaction.Account, amount)
|
||||||
|
fmt.Fprintf(f, "%-50s%-s\n", "", remote)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatGMailDate(s string) string {
|
||||||
|
for _, format := range []string{
|
||||||
|
"[Mon, 2 Jan 2006 15:04:05 -0700 (MST)]",
|
||||||
|
"[Mon, 2 Jan 2006 15:04:05 -0700]",
|
||||||
|
} {
|
||||||
|
time, err := time.Parse(format, s)
|
||||||
|
if err == nil {
|
||||||
|
return time.Format("2006-01-02")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|||||||
155
upload_test.go
Normal file
155
upload_test.go
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"local/storage"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUploadPTTodo(t *testing.T) {
|
||||||
|
addr := path.Join(t.TempDir(), "test.upload.pttodo")
|
||||||
|
config := Config{TodoAddr: addr, TodoTag: "expense"}
|
||||||
|
xaction := func() *Transaction {
|
||||||
|
return &Transaction{
|
||||||
|
ID: "id",
|
||||||
|
Bank: UCCU,
|
||||||
|
Amount: "1.23",
|
||||||
|
Vendor: "vendor vendor",
|
||||||
|
Date: "today",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Run("full file", func(t *testing.T) {
|
||||||
|
if err := ioutil.WriteFile(addr, []byte(`
|
||||||
|
todo:
|
||||||
|
- first
|
||||||
|
- todo: second
|
||||||
|
scheduled: []
|
||||||
|
done: []
|
||||||
|
`), os.ModePerm); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err := uploadPTTodo(config, xaction())
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile(addr)
|
||||||
|
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 {
|
||||||
|
t.Errorf("full file came out wrong: got %s", b)
|
||||||
|
}
|
||||||
|
if !bytes.Contains(b, []byte(xaction().Format())) {
|
||||||
|
t.Errorf("full file didnt get target: %s", string(b))
|
||||||
|
}
|
||||||
|
t.Logf("%s", b)
|
||||||
|
})
|
||||||
|
t.Run("no file", func(t *testing.T) {
|
||||||
|
os.Remove(addr)
|
||||||
|
err := uploadPTTodo(config, xaction())
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile(addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if !bytes.Contains(b, []byte(xaction().Format())) {
|
||||||
|
t.Errorf("no file didnt get target: %s", string(b))
|
||||||
|
}
|
||||||
|
t.Logf("%s", b)
|
||||||
|
})
|
||||||
|
t.Run("empty file", func(t *testing.T) {
|
||||||
|
if err := ioutil.WriteFile(addr, []byte{}, os.ModePerm); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err := uploadPTTodo(config, xaction())
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile(addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if !bytes.Contains(b, []byte(xaction().Format())) {
|
||||||
|
t.Errorf("empty file didnt get target: %s", string(b))
|
||||||
|
}
|
||||||
|
t.Logf("%s", b)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadLedger(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
transaction Transaction
|
||||||
|
}{
|
||||||
|
"simple": {
|
||||||
|
transaction: Transaction{
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
Bank: Chase,
|
||||||
|
Amount: "1.10",
|
||||||
|
Vendor: "my's totally realistic!!! VENDOR 11",
|
||||||
|
Date: "today",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, d := range cases {
|
||||||
|
c := d
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
config := Config{
|
||||||
|
TodoAddr: path.Join(t.TempDir(), name),
|
||||||
|
Uploader: UploaderLedger,
|
||||||
|
Storage: storage.NewMap(),
|
||||||
|
}
|
||||||
|
if err := uploadLedger(config, &c.transaction); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile(config.TodoAddr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Contains(b, []byte("mystotallyrealisticVENDOR11")) {
|
||||||
|
t.Fatal(string(b))
|
||||||
|
}
|
||||||
|
t.Logf("\n%s", b)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatGMailDate(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
input string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
"ok": {
|
||||||
|
input: "[Tue, 20 Jul 2021 23:46:00 -0400 (EDT)]",
|
||||||
|
want: "2021-07-20",
|
||||||
|
},
|
||||||
|
"bad": {
|
||||||
|
input: "2021-07-20",
|
||||||
|
want: "2021-07-20",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, d := range cases {
|
||||||
|
c := d
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
got := formatGMailDate(c.input)
|
||||||
|
if got != c.want {
|
||||||
|
t.Fatal(c.input, c.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user