this repo has no description
0
fork

Configure Feed

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

a few more gosky commands

+228 -48
+6
README.md
··· 1 + # gosky 2 + 3 + A collection of utilities for interacting with atproto 4 + 5 + 6 +
+22 -1
api/atproto.go
··· 62 62 } 63 63 64 64 type CreateRecordResponse struct { 65 - Uri string `json:"url"` 65 + Uri string `json:"uri"` 66 66 Cid string `json:"cid"` 67 67 } 68 68 ··· 140 140 } 141 141 142 142 return &out, nil 143 + } 143 144 145 + type RecordResponse[T JsonLD] struct { 146 + Uri string `json:"uri"` 147 + Cid string `json:"cid"` 148 + Value T `json:"value"` 149 + 150 + } 151 + 152 + func RepoGetRecord[T JsonLD](atp *ATProto, ctx context.Context, user string, collection string, rkey string) (*RecordResponse[T], error) { 153 + params := map[string]interface{}{ 154 + "user": user, 155 + "collection": collection, 156 + "rkey": rkey, 157 + } 158 + 159 + var out RecordResponse[T] 160 + if err := atp.C.Do(ctx, xrpc.Query, "com.atproto.repo.getRecord", params, nil, &out); err != nil { 161 + return nil, err 162 + } 163 + 164 + return &out, nil 144 165 }
+17
api/bsky.go
··· 3 3 import ( 4 4 "context" 5 5 "encoding/json" 6 + "fmt" 6 7 7 8 "github.com/whyrusleeping/gosky/xrpc" 8 9 ) ··· 142 143 143 144 return &out, nil 144 145 } 146 + 147 + func (b *BskyApp) FeedSetVote(ctx context.Context, subject *PostRef, direction string) error { 148 + body := map[string]interface{}{ 149 + "subject": subject, 150 + "direction": direction, 151 + } 152 + 153 + var out map[string]interface{} 154 + if err := b.C.Do(ctx, xrpc.Procedure, "app.bsky.feed.setVote", nil, body, &out); err != nil { 155 + return err 156 + } 157 + 158 + fmt.Println(out) 159 + 160 + return nil 161 + }
+23 -10
carstore/bs.go
··· 51 51 } 52 52 53 53 type blockRef struct { 54 - gorm.Model 54 + ID uint `gorm:"primarykey"` 55 55 Cid string `gorm:"index"` 56 56 Shard uint 57 57 Offset int64 ··· 72 72 } 73 73 74 74 func (uv *userView) Has(ctx context.Context, k cid.Cid) (bool, error) { 75 - // TODO: for now, im using a join to ensure we only query blocks from the 76 - // correct user. maybe it makes sense to put the user in the blockRef 77 - // directly? tradeoff of time vs space 78 75 var count int64 79 76 if err := uv.cs.meta. 80 77 Model(blockRef{}). ··· 226 223 //if err != gorm.ErrRecordNotFound { 227 224 return nil, err 228 225 //} 226 + } 227 + 228 + if lastShard.Root != "" && lastShard.Root != prev.String() { 229 + return nil, fmt.Errorf("attempted a delta session on top of the wrong previous head") 229 230 } 230 231 231 232 return &DeltaSession{ ··· 411 412 User: ds.user, 412 413 } 413 414 415 + // TODO: there should be a way to create the shard and block_refs that 416 + // reference it in the same query, would save a lot of time 414 417 if err := ds.cs.meta.Create(&shard).Error; err != nil { 415 418 return err 416 419 } 417 420 418 421 var offset int64 = hnw 419 - brefs := make([]*blockRef, 0, len(ds.blks)) 422 + //brefs := make([]*blockRef, 0, len(ds.blks)) 423 + brefs := make([]map[string]interface{}, 0, len(ds.blks)) 420 424 for k, blk := range ds.blks { 421 425 nw, err := LdWrite(fi, k.Bytes(), blk.RawData()) 422 426 if err != nil { 423 427 return err 424 428 } 425 429 426 - brefs = append(brefs, &blockRef{ 427 - Cid: k.String(), 428 - Offset: offset, 429 - Shard: shard.ID, 430 + /* 431 + brefs = append(brefs, &blockRef{ 432 + Cid: k.String(), 433 + Offset: offset, 434 + Shard: shard.ID, 435 + }) 436 + */ 437 + // adding things to the db by map is the only way to get gorm to not 438 + // add the 'returning' clause, which costs a lot of time 439 + brefs = append(brefs, map[string]interface{}{ 440 + "cid": k.String(), 441 + "offset": offset, 442 + "shard": shard.ID, 430 443 }) 431 444 432 445 offset += nw 433 446 } 434 447 435 - if err := ds.cs.meta.Create(brefs).Error; err != nil { 448 + if err := ds.cs.meta.Table("block_refs").Create(brefs).Error; err != nil { 436 449 return err 437 450 } 438 451
+44 -2
carstore/repo_test.go
··· 10 10 "testing" 11 11 "time" 12 12 13 + sqlbs "github.com/ipfs/go-bs-sqlite3" 13 14 "github.com/ipfs/go-cid" 14 15 flatfs "github.com/ipfs/go-ds-flatfs" 15 16 blockstore "github.com/ipfs/go-ipfs-blockstore" ··· 31 32 } 32 33 33 34 dbstr := "file::memory:" 34 - db, err := gorm.Open(sqlite.Open(dbstr)) 35 + //dbstr := filepath.Join(tempdir, "foo.db") 36 + db, err := gorm.Open(sqlite.Open(dbstr), 37 + &gorm.Config{ 38 + SkipDefaultTransaction: true, 39 + }) 35 40 if err != nil { 36 41 return nil, nil, err 37 42 } ··· 52 57 return nil, nil, err 53 58 } 54 59 55 - ffds, err := flatfs.CreateOrOpen(tempdir, flatfs.IPFS_DEF_SHARD, true) 60 + ffds, err := flatfs.CreateOrOpen(tempdir, flatfs.IPFS_DEF_SHARD, false) 56 61 if err != nil { 57 62 return nil, nil, err 58 63 } ··· 235 240 head = nroot 236 241 } 237 242 } 243 + 244 + func BenchmarkRepoWritesSqlite(b *testing.B) { 245 + ctx := context.TODO() 246 + 247 + bs, err := sqlbs.Open("file::memory:", sqlbs.Options{}) 248 + if err != nil { 249 + b.Fatal(err) 250 + } 251 + 252 + ncid, err := setupRepo(ctx, bs) 253 + if err != nil { 254 + b.Fatal(err) 255 + } 256 + 257 + head := ncid 258 + b.ResetTimer() 259 + for i := 0; i < b.N; i++ { 260 + 261 + rr, err := repo.OpenRepo(ctx, bs, head) 262 + if err != nil { 263 + b.Fatal(err) 264 + } 265 + 266 + if err := rr.CreateRecord(ctx, "app.bsky.feed.post", &api.PostRecord{ 267 + Text: fmt.Sprintf("hey look its a tweet %s", time.Now()), 268 + }); err != nil { 269 + b.Fatal(err) 270 + } 271 + 272 + nroot, err := rr.Commit(ctx) 273 + if err != nil { 274 + b.Fatal(err) 275 + } 276 + 277 + head = nroot 278 + } 279 + }
+44 -4
cmd/gosky/main.go
··· 22 22 Value: "https://pds.staging.bsky.dev", 23 23 }, 24 24 &cli.StringFlag{ 25 - Name: "account", 25 + Name: "auth", 26 26 }, 27 27 } 28 28 app.Commands = []*cli.Command{ ··· 34 34 feedGetCmd, 35 35 feedGetAuthorCmd, 36 36 actorGetSuggestionsCmd, 37 + feedSetVoteCmd, 37 38 } 38 39 39 40 app.RunAndExitOnError() ··· 210 211 211 212 var feedGetCmd = &cli.Command{ 212 213 Name: "getFeed", 214 + Flags: []cli.Flag{ 215 + &cli.IntFlag{ 216 + Name: "count", 217 + Value: 100, 218 + }, 219 + }, 213 220 Action: func(cctx *cli.Context) error { 214 221 bsky, err := cliutil.GetBskyClient(cctx, true) 215 222 if err != nil { ··· 219 226 ctx := context.TODO() 220 227 221 228 algo := "reverse-chronological" 222 - tl, err := bsky.FeedGetTimeline(ctx, algo, 99, nil) 229 + tl, err := bsky.FeedGetTimeline(ctx, algo, cctx.Int("count"), nil) 223 230 if err != nil { 224 231 return err 225 232 } 226 233 227 - fmt.Println(tl.Cursor) 228 234 for _, it := range tl.Feed { 229 235 b, err := json.MarshalIndent(it, "", " ") 230 236 if err != nil { ··· 232 238 } 233 239 234 240 fmt.Println(string(b)) 235 - 236 241 } 237 242 238 243 return nil ··· 307 312 308 313 }, 309 314 } 315 + 316 + var feedSetVoteCmd = &cli.Command{ 317 + Name: "feedSetVote", 318 + Action: func(cctx *cli.Context) error { 319 + atpc, err := cliutil.GetATPClient(cctx, true) 320 + if err != nil { 321 + return err 322 + } 323 + 324 + bskyc, err := cliutil.GetBskyClient(cctx, true) 325 + if err != nil { 326 + return err 327 + } 328 + 329 + arg := cctx.Args().First() 330 + 331 + parts := strings.Split(arg, "/") 332 + last := parts[len(parts)-1] 333 + kind := parts[len(parts)-2] 334 + user := parts[2] 335 + 336 + ctx := context.TODO() 337 + resp, err := api.RepoGetRecord[*api.PostRecord](atpc, ctx, user, kind, last) 338 + if err != nil { 339 + return err 340 + } 341 + 342 + err = bskyc.FeedSetVote(ctx, &api.PostRef{Uri: resp.Uri, Cid: resp.Cid}, cctx.Args().Get(1)) 343 + if err != nil { 344 + return err 345 + } 346 + return nil 347 + 348 + }, 349 + }
+1
go.mod
··· 32 32 github.com/hashicorp/golang-lru v0.5.4 // indirect 33 33 github.com/ipfs/bbloom v0.0.4 // indirect 34 34 github.com/ipfs/go-blockservice v0.3.0 // indirect 35 + github.com/ipfs/go-bs-sqlite3 v0.0.0-20221122195556-bfcee1be620d // indirect 35 36 github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect 36 37 github.com/ipfs/go-ipfs-exchange-interface v0.1.0 // indirect 37 38 github.com/ipfs/go-ipfs-util v0.0.2 // indirect
+4
go.sum
··· 264 264 github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= 265 265 github.com/ipfs/go-blockservice v0.3.0 h1:cDgcZ+0P0Ih3sl8+qjFr2sVaMdysg/YZpLj5WJ8kiiw= 266 266 github.com/ipfs/go-blockservice v0.3.0/go.mod h1:P5ppi8IHDC7O+pA0AlGTF09jruB2h+oP3wVVaZl8sfk= 267 + github.com/ipfs/go-bs-sqlite3 v0.0.0-20220831203040-60c689e583c7 h1:fz87EjHgBPCs3+VIBz05HhRxJE3dmBq9SRZLSfB8QFI= 268 + github.com/ipfs/go-bs-sqlite3 v0.0.0-20220831203040-60c689e583c7/go.mod h1:teLHjK8IzDTwi0P/01GMsruXAYRediT8hY4ABcsusag= 269 + github.com/ipfs/go-bs-sqlite3 v0.0.0-20221122195556-bfcee1be620d h1:9V+GGXCuOfDiFpdAHz58q9mKLg447xp0cQKvqQrAwYE= 270 + github.com/ipfs/go-bs-sqlite3 v0.0.0-20221122195556-bfcee1be620d/go.mod h1:pMbnFyNAGjryYCLCe59YDLRv/ujdN+zGJBT1umlvYRM= 267 271 github.com/ipfs/go-car v0.0.4 h1:zLhxykvk4SFU4oIpgcIoiolVL3jqcK0hjqcQfUSs4dk= 268 272 github.com/ipfs/go-car v0.0.4/go.mod h1:eZX0EppfsvSQN8IsJnx57bheogWMgQjJVWU/fDA7ySQ= 269 273 github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
+67 -31
lex/gen.go
··· 1 1 package lex 2 2 3 3 import ( 4 + "bytes" 4 5 "encoding/json" 5 6 "fmt" 7 + "go/format" 6 8 "io" 7 9 "os" 10 + "os/exec" 8 11 "sort" 9 12 "strings" 10 13 ) ··· 59 62 } 60 63 61 64 type outputType struct { 62 - Name string 63 - Type TypeSchema 65 + Name string 66 + Type TypeSchema 67 + Record bool 64 68 } 65 69 66 70 func (s *Schema) AllTypes(prefix string) []outputType { 67 71 var out []outputType 68 72 69 - var walk func(name string, ts TypeSchema) 70 - walk = func(name string, ts TypeSchema) { 73 + var walk func(name string, ts TypeSchema, record bool) 74 + walk = func(name string, ts TypeSchema, record bool) { 71 75 if ts.Type == "object" || 72 76 (ts.Type == "" && len(ts.OneOf) > 0) { 73 77 out = append(out, outputType{ 74 - Name: name, 75 - Type: ts, 78 + Name: name, 79 + Type: ts, 80 + Record: record, 76 81 }) 77 82 } 78 83 79 84 for childname, val := range ts.Properties { 80 - walk(name+"_"+strings.Title(childname), val) 85 + walk(name+"_"+strings.Title(childname), val, false) 81 86 } 82 87 83 88 if ts.Items != nil { 84 - walk(name+"_Elem", *ts.Items) 89 + walk(name+"_Elem", *ts.Items, false) 85 90 } 86 91 } 87 92 88 93 tname := s.nameFromID(s.ID, prefix) 89 94 90 95 for name, def := range s.Defs { 91 - walk(tname+"_"+strings.Title(name), def) 96 + walk(tname+"_"+strings.Title(name), def, false) 92 97 } 93 98 94 99 if s.Input != nil { 95 - walk(tname+"_Input", s.Input.Schema) 100 + walk(tname+"_Input", s.Input.Schema, false) 96 101 } 97 102 if s.Output != nil { 98 - walk(tname+"_Output", s.Output.Schema) 103 + walk(tname+"_Output", s.Output.Schema, false) 99 104 } 100 105 101 106 if s.Type == "record" { 102 - walk(tname, *s.Record) 107 + walk(tname, *s.Record, false) 103 108 } 104 109 105 110 return out ··· 120 125 } 121 126 122 127 func GenCodeForSchema(pkg string, prefix string, fname string, reqcode bool, s *Schema) error { 123 - fi, err := os.Create(fname) 124 - if err != nil { 125 - return err 126 - } 127 - defer fi.Close() 128 + buf := new(bytes.Buffer) 128 129 129 130 s.prefix = prefix 130 131 131 - fmt.Fprintf(fi, "package %s\n\n", pkg) 132 - fmt.Fprintf(fi, "import (\n") 133 - fmt.Fprintf(fi, "\t\"context\"\n") 134 - fmt.Fprintf(fi, "\t\"fmt\"\n") 135 - fmt.Fprintf(fi, "\t\"encoding/json\"\n") 136 - fmt.Fprintf(fi, "\t\"github.com/whyrusleeping/gosky/xrpc\"\n") 137 - fmt.Fprintf(fi, "\t\"github.com/whyrusleeping/gosky/lex/util\"\n") 138 - fmt.Fprintf(fi, ")\n\n") 132 + fmt.Fprintf(buf, "package %s\n\n", pkg) 133 + fmt.Fprintf(buf, "import (\n") 134 + fmt.Fprintf(buf, "\t\"context\"\n") 135 + fmt.Fprintf(buf, "\t\"fmt\"\n") 136 + fmt.Fprintf(buf, "\t\"encoding/json\"\n") 137 + fmt.Fprintf(buf, "\t\"github.com/whyrusleeping/gosky/xrpc\"\n") 138 + fmt.Fprintf(buf, "\t\"github.com/whyrusleeping/gosky/lex/util\"\n") 139 + fmt.Fprintf(buf, ")\n\n") 139 140 140 141 tps := s.AllTypes(prefix) 141 142 142 143 for _, ot := range tps { 143 - if err := s.WriteType(ot.Name, ot.Type, fi); err != nil { 144 + if err := s.WriteType(ot.Name, ot.Type, buf); err != nil { 145 + return err 146 + } 147 + } 148 + 149 + if reqcode { 150 + if err := writeMethods(prefix, s, buf); err != nil { 144 151 return err 145 152 } 146 153 } 147 154 148 - if !reqcode { 149 - return nil 155 + formatted, err := format.Source(buf.Bytes()) 156 + if err != nil { 157 + return fmt.Errorf("failed to format generated file: %w", err) 158 + } 159 + 160 + fixed, err := fixImports(formatted) 161 + if err != nil { 162 + return err 163 + } 164 + 165 + if err := os.WriteFile(fname, fixed, 0664); err != nil { 166 + return err 167 + } 168 + 169 + return nil 170 + } 171 + 172 + func fixImports(b []byte) ([]byte, error) { 173 + cmd := exec.Command("goimports") 174 + 175 + cmd.Stdin = bytes.NewReader(b) 176 + buf := new(bytes.Buffer) 177 + cmd.Stdout = buf 178 + 179 + if err := cmd.Run(); err != nil { 180 + return nil, err 150 181 } 182 + 183 + return buf.Bytes(), nil 184 + } 185 + 186 + func writeMethods(prefix string, s *Schema, w io.Writer) error { 151 187 switch s.Type { 152 188 case "token": 153 189 n := s.nameFromID(s.ID, prefix) 154 - fmt.Fprintf(fi, "const %s = %q\n", n, s.ID) 190 + fmt.Fprintf(w, "const %s = %q\n", n, s.ID) 155 191 return nil 156 192 case "record": 157 193 return nil 158 194 case "query": 159 - return s.WriteRPC(fi, prefix) 195 + return s.WriteRPC(w, prefix) 160 196 case "procedure": 161 - return s.WriteRPC(fi, prefix) 197 + return s.WriteRPC(w, prefix) 162 198 default: 163 199 return fmt.Errorf("unrecognized lexicon type %q", s.Type) 164 200 }