func (s *Service) fetchEvents(
	ctx context.Context,
	repos map[int64]repository,  
	commits map[string]event.Commit,  
) (
	events []*githubv3.Event,
	_ map[int64]repository,  
	_ map[string]event.Commit,  
	prs map[string]bool,  
	eventIDs map[*githubv3.Event]uint64,  
	pollInterval time.Duration,
	err error,
) {
	events, resp, err := s.clV3.Activity.ListEventsPerformedByUser(ctx, s.user.Login, true, &githubv3.ListOptions{PerPage: 100})
	if err != nil {
		return nil, nil, nil, nil, nil, 0, err
	}
	if pi, err := strconv.Atoi(resp.Header.Get("X-Poll-Interval")); err == nil {
		pollInterval = time.Duration(pi) * time.Second
	}
	prs = make(map[string]bool)
	eventIDs = make(map[*githubv3.Event]uint64)
	usedRepos := make(map[int64]bool)     
	usedCommits := make(map[string]bool)  
	for _, e := range events {
		payload, err := e.ParsePayload()
		if err != nil {
			return nil, nil, nil, nil, nil, 0, fmt.Errorf("fetchEvents: ParsePayload failed: %v", err)
		}
		usedRepos[*e.Repo.ID] = true
		if _, ok := repos[*e.Repo.ID]; !ok {
			modulePath, err := s.fetchModulePath(ctx, *e.Repo.ID, "github.com/"+*e.Repo.Name)
			if err != nil && strings.HasPrefix(err.Error(), "Could not resolve to a node ") {  
				log.Printf("fetchModulePath: repository id=%d name=%q was not found: %v\n", *e.Repo.ID, *e.Repo.Name, err)
				modulePath = "github.com/" + *e.Repo.Name
			} else if err != nil {
				return nil, nil, nil, nil, nil, 0, fmt.Errorf("fetchModulePath: %v", err)
			}
			repos[*e.Repo.ID] = repository{ModulePath: modulePath}
		}
		switch p := payload.(type) {
		case *githubv3.PushEvent:
			for _, c := range p.Commits {
				usedCommits[*c.SHA] = true
				if _, ok := commits[*c.SHA]; ok {
					continue
				}
				commit, err := s.fetchCommit(ctx, *e.Repo.ID, *c.SHA)
				if err != nil && strings.HasPrefix(err.Error(), "Could not resolve to a node ") {  
					log.Printf("fetchEvents: commit %s@%s was not found: %v\n", *e.Repo.Name, *c.SHA, err)
					avatarURL := "https://secure.gravatar.com/avatar?d=mm&f=y&s=96"
					if *c.Author.Email == s.user.Email {
						avatarURL = s.user.AvatarURL
					}
					commit = event.Commit{
						SHA:             *c.SHA,
						Message:         *c.Message,
						AuthorAvatarURL: avatarURL,
					}
				} else if err != nil {
					return nil, nil, nil, nil, nil, 0, fmt.Errorf("fetchCommit: %v", err)
				}
				commits[*c.SHA] = commit
			}
		case *githubv3.CommitCommentEvent:
			usedCommits[*p.Comment.CommitID] = true
			if _, ok := commits[*p.Comment.CommitID]; ok {
				continue
			}
			commit, err := s.fetchCommit(ctx, *e.Repo.ID, *p.Comment.CommitID)
			if err != nil && strings.HasPrefix(err.Error(), "Could not resolve to a node ") {  
				log.Printf("fetchEvents: commit %s@%s was not found: %v\n", *e.Repo.Name, *p.Comment.CommitID, err)
				commit = event.Commit{
					SHA:             *p.Comment.CommitID,
					AuthorAvatarURL: "https://secure.gravatar.com/avatar?d=mm&f=y&s=96",
				}
			} else if err != nil {
				return nil, nil, nil, nil, nil, 0, fmt.Errorf("fetchCommit: %v", err)
			}
			commits[*p.Comment.CommitID] = commit
		case *githubv3.IssueCommentEvent:
			if p.Issue.PullRequestLinks == nil {
				continue
			}
			if _, ok := prs[*p.Issue.PullRequestLinks.URL]; ok {
				continue
			}
			merged, err := s.fetchPullRequestMerged(ctx, *p.Issue.PullRequestLinks.URL)
			if err != nil {
				return nil, nil, nil, nil, nil, 0, fmt.Errorf("fetchPullRequestMerged: %v", err)
			}
			prs[*p.Issue.PullRequestLinks.URL] = merged
		case *githubv3.IssuesEvent:
			if *p.Action == "closed" || *p.Action == "reopened" {
				id, err := s.fetchEventID(ctx, e)
				if err != nil {
					log.Printf("fetchEvents: fetchEventID: %v\n", err)
				} else {
					eventIDs[e] = id
				}
			}
		case *githubv3.PullRequestEvent:
			if *p.Action == "closed" || *p.Action == "reopened" {
				id, err := s.fetchEventID(ctx, e)
				if err != nil {
					log.Printf("fetchEvents: fetchEventID: %v\n", err)
				} else {
					eventIDs[e] = id
				}
			}
		}
	}
	for id := range repos {
		if !usedRepos[id] {
			delete(repos, id)
		}
	}
	for sha := range commits {
		if !usedCommits[sha] {
			delete(commits, sha)
		}
	}
	return events, repos, commits, prs, eventIDs, pollInterval, nil
}
fetchEvents fetches events, repository module paths, mentioned commits and PRs from GitHub.
Provided repos and commits must be non-nil, and they're used as a starting point.
Only missing repos and commits are fetched, and unused ones are removed at the end.
package github
import (
	"bytes"
	"context"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"runtime/debug"
	"strconv"
	"strings"
	"time"
	"dmitri.shuralyov.com/go/prefixtitle"
	"dmitri.shuralyov.com/route/github"
	"dmitri.shuralyov.com/state"
	githubv3 "github.com/google/go-github/github"
	"github.com/shurcooL/events/event"
	"github.com/shurcooL/githubv4"
	"github.com/shurcooL/users"
	"golang.org/x/mod/modfile"
)
func (s *Service) pollList() (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("internal panic: %v\n\n%s", e, debug.Stack())
		}
	}()
	for {
		s.list.mu.Lock()
		repos := make(map[int64]repository, len(s.list.repos))
		for id, r := range s.list.repos {
			repos[id] = r
		}
		commits := make(map[string]event.Commit, len(s.list.commits))
		for sha, c := range s.list.commits {
			commits[sha] = c
		}
		s.list.mu.Unlock()
		events, repos, commits, prs, eventIDs, pollInterval, fetchError := s.fetchEvents(context.Background(), repos, commits)
		if fetchError != nil {
			log.Println("fetchEvents:", fetchError)
		}
		s.list.mu.Lock()
		if fetchError == nil {
			s.list.events, s.list.repos, s.list.commits, s.list.prs, s.list.eventIDs = events, repos, commits, prs, eventIDs
		}
		s.list.fetchError = fetchError
		s.list.mu.Unlock()
		if pollInterval < time.Minute {
			pollInterval = time.Minute
		}
		time.Sleep(pollInterval)
	}
}