commit 19e22df93f73c41fc4b5d185ffae46697b1e04b1 Author: Colin Date: Sat May 4 08:25:54 2024 -0400 imap-json-fetcher diff --git a/README.md b/README.md new file mode 100644 index 0000000..9f2883c --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# IMAP JSON Fetcher + +## Setup + +Before running the `imap-json-fetcher`, ensure that the following environment variables are set: + +- `IMAP_HOST`: Your IMAP server address (e.g., `imap.example.com:993`). +- `IMAP_USERNAME`: Your email username. +- `IMAP_PASSWORD`: Your email password. + +You can set these environment variables in a Unix-like terminal as follows: + +```bash +export IMAP_HOST="your_imap_server_address" +export IMAP_USERNAME="your_username" +export IMAP_PASSWORD="your_password" +``` + +## Running the Script + +To run the `imap-json-fetcher`, navigate to the `dist` directory where the binary is located, and use the following command: + +```bash +./imap-json-fetcher +``` + +This assumes that you have already compiled the binary and placed it in the `dist` directory. + +## Output + +The `imap-json-fetcher` will listen for new emails and output the extracted email addresses in separate JSON files, one per email, named in the format `email-seqNum.json`. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..64446cd --- /dev/null +++ b/build.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Default architecture +DEFAULT_ARCH="linux/amd64" + +# Supported architectures (adjust as needed) +ARCHITECTURES=("linux/amd64" "linux/arm64" "linux/arm/v7" "darwin/amd64" "darwin/arm64") + +# Ensure all necessary directories exist and go modules are ready +prepare_build() { + # Create necessary directories if they don't exist + mkdir -p dist + mkdir -p build_logs + + # Initialize go modules if go.mod does not exist + if [ ! -f go.mod ]; then + echo "Initializing Go modules" + go mod init imap-json-fetcher # Replace 'yourmodule' with your actual module name or path + fi + + # Fetch and ensure all dependencies are up to date + echo "Checking dependencies..." + go mod tidy +} + +# Build function +build_binary() { + os=$1 + arch=$2 + output_name="imap-json-fetcher" + + if [[ "$os/$arch" != "$DEFAULT_ARCH" ]]; then + output_name="${output_name}_${os}_${arch}" + fi + + output_name="dist/${output_name}" + + # Dynamic Linking + echo "Building dynamically linked for ${os}/${arch} -> ${output_name}" + GOOS=${os} GOARCH=${arch} go build -o ${output_name} main.go 2>build_logs/${os}_${arch}_build.log + if [ $? -eq 0 ]; then + echo "Successfully built ${output_name}" + else + echo "Failed to build ${output_name}. Check build_logs/${os}_${arch}_build.log for errors." + fi + + # Static Linking + if [[ "$os" == "linux" ]]; then # Typically, static linking is most relevant for Linux environments + static_output_name="${output_name}_static" + echo "Building statically linked for ${os}/${arch} -> ${static_output_name}" + CGO_ENABLED=0 GOOS=${os} GOARCH=${arch} go build -a -ldflags '-extldflags "-static"' -o ${static_output_name} main.go 2>build_logs/${os}_${arch}_static_build.log + if [ $? -eq 0 ]; then + echo "Successfully built ${static_output_name}" + else + echo "Failed to build ${static_output_name}. Check build_logs/${os}_${arch}_static_build.log for errors." + fi + fi +} + +# Main Build Process +prepare_build +for arch in "${ARCHITECTURES[@]}"; do + IFS='/' read -r -a parts <<< "$arch" # Split architecture string + os=${parts[0]} + arch=${parts[1]} + build_binary $os $arch +done + +echo "Build process completed." + diff --git a/build_logs/darwin_amd64_build.log b/build_logs/darwin_amd64_build.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/darwin_arm64_build.log b/build_logs/darwin_arm64_build.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_amd64_build.log b/build_logs/linux_amd64_build.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_amd64_static_build.log b/build_logs/linux_amd64_static_build.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_arm64_build.log b/build_logs/linux_arm64_build.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_arm64_static_build.log b/build_logs/linux_arm64_static_build.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_arm_build.log b/build_logs/linux_arm_build.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_arm_static_build.log b/build_logs/linux_arm_static_build.log new file mode 100644 index 0000000..e69de29 diff --git a/dist/imap-json-fetcher b/dist/imap-json-fetcher new file mode 100755 index 0000000..98398cb Binary files /dev/null and b/dist/imap-json-fetcher differ diff --git a/dist/imap-json-fetcher_darwin_amd64 b/dist/imap-json-fetcher_darwin_amd64 new file mode 100755 index 0000000..3078993 Binary files /dev/null and b/dist/imap-json-fetcher_darwin_amd64 differ diff --git a/dist/imap-json-fetcher_darwin_arm64 b/dist/imap-json-fetcher_darwin_arm64 new file mode 100755 index 0000000..d321ede Binary files /dev/null and b/dist/imap-json-fetcher_darwin_arm64 differ diff --git a/dist/imap-json-fetcher_linux_arm b/dist/imap-json-fetcher_linux_arm new file mode 100755 index 0000000..8ca4603 Binary files /dev/null and b/dist/imap-json-fetcher_linux_arm differ diff --git a/dist/imap-json-fetcher_linux_arm64 b/dist/imap-json-fetcher_linux_arm64 new file mode 100755 index 0000000..468fbe8 Binary files /dev/null and b/dist/imap-json-fetcher_linux_arm64 differ diff --git a/dist/imap-json-fetcher_linux_arm64_static b/dist/imap-json-fetcher_linux_arm64_static new file mode 100755 index 0000000..9ad6af6 Binary files /dev/null and b/dist/imap-json-fetcher_linux_arm64_static differ diff --git a/dist/imap-json-fetcher_linux_arm_static b/dist/imap-json-fetcher_linux_arm_static new file mode 100755 index 0000000..b589e3b Binary files /dev/null and b/dist/imap-json-fetcher_linux_arm_static differ diff --git a/dist/imap-json-fetcher_static b/dist/imap-json-fetcher_static new file mode 100755 index 0000000..98531bc Binary files /dev/null and b/dist/imap-json-fetcher_static differ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..743bbb0 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module imap-json-fetcher + +go 1.21.1 + +require github.com/emersion/go-imap v1.2.1 + +require ( + github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect + golang.org/x/text v0.3.7 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..827662c --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA= +github.com/emersion/go-imap v1.2.1/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= +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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/main.go b/main.go new file mode 100644 index 0000000..5fbb153 --- /dev/null +++ b/main.go @@ -0,0 +1,108 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "time" + + "github.com/emersion/go-imap" + "github.com/emersion/go-imap/client" + "github.com/emersion/go-message/mail" +) + +func main() { + host := os.Getenv("IMAP_HOST") + username := os.Getenv("IMAP_USERNAME") + password := os.Getenv("IMAP_PASSWORD") + + if host == "" || username == "" || password == "" { + log.Fatal("Environment variables IMAP_HOST, IMAP_USERNAME, and IMAP_PASSWORD must be set") + } + + log.Println("Connecting to server...") + + // Connect to the IMAP server + c, err := client.DialTLS(host, nil) + if err != nil { + log.Fatalf("Failed to connect to IMAP server: %v", err) + } + log.Println("Connected") + defer c.Logout() + + // Login + if err := c.Login(username, password); err != nil { + log.Fatalf("Failed to log in: %v", err) + } + log.Println("Logged in") + + // Select INBOX + _, err = c.Select("INBOX", false) + if err != nil { + log.Fatalf("Failed to select INBOX: %v", err) + } + + // Set up criteria to search for new unseen emails + criteria := imap.NewSearchCriteria() + criteria.WithoutFlags = []string{imap.SeenFlag} + ids, err := c.Search(criteria) + if err != nil { + log.Fatalf("Failed to search emails: %v", err) + } + if len(ids) == 0 { + log.Println("No new emails") + return + } + + seqset := new(imap.SeqSet) + seqset.AddNum(ids...) + + section := &imap.BodySectionName{} + items := []imap.FetchItem{section.FetchItem(), imap.FetchEnvelope} + messages := make(chan *imap.Message) + go func() { + if err := c.Fetch(seqset, items, messages); err != nil { + log.Fatalf("Failed to fetch emails: %v", err) + } + }() + + for msg := range messages { + if msg == nil { + continue + } + + r := mail.NewReader(msg.GetBody(section)) + email, err := r.NextPart() + if err != nil { + log.Fatalf("Failed to read message body: %v", err) + } + + body, err := ioutil.ReadAll(email) + if err != nil { + log.Fatalf("Failed to read body content: %v", err) + } + + emailData := map[string]interface{}{ + "subject": msg.Envelope.Subject, + "from": msg.Envelope.From, + "to": msg.Envelope.To, + "date": msg.Envelope.Date, + "body": string(body), + } + + emailJSON, err := json.MarshalIndent(emailData, "", " ") + if err != nil { + log.Fatalf("Failed to marshal JSON: %v", err) + } + + timestamp := time.Now().Format("20060102-150405") + fileName := fmt.Sprintf("email-%s.json", timestamp) + err = os.WriteFile(fileName, emailJSON, 0644) + if err != nil { + log.Fatalf("Failed to write file %s: %v", fileName, err) + } + log.Printf("Saved %s", fileName) + } +}