-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
639f24e
commit bf5690b
Showing
11 changed files
with
264 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,51 @@ | ||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= | ||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= | ||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= | ||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= | ||
github.com/glennzw/go-imap v0.0.0-20200213170711-35ad56e460d4 h1:pQAqfr44gthAWySJTn3ND5+Ho3yJjlFeUIXaNBQCW/8= | ||
github.com/glennzw/go-imap v0.0.0-20200213170711-35ad56e460d4/go.mod h1:PmwW3hrodDWXh1ZW9S/NsHYFI0USpChBw9yZ/CBeWwA= | ||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= | ||
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= | ||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= | ||
github.com/google/go-github/v44 v44.0.1-0.20220502191311-417479672f91 h1:07IRiIaXrkBvpln6reaLXjWorQTQ2PvwiSNgW4YwJeA= | ||
github.com/google/go-github/v44 v44.0.1-0.20220502191311-417479672f91/go.mod h1:iWn00mWcP6PRWHhXm0zuFJ8wbEjE5AGO5D5HXYM4zgw= | ||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= | ||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= | ||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= | ||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= | ||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= | ||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= | ||
github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o= | ||
github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= | ||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= | ||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= | ||
github.com/jprobinson/eazye v0.0.0-20200316195029-00167c745a93 h1:o/YCOvH76r/so3yha6jmtDDbI0nDizdwnZXNeQ+4hME= | ||
github.com/jprobinson/eazye v0.0.0-20200316195029-00167c745a93/go.mod h1:PrbzpJpOHK6YA5SptY8xl6RxWRZ2YGTDvVFzP/hjN4I= | ||
github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ= | ||
github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= | ||
github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo= | ||
github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= | ||
github.com/mxk/go-imap v0.0.0-20150429134902-531c36c3f12d h1:+DgqA2tuWi/8VU+gVgBAa7+WZrnFbPKhQWbKBB54cVs= | ||
github.com/mxk/go-imap v0.0.0-20150429134902-531c36c3f12d/go.mod h1:xacC5qXZnL/ooiitVoe3BtI1OotFTqi5zICBs9J5Fyk= | ||
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c h1:P6XGcuPTigoHf4TSu+3D/7QOQ1MbL6alNwrGhcW7sKw= | ||
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c/go.mod h1:YnNlZP7l4MhyGQ4CBRwv6ohZTPrUJJZtEv4ZgADkbs4= | ||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||
github.com/reugn/go-quartz v0.3.9 h1:0peG19P1obG+Yx4bFrHTRUWLO9BOkg24W97soIaZ9zo= | ||
github.com/reugn/go-quartz v0.3.9/go.mod h1:Tf7ynScQD/H5V02OKi0sux5d95tZJQmdJWwdDsVOx1M= | ||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= | ||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= | ||
github.com/sloonz/go-qprintable v0.0.0-20210417175225-715103f9e6eb h1:T+USeSgAg9MysHPeOQ2W3KAuBQHVZzG0XMHyfHN88Yg= | ||
github.com/sloonz/go-qprintable v0.0.0-20210417175225-715103f9e6eb/go.mod h1:WKd1iQMtoZdaS9rlKDPprxWJoan2hkQA9BcGt+oxezs= | ||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= | ||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= | ||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= | ||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= | ||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= | ||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= | ||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= | ||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package jobs | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"hash/fnv" | ||
"io" | ||
"log" | ||
"os" | ||
"regexp" | ||
"strings" | ||
"text/template" | ||
"time" | ||
|
||
"github.com/jackc/pgx" | ||
"github.com/jprobinson/eazye" | ||
"github.com/karashiiro/operator/pkg/outlook" | ||
"github.com/microcosm-cc/bluemonday" | ||
) | ||
|
||
var githubPattern = regexp.MustCompile(`(?i)github:\s*(?P<github>\S*)`) | ||
var intervalPattern = regexp.MustCompile(`(?i)interval:\s*(?P<interval>\S*)`) | ||
|
||
type ReceiveEmailsJob struct { | ||
Pool *pgx.ConnPool | ||
Policy *bluemonday.Policy | ||
} | ||
|
||
type newReader struct { | ||
Email string | ||
GitHub string | ||
ReportInterval time.Duration | ||
} | ||
|
||
func (j *ReceiveEmailsJob) Execute() { | ||
auth := eazye.MailboxInfo{ | ||
Host: os.Getenv("OPERATOR_IMAP_SERVER"), | ||
TLS: true, | ||
User: os.Getenv("OPERATOR_EMAIL"), | ||
Pwd: os.Getenv("OPERATOR_PASSWORD"), | ||
Folder: os.Getenv("OPERATOR_INBOX"), | ||
} | ||
|
||
emails, err := eazye.GetUnread(auth, true, false) | ||
if err != nil { | ||
log.Printf("Failed to get incoming emails: %v\n", err) | ||
} | ||
|
||
newReaders := make([]*newReader, 0) | ||
for _, email := range emails { | ||
// Parse out the email information | ||
subjectCleaned := strings.TrimSpace(email.Subject) | ||
if strings.HasPrefix(subjectCleaned, "[op] subscribe") { | ||
r, err := j.subscribe(email) | ||
if err != nil { | ||
log.Printf("Failed to parse subscription email: %v\n", err) | ||
} | ||
|
||
newReaders = append(newReaders, r) | ||
} | ||
} | ||
|
||
// Save new readers to the database | ||
if len(newReaders) > 0 { | ||
readerConn, err := j.Pool.Acquire() | ||
if err != nil { | ||
log.Printf("Failed to acquire database connection: %v\n", err) | ||
return | ||
} | ||
defer j.Pool.Release(readerConn) | ||
|
||
for _, r := range newReaders { | ||
_, err := storeReader(readerConn, r) | ||
if err != nil { | ||
log.Printf("Failed to add new reader: %v\n", err) | ||
} | ||
|
||
log.Printf("Sending subscription confirmation email to %s\n", r.Email) | ||
|
||
var subscribeMessage bytes.Buffer | ||
buildSubscriptionTemplate(&subscribeMessage, r.ReportInterval) | ||
|
||
err = outlook.SendEmail(r.Email, "Subscription confirmed", subscribeMessage.String()) | ||
if err != nil { | ||
log.Printf("Unable to send mail: %v\n", err) | ||
continue | ||
} | ||
|
||
log.Printf("Added reader %s\n", r.Email) | ||
} | ||
} | ||
} | ||
|
||
func (j *ReceiveEmailsJob) Description() string { | ||
return "ReceiveEmailsJob" | ||
} | ||
|
||
func (j *ReceiveEmailsJob) Key() int { | ||
h := fnv.New32a() | ||
h.Write([]byte(j.Description())) | ||
return int(h.Sum32()) | ||
} | ||
|
||
func (j *ReceiveEmailsJob) subscribe(email eazye.Email) (*newReader, error) { | ||
r := &newReader{ | ||
Email: j.Policy.Sanitize(email.From.Address), | ||
} | ||
|
||
bodyLines := strings.Split(string(email.Text), "\n") | ||
for _, line := range bodyLines { | ||
lineCleaned := strings.TrimSpace(line) | ||
|
||
// Parse their optionally-provided GitHub username | ||
githubMatches := githubPattern.FindStringSubmatch(lineCleaned) | ||
if len(githubMatches) != 0 { | ||
github := githubMatches[githubPattern.SubexpIndex("github")] | ||
r.GitHub = j.Policy.Sanitize(github) | ||
continue | ||
} | ||
|
||
// Parse their requested reporting interval | ||
intervalMatches := intervalPattern.FindStringSubmatch(lineCleaned) | ||
if len(intervalMatches) != 0 { | ||
interval, err := time.ParseDuration(intervalMatches[intervalPattern.SubexpIndex("interval")]) | ||
if err == nil { | ||
r.ReportInterval = interval | ||
continue | ||
} | ||
} | ||
} | ||
|
||
// Validate reporting interval | ||
if r.ReportInterval.Minutes() <= 0 { | ||
return nil, fmt.Errorf("user attempted to set a reporting interval of 0 or less") | ||
} | ||
|
||
return r, nil | ||
} | ||
|
||
func buildSubscriptionTemplate(w io.Writer, interval time.Duration) error { | ||
t, err := template.ParseFiles("./templates/subscribe-template.gohtml") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = t.Execute(w, struct { | ||
Interval time.Duration | ||
}{ | ||
Interval: interval, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func storeReader(conn *pgx.Conn, r *newReader) (int64, error) { | ||
t, err := conn.Exec(` | ||
INSERT INTO Reader (email, github, report_interval, active) | ||
VALUES | ||
($1, $2, $3, TRUE) | ||
`, r.Email, r.GitHub, r.ReportInterval) | ||
return t.RowsAffected(), err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package outlook | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/jordan-wright/email" | ||
) | ||
|
||
func SendEmail(to, subject, body string) error { | ||
auth := LoginAuth(os.Getenv("OPERATOR_EMAIL"), os.Getenv("OPERATOR_PASSWORD")) | ||
e := email.NewEmail() | ||
e.To = []string{to} | ||
e.From = fmt.Sprintf("Caprine Operator <%s>", os.Getenv("OPERATOR_EMAIL")) | ||
e.Subject = subject | ||
e.HTML = []byte(body) | ||
|
||
err := e.Send(os.Getenv("OPERATOR_SMTP_SERVER"), auth) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
<p> | ||
You are subscribed to operator updates! If any updates have occurred, you will be emailed | ||
within {{.Interval}}. | ||
</p> |