feat!: Finalize code

This commit is contained in:
TheWanderingCrow 2025-11-12 09:59:32 -05:00
parent 006f301484
commit 80f5b39961

View file

@ -1,10 +1,17 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"github.com/joho/godotenv"
"io"
"log"
"net/http"
"net/smtp"
"os"
"strings"
"time"
)
type BudgetClient struct {
@ -14,6 +21,41 @@ type BudgetClient struct {
fullUrl string
}
type BudgetMonthsResponse struct {
Data struct {
Month string `json:"month"`
IncomeAvailable int `json:"incomeAvailable"`
LastMonthOverspent int `json:"lastMonthOverspent"`
ForNextMonth int `json:"forNextMonth"`
TotalBudgeted int `json:"totalBudgeted"`
ToBudget int `json:"toBudget"`
FromLastMonth int `json:"fromLastMonth"`
TotalIncome int `json:"totalIncome"`
TotalSpent int `json:"totalSpent"`
TotalBalance int `json:"totalBalance"`
CategoryGroups []struct {
ID string `json:"id"`
Name string `json:"name"`
IsIncome bool `json:"is_income"`
Hidden bool `json:"hidden"`
Budgeted int `json:"budgeted"`
Spent int `json:"spent"`
Balance int `json:"balance"`
Categories []struct {
ID string `json:"id"`
Name string `json:"name"`
IsIncome bool `json:"is_income"`
Hidden bool `json:"hidden"`
GroupID string `json:"group_id"`
Budgeted int `json:"budgeted"`
Spent int `json:"spent"`
Balance int `json:"balance"`
Carryover bool `json:"carryover"`
} `json:"categories"`
} `json:"categoryGroups"`
} `json:"data"`
}
func CreateBudgetClient(baseUrl string, apiKey string, syncId string) *BudgetClient {
fullUrl := baseUrl + "/v1/budgets/" + syncId
client := BudgetClient{baseUrl, apiKey, syncId, fullUrl}
@ -25,7 +67,7 @@ func CreateBudgetClient(baseUrl string, apiKey string, syncId string) *BudgetCli
// method is POST, GET, ect
// route is the route to call with a leading / ex. /accounts/banksync
// headers is a map of strings expecting "header" "value"
func (b BudgetClient) callApi(method string, route string, headers map[string]string) bool {
func (b BudgetClient) callApi(method string, route string, headers map[string]string) *http.Response {
var httpClient http.Client
req, err := http.NewRequest(method, b.fullUrl+route, http.NoBody)
if err != nil {
@ -39,12 +81,35 @@ func (b BudgetClient) callApi(method string, route string, headers map[string]st
if err != nil {
log.Fatal(err)
}
log.Println(resp)
return resp
}
// Triggers a bank sync, returns true if successful
func (b BudgetClient) BankSync() bool {
resp := b.callApi("POST", "/accounts/banksync", nil)
return resp.StatusCode == http.StatusOK
}
func (b BudgetClient) BankSync() bool {
return b.callApi("POST", "/accounts/banksync", nil)
func (b BudgetClient) GetBudgetMonths() *BudgetMonthsResponse {
currentTime := time.Now()
year, month := currentTime.Year(), int(currentTime.Month())
budgetMonth := fmt.Sprintf("%v-%v", year, month)
resp := b.callApi("GET", "/months/"+budgetMonth, nil)
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Fatal("GetBudgetAmounts failed with: " + string(resp.Status))
}
var data bytes.Buffer
_, err := io.Copy(&data, resp.Body)
if err != nil {
log.Println(err)
}
var budgetMonths BudgetMonthsResponse
err = json.Unmarshal(data.Bytes(), &budgetMonths)
return &budgetMonths
}
func main() {
@ -57,6 +122,52 @@ func main() {
var baseUrl = os.Getenv("BASE_URL")
var apiKey = os.Getenv("API_KEY")
var syncId = os.Getenv("SYNC_ID")
var smtpUsername = os.Getenv("SMTP_USERNAME")
var smtpPassword = os.Getenv("SMTP_PASSWORD")
var smtpHost = os.Getenv("SMTP_HOST")
var smtpRecipients = os.Getenv("SMTP_RECIPIENTS")
client := CreateBudgetClient(baseUrl, apiKey, syncId)
client.BankSync()
//if !client.BankSync() {
// log.Println("Bank Sync failed, information may not be up to date")
//}
budgetMonths := client.GetBudgetMonths()
subject := "Subject: Budget Report\n"
mime := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"
const bodyTemplate = `
<p>Good day,</p>
<p>Your unallocated budget total as of %s: <b>$%v</b></p>
<p>Category breakdown:</p>
`
var categories strings.Builder
for _, v := range budgetMonths.Data.CategoryGroups {
for _, v2 := range v.Categories {
if !v2.IsIncome {
bal := float32(v2.Balance)
bal /= 100
fmt.Fprintf(&categories, "<p>%v: <b>$%v</b></p>", v2.Name, bal)
}
}
}
categories.Write([]byte("<p>This is an automated report generated by <a href=\"https://git.wanderingcrow.net/TheWanderingCrow/actualbudget-report\">actualbudget-report</a>"))
currentTime := time.Now()
toBudget := float32(budgetMonths.Data.ToBudget)
toBudget /= 100
body := fmt.Sprintf(bodyTemplate, currentTime.Format(time.RFC850), toBudget)
message := []byte(subject + mime + body + categories.String())
var auth smtp.Auth
if os.Getenv("ENVIRONMENT") == "dev" {
auth = nil
} else {
auth = smtp.PlainAuth("", smtpUsername, smtpPassword, smtpHost)
}
err := smtp.SendMail(smtpHost, auth, smtpUsername, []string{smtpRecipients}, []byte(message))
if err != nil {
log.Fatal(err)
}
}