Usage¶
Common¶
- You should have a protocol object:
phoneCallProtocol(min_layer=65, max_layer=VoIPController.CONNECTION_MAX_LAYER, udp_p2p=True, udp_reflector=True)
- All VoIP-related updates have type of
updatePhoneCall
withphone_call
field of typesphoneCallEmpty
,phoneCallWaiting
,phoneCallRequested
,phoneCallAccepted
,phoneCall
orphoneCallDiscarded
- Use
tgvoip.utils.generate_visualization()
withauth_key
andg_a
for outgoing org_a_or_b
for incoming calls to get emojis if you need them
Starting conversation¶
- Create a
VoIPController
instance - Call
tgvoip.VoIPController.set_send_audio_frame_callback()
(see docs for arguments) if needed, otherwise silence will be sent - Call
tgvoip.VoIPController.set_recv_audio_frame_callback()
(see docs for arguments) if needed, otherwise nothing will be done to incoming audio stream - Add state change handlers to
tgvoip.VoIPController.call_state_changed_handlers
(see docs for handler format) list if needed - Add signal bars change handlers to
tgvoip.VoIPController.signal_bars_changed_handlers
(see docs for handler format) list if needed - Invoke
help.getConfig()
(result is later referred asconfig
) - Call
tgvoip.VoIPController.set_config()
(arguments are:config.call_packet_timeout_ms / 1000., config.call_connect_timeout_ms / 1000., DataSaving.NEVER, call.id
) - Call
tgvoip.VoIPController.set_encryption_key()
(arguments are:i2b(auth_key), is_outgoing
whereis_outgoing
is a corresponding boolean value) - Build a
list
oftgvoip.Endpoint
objects fromcall.connection
(single) andcall.alternative_connections
(another list) - Call
tgvoip.VoIPController.set_remote_endpoints()
(arguments are:endpoints, call.p2p_allowed, False, call.protocol.max_layer
) - Call
tgvoip.VoIPController.start()
- Call
tgvoip.VoIPController.connect()
Discarding call¶
- Build
peer
:inputPhoneCall(id=call.id, access_hash=call.access_hash)
- Get call duration using
tgvoip.VoIPController.call_duration
- Get connection ID using
tgvoip.VoIPController.get_preferred_relay_id()
- Build a suitable
reason
object (types are:phoneCallDiscardReasonBusy
,phoneCallDiscardReasonDisconnect
,phoneCallDiscardReasonHangup
,phoneCallDiscardReasonMissed
) - Invoke
phone.discardCall(peer, duration, connection_id, reason)
. You might getCALL_ALREADY_DECLINED
error, this is fine - Destroy the
tgvoip.VoIPController
object
Ending conversation¶
- Send call rating and debug log if call ended normally (not failed): TBD
- Destroy the
tgvoip.VoIPController
object, everything will be done automatically
Making outgoing calls¶
- Get a
user_id
object for user you want to call (of typeinputPeerUser
) - Request a Diffie-Hellman config using
messages.getDhConfig(version=0, random_length=256)
- Check received config using
tgvoip.utils.check_dhc()
. If check is not passed, do not make the call. You might want to cache received config because check is expensive - Choose a random value
a
,1 < a < dhc.p-1
- Calculate
g_a
:pow(dhc.g, a, dhc.p)
- Calculate
g_a_hash
:sha256(g_a)
- Choose a random value
random_id
,0 <= random_id <= 0x7fffffff-1
- Invoke
phone.requestCall(user_id, random_id, g_a_hash, protocol)
- Wait for an update with
phoneCallAccepted
object, it means that other party has accepted the call. You also might get aphoneCallDiscarded
object, it means that other party has declined the call - If you have got a
phoneCallDiscarded
object, stop thetgvoip.VoIPController
. Otherwise, continue - Check a
g_b
value from receivedphoneCallAccepted
(later referred ascall
) object usingtgvoip.utils.check_g()
. If check is not passed, stop the call - Calculate
auth_key
:pow(call.g_b, a, dhc.p)
- Calculate
key_fingerprint
usingtgvoip.utils.calc_fingerprint()
- Build
peer
:inputPhoneCall(id=call.id, access_hash=call.access_hash)
- Invoke
phone.confirmCall(key_fingerprint, peer, g_a, protocol)
- Start the conversation
Receiving calls¶
- You will receive an update containing
phoneCallRequested
object (later referred ascall
). You might discard it right away (use0
for duration and connection_id) - Request a Diffie-Hellman config using
messages.getDhConfig(version=0, random_length=256)
- Check received config using
tgvoip.utils.check_dhc()
. If check is not passed, do not make the call. You might want to cache received config because check is expensive - Choose a random value
b
,1 < b < dhc.p-1
- Calculate
g_b
:pow(dhc.g, b, dhc.p)
- Save
call.g_a_hash
- Build
peer
:inputPhoneCall(id=call.id, access_hash=call.access_hash)
- Invoke
phone.acceptCall(peer, g_b, protocol)
. You might getCALL_ALREADY_DISCARDED
orCALL_ALREADY_ACCEPTED
errors, then you should stop current conversation. Also, if response containsphoneCallDiscarded
object you should stop the call - Wait for an update with
phoneCall
object (later referred ascall
) - Check that
call.g_a_or_b
is not empty andsha256(call.g_a_or_b)
equals tog_a_hash
you saved before. If it doesn’t match, stop the call - Check a
call.g_a_or_b
value object usingtgvoip.utils.check_g()
(second argument isdhc.p
). If check is not passed, stop the call - Calculate
auth_key
:pow(call.g_a_or_b, b, dhc.p)
- Calculate
key_fingerprint
usingtgvoip.utils.calc_fingerprint()
- Check that
key_fingerprint
you have just calculated matchescall.key_fingerprint
. If it doesn’t match, stop the call - Start the conversation