@@ -14,6 +14,7 @@ import (
14
14
bk "github.com/tailscale/go-bluesky"
15
15
"github.com/till/golangoss-bluesky/internal/bluesky"
16
16
"github.com/till/golangoss-bluesky/internal/content"
17
+ "github.com/till/golangoss-bluesky/internal/utils"
17
18
"github.com/urfave/cli/v2"
18
19
)
19
20
34
35
githubToken string = ""
35
36
36
37
checkInterval time.Duration = 15 * time .Minute
38
+ // How long to wait before retrying after a connection failure
39
+ reconnectDelay time.Duration = 2 * time .Minute
37
40
)
38
41
39
42
func init () {
@@ -44,6 +47,75 @@ func init() {
44
47
ctx = context .Background ()
45
48
}
46
49
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
+
47
119
func main () {
48
120
bot := cli.App {
49
121
Name : "golangoss-bluesky" ,
@@ -88,25 +160,7 @@ func main() {
88
160
},
89
161
90
162
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
110
164
mc , err := minio .New (awsEndpoint , & minio.Options {
111
165
Creds : credentials .NewStaticV4 (awsAccessKeyId , awsSecretKey , "" ),
112
166
Secure : true ,
@@ -115,47 +169,17 @@ func main() {
115
169
return fmt .Errorf ("failed to initialize minio client: %v" , err )
116
170
}
117
171
118
- // ensure the bucket exists
172
+ // Ensure the bucket exists
119
173
if err := mc .MakeBucket (ctx , cacheBucket , minio.MakeBucketOptions {}); err != nil {
120
174
return fmt .Errorf ("failed to create bucket: %v" , err )
121
175
}
122
176
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 )
153
178
},
154
179
}
155
180
156
181
if err := bot .Run (os .Args ); err != nil {
157
- slog . ErrorContext (ctx , err . Error () )
182
+ utils . LogErrorWithContext (ctx , err )
158
183
os .Exit (1 )
159
184
}
160
-
161
185
}
0 commit comments