Créer une conférence à partir de 2 appels

Bonjour,

J’essaie de créer une conférence d’appel audio.

J’ai deux appels que je stock dans un objet callSessions
et j’ai un objet de mon appel actif dans callSession

callSessions = {
5trjkpi2f30v553292lm: {
answerTime: Date Tue Nov 21 2023 09:16:21 GMT+0100 (heure normale d’Europe centrale)
answeredBySystem: undefined
autoAnswer: false
call: undefined
callId: undefined
callerNumber: undefined
cameraEnabled: false
conference: false
creationTime: Date Tue Nov 21 2023 09:16:15 GMT+0100 (heure normale d’Europe centrale)
dialedExtension: ""
displayName: "1006"
endTime: null
ignored: false
isCaller: true
muted: false
number: "1006"
paused: false
recording: false
recordingPaused: false
ringing: false
screensharing: false
sipCallId: "5trjkpi2f30v553292lm"
sipSession: Object { pendingReinvite: false, pendingReinviteAck: false, _state: "Established", … }
sipStatus: "Established"
startTime: Date Tue Nov 21 2023 09:16:15 GMT+0100 (heure normale d’Europe centrale)
type: "CallSession"
videoMuted: false
videoRemotelyDowngraded: undefined
},
5trjkf8fv5o58ko3thoi: {
answerTime: Date Tue Nov 21 2023 09:16:35 GMT+0100 (heure normale d’Europe centrale)
answeredBySystem: undefined
autoAnswer: false
call: undefined
callId: "5trjkf8fv5o58ko3thoi"
callerNumber: undefined
cameraEnabled: false
conference: false
creationTime: Date Tue Nov 21 2023 09:16:30 GMT+0100 (heure normale d’Europe centrale)
dialedExtension: ""
displayName: "1005"
endTime: null
ignored: false
isCaller: true
muted: false
number: "1005"
paused: false
recording: false
recordingPaused: false
ringing: false
screensharing: false
sipCallId: "5trjkf8fv5o58ko3thoi"
sipSession: Object { pendingReinvite: false, pendingReinviteAck: false, _state: "Established", … }
sipStatus: "Established"
startTime: Date Tue Nov 21 2023 09:16:30 GMT+0100 (heure normale d’Europe centrale)
type: "CallSession"
videoMuted: false
videoRemotelyDowngraded: undefined
}
}

mon appel actif

callSession = {
answerTime: Date Tue Nov 21 2023 09:16:35 GMT+0100 (heure normale d’Europe centrale)
answeredBySystem: undefined
autoAnswer: false
call: undefined
callId: undefined
callerNumber: undefined
cameraEnabled: false
conference: false
creationTime: Date Tue Nov 21 2023 09:16:30 GMT+0100 (heure normale d’Europe centrale)
dialedExtension: ""
displayName: "1005"
endTime: null
ignored: false
isCaller: true
muted: false
number: "1005"
paused: false
recording: false
recordingPaused: false
ringing: false
screensharing: false
sipCallId: "5trjkf8fv5o58ko3thoi"
sipSession: Object { pendingReinvite: false, pendingReinviteAck: false, _state: "Established", … }
sipStatus: "Established"
startTime: Date Tue Nov 21 2023 09:16:30 GMT+0100 (heure normale d’Europe centrale)
type: "CallSession"
videoMuted: false
videoRemotelyDowngraded: undefined
}

ici, je récupère dans callSessions l’appel qui n’est pas identique à callSession (l’appel en cours) et le met dans un tableau

autreObjet = [
0: {
answerTime: Date Tue Nov 21 2023 09:16:21 GMT+0100 (heure normale d’Europe centrale)
answeredBySystem: undefined
autoAnswer: false
call: undefined
callId: undefined
callerNumber: undefined
cameraEnabled: false
conference: false
creationTime: Date Tue Nov 21 2023 09:16:15 GMT+0100 (heure normale d’Europe centrale)
dialedExtension: ""
displayName: "1006"
endTime: null
ignored: false
isCaller: true
muted: false
number: "1006"
paused: false
recording: false
recordingPaused: false
ringing: false
screensharing: false
sipCallId: "5trjkpi2f30v553292lm"
sipSession: Object { pendingReinvite: false, pendingReinviteAck: false, _state: "Established", … }
sipStatus: "Established"
startTime: Date Tue Nov 21 2023 09:16:15 GMT+0100 (heure normale d’Europe centrale)
type: "CallSession"
videoMuted: false
videoRemotelyDowngraded: undefined
}
]

lorsque je fais:
callSession.getTalkingToIds(), j’obtiens un tableau vide
et
callSession.callId: undefined

j’utilise donc ce code

const merge2Calls = async () => {
    let adHocConference;
    // if callSessions = 2 add second call
    if(Object.keys(callSessions).length === 2) {
      let autreObjet = Object.values(callSessions).find(objet => objet !== callSessions[callSession.getId()]);
      adHocConference = await Wazo.Phone.startConference(callSession.sipSession._id, [autreObjet]);
    }

si je mets:
adHocConference = await Wazo.Phone.startConference(callSession, [autreObjet]);
cela me dit
host_call_id [ “Missing data for required field.” ]
Si je met
callSession.callId = callSession.sipSession._id
cela me renvoi l’erreur
TypeError: cyclic object value

quand je regarde le code source, j’ai

async startConference(host: CallSession, otherCalls: CallSession[]): Promise<AdHocAPIConference> {
    const participants = [host, ...otherCalls].reduce((acc: Record<string, any>, participant: CallSession) => {
      acc[participant.getTalkingToIds()[0]] = participant;
      return acc;
    }, {});

    if (!this.phone) {
      return Promise.reject();
    }

    const adHocConference = new AdHocAPIConference({
      phone: this.phone,
      host,
      participants,
    });
    return adHocConference.start();
  }

et participant.getTalkingToIds()[0] renvoi
“TypeError: participant.getTalkingToIds is not a function”
Il aurait fallu avoir participant[0].getTalkingToIds() mais cela me renvoi un tableau vide

Pouvez-vous m’aider à comprendre comment utiliser la fonction ?

merci !!

Hello Julien,

Pour obtenir le talking_to il faut écouter les messages de type call_created et call_updated de la Websocket Wazo.

Le SDK ne permet pas encore de mettre à jour automatiquement ces valeurs, il faut le faire sois même.

Manu

Merci pour la réponse rapide !

J’ai regardé un peu le code du SDK.
il va chercher dans une propriété call qui est undefined par défaut.

j’ai donc récupéré l’event et mis à jour mon appel callSession

const onCallUpdated = (data) => {
    // mise à jour de l'appel
    const userUuid = user.uuid; 
    const isCurrentUserAffected = data[`user_uuid:${userUuid}`] === true;

    if(isCurrentUserAffected) {
      // mettre à jour l'appel en cours
      setCallSession((prevCallSession) => {
        const updatedCallsession = Object.assign(Object.create(Object.getPrototypeOf(prevCallSession)), prevCallSession, {
          ...prevCallSession,
          callId: data.data.call_id,
          callerNumber: data.data.peer_caller_id_number,
          displayName: data.data.peer_caller_id_name,
          dialedExtension: data.data.dialed_extension,
          call : {
            type: prevCallSession.type,
            id: data.data.call_id,
            sipCallId: data.data.sip_call_id,
            callerName: data.data.caller_id_name,
            callerNumber: data.data.caller_id_number,
            calleeName: data.data.peer_caller_id_name,
            calleeNumber: data.data.peer_caller_id_number,
            dialedExtension: data.data.dialed_extension,
            lineId: data.data.line_id,
            isCaller: data.data.is_caller,
            isVideo: data.data.is_video,
            onHold: data.data.on_hold,
            muted: data.data.muted,
            status: data.data.status,
            startingTime: new Date(prevCallSession.startTime),
            talkingToIds: [data.data.talking_to],
            recording: prevCallSession.recording,
          }
        });

        // mettre à jour dans l'objet callSessions qui contient tous les appels en cours
        setCallSessions((prevCallSessions) => {
          const updatedCallSessions = { ...prevCallSessions };
          updatedCallSessions[updatedCallsession.getId()] = updatedCallsession;
          return updatedCallSessions;
        });
        
        return updatedCallsession
      })

    }
  }

Dans mon cas

callSession.call.talkingToIds = [
0 : {
1700657161.93: "0e3e7153-873c-4c0f-8d46-b43426eae511"
}
]

ça correspond à callId : user.uuid, ce qui me semble cohérent.

callSession.getTalkingToIds() me retourne bien le tableau, avec l’objet.

mais quand je fais le
Wazo.Phone.startConference(callSession, [otherCallSession])
j’ai le message:

"Adhoc conference participant call not found"
détails: participant_call_id	"[object Object]"

La fonction ne s’attend donc pas à un objet, met à un tableau de Call_id

donc je modifie
talkingToIds: [data.data.talking_to],
par
talkingToIds: Object.keys(data.data.talking_to)

et là mon appel est bien merge !! :slight_smile:
y’a même le callSession.conference: true !!
Si c’est pas magique !!

Bon, j’ai un autre soucis, sinon ça serait trop simple.

après le merge, toujours dans onCallUpdated, j’ai l’erreur
updatedCallsession.getId is not a function

grâce aux Events que tu m’as donné et au code du SDK, j’ai pu retrouver mes petits et avancer !!

je vais creuser l’erreur qui suit le merge.

cheers !

J’ai l’impression que ça fait un hangup sur mes 2 appels et me retourne l’objet adHocConference qui contient les informations

adHocConference.host = la callSession en cours avant le merge

adHocConference.phone.callSessions qui contient les callSessions[callSession.getId()] qui est dans mon cas uniquement celle qui correspond à adHocConference.host
l’autre à disparue ??

adHocconference.phone.currentCallSession qui est égale à adHocConference.host mais avec la propriété call: undefined

et je me demande comment gérer
adHocConference.paused
adHocConference.muted

c’est plus compliqué que simplement garder mon callSession qui se met à jour en conférence.
Là, il me faut gérer un peu plus de chose…

cheers !

je reviens dessus, je n’ai pas pu trop avancé …
mais un peu quand même :slight_smile:

mais il y a une chose que je n’arrive pas à faire.

Voilà la situation
je suis A
j’ai 2 appels en cours avec B et C
je merge les appels en une conférence
jusque là, tout va bien.
Si B ou C raccroche, je reste en ligne avec l’autre participant, c’est juste parfait.

Mais si c’est moi (A, qui est créé la conférence) qui raccroche, cela raccroche toutes les lignes, alors que je souhaite que B et C restent en communication ensemble.

C’est un peu comme si je voulais faire un transfert indirect, mais en passant par une conférence.
Après tout, je peux faire une conférence, et que les 2 autres interlocuteurs veulent entamer une discussion à laquelle je ne suis pas nécessaire et donc quitter la conférence que j’ai initié tout en laissant les autres continuer l’appel.

Est-ce que cela est prévu ? réalisable ?

j’ai testé adHocConference.hangup(), ça raccroche pour tout le monde
j’ai testé adHocConference.removeParticipant(adHocconference.host), ça raccroche le premier appel, donc B n’est plus en relation avec C

alors que je veux que B et C restent ensemble

merci !

Salut @manu,

Tu aurais une idée pour me guider pour le cas énoncé sur le précédent commentaire ?

merci ! (et cheers)

yop,

J’ai fini par trouver ma réponse:

An adhoc conference allows a user to merge multiple calls in one conversation. It acts like a conference room, but has no dedicated extension. The user creating the adhoc conference acts as the owner of the conference and controls who enters or leaves the conference. The conference will be destroyed when the owner leaves the conference.

Donc impossible de quitter tout en laissant les participants entre eux.

C’est dommage, car c’est une fonction que l’on retrouve de manière classique en téléphonie.

:upside_down_face: