4
4
"context"
5
5
"encoding/json"
6
6
"errors"
7
- "fmt"
8
7
"net/http"
9
8
"net/url"
10
9
"strconv"
@@ -15,10 +14,12 @@ import (
15
14
)
16
15
17
16
var (
18
- // ErrSessionClosed is returned when attempting to write to a closed session.
17
+ // ErrSessionClosed is returned when attempting to write to a closed
18
+ // session.
19
19
ErrSessionClosed = errors .New ("session closed" )
20
20
21
- // ErrDisconnected is returned when attempting to write to a disconnected client.
21
+ // ErrDisconnected is returned when attempting to write to a disconnected
22
+ // client.
22
23
ErrDisconnected = errors .New ("client is disconnected" )
23
24
)
24
25
@@ -72,8 +73,10 @@ type RealTimeBaseTranscript struct {
72
73
// The partial transcript for your audio
73
74
Text string `json:"text"`
74
75
75
- // An array of objects, with the information for each word in the transcription text.
76
- // Includes the start and end time of the word in milliseconds, the confidence score of the word, and the text, which is the word itself.
76
+ // An array of objects, with the information for each word in the
77
+ // transcription text. Includes the start and end time of the word in
78
+ // milliseconds, the confidence score of the word, and the text, which is
79
+ // the word itself.
77
80
Words []Word `json:"words"`
78
81
}
79
82
@@ -116,8 +119,10 @@ var DefaultSampleRate = 16_000
116
119
type RealTimeClient struct {
117
120
baseURL * url.URL
118
121
apiKey string
122
+ token string
119
123
120
- conn * websocket.Conn
124
+ conn * websocket.Conn
125
+ httpClient * http.Client
121
126
122
127
mtx sync.RWMutex
123
128
sessionOpen bool
@@ -126,6 +131,10 @@ type RealTimeClient struct {
126
131
done chan bool
127
132
128
133
handler RealTimeHandler
134
+
135
+ sampleRate int
136
+ encoding RealTimeEncoding
137
+ wordBoost []string
129
138
}
130
139
131
140
func (c * RealTimeClient ) isSessionOpen () bool {
@@ -148,7 +157,8 @@ type RealTimeError struct {
148
157
149
158
type RealTimeClientOption func (* RealTimeClient )
150
159
151
- // WithRealTimeBaseURL sets the API endpoint used by the client. Mainly used for testing.
160
+ // WithRealTimeBaseURL sets the API endpoint used by the client. Mainly used for
161
+ // testing.
152
162
func WithRealTimeBaseURL (rawurl string ) RealTimeClientOption {
153
163
return func (c * RealTimeClient ) {
154
164
if u , err := url .Parse (rawurl ); err == nil {
@@ -157,12 +167,22 @@ func WithRealTimeBaseURL(rawurl string) RealTimeClientOption {
157
167
}
158
168
}
159
169
170
+ // WithRealTimeAuthToken configures the client to authenticate using an
171
+ // AssemblyAI API key.
160
172
func WithRealTimeAPIKey (apiKey string ) RealTimeClientOption {
161
173
return func (rtc * RealTimeClient ) {
162
174
rtc .apiKey = apiKey
163
175
}
164
176
}
165
177
178
+ // WithRealTimeAuthToken configures the client to authenticate using a temporary
179
+ // token generated using [CreateTemporaryToken].
180
+ func WithRealTimeAuthToken (token string ) RealTimeClientOption {
181
+ return func (rtc * RealTimeClient ) {
182
+ rtc .token = token
183
+ }
184
+ }
185
+
166
186
func WithHandler (handler RealTimeHandler ) RealTimeClientOption {
167
187
return func (rtc * RealTimeClient ) {
168
188
rtc .handler = handler
@@ -171,24 +191,13 @@ func WithHandler(handler RealTimeHandler) RealTimeClientOption {
171
191
172
192
func WithRealTimeSampleRate (sampleRate int ) RealTimeClientOption {
173
193
return func (rtc * RealTimeClient ) {
174
- if sampleRate > 0 {
175
- vs := rtc .baseURL .Query ()
176
- vs .Set ("sample_rate" , strconv .Itoa (sampleRate ))
177
- rtc .baseURL .RawQuery = vs .Encode ()
178
- }
194
+ rtc .sampleRate = sampleRate
179
195
}
180
196
}
181
197
182
198
func WithRealTimeWordBoost (wordBoost []string ) RealTimeClientOption {
183
199
return func (rtc * RealTimeClient ) {
184
- vs := rtc .baseURL .Query ()
185
-
186
- if len (wordBoost ) > 0 {
187
- b , _ := json .Marshal (wordBoost )
188
- vs .Set ("word_boost" , string (b ))
189
- }
190
-
191
- rtc .baseURL .RawQuery = vs .Encode ()
200
+ rtc .wordBoost = wordBoost
192
201
}
193
202
}
194
203
@@ -205,26 +214,26 @@ const (
205
214
206
215
func WithRealTimeEncoding (encoding RealTimeEncoding ) RealTimeClientOption {
207
216
return func (rtc * RealTimeClient ) {
208
- vs := rtc .baseURL .Query ()
209
- vs .Set ("encoding" , string (encoding ))
210
- rtc .baseURL .RawQuery = vs .Encode ()
217
+ rtc .encoding = encoding
211
218
}
212
219
}
213
220
214
221
func NewRealTimeClientWithOptions (options ... RealTimeClientOption ) * RealTimeClient {
215
222
client := & RealTimeClient {
216
223
baseURL : & url.URL {
217
- Scheme : "wss" ,
218
- Host : "api.assemblyai.com" ,
219
- Path : "/v2/realtime/ws" ,
220
- RawQuery : fmt .Sprintf ("sample_rate=%v" , DefaultSampleRate ),
224
+ Scheme : "wss" ,
225
+ Host : "api.assemblyai.com" ,
226
+ Path : "/v2/realtime/ws" ,
221
227
},
228
+ httpClient : & http.Client {},
222
229
}
223
230
224
231
for _ , option := range options {
225
232
option (client )
226
233
}
227
234
235
+ client .baseURL .RawQuery = client .queryFromOptions ()
236
+
228
237
return client
229
238
}
230
239
@@ -261,7 +270,6 @@ func NewRealTimeClient(apiKey string, handler RealTimeHandler) *RealTimeClient {
261
270
// Closes the any open WebSocket connection in case of errors.
262
271
func (c * RealTimeClient ) Connect (ctx context.Context ) error {
263
272
header := make (http.Header )
264
- header .Set ("Authorization" , c .apiKey )
265
273
266
274
opts := & websocket.DialOptions {
267
275
HTTPHeader : header ,
@@ -360,6 +368,33 @@ func (c *RealTimeClient) Connect(ctx context.Context) error {
360
368
return nil
361
369
}
362
370
371
+ func (c * RealTimeClient ) queryFromOptions () string {
372
+ values := url.Values {}
373
+
374
+ // Temporary token
375
+ if c .token != "" {
376
+ values .Set ("token" , c .token )
377
+ }
378
+
379
+ // Sample rate
380
+ if c .sampleRate > 0 {
381
+ values .Set ("sample_rate" , strconv .Itoa (c .sampleRate ))
382
+ }
383
+
384
+ // Encoding
385
+ if c .encoding != "" {
386
+ values .Set ("encoding" , string (c .encoding ))
387
+ }
388
+
389
+ // Word boost
390
+ if len (c .wordBoost ) > 0 {
391
+ b , _ := json .Marshal (c .wordBoost )
392
+ values .Set ("word_boost" , string (b ))
393
+ }
394
+
395
+ return values .Encode ()
396
+ }
397
+
363
398
// Disconnect sends the terminate_session message and waits for the server to
364
399
// send a SessionTerminated message before closing the connection.
365
400
func (c * RealTimeClient ) Disconnect (ctx context.Context , waitForSessionTermination bool ) error {
@@ -405,3 +440,30 @@ func (c *RealTimeClient) SetEndUtteranceSilenceThreshold(ctx context.Context, th
405
440
EndUtteranceSilenceThreshold : threshold ,
406
441
})
407
442
}
443
+
444
+ // RealTimeService groups operations related to the real-time transcription.
445
+ type RealTimeService struct {
446
+ client * Client
447
+ }
448
+
449
+ // CreateTemporaryToken creates a temporary token that can be used to
450
+ // authenticate a real-time client.
451
+ func (svc * RealTimeService ) CreateTemporaryToken (ctx context.Context , expiresIn int64 ) (* RealtimeTemporaryTokenResponse , error ) {
452
+ params := & CreateRealtimeTemporaryTokenParams {
453
+ ExpiresIn : Int64 (expiresIn ),
454
+ }
455
+
456
+ req , err := svc .client .newJSONRequest ("POST" , "/v2/realtime/token" , params )
457
+ if err != nil {
458
+ return nil , err
459
+ }
460
+
461
+ var tokenResponse RealtimeTemporaryTokenResponse
462
+ resp , err := svc .client .do (ctx , req , & tokenResponse )
463
+ if err != nil {
464
+ return nil , err
465
+ }
466
+ defer resp .Body .Close ()
467
+
468
+ return & tokenResponse , nil
469
+ }
0 commit comments