[mirror] a bluesky bot to post golang projects
4
fork

Configure Feed

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

Chore(refactor): wrap app in urfave/cli

+113 -61
+99 -54
cmd/bot/main.go
··· 3 3 import ( 4 4 "context" 5 5 "errors" 6 + "fmt" 6 7 "os" 7 8 "time" 8 9 ··· 13 14 bk "github.com/tailscale/go-bluesky" 14 15 "github.com/till/golangoss-bluesky/internal/bluesky" 15 16 "github.com/till/golangoss-bluesky/internal/content" 17 + "github.com/urfave/cli/v2" 16 18 ) 17 19 18 20 var ( ··· 22 24 cacheBucket string = "golangoss-cache-bucket" 23 25 24 26 ctx context.Context 27 + 28 + // for cache 29 + awsEndpoint string = "" 30 + awsAccessKeyId string = "" 31 + awsSecretKey string = "" 32 + 33 + // for github crawling 34 + githubToken string = "" 25 35 ) 26 36 27 37 func init() { ··· 30 40 }))) 31 41 32 42 ctx = context.Background() 43 + } 33 44 34 - if _, status := os.LookupEnv("BLUESKY_APP_KEY"); !status { 35 - slog.ErrorContext(ctx, "no app key") 36 - os.Exit(1) 37 - } 45 + func main() { 46 + bot := cli.App{ 47 + Name: "golangoss-bluesky", 48 + Flags: []cli.Flag{ 49 + &cli.StringFlag{ 50 + Name: "bluesky-app-key", 51 + EnvVars: []string{"BLUESKY_APP_KEY"}, 52 + Required: true, 53 + Destination: &blueskyAppKey, 54 + }, 55 + &cli.StringFlag{ 56 + Name: "aws-endpoint", 57 + EnvVars: []string{"AWS_ENDPOINT"}, 58 + Required: true, 59 + Destination: &awsEndpoint, 60 + }, 61 + &cli.StringFlag{ 62 + Name: "aws-access-key-id", 63 + EnvVars: []string{"AWS_ACCESS_KEY_ID"}, 64 + Required: true, 65 + Destination: &awsAccessKeyId, 66 + }, 67 + &cli.StringFlag{ 68 + Name: "aws-secret-key", 69 + EnvVars: []string{"AWS_SECRET_KEY"}, 70 + Required: true, 71 + Destination: &awsSecretKey, 72 + }, 73 + &cli.StringFlag{ 74 + Name: "github-token", 75 + EnvVars: []string{"GITHUB_TOKEN"}, 76 + Required: true, 77 + Destination: &githubToken, 78 + }, 79 + }, 80 + 81 + Action: func(cCtx *cli.Context) error { 82 + client, err := bk.Dial(ctx, bk.ServerBskySocial) 83 + if err != nil { 84 + return fmt.Errorf("failed to open connection: %v", err) 85 + } 86 + defer client.Close() 38 87 39 - blueskyAppKey = os.Getenv("BLUESKY_APP_KEY") 40 - } 88 + if err := client.Login(ctx, blueskyHandle, blueskyAppKey); err != nil { 89 + switch { 90 + case errors.Is(err, bk.ErrMasterCredentials): 91 + return fmt.Errorf("you're not allowed to use your full-access credentials, please create an appkey") 92 + case errors.Is(err, bk.ErrLoginUnauthorized): 93 + return fmt.Errorf("username of application password seems incorrect, please double check") 94 + case err != nil: 95 + return fmt.Errorf("something else went wrong, please look at the returned error") 96 + } 97 + } 98 + 99 + // init s3 client 100 + mc, err := minio.New(awsEndpoint, &minio.Options{ 101 + Creds: credentials.NewStaticV4(awsAccessKeyId, awsSecretKey, ""), 102 + Secure: true, 103 + }) 104 + if err != nil { 105 + return fmt.Errorf("failed to initialize minio client: %v", err) 106 + } 41 107 42 - func main() { 43 - client, err := bk.Dial(ctx, bk.ServerBskySocial) 44 - if err != nil { 45 - panic(err) 46 - } 47 - defer client.Close() 108 + // ensure the bucket exists 109 + if err := mc.MakeBucket(ctx, cacheBucket, minio.MakeBucketOptions{}); err != nil { 110 + return fmt.Errorf("failed to create bucket: %v", err) 111 + } 48 112 49 - if err := client.Login(ctx, blueskyHandle, blueskyAppKey); err != nil { 50 - switch { 51 - case errors.Is(err, bk.ErrMasterCredentials): 52 - panic("You're not allowed to use your full-access credentials, please create an appkey") 53 - case errors.Is(err, bk.ErrLoginUnauthorized): 54 - panic("Username of application password seems incorrect, please double check") 55 - case err != nil: 56 - panic("Something else went wrong, please look at the returned error") 57 - } 58 - } 113 + c := bluesky.Client{ 114 + Client: client, 115 + } 59 116 60 - // init s3 client 61 - mc, err := minio.New(os.Getenv("AWS_ENDPOINT"), &minio.Options{ 62 - Creds: credentials.NewStaticV4(os.Getenv("AWS_ACCESS_KEY_ID"), os.Getenv("AWS_SECRET_KEY"), ""), 63 - Secure: true, 64 - }) 65 - if err != nil { 66 - slog.ErrorContext(ctx, "Failed to initialize MinIO client", slog.Any("err", err)) 67 - os.Exit(1) 68 - } 117 + cacheClient := &content.CacheClientS3{ 118 + MC: mc, 119 + Bucket: cacheBucket, 120 + CTX: ctx, 121 + } 69 122 70 - // ensure the bucket exists 71 - if err := mc.MakeBucket(ctx, cacheBucket, minio.MakeBucketOptions{}); err != nil { 72 - slog.ErrorContext(ctx, "failed to create bucket", slog.Any("err", err)) 73 - os.Exit(1) 74 - } 123 + if err := content.Start(githubToken, cacheClient); err != nil { 124 + return fmt.Errorf("failed to start service: %v", err) 125 + } 75 126 76 - c := bluesky.Client{ 77 - Client: client, 78 - } 127 + for { 128 + slog.DebugContext(ctx, "checking...") 129 + if err := content.Do(ctx, c); err != nil { 130 + slog.ErrorContext(ctx, err.Error()) 131 + os.Exit(1) 132 + } 79 133 80 - cacheClient := &content.CacheClientS3{ 81 - MC: mc, 82 - Bucket: cacheBucket, 83 - CTX: ctx, 134 + time.Sleep(5 * time.Minute) 135 + } 136 + return nil 137 + }, 84 138 } 85 139 86 - if err := content.Start(cacheClient); err != nil { 87 - slog.ErrorContext(ctx, "failed to start service", slog.Any("err", err)) 140 + if err := bot.Run(os.Args); err != nil { 141 + slog.ErrorContext(ctx, err.Error()) 88 142 os.Exit(1) 89 143 } 90 144 91 - for { 92 - slog.DebugContext(ctx, "checking...") 93 - if err := content.Do(ctx, c); err != nil { 94 - slog.ErrorContext(ctx, err.Error()) 95 - os.Exit(1) 96 - } 97 - 98 - time.Sleep(5 * time.Minute) 99 - } 100 145 }
+4
go.mod
··· 10 10 require ( 11 11 github.com/carlmjohnson/versioninfo v0.22.5 // indirect 12 12 github.com/cespare/xxhash/v2 v2.2.0 // indirect 13 + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect 13 14 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 14 15 github.com/dustin/go-humanize v1.0.1 // indirect 15 16 github.com/felixge/httpsnoop v1.0.4 // indirect ··· 23 24 github.com/klauspost/compress v1.17.11 // indirect 24 25 github.com/minio/md5-simd v1.1.2 // indirect 25 26 github.com/rs/xid v1.6.0 // indirect 27 + github.com/russross/blackfriday/v2 v2.1.0 // indirect 28 + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect 26 29 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect 27 30 go.opentelemetry.io/otel v1.21.0 // indirect 28 31 go.opentelemetry.io/otel/metric v1.21.0 // indirect ··· 73 76 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect 74 77 github.com/spaolacci/murmur3 v1.1.0 // indirect 75 78 github.com/ureeves/jwt-go-secp256k1 v0.2.0 // indirect 79 + github.com/urfave/cli/v2 v2.27.5 76 80 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e // indirect 77 81 go.uber.org/atomic v1.11.0 // indirect 78 82 go.uber.org/multierr v1.11.0 // indirect
+8
go.sum
··· 21 21 github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 22 22 github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 23 23 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 24 + github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= 25 + github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 24 26 github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 27 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 26 28 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= ··· 181 183 github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= 182 184 github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= 183 185 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 186 + github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 187 + github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 184 188 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 185 189 github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= 186 190 github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= ··· 200 204 github.com/ureeves/jwt-go-secp256k1 v0.2.0 h1:A2D2F5E8a+WxZdkO9YviVxA9aUo3IqSvA/4zQztOZ5A= 201 205 github.com/ureeves/jwt-go-secp256k1 v0.2.0/go.mod h1:7WMTEkrUxSM5PEesinVfdsiq5vu7kUJvLZUXBmL1svM= 202 206 github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 207 + github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= 208 + github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= 203 209 github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= 204 210 github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= 205 211 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4= 206 212 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= 213 + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= 214 + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= 207 215 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 208 216 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 209 217 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+2 -7
internal/content/content.go
··· 4 4 "context" 5 5 "fmt" 6 6 "log/slog" 7 - "os" 8 7 "strings" 9 8 10 9 "github.com/ezeoleaf/larry/cache" ··· 17 16 provider github.Provider 18 17 ) 19 18 20 - func Start(cacheClient cache.Client) error { 21 - if _, status := os.LookupEnv("GITHUB_TOKEN"); !status { 22 - panic("No github token") 23 - } 24 - 19 + func Start(token string, cacheClient cache.Client) error { 25 20 cfg := config.Config{ 26 21 Language: "go", 27 22 } 28 23 29 - provider = github.NewProvider(os.Getenv("GITHUB_TOKEN"), cfg, cacheClient) 24 + provider = github.NewProvider(token, cfg, cacheClient) 30 25 return nil 31 26 } 32 27