New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Call discarded after confirming #3981
Comments
I do not have experience with Telegram calls so unfortunately I can't really help. There's nothing in Telethon in particular that would be declining the calls automatically, so I'm not sure what could be wrong. Maybe it's auto-declined if some parameter is wrong or something, no idea. |
Well I guess I can keep this open and try to take a closer look eventually. |
I took a look and noticed the following:
Other than that, yeah, I have no idea. Does this behavior occur if Telethon calls Telegram Desktop? Telegram Android? Telegram X? Pyrogram? Does this happen if those call Telethon instead? |
I tried the above code to call another account of mine on Telegram X and this is the output:
If I do it but discard:
If I do it accepting with Telegram I do reach If I do it discarding with Telegram I get So yeah it's really strange. |
Actually |
Thanks a lot for your time @Lonami!! import logging
logging.basicConfig(format='[%(levelname) 5s/%(asctime)s] %(name)s: %(message)s', level=logging.WARNING)
import asyncio
import hashlib
import os
import random
from telethon import TelegramClient, events
from telethon.tl.functions.messages import GetDhConfigRequest
from telethon.tl.functions.phone import RequestCallRequest, ConfirmCallRequest
from telethon.tl.types import PhoneCallEmpty, PhoneCallWaiting, \
PhoneCallRequested, PhoneCallDiscarded, PhoneCall, PhoneCallAccepted, \
UpdatePhoneCall, PhoneCallProtocol, InputPhoneCall, Updates, UpdateShort
from pyrogram.raw import types
api_id = 0
api_hash = ''
phone_number = ''
call_state = None
client = TelegramClient('call_test', api_id, api_hash)
@client.on(events.Raw)
async def handler(update):
await process_update(update)
class DH:
def __init__(self, dhc: types.messages.DhConfig):
self.p = bytes_to_integer(dhc.p)
self.g = dhc.g
self.resp = dhc
class DynamicDict:
def __setattr__(self, key, value):
self.__dict__[key] = value
def get_rand_bytes(dh_config, length=256):
return bytes(x ^ y for x, y in zip(
os.urandom(length), dh_config.resp.random
))
def bytes_to_integer(bytes):
return int.from_bytes(bytes, 'big')
def integer_to_bytes(integer):
return int.to_bytes(
integer,
length=(integer.bit_length() + 8 - 1) // 8, # 8 bits per byte,
byteorder='big',
signed=False
)
def calc_fingerprint(key):
return int.from_bytes(
bytes(hashlib.sha1(key).digest()[-8:]), 'little', signed=True
)
async def process_update(update):
if isinstance(update, UpdatePhoneCall):
print('[CALL] Phone call update:', update.phone_call)
await process_phone_call(update.phone_call)
else:
print('Ignoring', type(update).__name__)
async def process_phone_call(call):
global call_state
if isinstance(call, PhoneCallEmpty):
print('[CALL] Phone call empty')
elif isinstance(call, PhoneCallWaiting):
print('[CALL] Waiting for', call.participant_id, 'to answer...')
elif isinstance(call, PhoneCallRequested):
print('[CALL] Incoming call from', call.participant_id)
# accept_call(call)
elif isinstance(call, PhoneCallDiscarded):
state = call_state
print('[CALL]', state.user_id, 'discarded your call because of', type(call.reason).__name__) # del calls[call.id]
client.disconnect()
elif isinstance(call, PhoneCall):
print('[CALL] Call', call.id, 'is now', type(call).__name__)
process_full_call(call)
elif isinstance(call, PhoneCallAccepted):
print('[CALL] Call accepted by', call.participant_id, '! Doing stage 2')
await call_stage_two(call)
else:
print('[call] ignoring', type(call), call)
async def call_stage_two(call):
global call_state
state = call_state
state.prt_proto = call.protocol # TODO Hm, why not my_proto?
print('prt', state.prt_proto)
print('myp', state.my_proto)
state.g_b = int.from_bytes(call.g_b, 'big')
state.key = pow(base=state.g_b, exp=state.a, mod=state.p)
state.key_fingerprint = calc_fingerprint(integer_to_bytes(state.key))
call_state = state
phone_call = await client(ConfirmCallRequest(
peer=InputPhoneCall(call.id, call.access_hash),
g_a=integer_to_bytes(state.g_a),
key_fingerprint=state.key_fingerprint,
protocol=state.prt_proto
))
print('[CALL] Call confirmed:', phone_call)
def process_full_call(call):
global call_state
state = call_state
print('[CALL] Processing full call', call)
print('[CALL] Full state currently', state.__dict__)
state.connections = call.connections
# Validate fingerprint
state.key = pow(state.g_b, state.a, state.p)
state.key_fingerprint = calc_fingerprint(integer_to_bytes(state.key))
state.pt_key_fingerprint = call.key_fingerprint
if state.pt_key_fingerprint != state.key_fingerprint:
print('[CALL] Fingerprint mismatch! Got', state.pt_key_fingerprint, 'expected', state.key_fingerprint)
else:
print('[CALL] Fingerprint is correct!')
call_state = state
print('[CALL] Call #', call.id, 'ready!')
async def main():
await client.start()
dhc = DH(await client(GetDhConfigRequest(0, 256)))
state = DynamicDict()
state.incoming = False
state.user_id = "@user"
state.random_id = random.randint(0, 0x7fffffff - 1)
state.g = dhc.g
state.p = dhc.p
state.a = 0
while not (1 < state.a < state.p - 1):
# "A chooses a random value of a, 1 < a < p-1"
state.a = int.from_bytes(get_rand_bytes(dh_config=dhc), 'big')
state.a = random.randint(2, state.p - 1)
state.g_a = pow(base=state.g, exp=state.a, mod=state.p)
state.g_a_hash = hashlib.sha256(integer_to_bytes(state.g_a)).digest()
state.my_proto = PhoneCallProtocol(
min_layer=92,
max_layer=92,
udp_p2p=True,
udp_reflector=True,
library_versions=['3.0.0'],
)
phone_call = await client(RequestCallRequest(
user_id=state.user_id,
random_id=state.random_id,
g_a_hash=state.g_a_hash,
protocol=state.my_proto
))
print("Phone call (", phone_call.phone_call.id, ") request sent, waiting for updates...")
state.peer = InputPhoneCall(phone_call.phone_call.id, phone_call.phone_call.access_hash)
global call_state
call_state = state
await client.run_until_disconnected()
asyncio.run(main()) |
I'm going through the list of issues to fix some of them and found this again. I don't think this issue is actionable from my side. Were you able to make any progress? Would you mind if I closed this issue here (if it's not something I can "fix" in Telethon, it doesn't make much sense to keep an issue open for it)? If you have an issue for this elsewhere, I can watch that issue, so I can participate there instead. |
Closing due to the lack of response, but we can reopen if you think there's a need. |
to switch off How would I disconnect the call? |
Checklist
pip install -U https://github.com/LonamiWebs/Telethon/archive/v1.zip
and triggered the bug in the latest version.I have updated the code from Telethon-calls to the lastest version, I managed to request and confirm the call, but after confirming the call it is disconnected. Any idea why this is happening? Thanks!
Code that causes the issue
Logs
The text was updated successfully, but these errors were encountered: