comparison calsync/gcalclient/gcalclient.go @ 52:5f7c393577e9

now gets sync updates (for 30 sec)
author drewp@bigasterisk.com
date Mon, 19 Aug 2024 19:19:13 -0700
parents a9b720445bcf
children f248f018a663
comparison
equal deleted inserted replaced
51:a9b720445bcf 52:5f7c393577e9
33 33
34 func MakeEventUrl3(googleCalId string, evId string) string { 34 func MakeEventUrl3(googleCalId string, evId string) string {
35 return MakeEventUrl("http://bigasterisk.com/calendar/"+ 35 return MakeEventUrl("http://bigasterisk.com/calendar/"+
36 url.QueryEscape(googleCalId), evId) 36 url.QueryEscape(googleCalId), evId)
37 } 37 }
38
38 func New(ctx context.Context) (*GCalClient, error) { 39 func New(ctx context.Context) (*GCalClient, error) {
39 // If modifying these scopes, delete your previously saved token.json.
40 err, srv := newService(ctx) 40 err, srv := newService(ctx)
41 if err != nil { 41 if err != nil {
42 log.Fatalf("Unable to retrieve Calendar client: %v", err) 42 log.Fatalf("Unable to retrieve Calendar client: %v", err)
43 } 43 }
44 return &GCalClient{ctx, srv}, nil 44 return &GCalClient{ctx, srv}, nil
56 } 56 }
57 list.Items = list.Items[:4] 57 list.Items = list.Items[:4]
58 return list.Items, nil 58 return list.Items, nil
59 } 59 }
60 60
61 // FindEvents considers all calendars 61 type FindEventsMessage struct {
62 func (gc *GCalClient) FindEvents(mc *mongoclient.MongoClient, s time.Time, maxEventsPerCalendar int64) ([]*CalendarEvent, error) { 62 // either non-nil this:
63 Event *CalendarEvent
64 // or these:
65 CalId string
66 OlderThanThisIsDeletable time.Time
67 }
68
69 // FindEvents considers all calendars. It runs forever.
70 func (gc *GCalClient) FindEvents(
71 mc *mongoclient.MongoClient,
72 // For each calendar, after events in this time range have been sent to
73 // `out`, the chan will get the other kind of FindEventsMessage (CalId,
74 // ...). That message signals that the caller may cull old events on the given
75 // calendar. After that point, all events will be updates (including
76 // deletes).
77 initialFillStart, initialFillEnd time.Time,
78 out chan *FindEventsMessage,
79 ) error {
80
63 cals, err := mc.GetAllCals() 81 cals, err := mc.GetAllCals()
64 if err != nil { 82 if err != nil {
65 return nil, err 83 return err
66 } 84 }
67 log.Println("reading", len(cals), "calendars") 85 log.Println("reading", len(cals), "calendars")
68 ret := make([]*CalendarEvent, 0) 86 for calNum, cal := range cals {
69 for _, cal := range cals { 87 t := time.Now()
70 calUrl := cal.Url 88 log.Println(" cal", calNum, cal.Url)
71 events, err := gc.srv. 89 log.Println(" cal", calNum, "readEventsInRange", "from", initialFillStart, "to", initialFillEnd)
72 Events.List(cal.GoogleId). 90 syncToken, err := gc.readEventsInRange(&cal, initialFillStart, initialFillEnd, out)
91 if err != nil {
92 return err
93 }
94
95 out <- &FindEventsMessage{nil, cal.GoogleId, t}
96
97 ew := gc.NewEventWatch(&cal, t, syncToken, out)
98
99 for loop := 0; loop < 30; loop++ {
100 log.Println("")
101 log.Println("tail loop", loop, "for", cal.Url)
102 err := ew.GetMoreEvents()
103 if err != nil {
104 return err
105 }
106 time.Sleep(2 * time.Second)
107 }
108 }
109
110 return nil
111 }
112
113 // Synchronous.
114 func (gc *GCalClient) readEventsInRange(
115 cal *mongoclient.MongoCal,
116 initialFillStart, initialFillEnd time.Time,
117 out chan *FindEventsMessage,
118 ) (string, error) {
119 log.Println(
120 " get initial events for", cal.Url, "between",
121 initialFillStart, "and", initialFillEnd)
122
123 pageToken := ""
124 syncToken := ""
125
126 for {
127 log.Println(" getting another page", pageToken)
128 events, err := rangedEventsCall(gc.srv, cal.GoogleId, initialFillStart, initialFillEnd, pageToken).Do()
129 if err != nil {
130 return "", err
131 }
132
133 log.Println(" got", len(events.Items), "events, sync=", events.NextSyncToken)
134 if len(events.Items) == 0 {
135 break
136 }
137
138 sendEvents(events, cal, out)
139
140 syncToken = events.NextSyncToken
141 if events.NextPageToken == "" {
142 break
143 }
144 pageToken = events.NextPageToken
145 }
146 return syncToken, nil
147 }
148
149 // Send a page of calendar.Events over a channel, as CalendarEvent structs.
150 func sendEvents(events *calendar.Events, cal *mongoclient.MongoCal, out chan *FindEventsMessage) {
151 for _, event := range events.Items {
152 if event.Status == "cancelled" {
153 log.Fatal("todo")
154 }
155 out <- &FindEventsMessage{
156 Event: &CalendarEvent{
157 Event: event,
158 CalendarUrl: cal.Url,
159 EventUrl: MakeEventUrl(cal.Url, event.Id),
160 }}
161 }
162 }
163
164 type eventWatch struct {
165 gc *GCalClient
166 cal *mongoclient.MongoCal
167 nextSyncToken string
168 nextPageToken string
169 modSince time.Time
170 out chan *FindEventsMessage
171 }
172
173 func (gc *GCalClient) NewEventWatch(
174 cal *mongoclient.MongoCal,
175 modSince time.Time,
176 syncToken string,
177 out chan *FindEventsMessage,
178 ) *eventWatch {
179 ew := &eventWatch{gc, cal, syncToken, "", modSince, out}
180 return ew
181 }
182
183 // Call this when there are likely new changes to sync.
184 func (w *eventWatch) GetMoreEvents() error {
185 call := syncEventsCall(w.gc.srv, w.cal.GoogleId)
186 log.Println("listing events on", w.cal.GoogleId, "with")
187
188 if w.nextPageToken != "" {
189 call = call.PageToken(w.nextPageToken)
190 log.Println(" pageToken", w.nextPageToken)
191 } else if w.nextSyncToken != "" {
192 call = call.SyncToken(w.nextSyncToken)
193 log.Println(" syncToken", w.nextSyncToken)
194 } else {
195 call = call.UpdatedMin((w.modSince.Format(time.RFC3339)))
196 log.Println(" updatedMin", w.modSince.Format(time.RFC3339))
197 }
198 ret, err := call.Do()
199 if err != nil {
200 return err
201 }
202 w.nextSyncToken = ret.NextSyncToken
203 w.nextPageToken = ret.NextPageToken
204 log.Println(len(ret.Items), "more events received")
205 sendEvents(ret, w.cal, w.out)
206 log.Println("got nextSyncToken=", w.nextSyncToken)
207 log.Println("got nextPageToken=", w.nextPageToken)
208 return err
209 }
210
211 func rangedEventsCall(srv *calendar.Service, calGoogleId string,
212 initialFillStart, initialFillEnd time.Time, pageToken string) *calendar.EventsListCall {
213 return srv.Events.List(calGoogleId).
73 ShowDeleted(false). 214 ShowDeleted(false).
74 SingleEvents(true). 215 SingleEvents(true).
75 TimeMin(s.Format(time.RFC3339)). 216 TimeMin(initialFillStart.Format(time.RFC3339)).
76 MaxResults(maxEventsPerCalendar). 217 TimeMax(initialFillEnd.Format(time.RFC3339)).
77 OrderBy("startTime"). 218 MaxResults(4).
78 Do() 219 PageToken(pageToken)
79 if err != nil { 220 }
80 return nil, err 221
81 } 222 func syncEventsCall(srv *calendar.Service, calGoogleId string) *calendar.EventsListCall {
82 log.Println(len(events.Items), "events from", calUrl) 223 return srv.Events.List(calGoogleId).
83 for _, event := range events.Items { 224 ShowDeleted(true).
84 ev := &CalendarEvent{ 225 SingleEvents(true).
85 Event: event, 226 MaxResults(4)
86 CalendarUrl: calUrl, 227 }
87 EventUrl: MakeEventUrl(calUrl, event.Id),
88 }
89 ret = append(ret, ev)
90 }
91 }
92 return ret, nil
93 }