mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-25 10:53:44 +03:00
Added ability to upload and send media, and more fixes
Uploading and sending media are different things. Once you have uploaded a media file, you can send it to many users without uploading it again, since you have a handle to the uploaded file. Other fixes include not showing additional data on error messages and not generating correct code for sending bytes
This commit is contained in:
parent
cdb1674a27
commit
7e78b1b6dc
|
@ -163,10 +163,11 @@ class RPCError(Exception):
|
||||||
# Get additional_data, if any
|
# Get additional_data, if any
|
||||||
if match.groups():
|
if match.groups():
|
||||||
self.additional_data = int(match.group(1))
|
self.additional_data = int(match.group(1))
|
||||||
|
super().__init__(self, error_msg.format(self.additional_data))
|
||||||
else:
|
else:
|
||||||
self.additional_data = None
|
self.additional_data = None
|
||||||
|
super().__init__(self, error_msg)
|
||||||
|
|
||||||
super().__init__(self, error_msg)
|
|
||||||
called_super = True
|
called_super = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
12
main.py
12
main.py
|
@ -61,6 +61,7 @@ if __name__ == '__main__':
|
||||||
print('You are now sending messages to "{}". Available commands:'.format(display))
|
print('You are now sending messages to "{}". Available commands:'.format(display))
|
||||||
print(' !q: Quits the current chat.')
|
print(' !q: Quits the current chat.')
|
||||||
print(' !h: prints the latest messages (message History) of the chat.')
|
print(' !h: prints the latest messages (message History) of the chat.')
|
||||||
|
print(' !p <path>: sends a Photo located at the given path')
|
||||||
|
|
||||||
# And start a while loop to chat
|
# And start a while loop to chat
|
||||||
while True:
|
while True:
|
||||||
|
@ -80,6 +81,17 @@ if __name__ == '__main__':
|
||||||
date = datetime.fromtimestamp(msg.date)
|
date = datetime.fromtimestamp(msg.date)
|
||||||
print('[{}:{}] {}: {}'.format(date.hour, date.minute, name, msg.message))
|
print('[{}:{}] {}: {}'.format(date.hour, date.minute, name, msg.message))
|
||||||
|
|
||||||
|
# Send photo
|
||||||
|
elif msg.startswith('!p '):
|
||||||
|
file_path = msg[len('!p '):] # Slice the message to get the path
|
||||||
|
|
||||||
|
print('Uploading {}...'.format(file_path))
|
||||||
|
input_file = client.upload_file(file_path)
|
||||||
|
|
||||||
|
# After we have the handle to the uploaded file, send it to our peer
|
||||||
|
client.send_photo_file(input_file, input_peer)
|
||||||
|
print('Media sent!')
|
||||||
|
|
||||||
# Send chat message (if any)
|
# Send chat message (if any)
|
||||||
elif msg:
|
elif msg:
|
||||||
client.send_message(input_peer, msg, markdown=True, no_web_page=True)
|
client.send_message(input_peer, msg, markdown=True, no_web_page=True)
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
# This file structure is based on TLSharp
|
# This file structure is based on TLSharp
|
||||||
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/TelegramClient.cs
|
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/TelegramClient.cs
|
||||||
import platform
|
import platform
|
||||||
|
from datetime import datetime
|
||||||
|
from hashlib import md5
|
||||||
|
from os import path
|
||||||
|
|
||||||
import utils
|
import utils
|
||||||
import network.authenticator
|
import network.authenticator
|
||||||
|
@ -12,11 +15,18 @@ from parser.markdown_parser import parse_message_entities
|
||||||
# For sending and receiving requests
|
# For sending and receiving requests
|
||||||
from tl import MTProtoRequest
|
from tl import MTProtoRequest
|
||||||
from tl import Session
|
from tl import Session
|
||||||
from tl.types import PeerUser, PeerChat, PeerChannel, InputPeerUser, InputPeerChat, InputPeerChannel, InputPeerEmpty
|
|
||||||
|
# The Requests and types that we'll be using
|
||||||
|
from tl.types import \
|
||||||
|
PeerUser, PeerChat, PeerChannel, \
|
||||||
|
InputPeerUser, InputPeerChat, InputPeerChannel, InputPeerEmpty, \
|
||||||
|
InputFile, InputMediaUploadedPhoto
|
||||||
|
|
||||||
from tl.functions import InvokeWithLayerRequest, InitConnectionRequest
|
from tl.functions import InvokeWithLayerRequest, InitConnectionRequest
|
||||||
from tl.functions.help import GetConfigRequest
|
from tl.functions.help import GetConfigRequest
|
||||||
from tl.functions.auth import SendCodeRequest, SignInRequest
|
from tl.functions.auth import SendCodeRequest, SignInRequest
|
||||||
from tl.functions.messages import GetDialogsRequest, GetHistoryRequest, SendMessageRequest
|
from tl.functions.upload import SaveFilePartRequest
|
||||||
|
from tl.functions.messages import GetDialogsRequest, GetHistoryRequest, SendMessageRequest, SendMediaRequest
|
||||||
|
|
||||||
|
|
||||||
class TelegramClient:
|
class TelegramClient:
|
||||||
|
@ -198,6 +208,66 @@ class TelegramClient:
|
||||||
for msg in result.messages # ...from all the messages...
|
for msg in result.messages # ...from all the messages...
|
||||||
for usr in result.users]) # ...from all of the available users
|
for usr in result.users]) # ...from all of the available users
|
||||||
|
|
||||||
|
# TODO Handle media downloading/uploading in a different session:
|
||||||
|
# "It is recommended that large queries (upload.getFile, upload.saveFilePart)
|
||||||
|
# be handled through a separate session and a separate connection"
|
||||||
|
def upload_file(self, file_path, part_size_kb=64, file_name=None):
|
||||||
|
"""Uploads the specified media with the given chunk (part) size, in KB.
|
||||||
|
If no custom file name is specified, the real file name will be used.
|
||||||
|
This method will fail when trying to upload files larger than 10MB!"""
|
||||||
|
|
||||||
|
part_size = int(part_size_kb * 1024)
|
||||||
|
if part_size % 1024 != 0:
|
||||||
|
raise ValueError('The part size must be evenly divisible by 1024')
|
||||||
|
|
||||||
|
# Multiply the datetime timestamp by 10^6 to get the ticks
|
||||||
|
# This is high likely going to be unique
|
||||||
|
file_id = int(datetime.now().timestamp() * (10 ** 6))
|
||||||
|
part_index = 0
|
||||||
|
hash_md5 = md5()
|
||||||
|
|
||||||
|
with open(file_path, 'rb') as file:
|
||||||
|
while True:
|
||||||
|
# Read the file by in chunks of size part_size
|
||||||
|
part = file.read(part_size)
|
||||||
|
|
||||||
|
print('Sending {}'.format(len(part)))
|
||||||
|
|
||||||
|
# If we have read no data (0 bytes), the file is over
|
||||||
|
# So there is nothing left to upload
|
||||||
|
if not part:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Invoke the file upload and increment both the part index and MD5 checksum
|
||||||
|
result = self.invoke(SaveFilePartRequest(file_id, part_index, part))
|
||||||
|
if result:
|
||||||
|
part_index += 1
|
||||||
|
hash_md5.update(part)
|
||||||
|
else:
|
||||||
|
raise ValueError('Could not upload file part #{}'.format(part_index))
|
||||||
|
|
||||||
|
# Set a default file name if None was specified
|
||||||
|
if not file_name:
|
||||||
|
file_name = path.basename(file_path)
|
||||||
|
|
||||||
|
# After the file has been uploaded, we can return a handle pointing to it
|
||||||
|
return InputFile(id=file_id,
|
||||||
|
parts = part_index,
|
||||||
|
name=file_name,
|
||||||
|
md5_checksum=hash_md5.hexdigest())
|
||||||
|
|
||||||
|
def send_photo_file(self, input_file, input_peer, caption=''):
|
||||||
|
"""Sends a previously uploaded input_file
|
||||||
|
(which should be a photo) to an input_peer"""
|
||||||
|
self.send_media_file(
|
||||||
|
InputMediaUploadedPhoto(input_file, caption), input_peer)
|
||||||
|
|
||||||
|
def send_media_file(self, input_media, input_peer):
|
||||||
|
"""Sends any input_media (contact, document, photo...) to an input_peer"""
|
||||||
|
self.invoke(SendMediaRequest(peer=input_peer,
|
||||||
|
media=input_media,
|
||||||
|
random_id=utils.generate_random_long()))
|
||||||
|
|
||||||
def invoke(self, request):
|
def invoke(self, request):
|
||||||
"""Invokes an MTProtoRequest and returns its results"""
|
"""Invokes an MTProtoRequest and returns its results"""
|
||||||
if not issubclass(type(request), MTProtoRequest):
|
if not issubclass(type(request), MTProtoRequest):
|
||||||
|
|
|
@ -282,7 +282,7 @@ def write_onsend_code(builder, arg, args, name=None):
|
||||||
pass # These are actually NOT written! Only used for flags
|
pass # These are actually NOT written! Only used for flags
|
||||||
|
|
||||||
elif 'bytes' == arg.type:
|
elif 'bytes' == arg.type:
|
||||||
builder.writeln('writer.write({})'.format(name))
|
builder.writeln('writer.tgwrite_bytes({})'.format(name))
|
||||||
|
|
||||||
elif 'date' == arg.type: # Custom format
|
elif 'date' == arg.type: # Custom format
|
||||||
builder.writeln('writer.tgwrite_date({})'.format(name))
|
builder.writeln('writer.tgwrite_date({})'.format(name))
|
||||||
|
|
|
@ -106,6 +106,15 @@ class BinaryReader:
|
||||||
constructor_id = self.read_int(signed=False)
|
constructor_id = self.read_int(signed=False)
|
||||||
clazz = tlobjects.get(constructor_id, None)
|
clazz = tlobjects.get(constructor_id, None)
|
||||||
if clazz is None:
|
if clazz is None:
|
||||||
|
# The class was None, but there's still a
|
||||||
|
# chance of it being a manually parsed value like bool!
|
||||||
|
value = constructor_id
|
||||||
|
if value == 0x997275b5: # boolTrue
|
||||||
|
return True
|
||||||
|
elif value == 0xbc799737: # boolFalse
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If there was still no luck, give up
|
||||||
raise TypeNotFoundError(constructor_id)
|
raise TypeNotFoundError(constructor_id)
|
||||||
|
|
||||||
# Now we need to determine the number of parameters of the class, so we can
|
# Now we need to determine the number of parameters of the class, so we can
|
||||||
|
|
Loading…
Reference in New Issue
Block a user