You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have a chatbot that connects through eventsource to receive messages, the issue is that sometimes when the user switches app and comes back, it throws the following error:
Hi, this looks like a normal error event to me. The app shouldn't crash from an error event like this. Are you maybe throwing an exception in the error handler?
Also check out this comment about handling reconnections when the app is resumed after being suspended: #65 (comment)
Yes, this is how I handle the reconnections, but as I said, from time to time it works and in others it throws that error and stops working, I will leave the implementation I have.
// chatcontext.tsximport{getClient}from'@/services/botpress.services';import{useTextToSpeech}from'@/hooks/useTextToSpeech';import{useAuthStore}from'@/hooks/useAuthStore';exportconstChatProvider=({ children }: ChatProviderProps)=>{constuser=useAuthStore((state)=>state.user);constcountries=userSoa?.cltId;constclientRef=useRef(getClient());const[isTyping,setIsTyping]=useState(false);constappState=useRef(AppState.currentState);const{ hasVoice }=useVoiceStore();const{ speech }=useTextToSpeech();const[appStateVisible,setAppStateVisible]=useState(appState.current);const{
userId,
userToken,
setUserId,
setUserToken,
addMessage,
setConversationId,
conversationId,
messages,}=useChatStore();constuserData={fullname: userSoa?.first_name!,email: user?.email!,countries: countries?.map((country)=>CLIENTS[country]).join(', ')!,};useEffect(()=>{constsubscription=AppState.addEventListener('change',(nextAppState)=>{if(appState.current.match(/inactive|background/)&&nextAppState==='active'){console.log('La aplicación ha vuelto al primer plano!');clientRef.current.connect();}elseif(nextAppState==='background'||nextAppState==='inactive'){console.log('La aplicación ha ido al fondo!');clientRef.current.disconnect();}appState.current=nextAppState;setAppStateVisible(appState.current);});return()=>{subscription.remove();};},[]);useEffect(()=>{constclient=clientRef.current;constconnectClient=async()=>{try{if(userId&&userToken){awaitclient.connect({
userId,
userToken,},userData,{name: user?.first_name!,},);}else{constres=awaitclient.connect(undefined,userData,{name: user?.first_name!,});setUserId(res?.user.id);setUserToken(res?.key);}if(conversationId){awaitclient.switchConversation(conversationId);}}catch(err){console.log(err);}};connectClient();consthandleMessage=(message: any)=>{addMessage({id: message?.id,conversationId: message?.conversationId,direction: 'incoming',authorId: message?.authorId,sentOn: message.sentOn,payload: message?.payload,});if(message?.payload?.block?.type==='text'&&hasVoice){console.log('reproduciendo mensaje');speech(message?.payload?.block?.text);}};client.on('message',handleMessage);consthandleTyping=(typing: any)=>{setIsTyping(typing.isTyping);};client.on('isTyping',handleTyping);return()=>{client.off('message',handleMessage);client.off('isTyping',handleTyping);client.disconnect();};},[hasVoice,appStateVisible]);constsendMessage=async(message: string)=>{try{awaitclientRef.current.sendMessage({type: 'text',text: message});addMessage({direction: 'outgoing',sentOn: newDate(),text: message,type: 'text',});if(!conversationId){setConversationId(clientRef.current.conversationId);}}catch(error){console.error('Error al enviar mensaje:',error);}};return(<ChatContext.Providervalue={{isTyping,messages,sendMessage,}}>{children}</ChatContext.Provider>);};
// botpress.services.tsexportconstgetClient=()=>{returnnewBotpress();};classBotpressextendsEventEmitter{publicuserToken: string='';publicuserId: string='';publicconversationId: string='';private_state: any={};constructor(){super();this._state={};}privateasync_userExists({ userToken }: UserCredentials){try{returnawaitbotpressApi.get('/users/me',{headers: {'x-user-key': userToken,},}).then(({ data })=>{returndata;});}catch(err){console.log(err);returnfalse;}}privateasync_initialConnect(data: InitialConnect){try{returnawaitbotpressApi.post<any,AxiosResponse<UsersResponse>>('/users',data).then(({ data })=>{this.userToken=data.key;this.userId=data.user.id;returndata;});}catch(err){console.log(err);}}privateasync_reconnect({ userToken }: UserCredentials,data: InitialConnect){try{returnawaitbotpressApi.put(`/users/me`,data,{headers: {'x-user-key': userToken,},}).then(({ data })=>{this.userToken=userToken;this.userId=data.user.id;returndata;});}catch(err){console.log(err);}}publicasyncconnect(userCredentials?: UserCredentials,userData: UserData={}asUserData,userOptions: UserOptions={}asUserOptions,){returnuserCredentials
? (awaitthis._userExists(userCredentials))
? this._reconnect(userCredentials,{data: userData, ...userOptions})
: this._initialConnect({data: userData, ...userOptions})
: this._initialConnect({data: userData, ...userOptions});}privateasync_createNewConversation({
status,
userId,
userKey,}: {userKey: string;userId: string;status: string;}){console.log('Creando nueva conversación...');const{conversation: { id },}=awaitthis.createConversations();this.conversationId=id;// Connect the user to the conversationawaitthis._connectConversation({userId: this.userId,userToken: userKey},id);returnthis._createEvent({userToken: userKey,conversationId: id,payload: {type: 'conversation_started',data: {},},});}privateasync_createEvent({
userToken,
conversationId,
payload,}: {userToken: string;conversationId: string;payload: {type: string;data: any;};}){try{returnawaitbotpressApi.post(`/events`,{ conversationId, payload },{headers: {'x-user-key': userToken,},},).then(({ data })=>data);}catch(err){console.log(err);}}privateasynccreateConversations(data={}){try{returnawaitbotpressApi.post('/conversations',data,{headers: {'x-user-key': this.userToken,},}).then(({ data })=>data);}catch(err){console.log(err);}}privateasync_connectConversation(userCredentials: UserCredentials,conversationId: string){if(this._state.signalEmitter){console.log('Desconectando el canal existente...');this.disconnect();}consturl=`https://webchat.botpress.cloud/${getEnv('EXPO_PUBLIC_BOTPRESS_CLIENT_ID')}/conversations/${conversationId}/listen`;consteventSource=newEventSource(url,{headers: {'x-user-key': userCredentials.userToken},lineEndingCharacter: '\n',});// Limpiar listeners previoseventSource.removeAllEventListeners();// Manejo del evento 'open'eventSource.addEventListener('open',()=>{console.log('Conexión a la conversación abierta');this.emit('conversation',conversationId);this._state={status: 'conversation_created',userId: userCredentials.userId,userKey: userCredentials.userToken,conversationId: conversationId,signalEmitter: eventSource,};});// Manejo del evento 'message'eventSource.addEventListener('message',(event)=>{if(event.data==='ping')return;constpayload=event.data ? JSON.parse(event.data) : event;try{this._handleEvent(payload,userCredentials);}catch(error){console.error('Error al procesar el mensaje:',error);}});// Manejo del evento 'error'eventSource.addEventListener('error',async(error)=>{console.error('Error en la conexión del EventStream:',error);this.emit('error',newError(`Conexión a la conversación perdida: ${error.message}`));});// Guardar el EventSource en el estadothis._state.signalEmitter=eventSource;}private_handleEvent(data: any,userCredentials: UserCredentials){consttype=data.type;switch(type){case'message_created':
if(data.data.userId!==userCredentials.userId){this.emit('message',this._mapMessage(data.data));}break;case'webchat_visibility':
this.emit('webchatVisibility',data.visibility);break;case'webchat_config':
this.emit('webchatConfig',data.config);break;case'typing_started':
// console.log('Está escribiendo...');this.emit('isTyping',{isTyping: true,timeout: data.timeout??5000});break;case'typing_stopped':
// console.log('Dejó de escribir...');this.emit('isTyping',{isTyping: false,timeout: 0});break;case'custom':
this.emit('customEvent',data.event);break;case'message':
// console.log('Mensaje:', data);this.emit('ping',data);break;default:
console.debug('Evento desconocido:',data);}}private_mapMessage(message: any){const{ metadata }=message,{ payload }=messageAdapter(message.payload);return{id: message.id,conversationId: message.conversationId,authorId: message.userId,sentOn: newDate(message.createdAt),payload: payload,
metadata,};}publicsendMessage=async(payload: {type: string;text: string})=>{this._state=this._state==='conversation_created'
? this._state
: {status: 'conversation_creating',userId: this.userId,userKey: this.userToken,};this._state.status==='conversation_creating'&&!this.conversationId&&(awaitthis._createNewConversation(this._state));awaitthis._createMessage({conversationId: this.conversationId,
payload,});this.emit('messageSent',payload);};privateasync_createMessage({
conversationId,
payload,}: {conversationId: string;payload: {type: string;text: string;};}){try{returnawaitbotpressApi.post(`/messages`,{ conversationId, payload },{headers: {'x-user-key': this.userToken,},},).then(({ data })=>data);}catch(err){console.log(err);}}publicasyncswitchConversation(conversationId: string){this.conversationId=conversationId;this._state=(this._state==='conversation_created'&&this.conversationId===conversationId)||this._state==='conversation_creating'
? {status: 'conversation_creating',userId: this.userId,userKey: this.userToken,}
: this._state;awaitthis._connectConversation({userId: this.userId,userToken: this.userToken},conversationId,);}publicdisconnect(){if(this._state.signalEmitter){this._state.signalEmitter.removeAllEventListeners();this._state.signalEmitter.close();this.removeAllListeners();this._state.signalEmitter=null;// Limpiar la referencia}}}
Thanks... What I could notice with the debug is that the connection was not being closed and new connections were being created every time you went back to the app, I corrected this and so far the app has not broken.
Could this have caused a memory leak that caused the app to stop working at some point?
Activity
EmilJunker commentedon Nov 26, 2024
Hi, this looks like a normal error event to me. The app shouldn't crash from an error event like this. Are you maybe throwing an exception in the error handler?
Also check out this comment about handling reconnections when the app is resumed after being suspended: #65 (comment)
Xizted commentedon Nov 26, 2024
Yes, this is how I handle the reconnections, but as I said, from time to time it works and in others it throws that error and stops working, I will leave the implementation I have.
EmilJunker commentedon Nov 26, 2024
The only advice I can give you is to pass the
debug: trueoption to the EventSource and then have a look at the log output.Xizted commentedon Nov 26, 2024
Thanks... What I could notice with the debug is that the connection was not being closed and new connections were being created every time you went back to the app, I corrected this and so far the app has not broken.
Could this have caused a memory leak that caused the app to stop working at some point?
Xizted commentedon Nov 27, 2024
The bug has been mitigated on Android, but on iOS it still occurs.... Is there a difference in how connections are handled?