bankofamerica impl charges

master v0.9
Bel LaPointe 2021-09-07 15:46:01 -06:00
parent 6f3bf1f6a4
commit 10bc441e1e
6 changed files with 136 additions and 8 deletions

View File

@ -6,10 +6,13 @@ const (
Chase Bank = iota + 1
Citi Bank = iota + 1
UCCU Bank = iota + 1
BankOfAmerica Bank = iota + 1
)
func (b Bank) String() string {
switch b {
case BankOfAmerica:
return "BankOfAmerica"
case Chase:
return "Chase"
case Citi:

View File

@ -7,6 +7,7 @@ import (
"local/args"
"local/oauth2"
"local/storage"
"log"
"net/http"
"strings"
)
@ -55,7 +56,7 @@ func NewConfig() Config {
as.Append(args.STRING, "todolist", "todo list", "")
as.Append(args.STRING, "todotag", "todo tag", "expense")
as.Append(args.STRING, "banks", "uccu,citi,chase", "uccu,citi,chase")
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")
@ -91,11 +92,13 @@ func NewConfig() Config {
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")

View File

@ -15,6 +15,7 @@ type scraper interface {
scrape(*mail.Message) ([]*Transaction, error)
}
type bankOfAmericaScraper struct{}
type chaseScraper struct{}
type citiScraper struct{}
type uccuScraper struct{}
@ -36,6 +37,9 @@ func buildScraper(m *mail.Message, banks map[Bank]bool) (scraper, error) {
if strings.Contains(from, "Chase") && banks[Chase] {
return newChaseScraper(), nil
}
if strings.Contains(from, "Bank of America") && banks[BankOfAmerica] {
return newBankOfAmericaScraper(), nil
}
if strings.Contains(from, "Citi") && banks[Citi] {
return newCitiScraper(), nil
}
@ -45,6 +49,10 @@ func buildScraper(m *mail.Message, banks map[Bank]bool) (scraper, error) {
return nil, errors.New("unknown sender: " + from)
}
func newBankOfAmericaScraper() scraper {
return &bankOfAmericaScraper{}
}
func newChaseScraper() scraper {
return &chaseScraper{}
}
@ -238,3 +246,36 @@ func (c *uccuScraper) scrape(m *mail.Message) ([]*Transaction, error) {
transaction := NewTransaction(UCCU.String(), fmt.Sprintf("%.2f", f), "?", fmt.Sprint(m.Header["Date"]), UCCU)
return []*Transaction{transaction}, nil
}
func (c *bankOfAmericaScraper) scrape(m *mail.Message) ([]*Transaction, error) {
subject := fmt.Sprint(m.Header["Subject"])
if strings.Contains(subject, "Credit card transaction") {
return c.scrapeCharge(m)
}
return nil, errors.New("not impl")
}
func (c *bankOfAmericaScraper) scrapeCharge(m *mail.Message) ([]*Transaction, error) {
b, err := ioutil.ReadAll(m.Body)
if err != nil {
return nil, err
}
amount := ""
acc := ""
for _, line := range bytes.Split(b, []byte("\n")) {
if amount == "" && bytes.HasPrefix(line, []byte("Amount: $")) {
words := bytes.Split(bytes.TrimSpace(line), []byte(" "))
lastword := words[len(words)-1][1:]
escapedfloat := bytes.TrimPrefix(lastword, []byte("$"))
fixEscape := bytes.ReplaceAll(escapedfloat, []byte("=2E"), []byte("."))
amount = string(fixEscape)
} else if acc == "" && bytes.HasPrefix(line, []byte("Where: ")) {
acc = string(bytes.TrimSpace(bytes.TrimPrefix(line, []byte("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
}

View File

@ -108,3 +108,38 @@ func TestScrapeChase2020(t *testing.T) {
}
t.Logf("%+v", 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
View 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

View File

@ -22,7 +22,7 @@ func (t *Transaction) Format() string {
}
func (t *Transaction) String() string {
return fmt.Sprint(*t)
return fmt.Sprintf("%+v", *t)
}
func NewTransaction(account, amount, vendor, date string, bank Bank) *Transaction {