commit c8bb98dcd39311ea0d1f9f677f26fcaa629e187f Author: Jim Myhrberg Date: Thu Jul 8 11:37:59 2021 +0100 feat(spammy-recruiters): add spammy-recruiters generator diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7cd259f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin/* +*.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1417e60 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +# +# Build +# + +BINDIR := bin +CMDDIR := cmd + +SOURCES := $(shell find * -name "*.go" -or -name "go.mod" -or -name "go.sum" \ + -or -name "Makefile") +BINS := $(shell test -d "$(CMDDIR)" && cd "$(CMDDIR)" && \ + find * -maxdepth 0 -type d -exec echo $(BINDIR)/{} \;) + +.PHONY: build +build: $(BINS) + +$(BINS): $(BINDIR)/%: $(SOURCES) + mkdir -p "$(dir $@)" + cd "$(CMDDIR)/$*" && go build -a -o "$(CURDIR)/$(BINDIR)/$*" + +# +# Rules +# + +rules: spammy-recruiters.json + +.PHONY: spammy-recruiters.json +spammy-recruiters.json: bin/spammy-recruiters + bin/spammy-recruiters -o "$@" diff --git a/README.md b/README.md new file mode 100644 index 0000000..1aa7cd6 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# fastmail-rules + +Random collection of things that generate Fastmail rules in JSON format from +various sources, for easy import into Fastmail. + +The whole repo is a quick and dirty hack for my own personal use, and I have no +plans to make any of this user-friendly. If you want to know what it does, just +poke around the source code :) diff --git a/cmd/spammy-recruiters/main.go b/cmd/spammy-recruiters/main.go new file mode 100644 index 0000000..6f69b75 --- /dev/null +++ b/cmd/spammy-recruiters/main.go @@ -0,0 +1,136 @@ +package main + +import ( + "bytes" + "context" + "encoding/json" + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "strings" + "time" +) + +var defaultURL = "https://github.com/drcongo/spammy-recruiters/" + + "raw/master/spammers.txt" + +var urlFlag = flag.String("url", defaultURL, + "URL to download spammy recruiters list from", +) + +var fileFlag = flag.String("file", "", + "file to get spammy recruiters from instead of downloading them from URL", +) + +var outputFlag = flag.String("o", "", + "Output file path, prints to STDOUT if empty", +) + +func main() { + flag.Parse() + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + var spammers []byte + var err error + if *fileFlag != "" { + spammers, err = os.ReadFile(*fileFlag) + } else { + spammers, err = spammersFromURL(ctx, *urlFlag) + } + if err != nil { + log.Fatal(err) + } + + search := formatSpammers(spammers) + rule, err := renderRule(search) + if err != nil { + log.Fatal(err) + } + + if *outputFlag == "" { + fmt.Println(string(rule)) + } else { + err = os.WriteFile(*outputFlag, rule, 0o644) + if err != nil { + log.Fatal(err) + } + } +} + +type Rule struct { + Combinator string `json:"combinator,omitempty"` + Conditions string `json:"conditions,omitempty"` + Discard bool `json:"discard"` + FileIn string `json:"fileIn,omitempty"` + MarkFlagged bool `json:"markFlagged"` + MarkRead bool `json:"markRead"` + MarkSpam bool `json:"markSpam"` + Name string `json:"name,omitempty"` + PreviousFileInName string `json:"previousFileInName,omitempty"` + RedirectTo string `json:"redirectTo,omitempty"` + Search string `json:"search,omitempty"` + ShowNotification bool `json:"showNotification"` + SkipInbox bool `json:"skipInbox"` + SnoozeUntil string `json:"snoozeUntil,omitempty"` + Stop bool `json:"stop"` +} + +func spammersFromURL(ctx context.Context, u string) ([]byte, error) { + req, err := http.NewRequestWithContext(ctx, "GET", u, nil) + if err != nil { + return nil, err + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + b, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return b, nil +} + +func formatSpammers(spammers []byte) string { + spammers = bytes.ReplaceAll(spammers, []byte("\n"), []byte{}) + spammers = bytes.ReplaceAll(spammers, []byte("\r"), []byte{}) + + parts := bytes.Split(spammers, []byte(" OR ")) + conds := []string{} + + for _, part := range parts { + cond := strings.TrimSpace(string(part)) + if strings.Contains(cond, " ") { + cond = "(" + cond + ")" + } + conds = append(conds, "from:"+cond) + } + + return strings.Join(conds, " OR ") +} + +func renderRule(search string) ([]byte, error) { + rule := Rule{ + Combinator: "any", + Discard: false, + FileIn: "Recruiter Spam", + MarkFlagged: false, + MarkRead: false, + MarkSpam: false, + Name: "Spammy Recruiters", + Search: search, + ShowNotification: false, + SkipInbox: true, + Stop: false, + } + + return json.MarshalIndent([]Rule{rule}, "", " ") +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3ca58fd --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/jimeh/fastmail-rules + +go 1.16