From c71af3e046495cc900c154a2539e96a456b20ebb Mon Sep 17 00:00:00 2001 From: Denis Zheleztsov Date: Thu, 14 Mar 2019 18:42:15 +0300 Subject: [PATCH] Initial --- cmd/args.go | 14 ++++++ cmd/config.go | 27 ++++++++++++ cmd/main.go | 71 ++++++++++++++++++++++++++++++ config/config.sample.json | 27 ++++++++++++ g2i/client.go | 16 +++++++ g2i/config.go | 33 ++++++++++++++ g2i/ghcomments.go | 88 +++++++++++++++++++++++++++++++++++++ g2i/github.go | 83 ++++++++++++++++++++++++++++++++++ g2i/go.mod | 9 ++++ g2i/go.sum | 14 ++++++ g2i/helpers.go | 14 ++++++ g2i/idec.go | 47 ++++++++++++++++++++ templates/hello_message.tpl | 13 ++++++ 13 files changed, 456 insertions(+) create mode 100644 cmd/args.go create mode 100644 cmd/config.go create mode 100644 cmd/main.go create mode 100644 config/config.sample.json create mode 100644 g2i/client.go create mode 100644 g2i/config.go create mode 100644 g2i/ghcomments.go create mode 100644 g2i/github.go create mode 100644 g2i/go.mod create mode 100644 g2i/go.sum create mode 100644 g2i/helpers.go create mode 100644 g2i/idec.go create mode 100644 templates/hello_message.tpl diff --git a/cmd/args.go b/cmd/args.go new file mode 100644 index 0000000..6ae20b2 --- /dev/null +++ b/cmd/args.go @@ -0,0 +1,14 @@ +package main + +import ( + "flag" +) + +var ( + filePath string +) + +func init() { + flag.StringVar(&filePath, "config", "config.json", "Path to the configuration file") + flag.Parse() +} diff --git a/cmd/config.go b/cmd/config.go new file mode 100644 index 0000000..13fea3d --- /dev/null +++ b/cmd/config.go @@ -0,0 +1,27 @@ +package main + +import ( + "io/ioutil" + "log" + + "encoding/json" + + "github.com/idec-net/github2idec/g2i" +) + +// loadConfig loads bot configuration from file +func loadConfig(path string) *g2i.Config { + log.Print("Loading configuration") + config := &g2i.Config{} + data, err := ioutil.ReadFile(path) + if err != nil { + panic(err) + } + + err = json.Unmarshal(data, config) + if err != nil { + panic(err) + } + + return config +} diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..de1cc8a --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "context" + "fmt" + "os" + + "text/template" + + "github.com/google/go-github/github" + "github.com/idec-net/github2idec/g2i" +) + +func main() { + ctx := context.Background() + config := loadConfig(filePath) + gClient := g2i.NewGithubClient(config, ctx) + eventsURL, err := gClient.EventsURL() + if err != nil { + panic(err) + } + + fmt.Println(eventsURL) + + // events, err := gClient.GetEvents(eventsURL) + // if err != nil { + // panic(err) + // } + + issues, err := gClient.GetIssues() + if err != nil { + panic(err) + } + + helloMessage := struct { + Sources, Name, Owner, Repo string + Issues []*github.Issue + }{ + Sources: "https://github.com/idec-net/github2idec", + Name: "Gdec", + Owner: config.Github.RepoOwner, + Repo: config.Github.Repo, + Issues: issues, + } + t, err := template.New("hello_message.tpl"). + ParseFiles("../templates/hello_message.tpl") + if err != nil { + panic(err) + } + err = t.Execute(os.Stdout, helloMessage) + if err != nil { + panic(err) + } + // for _, event := range events { + // if config.IsEventProcessable(*event.Type) { + // b, err := event.RawPayload.MarshalJSON() + // if err != nil { + // panic(err) + // } + // var comment g2i.GithubIssueComment + // err = json.Unmarshal(b, &comment) + // if err != nil { + // panic(err) + // } + // fmt.Printf("===============\nAuthor: %s\n", comment.Comment.User.Login) + // fmt.Printf("Issue: %s\nUrl: %s\n", comment.Issue.Title, comment.Comment.URL) + // fmt.Printf("CreatedAt: %s\n", comment.Comment.CreatedAt.Format("2006 Jan 2 15:04:05 UTC")) + // fmt.Printf("%s\n===============\n\n", comment.Comment.Body) + // } + // } +} diff --git a/config/config.sample.json b/config/config.sample.json new file mode 100644 index 0000000..27389dd --- /dev/null +++ b/config/config.sample.json @@ -0,0 +1,27 @@ +{ + "data": { + "path": "./data" + }, + "idec": { + "fetch": { + "echoes": [ + "idec.talks" + ], + "limit": 25, + "offset": -25 + }, + "authstring": "", + "node_url": "https://dynamic.lessmore.pw/idec/", + "hello_message": true, + "hello_message_template_path": "../templates/hello_message.tpl", + "top_post_id": "hOxZjLmgYv3Qwb32vJIK" + }, + "github": { + "repo_owner": "idec-net", + "repo": "netmail", + "token": "", + "watched_event_types": [ + "IssueCommentEvent" + ] + } +} diff --git a/g2i/client.go b/g2i/client.go new file mode 100644 index 0000000..235ceae --- /dev/null +++ b/g2i/client.go @@ -0,0 +1,16 @@ +package g2i + +import ( + "context" +) + +type Client struct { + GHClient *GithubClient + IDECClient *IDECClient +} + +func (c *Config) NewClient(ctx context.Context) (*Client, error) { + client := &Client{} + ghc := NewGithubClient(c, ctx) + return client, nil +} diff --git a/g2i/config.go b/g2i/config.go new file mode 100644 index 0000000..98dae03 --- /dev/null +++ b/g2i/config.go @@ -0,0 +1,33 @@ +package g2i + +type Config struct { + Data Data `json:"data"` + IDEC IDEC `json:"idec"` + Github Github `json:"github"` +} + +type Data struct { + Path string `json:"path"` +} + +type Fetch struct { + Echoes []string `json:"echoes"` + Limit int `json:"limit"` + Offset int `json:"offset"` +} + +type IDEC struct { + Fetch Fetch `json:"fetch"` + Authstring string `json:"authstring"` + NodeURL string `json:"node_url"` + HelloMessage bool `json:"hello_message"` + HelloMessageTemplatePath string `json:"hello_message_template_path"` + TopPostID string `json:"top_post_id"` +} + +type Github struct { + RepoOwner string `json:"repo_owner"` + Repo string `json:"repo"` + Token string `json:"token"` + WatchedEventTypes []string `json:"watched_event_types"` +} diff --git a/g2i/ghcomments.go b/g2i/ghcomments.go new file mode 100644 index 0000000..f9925d5 --- /dev/null +++ b/g2i/ghcomments.go @@ -0,0 +1,88 @@ +package g2i + +import ( + "time" +) + +type GithubIssueComment struct { + Action string `json:"action"` + Issue Issue `json:"issue"` + Comment Comment `json:"comment"` +} + +type User struct { + Login string `json:"login"` + ID int `json:"id"` + NodeID string `json:"node_id"` + AvatarURL string `json:"avatar_url"` + GravatarID string `json:"gravatar_id"` + URL string `json:"url"` + HTMLURL string `json:"html_url"` + FollowersURL string `json:"followers_url"` + FollowingURL string `json:"following_url"` + GistsURL string `json:"gists_url"` + StarredURL string `json:"starred_url"` + SubscriptionsURL string `json:"subscriptions_url"` + OrganizationsURL string `json:"organizations_url"` + ReposURL string `json:"repos_url"` + EventsURL string `json:"events_url"` + ReceivedEventsURL string `json:"received_events_url"` + Type string `json:"type"` + SiteAdmin bool `json:"site_admin"` +} + +type Labels struct { + ID int `json:"id"` + NodeID string `json:"node_id"` + URL string `json:"url"` + Name string `json:"name"` + Color string `json:"color"` + Default bool `json:"default"` +} + +type PullRequest struct { + URL string `json:"url"` + HTMLURL string `json:"html_url"` + DiffURL string `json:"diff_url"` + PatchURL string `json:"patch_url"` +} + +type Issue struct { + URL string `json:"url"` + RepositoryURL string `json:"repository_url"` + LabelsURL string `json:"labels_url"` + CommentsURL string `json:"comments_url"` + EventsURL string `json:"events_url"` + HTMLURL string `json:"html_url"` + ID int `json:"id"` + NodeID string `json:"node_id"` + Number int `json:"number"` + Title string `json:"title"` + User User `json:"user"` + Labels []Labels `json:"labels"` + State string `json:"state"` + Locked bool `json:"locked"` + Assignee interface{} `json:"assignee"` + Assignees []interface{} `json:"assignees"` + Milestone interface{} `json:"milestone"` + Comments int `json:"comments"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + ClosedAt interface{} `json:"closed_at"` + AuthorAssociation string `json:"author_association"` + PullRequest PullRequest `json:"pull_request"` + Body string `json:"body"` +} + +type Comment struct { + URL string `json:"url"` + HTMLURL string `json:"html_url"` + IssueURL string `json:"issue_url"` + ID int `json:"id"` + NodeID string `json:"node_id"` + User User `json:"user"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + AuthorAssociation string `json:"author_association"` + Body string `json:"body"` +} diff --git a/g2i/github.go b/g2i/github.go new file mode 100644 index 0000000..0c7c2f9 --- /dev/null +++ b/g2i/github.go @@ -0,0 +1,83 @@ +package g2i + +import ( + "context" + "log" + + "encoding/json" + "net/http" + + "github.com/google/go-github/github" +) + +type GithubClient struct { + c *github.Client + Owner string + Repo string + token string + ctx context.Context +} + +func NewGithubClient(config *Config, ctx context.Context) *GithubClient { + if config.Github.Token != "" { + + } + client := github.NewClient(nil) + return &GithubClient{client, config.Github.RepoOwner, config.Github.Repo, config.Github.Token, ctx} +} + +// GetIssues returns all repo issues +func (g *GithubClient) GetIssues() ([]*github.Issue, error) { + log.Print("Get repository issues") + issues, _, err := g.c.Issues.ListByRepo(g.ctx, g.Owner, g.Repo, nil) + return issues, err +} + +// GetRepoEvents +func (g *GithubClient) EventsURL() (string, error) { + var eventsURL string + repos, _, err := g.c.Repositories.ListByOrg(g.ctx, g.Owner, nil) + if err != nil { + return eventsURL, err + } + for i, _ := range repos { + if *repos[i].Name == g.Repo { + eventsURL = *repos[i].EventsURL + break + } + } + return eventsURL, nil +} + +// GetEvents returns repo events +func (g *GithubClient) GetEvents(eventsURL string) ([]github.Event, error) { + var events []github.Event + + client := &http.Client{} + req, err := http.NewRequest("GET", eventsURL, nil) + if err != nil { + return events, err + } + + resp, err := client.Do(req) + if err != nil { + return events, err + } + defer resp.Body.Close() + + err = json.NewDecoder(resp.Body).Decode(&events) + if err != nil { + return events, err + } + + return events, nil +} + +func (c *Config) IsEventProcessable(eventType string) bool { + for i, _ := range c.Github.WatchedEventTypes { + if eventType == c.Github.WatchedEventTypes[i] { + return true + } + } + return false +} diff --git a/g2i/go.mod b/g2i/go.mod new file mode 100644 index 0000000..f12b1ec --- /dev/null +++ b/g2i/go.mod @@ -0,0 +1,9 @@ +module github.com/idec-net/github2idec/g2i + +go 1.12 + +require ( + github.com/Difrex/go-idec v0.0.0-20170724073226-420b1f419842 + github.com/google/go-github v17.0.0+incompatible + github.com/google/go-github/v24 v24.0.1 // indirect +) diff --git a/g2i/go.sum b/g2i/go.sum new file mode 100644 index 0000000..a55fdd4 --- /dev/null +++ b/g2i/go.sum @@ -0,0 +1,14 @@ +github.com/Difrex/go-idec v0.0.0-20170724073226-420b1f419842 h1:xrw8RxKPECWc+8vxDaMFwiNGlYngXRcRB3QwTlOIHYg= +github.com/Difrex/go-idec v0.0.0-20170724073226-420b1f419842/go.mod h1:Krw/MoqqCqbZg0cRMw2FAU2JLifGYbw+iNK5FRYrCFs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-github/v24 v24.0.1 h1:KCt1LjMJEey1qvPXxa9SjaWxwTsCWSq6p2Ju57UR4Q4= +github.com/google/go-github/v24 v24.0.1/go.mod h1:CRqaW1Uns1TCkP0wqTpxYyRxRjxwvKU/XSS44u6X74M= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= diff --git a/g2i/helpers.go b/g2i/helpers.go new file mode 100644 index 0000000..da6860a --- /dev/null +++ b/g2i/helpers.go @@ -0,0 +1,14 @@ +package g2i + +import ( + "io" + "text/template" +) + +func (c *Config) generateTemplate(name, path string, data interface{}, wr io.Writer) error { + t, err := template.New(name).ParseFiles(c.IDEC.HelloMessageTemplatePath) + if err != nil { + return err + } + return t.Execute(wr, data) +} diff --git a/g2i/idec.go b/g2i/idec.go new file mode 100644 index 0000000..238ac6e --- /dev/null +++ b/g2i/idec.go @@ -0,0 +1,47 @@ +package g2i + +import ( + "log" + "strings" + + "bytes" + + idec "github.com/Difrex/go-idec" + "github.com/google/go-github/github" +) + +type IDECClient struct { + FetchConfig *idec.FetchConfig + config *Config +} + +func NewIDECClient(config *Config) *IDECClient { + return &IDECClient{ + config: config, + FetchConfig: &idec.FetchConfig{ + Limit: config.IDEC.Fetch.Limit, + Offset: config.IDEC.Fetch.Offset, + Node: config.IDEC.NodeURL, + Echoes: config.IDEC.Fetch.Echoes, + }, + } +} + +type HelloMessage struct { + Sources, Name, Owner, Repo string + Issues []*github.Issue +} + +func (i *IDECClient) PostHello() { + pointURL := strings.TrimRight(i.FetchConfig.Node, "/") + "/u/point" + message := idec.PointMessage{} + + body := &bytes.Buffer{} + err := i.config.generateTemplate("hello_message.tpl", i.config.IDEC.HelloMessageTemplatePath, i.FetchConfig, body) + if err != nil { + log.Print("Error", err.Error()) + } + + message.Body = body.String() + log.Print(message, pointURL) +} diff --git a/templates/hello_message.tpl b/templates/hello_message.tpl new file mode 100644 index 0000000..66621ba --- /dev/null +++ b/templates/hello_message.tpl @@ -0,0 +1,13 @@ +Привет! + +Это первое сообщение от {{.Name}} - бота слежения за задачами в репозитории на Github. +Сейчас я наблюдаю за проектом {{.Owner}}/{{.Repo}}. + +Текущие открытые задачи: +{{range $i, $issue := .Issues}} +* {{$issue.Title}} {{$issue.URL}} +{{end}} + +Новые события будут публиковаться в этой теме. + ++++ {{.Name}}: {{.Sources}}. GPLv3