11import { randomUUID } from "crypto" ;
2- import {
3- generateFakeSentinelToken ,
4- simulateBypassHeaders ,
5- solveSentinelChallenge ,
6- } from "./utils/utils" ;
2+ import { generateFakeSentinelToken , simulateBypassHeaders , solveSentinelChallenge } from "./utils/utils" ;
3+
4+ interface CompleteOptionsProfile {
5+ stream ?: boolean ;
6+ }
77
88export class ChatGPTReversed {
99 public static csrfToken : string | undefined = undefined ;
@@ -114,7 +114,17 @@ export class ChatGPTReversed {
114114 } ;
115115 }
116116
117- public async complete ( message : string ) : Promise < string > {
117+ public async complete (
118+ message : string ,
119+ options : { stream : true }
120+ ) : Promise < AsyncGenerator < { text : string ; metadata : any } > > ;
121+
122+ public async complete ( message : string , options ?: { stream ?: false } ) : Promise < string > ;
123+
124+ public async complete (
125+ message : string ,
126+ options ?: CompleteOptionsProfile
127+ ) : Promise < string | AsyncGenerator < { text : string ; metadata : any } > > {
118128 const sessionData = await this . rotateSessionData ( ) ;
119129
120130 if ( ! ChatGPTReversed . initialized ) {
@@ -129,6 +139,8 @@ export class ChatGPTReversed {
129139 preOaiUUID : sessionData . uuid ,
130140 } ) ;
131141
142+ const messageID = randomUUID ( ) ;
143+
132144 const response = await fetch ( "https://chatgpt.com/backend-anon/conversation" , {
133145 headers : {
134146 ...headers ,
@@ -140,79 +152,213 @@ export class ChatGPTReversed {
140152 action : "next" ,
141153 messages : [
142154 {
143- id : randomUUID ( ) ,
155+ id : messageID ,
144156 author : {
145157 role : "user" ,
146158 } ,
159+ create_time : Date . now ( ) ,
147160 content : {
148161 content_type : "text" ,
149162 parts : [ message ] ,
150163 } ,
151- metadata : { } ,
164+ metadata : {
165+ selected_all_github_repos : false ,
166+ selected_github_repos : [ ] ,
167+ serialization_metadata : {
168+ custom_symbol_offsets : [ ] ,
169+ } ,
170+ dictation : false ,
171+ } ,
152172 } ,
153173 ] ,
154- parent_message_id : randomUUID ( ) ,
174+ paragen_cot_summary_display_override : "allow" ,
175+ parent_message_id : "client-created-root" ,
155176 model : "auto" ,
156- timezone_offset_min : - 120 ,
177+ timezone_offset_min : - 60 ,
178+ timezone : "Europe/Berlin" ,
157179 suggestions : [ ] ,
158- history_and_training_disabled : false ,
180+ history_and_training_disabled : true ,
159181 conversation_mode : {
160182 kind : "primary_assistant" ,
161- plugin_ids : null ,
162183 } ,
163- force_paragen : false ,
164- force_paragen_model_slug : "" ,
165- force_nulligen : false ,
166- force_rate_limit : false ,
167- reset_rate_limits : false ,
168- websocket_request_id : randomUUID ( ) ,
169- force_use_sse : true ,
184+ system_hints : [ ] ,
185+ supports_buffering : true ,
186+ supported_encodings : [ "v1" ] ,
187+ client_contextual_info : {
188+ is_dark_mode : true ,
189+ time_since_loaded : 7 ,
190+ page_height : 911 ,
191+ page_width : 1080 ,
192+ pixel_ratio : 1 ,
193+ screen_height : 1080 ,
194+ screen_width : 1920 ,
195+ app_name : "chatgpt.com" ,
196+ } ,
170197 } ) ,
171198 method : "POST" ,
172199 } ) ;
173200
201+ if ( ! response . ok ) {
202+ throw new Error ( `Request failed with status ${ response . status } : ${ response . statusText } ` ) ;
203+ }
204+
174205 if ( response . body === null ) {
175206 throw new Error ( "Failed to receive response body. Please check your sessionToken and try again." ) ;
176207 }
177208
178- const reader = response . body . getReader ( ) ;
209+ if ( options ?. stream ) {
210+ return this . streamResponse ( response ) ;
211+ }
212+
213+ return this . collectFullResponse ( response ) ;
214+ }
215+
216+ private async collectFullResponse ( response : Response ) : Promise < string > {
217+ const reader = response . body ! . getReader ( ) ;
179218 const decoder = new TextDecoder ( ) ;
219+
180220 let result = "" ;
181- let buffer : any = "" ;
221+ let buffer = "" ;
222+ let finished = false ;
182223
183224 while ( true ) {
184225 const { done, value} = await reader . read ( ) ;
185226 if ( done ) break ;
186227
187228 buffer += decoder . decode ( value , { stream : true } ) ;
188229 let lines = buffer . split ( "\n" ) ;
189- buffer = lines . pop ( ) ;
230+ buffer = lines . pop ( ) || "" ;
190231
191232 for ( const line of lines ) {
192- if ( line . startsWith ( "data:" ) ) {
193- try {
194- const json = JSON . parse ( line . replace ( "data:" , "" ) . trim ( ) ) ;
195- if ( json . message ?. content ?. parts && json . message . status === "finished_successfully" ) {
233+ if ( ! line . startsWith ( "data:" ) ) continue ;
234+ const dataStr = line . replace ( "data:" , "" ) . trim ( ) ;
235+ if ( ! dataStr || dataStr === "[DONE]" ) continue ;
236+
237+ try {
238+ const json = JSON . parse ( dataStr ) ;
239+
240+ if ( json . message ) {
241+ if ( json . message . content && json . message . content . parts ) {
196242 result = json . message . content . parts [ 0 ] ;
243+ }
244+ if ( json . message . status === "finished_successfully" ) {
245+ finished = true ;
197246 break ;
198247 }
199- } catch ( e ) {
200- continue ;
248+ } else if ( json . o === "append" && json . p === "/message/content/parts/0" ) {
249+ result += json . v ;
250+ } else if ( Array . isArray ( json . v ) ) {
251+ for ( const op of json . v ) {
252+ if ( op . o === "append" && op . p === "/message/content/parts/0" ) {
253+ result += op . v ;
254+ }
255+ if ( op . p === "/message/status" && op . o === "replace" && op . v === "finished_successfully" ) {
256+ finished = true ;
257+ }
258+ }
201259 }
260+ } catch {
261+ continue ;
202262 }
203263 }
204- if ( result ) break ;
264+
265+ if ( finished ) break ;
205266 }
206267
207- if ( ! result && buffer . startsWith ( "data:" ) ) {
268+ if ( ! finished && buffer . startsWith ( "data:" ) ) {
208269 try {
209270 const json = JSON . parse ( buffer . replace ( "data:" , "" ) . trim ( ) ) ;
210- if ( json . message ?. content ?. parts && json . message . status === "finished_successfully" ) {
271+ if ( json . message && json . message . content && json . message . content . parts ) {
211272 result = json . message . content . parts [ 0 ] ;
212273 }
213- } catch ( e ) { }
274+ } catch {
275+ }
214276 }
215277
216278 return result ;
217279 }
280+
281+ private async * streamResponse ( response : Response ) : AsyncGenerator < { text : string ; metadata : any } > {
282+ const reader = response . body ! . getReader ( ) ;
283+ const decoder = new TextDecoder ( ) ;
284+
285+ let buffer = "" ;
286+ let fullText = "" ;
287+ let finished = false ;
288+
289+ while ( ! finished ) {
290+ const { done, value} = await reader . read ( ) ;
291+ if ( done ) break ;
292+
293+ buffer += decoder . decode ( value , { stream : true } ) ;
294+ let lines = buffer . split ( "\n" ) ;
295+ buffer = lines . pop ( ) || "" ;
296+
297+ for ( const line of lines ) {
298+ if ( ! line . startsWith ( "data:" ) ) continue ;
299+
300+ const dataStr = line . slice ( "data:" . length ) . trim ( ) ;
301+ if ( ! dataStr || dataStr === "[DONE]" ) continue ;
302+
303+ let json : any ;
304+ try {
305+ json = JSON . parse ( dataStr ) ;
306+ } catch {
307+ continue ;
308+ }
309+
310+ let deltaText = "" ;
311+ let metadata : any = undefined ;
312+
313+ if ( json . message ) {
314+ const parts = json . message . content ?. parts ;
315+ metadata = json . message . metadata ?? json . metadata ;
316+
317+ if ( Array . isArray ( parts ) && typeof parts [ 0 ] === "string" ) {
318+ const current = parts [ 0 ] ;
319+ if ( current . startsWith ( fullText ) ) {
320+ deltaText = current . slice ( fullText . length ) ;
321+ } else {
322+ deltaText = current ;
323+ }
324+ fullText = current ;
325+ }
326+
327+ if ( json . message . status === "finished_successfully" ) {
328+ finished = true ;
329+ }
330+ }
331+
332+ if ( json . o === "append" && json . p === "/message/content/parts/0" ) {
333+ deltaText += json . v ;
334+ fullText += json . v ;
335+ }
336+
337+ if ( Array . isArray ( json . v ) ) {
338+ for ( const op of json . v ) {
339+ if ( op . o === "append" && op . p === "/message/content/parts/0" ) {
340+ deltaText += op . v ;
341+ fullText += op . v ;
342+ }
343+ if ( op . p === "/message/status" && op . o === "replace" && op . v === "finished_successfully" ) {
344+ finished = true ;
345+ }
346+ }
347+ }
348+
349+ if ( json . type === "message_stream_complete" ) {
350+ finished = true ;
351+ }
352+
353+ if ( deltaText ) {
354+ yield {
355+ text : deltaText ,
356+ metadata : metadata ?? json . metadata ?? { } ,
357+ } ;
358+ }
359+
360+ if ( finished ) break ;
361+ }
362+ }
363+ }
218364}
0 commit comments