Show the request that caused RPC errors in their messages

This commit is contained in:
Lonami Exo 2018-10-15 18:35:51 +02:00
parent ee4c952290
commit 99129daeee
4 changed files with 31 additions and 55 deletions

View File

@ -2,9 +2,7 @@
This module holds all the base and automatically generated errors that the This module holds all the base and automatically generated errors that the
Telegram API has. See telethon_generator/errors.json for more. Telegram API has. See telethon_generator/errors.json for more.
""" """
import urllib.request
import re import re
from threading import Thread
from .common import ( from .common import (
ReadCancelledError, TypeNotFoundError, InvalidChecksumError, ReadCancelledError, TypeNotFoundError, InvalidChecksumError,
@ -17,57 +15,27 @@ from .rpcbaseerrors import *
from .rpcerrorlist import * from .rpcerrorlist import *
def report_error(code, message, report_method): def rpc_message_to_error(rpc_error, request):
"""
Reports an RPC error to pwrtelegram.
:param code: the integer code of the error (like 400).
:param message: the message representing the error.
:param report_method: the constructor ID of the function that caused it.
"""
try:
# Ensure it's signed
report_method = int.from_bytes(
report_method.to_bytes(4, 'big'), 'big', signed=True
)
url = urllib.request.urlopen(
'https://rpc.pwrtelegram.xyz?code={}&error={}&method={}'
.format(code, message, report_method),
timeout=5
)
url.read()
url.close()
except Exception as e:
"We really don't want to crash when just reporting an error"
def rpc_message_to_error(rpc_error, report_method=None):
""" """
Converts a Telegram's RPC Error to a Python error. Converts a Telegram's RPC Error to a Python error.
:param rpc_error: the RpcError instance. :param rpc_error: the RpcError instance.
:param report_method: if present, the ID of the method that caused it. :param request: the request that caused this error.
:return: the RPCError as a Python exception that represents this error. :return: the RPCError as a Python exception that represents this error.
""" """
if report_method is not None:
Thread(
target=report_error,
args=(rpc_error.error_code, rpc_error.error_message, report_method)
).start()
# Try to get the error by direct look-up, otherwise regex # Try to get the error by direct look-up, otherwise regex
cls = rpc_errors_dict.get(rpc_error.error_message, None) cls = rpc_errors_dict.get(rpc_error.error_message, None)
if cls: if cls:
return cls() return cls(request)
for msg_regex, cls in rpc_errors_re: for msg_regex, cls in rpc_errors_re:
m = re.match(msg_regex, rpc_error.error_message) m = re.match(msg_regex, rpc_error.error_message)
if m: if m:
capture = int(m.group(1)) if m.groups() else None capture = int(m.group(1)) if m.groups() else None
return cls(capture=capture) return cls(request, capture=capture)
cls = base_errors.get(rpc_error.error_code) cls = base_errors.get(rpc_error.error_code)
if cls: if cls:
return cls(rpc_error.error_message) return cls(request, rpc_error.error_message)
return RPCError(rpc_error.error_message, rpc_error.error_code) return RPCError(request, rpc_error.error_message, rpc_error.error_code)

View File

@ -3,11 +3,17 @@ class RPCError(Exception):
code = None code = None
message = None message = None
def __init__(self, message, code=None): def __init__(self, request, message, code=None):
super().__init__('RPCError {}: {}'.format(code or self.code, message)) super().__init__('RPCError {}: {}{}'.format(
code or self.code, message, self._fmt_request(request)))
self.code = code self.code = code
self.message = message self.message = message
@staticmethod
def _fmt_request(request):
return ' (caused by {})'.format(request.__class__.__name__)
def __reduce__(self): def __reduce__(self):
return type(self), (self.code, self.message) return type(self), (self.code, self.message)
@ -47,8 +53,8 @@ class ForbiddenError(RPCError):
code = 403 code = 403
message = 'FORBIDDEN' message = 'FORBIDDEN'
def __init__(self, message): def __init__(self, request, message):
super().__init__(message) super().__init__(request, message)
self.message = message self.message = message
@ -59,8 +65,8 @@ class NotFoundError(RPCError):
code = 404 code = 404
message = 'NOT_FOUND' message = 'NOT_FOUND'
def __init__(self, message): def __init__(self, request, message):
super().__init__(message) super().__init__(request, message)
self.message = message self.message = message
@ -72,8 +78,8 @@ class AuthKeyError(RPCError):
code = 406 code = 406
message = 'AUTH_KEY' message = 'AUTH_KEY'
def __init__(self, message): def __init__(self, request, message):
super().__init__(message) super().__init__(request, message)
self.message = message self.message = message
@ -97,8 +103,8 @@ class ServerError(RPCError):
code = 500 code = 500
message = 'INTERNAL' message = 'INTERNAL'
def __init__(self, message): def __init__(self, request, message):
super().__init__(message) super().__init__(request, message)
self.message = message self.message = message
@ -110,8 +116,8 @@ class BotTimeout(RPCError):
code = -503 code = -503
message = 'Timeout' message = 'Timeout'
def __init__(self, message): def __init__(self, request, message):
super().__init__(message) super().__init__(request, message)
self.message = message self.message = message
@ -154,8 +160,8 @@ class BadMessageError(Exception):
'Invalid container.' 'Invalid container.'
} }
def __init__(self, code): def __init__(self, request, code):
super().__init__(self.ErrorMessages.get( super().__init__(request, self.ErrorMessages.get(
code, code,
'Unknown error code (this should not happen): {}.'.format(code))) 'Unknown error code (this should not happen): {}.'.format(code)))

View File

@ -470,7 +470,7 @@ class MTProtoSender:
return return
if rpc_result.error: if rpc_result.error:
error = rpc_message_to_error(rpc_result.error) error = rpc_message_to_error(rpc_result.error, state.request)
self._send_queue.append( self._send_queue.append(
RequestState(MsgsAck([state.msg_id]), loop=self._loop)) RequestState(MsgsAck([state.msg_id]), loop=self._loop))

View File

@ -26,14 +26,16 @@ def generate_errors(errors, f):
# Error classes generation # Error classes generation
for error in errors: for error in errors:
f.write('\n\nclass {}({}):\n def __init__(self, **kwargs):\n' f.write('\n\nclass {}({}):\n'
' def __init__(self, request, **kwargs):\n'
' '.format(error.name, error.subclass)) ' '.format(error.name, error.subclass))
if error.has_captures: if error.has_captures:
f.write("self.{} = int(kwargs.get('capture', 0))\n " f.write("self.{} = int(kwargs.get('capture', 0))\n "
.format(error.capture_name)) .format(error.capture_name))
f.write('super(Exception, self).__init__({}' f.write('super(Exception, self).__init__('
'{} + self._fmt_request(request)'
.format(repr(error.description))) .format(repr(error.description)))
if error.has_captures: if error.has_captures: