@@ 1,13 1,13 @@
-icsmailer
+icsinvite
=========
In OSS, it appears nobody wants to be responsible for sending email invitations.
-icsmailer does this for you.
+icsinvite does this for you.
Features
========
-icsmailer is designed to watch a calendar collection and send invites to
+icsinvite is designed to watch a calendar collection and send invites to
attendees when it detects changes.
- The collection can be either a directory full of calendar events or a WebDAV calendar
@@ 24,7 24,7 @@ Non-features
============
- Event changes (e.g. reacting to reschedule events)
-- Encrypted emails (must be decrypted before icsmailer)
+- Encrypted emails (must be decrypted before icsinvite)
Design
@@ 33,7 33,6 @@ Design
icsmailer stands on the shoulders of:
- https://github.com/arran4/golang-ical
-- https://github.com/timshannon/bolthold
- https://github.com/jordan-wright/email
Alts
@@ 41,5 40,4 @@ Alts
- https://github.com/prologic/bitcask
- https://github.com/emersion/go-message
-
-
+- https://github.com/timshannon/bolthold
@@ 14,7 14,6 @@ import (
"os"
)
-// TODO environment configuration for DB and calendar locations
// TODO -C -- send only to changed attendees
// TODO -e -- input is an email
// TODO -d <path> -- database path
@@ 49,13 48,19 @@ func processFlags(args ...string) {
var err error
in, err = os.Open(send.One)
if err != nil {
- // TODO handle panic
- panic(err)
+ fmt.Errorf("unable to process input data: %s\n", err)
+ return
}
}
- if err := send.parseSend(in); err != nil {
- // TODO handle panic
- panic(err)
+ // Process confirmation
+ if send.MailInput {
+ // TODO: email confirmation handling
+ // Get confirmation from email
+ // Get sender from email
+ // Find ICS in calendar
+ // Update RSVP in ICS file
+ } else if err := send.sendInvites(in); err != nil {
+ fmt.Errorf("error processing calendar event: %s\n", err)
}
return
}
@@ 73,7 78,7 @@ func processFlags(args ...string) {
if err != nil {
return err
}
- if err = send.parseSend(in); err != nil {
+ if err = send.sendInvites(in); err != nil {
return err
}
}
@@ 111,17 116,17 @@ func calStats(path string) CalStats {
var VERSION string = "dev"
type Send struct {
- One string `names:"-f" usage:"process a single event; filename, or - for stdin"`
- Version bool `names:"-V" usage:"print version (& exit)"`
- Query bool `names:"-Q" usage:"print DB & calendar statistics"`
- NOP bool `names:"-N" usage:"do nothing"`
- NoEmail bool `names:"-S" usage:"do not send emails (but maybe persist to DB)"`
- OnlyChanged bool `names:"-C" usage:"only send emails to changed attendees"`
- Debug bool `names:"-D" usage:"be verbose"`
- DbPath string `names:"-d" usage:"the path to the database"`
- CalPath string `names:"-c" usage:"the path to the calendar"`
+ One string `names:"-f" usage:"Process a single event; filename, or - for stdin"`
+ Version bool `names:"-V" usage:"Print version (& exit)"`
+ Query bool `names:"-Q" usage:"Print DB & calendar statistics"`
+ NOP bool `names:"-N" usage:"Do nothing"`
+ NoEmail bool `names:"-S" usage:"Do not send emails (but maybe persist to DB)"`
+ OnlyChanged bool `names:"-C" usage:"Only send emails to changed attendees" env:"ICS_ONLY_CHANGED"`
+ Debug bool `names:"-D" usage:"Be verbose"`
+ DbPath string `names:"-d" usage:"The path to the database" env:"ICS_DB"`
+ CalPath string `names:"-c" usage:"The path to the calendar" env:"ICS_DIR"`
+ MailInput bool `names:"-e" usage:"Input data is an email containing an RSVP response"`
//NoPersist bool `names:"-Z" usage:"do not record actions to database (but maybe send emails)"`
- mailInput bool `names:"-e" usage:"input data is an email containing an RSVP response"`
}
func (t *Send) Metadata() map[string]flag.Flag {
@@ 162,7 167,7 @@ func (t *Send) Metadata() map[string]fla
},
"-e": {
Desc: `
- Usefel only with -f; ignored otherwise. The input is expected to be an email with
+ Useful only with -f; ignored otherwise. The input is expected to be an email with
an RSVP response either in the body, or as an attachment. The ICS file will be
updated with the RSVP response.
`,
@@ 171,30 176,23 @@ func (t *Send) Metadata() map[string]fla
Desc: `
The database is a tsv file in the form of:
<ICS UUID><tab>[<attendee>[,<attendee>]*]?
-
Example: icsmailer -d ~/.icsmailer.tsv
`,
},
"-c": {
Desc: `
- The calendar is a directory of .ics files, such as one managed by vdirsyncer. Example:
- icsmailer -c ~/.calendars/defaultcalendar/
+ The calendar is a directory of .ics files, such as one managed by vdirsyncer.
+ Example: icsmailer -c ~/.calendars/defaultcalendar/
`,
},
}
}
-func (s Send) parseSend(in io.Reader) error {
+func (s Send) sendInvites(in io.Reader) error {
cal, err := ics.ParseCalendar(in)
if err != nil {
return err
}
- s.sendInvites(cal)
- return nil
-}
-
-// TODO optionally send
-func (s Send) sendInvites(cal *ics.Calendar) error {
var valid = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
for _, e := range cal.Events() {
id := e.Id()
@@ 229,6 227,7 @@ func (s Send) sendInvites(cal *ics.Calen
if !s.NoEmail {
// TODO actually send
}
+ // TODO record in DB
}
return nil
}