Skip to content

Commit

Permalink
Merge pull request #4 from caarlos0/rewrite
Browse files Browse the repository at this point in the history
v2
  • Loading branch information
caarlos0 authored Dec 16, 2017
2 parents d39261a + 15eedb7 commit e33a14b
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 85 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
language: go
go: 1.8
go: 1.9
install: make setup
script: make ci
after_success:
Expand Down
52 changes: 41 additions & 11 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 6 additions & 11 deletions Gopkg.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@

[[dependencies]]
name = "github.com/caarlos0/spin"
version = "^1.0.0"

[[dependencies]]
[[constraint]]
branch = "master"
name = "github.com/google/go-github"

[[dependencies]]
name = "github.com/urfave/cli"
version = "^1.19.1"

[[dependencies]]
[[constraint]]
branch = "master"
name = "golang.org/x/oauth2"

[[constraint]]
name = "github.com/boltdb/bolt"
version = "1.3.1"
17 changes: 2 additions & 15 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SOURCE_FILES?=$$(go list ./... | grep -v /vendor/)
SOURCE_FILES?=./...
TEST_PATTERN?=.
TEST_OPTIONS?=

Expand All @@ -20,20 +20,7 @@ fmt: ## gofmt and goimports all go files
find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done

lint: ## Run all the linters
gometalinter --vendor --disable-all \
--enable=deadcode \
--enable=ineffassign \
--enable=gosimple \
--enable=staticcheck \
--enable=gofmt \
--enable=goimports \
--enable=dupl \
--enable=misspell \
--enable=errcheck \
--enable=vet \
--enable=vetshadow \
--deadline=10m \
./...
gometalinter --vendor ./...

ci: lint test ## Run all the tests and code checks

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ and put it in your crontab:
* * * * * /path/to/github-vacations -t My-Github-Token -o SomeOrg > /dev/null 2>&1
```

Your notifications will be stored on `%HOME/.vacations.db`. You can read them
when you get back by using `github-vacations read`.

![screenshot](screen.png)

Enjoy your vacations! 🏖

## Install
Expand Down
147 changes: 110 additions & 37 deletions cmd/github-vacations/main.go
Original file line number Diff line number Diff line change
@@ -1,57 +1,130 @@
package main

import (
"encoding/json"
"fmt"
"os"
"strings"

githubvacations "github.com/caarlos0/github-vacations"
"github.com/caarlos0/spin"
"github.com/urfave/cli"
"github.com/alecthomas/kingpin"
"github.com/apex/log"
"github.com/apex/log/handlers/cli"
"github.com/boltdb/bolt"
lib "github.com/caarlos0/github-vacations"
)

var version = "master"
func init() {
log.SetHandler(cli.Default)
}

const (
version = "master"
// TODO: this is a hack because printf won't accept `\e`
backslashE = string(0x1b)
)

var (
app = kingpin.New("github-vacations", "Automagically ignore all notifications related to work when you are on vacations")
dbPath = app.Flag("db", "Path to the database").Default("$HOME/.vacations.db").String()
check = app.Command("check", "Check for work notifications and mark them as read").Default()
org = check.Flag("org", "Organization name to ignore").Required().Short('o').String()
token = check.Flag("token", "Your GitHub token").Required().Short('t').Envar("GITHUB_TOKEN").String()
read = app.Command("read", "Read notifications that were previously marked as read")
)

func main() {
app := cli.NewApp()
app.Name = "github-vacations"
app.Usage = "Automagically ignore all notifications related to work when you are on vacations"
app.Version = version
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "org, o",
Usage: "Organization name to ignore",
},
cli.StringFlag{
Name: "token, t",
EnvVar: "GITHUB_TOKEN",
Usage: "GitHub token",
},
fmt.Printf("\n")
defer fmt.Printf("\n")
app.Version(version)
app.VersionFlag.Short('v')
app.HelpFlag.Short('h')
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
case check.FullCommand():
checkNotifications(*dbPath, *org, *token)
case read.FullCommand():
readNotification(*dbPath)
}
}

func readNotification(path string) {
log.Info("checking your notifications...")
db, err := bolt.Open(os.ExpandEnv(path), 0600, nil)
if err != nil {
log.WithError(err).Fatal("failed to open database")
}
app.Action = func(c *cli.Context) error {
var token = c.String("token")
var org = strings.ToLower(c.String("org"))
if token == "" {
return cli.NewExitError("missing GITHUB_TOKEN", 1)
defer closeDB(db)
if err := db.View(func(tx *bolt.Tx) error {
var bucket = tx.Bucket([]byte("notifications"))
if bucket == nil {
log.Warn("0 notifications to read")
return nil
}
var notifications = map[string][]lib.Notification{}
if err := bucket.ForEach(func(url, encoded []byte) error {
var notification lib.Notification
if err := json.Unmarshal(encoded, &notification); err != nil {
return err
}
notifications[notification.Repo] = append(notifications[notification.Repo], notification)
return nil
}); err != nil {
log.WithError(err).Fatal("failed to read bucket")
}
if org == "" {
return cli.NewExitError("missing organization to ignore", 1)
for repo, repoNotifications := range notifications {
fmt.Printf("\n")
cli.Default.Padding = 3
log.Infof("%s:", repo)
cli.Default.Padding = 6
for _, n := range repoNotifications {
log.Infof(
"%s]8;;%s\a%s%s]8;;\a",
backslashE, n.URL, n.Title, backslashE,
)
}
cli.Default.Padding = 3
}
var spin = spin.New("%s Helping you to not work...")
spin.Start()
count, err := githubvacations.MarkWorkNotificationsAsRead(token, org)
spin.Stop()
return nil
}); err != nil {
log.WithError(err).Fatal("failed to read database")
}
}

func checkNotifications(path, org, token string) {
log.Info("helping you not to work...")
db, err := bolt.Open(os.ExpandEnv(path), 0600, nil)
if err != nil {
log.WithError(err).Fatal("failed to open database")
}
defer closeDB(db)

notifications, err := lib.MarkWorkNotificationsAsRead(token, org)
if err != nil {
log.WithError(err).Fatal("failed check your notifications")
}
log.Infof("%d %s notifications mark as read", len(notifications), org)

if err := db.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte("notifications"))
if err != nil {
var msg = "failed to mark notifications as read"
if count > 0 {
msg = fmt.Sprintf("%v notifications marked as read, others failed", count)
return err
}
for _, notification := range notifications {
encoded, err := json.Marshal(notification)
if err != nil {
return err
}
if err := bucket.Put([]byte(notification.URL), encoded); err != nil {
return err
}
return cli.NewExitError(msg, 1)
}
fmt.Printf("%v notifications marked as read", count)
return nil
}); err != nil {
log.WithError(err).Fatal("failed to store your notifications")
}
if err := app.Run(os.Args); err != nil {
panic(err)
log.Infof("notifications stored on %s", path)
}

func closeDB(db *bolt.DB) {
if err := db.Close(); err != nil {
log.WithError(err).Error("failed to close db")
}
}
38 changes: 28 additions & 10 deletions github-vacations.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ import (
"golang.org/x/oauth2"
)

// Notification from github
type Notification struct {
URL, Title, Repo string
}

// MarkWorkNotificationsAsRead checks your notifications from work and mark
// them as read
func MarkWorkNotificationsAsRead(token, org string) (count int, err error) {
func MarkWorkNotificationsAsRead(token, org string) ([]Notification, error) {
var result []Notification
var ctx = context.Background()
var client = github.NewClient(oauth2.NewClient(
ctx,
Expand All @@ -23,19 +29,31 @@ func MarkWorkNotificationsAsRead(token, org string) (count int, err error) {
&github.NotificationListOptions{},
)
if err != nil {
return
return result, err
}
for _, notification := range notifications {
var owner = *notification.Repository.Owner.Login
if strings.ToLower(owner) == org {
if _, err = client.Activity.DeleteThreadSubscription(ctx, *notification.ID); err != nil {
return
var owner = notification.GetRepository().GetOwner().GetLogin()
if strings.ToLower(owner) == strings.ToLower(org) {
if _, err = client.Activity.DeleteThreadSubscription(ctx, notification.GetID()); err != nil {
return result, err
}
if _, err = client.Activity.MarkThreadRead(ctx, notification.GetID()); err != nil {
return result, err
}
if _, err = client.Activity.MarkThreadRead(ctx, *notification.ID); err != nil {
return
var url = notification.GetSubject().GetURL()
for old, new := range map[string]string{
"api.github.com": "github.com",
"/repos/": "/",
"/pulls/": "/pull/",
} {
url = strings.Replace(url, old, new, 1)
}
count++
result = append(result, Notification{
URL: url,
Title: notification.GetSubject().GetTitle(),
Repo: notification.GetRepository().GetFullName(),
})
}
}
return
return result, err
}
Binary file added screen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e33a14b

Please sign in to comment.