imap-json-fetcher

This commit is contained in:
Colin 2024-05-04 08:25:54 -04:00
commit 19e22df93f
21 changed files with 229 additions and 0 deletions

31
README.md Normal file
View File

@ -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`.

70
build.sh Executable file
View File

@ -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."

View File

View File

View File

View File

View File

View File

View File

View File

BIN
dist/imap-json-fetcher vendored Executable file

Binary file not shown.

BIN
dist/imap-json-fetcher_darwin_amd64 vendored Executable file

Binary file not shown.

BIN
dist/imap-json-fetcher_darwin_arm64 vendored Executable file

Binary file not shown.

BIN
dist/imap-json-fetcher_linux_arm vendored Executable file

Binary file not shown.

BIN
dist/imap-json-fetcher_linux_arm64 vendored Executable file

Binary file not shown.

BIN
dist/imap-json-fetcher_linux_arm64_static vendored Executable file

Binary file not shown.

BIN
dist/imap-json-fetcher_linux_arm_static vendored Executable file

Binary file not shown.

BIN
dist/imap-json-fetcher_static vendored Executable file

Binary file not shown.

10
go.mod Normal file
View File

@ -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
)

10
go.sum Normal file
View File

@ -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=

108
main.go Normal file
View File

@ -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)
}
}