this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Create and delete bookmarks via DM (#8)

authored by

Will Andrews and committed by
GitHub
ef0ce242 ebc9ad7a

+520 -90
+6 -2
bookmark_handler.go
··· 38 38 return 39 39 } 40 40 41 - uriSplit := strings.Split(atPostURI, "/") 42 - rkey := uriSplit[len(uriSplit)-1] 41 + rkey := getRKeyFromATURI(atPostURI) 43 42 44 43 postResp, err := bsky.FeedGetPosts(r.Context(), s.xrpcClient, []string{atPostURI}) 45 44 if err != nil { ··· 199 198 200 199 return resDid.Did, nil 201 200 } 201 + 202 + func getRKeyFromATURI(uri string) string { 203 + uriSplit := strings.Split(uri, "/") 204 + return uriSplit[len(uriSplit)-1] 205 + }
+490
dm_handler.go
··· 1 + package main 2 + 3 + import ( 4 + "bytes" 5 + "context" 6 + "encoding/json" 7 + "fmt" 8 + "io" 9 + "log/slog" 10 + "net/http" 11 + "os" 12 + "strings" 13 + "time" 14 + 15 + "github.com/pkg/errors" 16 + ) 17 + 18 + const ( 19 + httpClientTimeoutDuration = time.Second * 5 20 + transportIdleConnTimeoutDuration = time.Second * 90 21 + baseBskyURL = "https://bsky.social/xrpc" 22 + ) 23 + 24 + type auth struct { 25 + AccessJwt string `json:"accessJwt"` 26 + RefershJWT string `json:"refreshJwt"` 27 + Did string `json:"did"` 28 + } 29 + 30 + type accessData struct { 31 + handle string 32 + appPassword string 33 + } 34 + 35 + type ListConvosResponse struct { 36 + Cursor string `json:"cursor"` 37 + Convos []Convo `json:"convos"` 38 + } 39 + 40 + type Convo struct { 41 + ID string `json:"id"` 42 + Members []ConvoMember `json:"members"` 43 + UnreadCount int `json:"unreadCount"` 44 + } 45 + 46 + type ConvoMember struct { 47 + Did string `json:"did"` 48 + Handle string `json:"handle"` 49 + } 50 + 51 + type ErrorResponse struct { 52 + Error string `json:"error"` 53 + } 54 + 55 + type MessageResp struct { 56 + Messages []Message `json:"messages"` 57 + Cursor string `json:"cursor"` 58 + } 59 + 60 + type Message struct { 61 + ID string `json:"id"` 62 + Sender MessageSender `json:"sender"` 63 + Text string `json:"text"` 64 + Embed MessageEmbed `json:"embed"` 65 + } 66 + 67 + type MessageEmbed struct { 68 + Record MessageEmbedRecord `json:"record"` 69 + } 70 + 71 + type MessageEmbedRecord struct { 72 + URI string `json:"uri"` 73 + Author MessageEmbedRecordAuthor `json:"author"` 74 + Value MessageEmbedPost `json:"value"` 75 + } 76 + 77 + type MessageEmbedRecordAuthor struct { 78 + Did string `json:"did"` 79 + Handle string `json:"handle"` 80 + } 81 + 82 + type MessageEmbedPost struct { 83 + Text string `json:"text"` 84 + } 85 + 86 + type MessageSender struct { 87 + Did string `json:"did"` 88 + } 89 + 90 + type UpdateMessageReadRequest struct { 91 + ConvoID string `json:"convoId"` 92 + MessageID string `json:"messageId"` 93 + } 94 + 95 + type DmService struct { 96 + httpClient *http.Client 97 + accessData accessData 98 + auth auth 99 + timerDuration time.Duration 100 + pdsURL string 101 + bookmarkStore BookmarkStore 102 + } 103 + 104 + func NewDmService(bookmarkStore BookmarkStore, timerDuration time.Duration) (*DmService, error) { 105 + httpClient := http.Client{ 106 + Timeout: httpClientTimeoutDuration, 107 + Transport: &http.Transport{ 108 + IdleConnTimeout: transportIdleConnTimeoutDuration, 109 + }, 110 + } 111 + 112 + accessHandle := os.Getenv("MESSAGING_ACCESS_HANDLE") 113 + accessAppPassword := os.Getenv("MESSAGING_ACCESS_APP_PASSWORD") 114 + pdsURL := os.Getenv("MESSAGING_PDS_URL") 115 + 116 + service := DmService{ 117 + httpClient: &httpClient, 118 + accessData: accessData{ 119 + handle: accessHandle, 120 + appPassword: accessAppPassword, 121 + }, 122 + timerDuration: timerDuration, 123 + pdsURL: pdsURL, 124 + bookmarkStore: bookmarkStore, 125 + } 126 + 127 + auth, err := service.Authenicate() 128 + if err != nil { 129 + return nil, fmt.Errorf("authenticating: %w", err) 130 + } 131 + 132 + service.auth = auth 133 + 134 + return &service, nil 135 + } 136 + 137 + func (d *DmService) Start(ctx context.Context) { 138 + go d.RefreshTask(ctx) 139 + 140 + timer := time.NewTimer(d.timerDuration) 141 + defer timer.Stop() 142 + 143 + for { 144 + select { 145 + case <-ctx.Done(): 146 + slog.Warn("context canceled - stopping dm task") 147 + return 148 + case <-timer.C: 149 + err := d.HandleMessageTimer(ctx) 150 + if err != nil { 151 + slog.Error("handle message timer", "error", err) 152 + } 153 + timer.Reset(d.timerDuration) 154 + } 155 + } 156 + } 157 + 158 + func (d *DmService) HandleMessageTimer(ctx context.Context) error { 159 + convoResp, err := d.GetUnreadMessages() 160 + if err != nil { 161 + return fmt.Errorf("get unread messages: %w", err) 162 + } 163 + 164 + // TODO: handle the cursor pagination 165 + 166 + for _, convo := range convoResp.Convos { 167 + if convo.UnreadCount == 0 { 168 + continue 169 + } 170 + 171 + messageResp, err := d.GetMessages(ctx, convo.ID) 172 + if err != nil { 173 + slog.Error("failed to get messages for convo", "error", err, "convo id", convo.ID) 174 + continue 175 + } 176 + 177 + unreadCount := convo.UnreadCount 178 + unreadMessages := make([]Message, 0, convo.UnreadCount) 179 + // TODO: handle cursor pagination 180 + for _, msg := range messageResp.Messages { 181 + // TODO: techincally if I get to a message that's from the bot account, then there shouldn't be 182 + // an more unread messages? 183 + if msg.Sender.Did == d.auth.Did { 184 + continue 185 + } 186 + 187 + unreadMessages = append(unreadMessages, msg) 188 + unreadCount-- 189 + if unreadCount == 0 { 190 + break 191 + } 192 + } 193 + 194 + for _, msg := range unreadMessages { 195 + d.handleMessage(msg) 196 + 197 + err = d.MarkMessageRead(msg.ID, convo.ID) 198 + if err != nil { 199 + slog.Error("marking message read", "error", err) 200 + continue 201 + } 202 + } 203 + } 204 + 205 + return nil 206 + } 207 + 208 + func (d *DmService) handleMessage(msg Message) { 209 + // for now, ignore messages that don't have linked posts in them 210 + if msg.Embed.Record.URI == "" { 211 + return 212 + } 213 + 214 + rkey := getRKeyFromATURI(msg.Embed.Record.URI) 215 + msgAction := strings.ToLower(msg.Text) 216 + 217 + var err error 218 + 219 + switch { 220 + case strings.Contains(msgAction, "delete"): 221 + err = d.handleDeleteBookmark(msg) 222 + default: 223 + err = d.handleCreateBookmark(msg) 224 + } 225 + 226 + if err != nil { 227 + // TODO: perhaps continue here so that we don't mark the message as read so it can be tried again? Or perhaps send a message 228 + // too the user? 229 + slog.Error("failed to handle bookmark message", "error", err, "rkey", rkey, "sender", msg.Sender.Did) 230 + } 231 + } 232 + 233 + func (d *DmService) handleCreateBookmark(msg Message) error { 234 + content := msg.Embed.Record.Value.Text 235 + if len(content) > 75 { 236 + content = fmt.Sprintf("%s...", content[:75]) 237 + } 238 + 239 + publicURI := getPublicPostURIFromATURI(msg.Embed.Record.URI, msg.Embed.Record.Author.Handle) 240 + 241 + rkey := getRKeyFromATURI(msg.Embed.Record.URI) 242 + 243 + err := d.bookmarkStore.CreateBookmark(rkey, publicURI, msg.Embed.Record.URI, msg.Embed.Record.Author.Did, msg.Embed.Record.Author.Handle, msg.Sender.Did, content) 244 + if err != nil { 245 + return fmt.Errorf("creating bookmark: %w", err) 246 + } 247 + return nil 248 + } 249 + 250 + func (d *DmService) handleDeleteBookmark(msg Message) error { 251 + rkey := getRKeyFromATURI(msg.Embed.Record.URI) 252 + 253 + err := d.bookmarkStore.DeleteFeedPostsForBookmarkedPostURIandUserDID(msg.Embed.Record.URI, msg.Sender.Did) 254 + if err != nil { 255 + return fmt.Errorf("failed to delete feed posts of replies to bookmark for user: %w", err) 256 + } 257 + 258 + err = d.bookmarkStore.DeleteBookmark(rkey, msg.Sender.Did) 259 + if err != nil { 260 + return fmt.Errorf("failed to delete bookmark: %w", err) 261 + } 262 + 263 + return nil 264 + } 265 + 266 + func getPublicPostURIFromATURI(atURI, authorHandle string) string { 267 + atSplit := strings.Split(atURI, "app.bsky.feed.post") 268 + if len(atSplit) < 2 { 269 + slog.Error("can't get public post URI from AT uri", "at uri", atURI) 270 + return "" 271 + } 272 + return fmt.Sprintf("https://bsky.app/profile/%s/post%s", authorHandle, atSplit[1]) 273 + } 274 + 275 + func (d *DmService) GetUnreadMessages() (ListConvosResponse, error) { 276 + url := fmt.Sprintf("%s/xrpc/chat.bsky.convo.listConvos?readState=unread", d.pdsURL) 277 + request, err := http.NewRequest("GET", url, nil) 278 + if err != nil { 279 + return ListConvosResponse{}, fmt.Errorf("create new list convos http request: %w", err) 280 + } 281 + 282 + request.Header.Add("Content-Type", "application/json") 283 + request.Header.Add("Accept", "application/json") 284 + request.Header.Add("Atproto-Proxy", "did:web:api.bsky.chat#bsky_chat") 285 + request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", d.auth.AccessJwt)) 286 + 287 + resp, err := d.httpClient.Do(request) 288 + if err != nil { 289 + return ListConvosResponse{}, fmt.Errorf("do http request to list convos: %w", err) 290 + } 291 + defer resp.Body.Close() 292 + 293 + if resp.StatusCode != http.StatusOK { 294 + var errorResp ErrorResponse 295 + err = decodeResp(resp.Body, &errorResp) 296 + if err != nil { 297 + return ListConvosResponse{}, err 298 + } 299 + 300 + return ListConvosResponse{}, fmt.Errorf("listing convos responded with code %d: %s", resp.StatusCode, errorResp.Error) 301 + } 302 + 303 + var listConvoResp ListConvosResponse 304 + err = decodeResp(resp.Body, &listConvoResp) 305 + if err != nil { 306 + return ListConvosResponse{}, err 307 + } 308 + 309 + return listConvoResp, nil 310 + } 311 + 312 + func (d *DmService) MarkMessageRead(messageID, convoID string) error { 313 + bodyReq := UpdateMessageReadRequest{ 314 + ConvoID: convoID, 315 + MessageID: messageID, 316 + } 317 + 318 + bodyB, err := json.Marshal(bodyReq) 319 + if err != nil { 320 + return fmt.Errorf("marshal update message request body: %w", err) 321 + } 322 + 323 + r := bytes.NewReader(bodyB) 324 + 325 + url := fmt.Sprintf("%s/xrpc/chat.bsky.convo.updateRead", d.pdsURL) 326 + request, err := http.NewRequest("POST", url, r) 327 + if err != nil { 328 + return fmt.Errorf("create new list convos http request: %w", err) 329 + } 330 + 331 + request.Header.Add("Content-Type", "application/json") 332 + request.Header.Add("Accept", "application/json") 333 + request.Header.Add("Atproto-Proxy", "did:web:api.bsky.chat#bsky_chat") 334 + request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", d.auth.AccessJwt)) 335 + 336 + resp, err := d.httpClient.Do(request) 337 + if err != nil { 338 + return fmt.Errorf("do http request to update message read: %w", err) 339 + } 340 + defer resp.Body.Close() 341 + 342 + if resp.StatusCode == http.StatusOK { 343 + return nil 344 + } 345 + 346 + var errorResp ErrorResponse 347 + err = decodeResp(resp.Body, &errorResp) 348 + if err != nil { 349 + return err 350 + } 351 + 352 + return fmt.Errorf("listing convos responded with code %d: %s", resp.StatusCode, errorResp.Error) 353 + 354 + } 355 + 356 + func (d *DmService) Authenicate() (auth, error) { 357 + url := fmt.Sprintf("%s/com.atproto.server.createSession", baseBskyURL) 358 + 359 + requestData := map[string]interface{}{ 360 + "identifier": d.accessData.handle, 361 + "password": d.accessData.appPassword, 362 + } 363 + 364 + data, err := json.Marshal(requestData) 365 + if err != nil { 366 + return auth{}, errors.Wrap(err, "failed to marshal request") 367 + } 368 + 369 + r := bytes.NewReader(data) 370 + 371 + request, err := http.NewRequest("POST", url, r) 372 + if err != nil { 373 + return auth{}, errors.Wrap(err, "failed to create request") 374 + } 375 + 376 + request.Header.Add("Content-Type", "application/json") 377 + 378 + resp, err := d.httpClient.Do(request) 379 + if err != nil { 380 + return auth{}, errors.Wrap(err, "failed to make request") 381 + } 382 + defer resp.Body.Close() 383 + 384 + var loginResp auth 385 + err = decodeResp(resp.Body, &loginResp) 386 + if err != nil { 387 + return auth{}, err 388 + } 389 + 390 + return loginResp, nil 391 + } 392 + 393 + func (d *DmService) RefreshTask(ctx context.Context) { 394 + timer := time.NewTimer(time.Hour) 395 + defer timer.Stop() 396 + 397 + for { 398 + select { 399 + case <-ctx.Done(): 400 + return 401 + case <-timer.C: 402 + err := d.RefreshAuthenication(ctx) 403 + if err != nil { 404 + slog.Error("handle refresh auth timer", "error", err) 405 + // TODO: better retry with backoff probably 406 + timer.Reset(time.Minute) 407 + continue 408 + } 409 + timer.Reset(time.Hour) 410 + } 411 + } 412 + } 413 + 414 + func (d *DmService) RefreshAuthenication(ctx context.Context) error { 415 + url := fmt.Sprintf("%s/com.atproto.server.refreshSession", baseBskyURL) 416 + 417 + request, err := http.NewRequest("POST", url, nil) 418 + if err != nil { 419 + return errors.Wrap(err, "failed to create request") 420 + } 421 + 422 + request.Header.Add("Content-Type", "application/json") 423 + request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", d.auth.RefershJWT)) 424 + 425 + resp, err := d.httpClient.Do(request) 426 + if err != nil { 427 + return errors.Wrap(err, "failed to make request") 428 + } 429 + defer resp.Body.Close() 430 + 431 + var loginResp auth 432 + err = decodeResp(resp.Body, &loginResp) 433 + if err != nil { 434 + return err 435 + } 436 + 437 + d.auth = loginResp 438 + 439 + return nil 440 + } 441 + 442 + func (d *DmService) GetMessages(ctx context.Context, convoID string) (MessageResp, error) { 443 + url := fmt.Sprintf("%s/xrpc/chat.bsky.convo.getMessages?convoId=%s", d.pdsURL, convoID) 444 + request, err := http.NewRequest("GET", url, nil) 445 + if err != nil { 446 + return MessageResp{}, fmt.Errorf("create new get messages http request: %w", err) 447 + } 448 + 449 + request.Header.Add("Content-Type", "application/json") 450 + request.Header.Add("Accept", "application/json") 451 + request.Header.Add("Atproto-Proxy", "did:web:api.bsky.chat#bsky_chat") 452 + request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", d.auth.AccessJwt)) 453 + 454 + resp, err := d.httpClient.Do(request) 455 + if err != nil { 456 + return MessageResp{}, fmt.Errorf("do http request to get messages: %w", err) 457 + } 458 + defer resp.Body.Close() 459 + 460 + if resp.StatusCode != http.StatusOK { 461 + var errorResp ErrorResponse 462 + err = decodeResp(resp.Body, &errorResp) 463 + if err != nil { 464 + return MessageResp{}, err 465 + } 466 + 467 + return MessageResp{}, fmt.Errorf("listing convos responded with code %d: %s", resp.StatusCode, errorResp.Error) 468 + } 469 + 470 + var messageResp MessageResp 471 + err = decodeResp(resp.Body, &messageResp) 472 + if err != nil { 473 + return MessageResp{}, err 474 + } 475 + 476 + return messageResp, nil 477 + } 478 + 479 + func decodeResp(body io.Reader, result any) error { 480 + resBody, err := io.ReadAll(body) 481 + if err != nil { 482 + return errors.Wrap(err, "failed to read response") 483 + } 484 + 485 + err = json.Unmarshal(resBody, result) 486 + if err != nil { 487 + return errors.Wrap(err, "failed to unmarshal response") 488 + } 489 + return nil 490 + }
+1
feedgenerator.go
··· 11 11 12 12 type feedStore interface { 13 13 GetUsersFeed(usersDID string, cursor int64, limit int) ([]store.FeedPost, error) 14 + AddFeedPost(feedPost store.FeedPost) error 14 15 } 15 16 16 17 type FeedGenerator struct {
+5 -2
go.mod
··· 16 16 github.com/haileyok/atproto-oauth-golang v0.0.2 17 17 github.com/joho/godotenv v1.5.1 18 18 github.com/lestrrat-go/jwx/v2 v2.1.4 19 + github.com/pkg/errors v0.9.1 20 + github.com/stretchr/testify v1.10.0 19 21 ) 20 22 21 23 require ( ··· 23 25 github.com/bugsnag/panicwrap v1.3.4 // indirect 24 26 github.com/carlmjohnson/versioninfo v0.22.5 // indirect 25 27 github.com/cespare/xxhash/v2 v2.3.0 // indirect 28 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 26 29 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect 27 30 github.com/dustin/go-humanize v1.0.1 // indirect 28 31 github.com/felixge/httpsnoop v1.0.4 // indirect ··· 49 52 github.com/ipfs/go-log v1.0.5 // indirect 50 53 github.com/ipfs/go-log/v2 v2.5.1 // indirect 51 54 github.com/ipfs/go-metrics-interface v0.3.0 // indirect 52 - github.com/jbenet/goprocess v0.1.4 // indirect 53 55 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect 54 56 github.com/klauspost/compress v1.17.9 // indirect 55 57 github.com/klauspost/cpuid/v2 v2.2.10 // indirect ··· 67 69 github.com/multiformats/go-multihash v0.2.3 // indirect 68 70 github.com/multiformats/go-varint v0.0.7 // indirect 69 71 github.com/opentracing/opentracing-go v1.2.0 // indirect 70 - github.com/pkg/errors v0.9.1 // indirect 72 + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 71 73 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect 72 74 github.com/prometheus/client_golang v1.19.1 // indirect 73 75 github.com/prometheus/client_model v0.6.1 // indirect ··· 93 95 golang.org/x/time v0.8.0 // indirect 94 96 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect 95 97 google.golang.org/protobuf v1.34.2 // indirect 98 + gopkg.in/yaml.v3 v3.0.1 // indirect 96 99 lukechampine.com/blake3 v1.4.0 // indirect 97 100 modernc.org/libc v1.37.6 // indirect 98 101 modernc.org/mathutil v1.6.0 // indirect
+10 -86
go.sum
··· 1 1 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 - github.com/a-h/templ v0.2.793 h1:Io+/ocnfGWYO4VHdR0zBbf39PQlnzVCVVD+wEEs6/qY= 3 - github.com/a-h/templ v0.2.793/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w= 4 2 github.com/a-h/templ v0.3.833 h1:L/KOk/0VvVTBegtE0fp2RJQiBm7/52Zxv5fqlEHiQUU= 5 3 github.com/a-h/templ v0.3.833/go.mod h1:cAu4AiZhtJfBjMY0HASlyzvkrtjnHWPeEsyGK2YYmfk= 6 4 github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA= ··· 10 8 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 11 9 github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow= 12 10 github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q= 13 - github.com/bluesky-social/indigo v0.0.0-20250301025210-a4e0cc37e188 h1:1sQaG37xk08/rpmdhrmMkfQWF9kZbnfHm9Zav3bbSMk= 14 - github.com/bluesky-social/indigo v0.0.0-20250301025210-a4e0cc37e188/go.mod h1:NVBwZvbBSa93kfyweAmKwOLYawdVHdwZ9s+GZtBBVLA= 15 11 github.com/bluesky-social/indigo v0.0.0-20250305203105-a2e0aaff387e h1:cLBQmPW9D6F7F3FRoFeu5yyHzWo77nSun3plvHLM7yM= 16 12 github.com/bluesky-social/indigo v0.0.0-20250305203105-a2e0aaff387e/go.mod h1:NVBwZvbBSa93kfyweAmKwOLYawdVHdwZ9s+GZtBBVLA= 17 13 github.com/bluesky-social/jetstream v0.0.0-20241031234625-0ab10bd041fe h1:jduuyDfsiwWrPiN7psqDehepl68uxQ4UYCIgoqb1D4o= ··· 29 25 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 30 26 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 31 27 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 32 - github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= 33 - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= 34 - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= 35 28 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= 36 29 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= 37 30 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 38 31 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 32 + github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= 33 + github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 39 34 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 40 35 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 41 36 github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= ··· 46 41 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 47 42 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 48 43 github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= 49 - github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 50 - github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 51 44 github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= 52 45 github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 53 46 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 54 47 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 55 - github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= 56 48 github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= 57 49 github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 58 - github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 59 - github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 60 50 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 51 + github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 61 52 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 62 53 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 63 54 github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= ··· 77 68 github.com/haileyok/atproto-oauth-golang v0.0.2/go.mod h1:jcZ4GCjo5I5RuE/RsAXg1/b6udw7R4W+2rb/cGyTDK8= 78 69 github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 79 70 github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 80 - github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= 81 - github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 82 71 github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= 83 - github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= 84 - github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= 72 + github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 85 73 github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= 86 74 github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= 87 75 github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= ··· 92 80 github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= 93 81 github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs= 94 82 github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM= 95 - github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= 96 - github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= 97 83 github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= 98 84 github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= 99 - github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= 100 - github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= 101 85 github.com/ipfs/go-datastore v0.8.2 h1:Jy3wjqQR6sg/LhyY0NIePZC3Vux19nLtg7dx0TVqr6U= 102 86 github.com/ipfs/go-datastore v0.8.2/go.mod h1:W+pI1NsUsz3tcsAACMtfC+IZdnQTnC/7VfPoJBQuts0= 103 87 github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= ··· 108 92 github.com/ipfs/go-ipfs-ds-help v1.1.1/go.mod h1:75vrVCkSdSFidJscs8n4W+77AtTpCIAdDGAwjitJMIo= 109 93 github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= 110 94 github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs= 111 - github.com/ipfs/go-ipld-cbor v0.1.0 h1:dx0nS0kILVivGhfWuB6dUpMa/LAwElHPw1yOGYopoYs= 112 - github.com/ipfs/go-ipld-cbor v0.1.0/go.mod h1:U2aYlmVrJr2wsUBU67K4KgepApSZddGRDWBYR0H4sCk= 113 95 github.com/ipfs/go-ipld-cbor v0.2.0 h1:VHIW3HVIjcMd8m4ZLZbrYpwjzqlVUfjLM7oK4T5/YF0= 114 96 github.com/ipfs/go-ipld-cbor v0.2.0/go.mod h1:Cp8T7w1NKcu4AQJLqK0tWpd1nkgTxEVB5C6kVpLW6/0= 115 97 github.com/ipfs/go-ipld-format v0.6.0 h1:VEJlA2kQ3LqFSIm5Vu6eIlSxD/Ze90xtc4Meten1F5U= ··· 119 101 github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= 120 102 github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= 121 103 github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= 122 - github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= 123 - github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= 124 104 github.com/ipfs/go-metrics-interface v0.3.0 h1:YwG7/Cy4R94mYDUuwsBfeziJCVm9pBMJ6q/JR9V40TU= 125 105 github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY= 126 - github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= 127 - github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= 128 - github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= 129 106 github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 130 107 github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 131 108 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= ··· 136 113 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 137 114 github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= 138 115 github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 139 - github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= 140 - github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 141 116 github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= 142 117 github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= 143 118 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= ··· 147 122 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 148 123 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 149 124 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 150 - github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= 151 125 github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= 152 126 github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= 153 127 github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= 154 128 github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= 155 - github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= 156 - github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= 157 129 github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= 158 130 github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= 159 131 github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= 160 132 github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= 161 - github.com/lestrrat-go/jwx/v2 v2.0.12 h1:3d589+5w/b9b7S3DneICPW16AqTyYXB7VRjgluSDWeA= 162 - github.com/lestrrat-go/jwx/v2 v2.0.12/go.mod h1:Mq4KN1mM7bp+5z/W5HS8aCNs5RKZ911G/0y2qUjAQuQ= 163 133 github.com/lestrrat-go/jwx/v2 v2.1.4 h1:uBCMmJX8oRZStmKuMMOFb0Yh9xmEMgNJLgjuKKt4/qc= 164 134 github.com/lestrrat-go/jwx/v2 v2.1.4/go.mod h1:nWRbDFR1ALG2Z6GJbBXzfQaYyvn751KuuyySN2yR6is= 165 - github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 166 135 github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= 167 136 github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 137 + github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 138 + github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 168 139 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 169 140 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 170 141 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= ··· 216 187 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 217 188 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 218 189 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 219 - github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 220 - github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 221 - github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 222 190 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 223 191 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 224 192 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 225 193 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 226 194 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 227 - github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 228 - github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 229 195 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 230 196 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 231 197 github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 232 198 github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= 233 199 github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= 234 - github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4= 235 - github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= 236 200 github.com/whyrusleeping/cbor-gen v0.3.1 h1:82ioxmhEYut7LBVGhGq8xoRkXPLElVuh5mV67AFfdv0= 237 201 github.com/whyrusleeping/cbor-gen v0.3.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= 238 202 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 239 203 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 240 204 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 241 - github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 242 205 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA= 243 206 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 244 207 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= 245 208 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I= 246 209 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 247 210 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 248 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= 249 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= 250 211 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= 251 212 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= 252 - go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= 253 - go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= 254 213 go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= 255 214 go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= 256 - go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= 257 - go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= 258 215 go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= 259 216 go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= 260 - go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= 261 - go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= 217 + go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= 218 + go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= 219 + go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= 220 + go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= 262 221 go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= 263 222 go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= 264 223 go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= ··· 281 240 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 282 241 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 283 242 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 284 - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 285 - golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= 286 - golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 287 - golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 288 243 golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= 289 244 golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= 290 245 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= ··· 292 247 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 293 248 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 294 249 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 295 - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 296 - golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 297 250 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 298 251 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 299 252 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 300 253 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 301 254 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 302 - golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 303 255 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 304 - golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 305 - golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 306 - golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 307 256 golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 308 257 golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 309 258 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 310 259 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 311 260 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 312 261 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 313 - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 314 - golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 315 262 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 316 263 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 317 264 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 318 265 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 319 266 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 320 267 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 321 - golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 322 268 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 323 - golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 324 - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 325 - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 326 - golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 327 269 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 328 - golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 329 - golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 330 - golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 331 - golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 332 270 golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 333 271 golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 334 272 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 335 - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 336 - golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 337 - golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 338 - golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= 339 273 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 340 274 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 341 - golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 342 - golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 343 - golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 344 - golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 345 275 golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= 346 276 golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 347 277 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= ··· 354 284 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 355 285 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 356 286 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 357 - golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 358 - golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 359 287 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 360 288 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 361 289 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 362 290 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 363 - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= 364 - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 365 291 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= 366 292 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 367 293 google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= ··· 378 304 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 379 305 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 380 306 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 381 - lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= 382 - lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= 383 307 lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w= 384 308 lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0= 385 309 modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw=
+8
main.go
··· 83 83 go consumeLoop(ctx, store) 84 84 } 85 85 86 + dmService, err := NewDmService(store, time.Second*30) 87 + if err != nil { 88 + slog.Error("create new dm service", "error", err) 89 + _ = bugsnag.Notify(err) 90 + return 91 + } 92 + go dmService.Start(ctx) 93 + 86 94 server, err := NewServer(443, feeder, feedHost, feedDidBase, store) 87 95 if err != nil { 88 96 slog.Error("create new server", "error", err)