Skip to content

Commit b7484ab

Browse files
committed
Fix(bluesky): run in a loop to catch connection errors
1 parent b819d6d commit b7484ab

File tree

1 file changed

+76
-52
lines changed

1 file changed

+76
-52
lines changed

cmd/bot/main.go

Lines changed: 76 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
bk "github.com/tailscale/go-bluesky"
1515
"github.com/till/golangoss-bluesky/internal/bluesky"
1616
"github.com/till/golangoss-bluesky/internal/content"
17+
"github.com/till/golangoss-bluesky/internal/utils"
1718
"github.com/urfave/cli/v2"
1819
)
1920

@@ -34,6 +35,8 @@ var (
3435
githubToken string = ""
3536

3637
checkInterval time.Duration = 15 * time.Minute
38+
// How long to wait before retrying after a connection failure
39+
reconnectDelay time.Duration = 2 * time.Minute
3740
)
3841

3942
func init() {
@@ -44,6 +47,75 @@ func init() {
4447
ctx = context.Background()
4548
}
4649

50+
// connectBluesky establishes a connection to Bluesky and logs in
51+
func connectBluesky(ctx context.Context) (*bk.Client, error) {
52+
client, err := bk.Dial(ctx, bk.ServerBskySocial)
53+
if err != nil {
54+
return nil, fmt.Errorf("failed to open connection: %v", err)
55+
}
56+
57+
if err := client.Login(ctx, blueskyHandle, blueskyAppKey); err != nil {
58+
client.Close()
59+
switch {
60+
case errors.Is(err, bk.ErrMasterCredentials):
61+
return nil, fmt.Errorf("you're not allowed to use your full-access credentials, please create an appkey")
62+
case errors.Is(err, bk.ErrLoginUnauthorized):
63+
return nil, fmt.Errorf("username of application password seems incorrect, please double check")
64+
default:
65+
return nil, fmt.Errorf("login failed: %v", err)
66+
}
67+
}
68+
69+
return client, nil
70+
}
71+
72+
// runWithReconnect attempts to run the bot with automatic reconnection on failure
73+
func runWithReconnect(ctx context.Context, mc *minio.Client) error {
74+
for {
75+
client, err := connectBluesky(ctx)
76+
if err != nil {
77+
slog.Error("failed to connect to Bluesky", "error", err)
78+
slog.Info("retrying connection", "delay", reconnectDelay)
79+
time.Sleep(reconnectDelay)
80+
continue
81+
}
82+
83+
c := bluesky.Client{
84+
Client: client,
85+
}
86+
87+
cacheClient := content.NewCacheClientS3(ctx, mc, cacheBucket)
88+
89+
// Initialize and start the cleanup handler
90+
cleanup := content.NewS3Cleanup(mc, cacheBucket)
91+
cleanup.Start(ctx)
92+
defer cleanup.Stop()
93+
94+
if err := content.Start(githubToken, cacheClient); err != nil {
95+
slog.Error("failed to start service", "error", err)
96+
client.Close()
97+
time.Sleep(reconnectDelay)
98+
continue
99+
}
100+
101+
// Run the main loop
102+
for {
103+
slog.DebugContext(ctx, "checking...")
104+
if err := content.Do(ctx, c); err != nil {
105+
if !errors.Is(err, content.ErrCouldNotContent) {
106+
slog.Error("error during content check", "error", err)
107+
client.Close()
108+
time.Sleep(reconnectDelay)
109+
break
110+
}
111+
slog.DebugContext(ctx, "backing off...")
112+
}
113+
114+
time.Sleep(checkInterval)
115+
}
116+
}
117+
}
118+
47119
func main() {
48120
bot := cli.App{
49121
Name: "golangoss-bluesky",
@@ -88,25 +160,7 @@ func main() {
88160
},
89161

90162
Action: func(cCtx *cli.Context) error {
91-
// FIXME: run this in a control loop; or we crash the app
92-
client, err := bk.Dial(ctx, bk.ServerBskySocial)
93-
if err != nil {
94-
return fmt.Errorf("failed to open connection: %v", err)
95-
}
96-
defer client.Close()
97-
98-
if err := client.Login(ctx, blueskyHandle, blueskyAppKey); err != nil {
99-
switch {
100-
case errors.Is(err, bk.ErrMasterCredentials):
101-
return fmt.Errorf("you're not allowed to use your full-access credentials, please create an appkey")
102-
case errors.Is(err, bk.ErrLoginUnauthorized):
103-
return fmt.Errorf("username of application password seems incorrect, please double check")
104-
default:
105-
return fmt.Errorf("something else went wrong, please look at the returned error")
106-
}
107-
}
108-
109-
// init s3 client
163+
// Initialize S3 client
110164
mc, err := minio.New(awsEndpoint, &minio.Options{
111165
Creds: credentials.NewStaticV4(awsAccessKeyId, awsSecretKey, ""),
112166
Secure: true,
@@ -115,47 +169,17 @@ func main() {
115169
return fmt.Errorf("failed to initialize minio client: %v", err)
116170
}
117171

118-
// ensure the bucket exists
172+
// Ensure the bucket exists
119173
if err := mc.MakeBucket(ctx, cacheBucket, minio.MakeBucketOptions{}); err != nil {
120174
return fmt.Errorf("failed to create bucket: %v", err)
121175
}
122176

123-
c := bluesky.Client{
124-
Client: client,
125-
}
126-
127-
cacheClient := content.NewCacheClientS3(ctx, mc, cacheBucket)
128-
129-
// Initialize and start the cleanup handler
130-
cleanup := content.NewS3Cleanup(ctx, mc, cacheBucket)
131-
cleanup.Start()
132-
defer cleanup.Stop()
133-
134-
if err := content.Start(githubToken, cacheClient); err != nil {
135-
return fmt.Errorf("failed to start service: %v", err)
136-
}
137-
138-
var runErr error
139-
140-
for {
141-
slog.DebugContext(ctx, "checking...")
142-
if err := content.Do(ctx, c); err != nil {
143-
if !errors.Is(err, content.ErrCouldNotContent) {
144-
runErr = err
145-
break
146-
}
147-
slog.DebugContext(ctx, "backing off...")
148-
}
149-
150-
time.Sleep(checkInterval)
151-
}
152-
return runErr
177+
return runWithReconnect(ctx, mc)
153178
},
154179
}
155180

156181
if err := bot.Run(os.Args); err != nil {
157-
slog.ErrorContext(ctx, err.Error())
182+
utils.LogErrorWithContext(ctx, err)
158183
os.Exit(1)
159184
}
160-
161185
}

0 commit comments

Comments
 (0)