mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-10 19:46:36 +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
|
||||
if match.groups():
|
||||
self.additional_data = int(match.group(1))
|
||||
super().__init__(self, error_msg.format(self.additional_data))
|
||||
else:
|
||||
self.additional_data = None
|
||||
|
||||
super().__init__(self, error_msg)
|
||||
|
||||
called_super = True
|
||||
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(' !q: Quits the current 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
|
||||
while True:
|
||||
|
@ -80,6 +81,17 @@ if __name__ == '__main__':
|
|||
date = datetime.fromtimestamp(msg.date)
|
||||
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)
|
||||
elif msg:
|
||||
client.send_message(input_peer, msg, markdown=True, no_web_page=True)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# This file structure is based on TLSharp
|
||||
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/TelegramClient.cs
|
||||
import platform
|
||||
from datetime import datetime
|
||||
from hashlib import md5
|
||||
from os import path
|
||||
|
||||
import utils
|
||||
import network.authenticator
|
||||
|
@ -12,11 +15,18 @@ from parser.markdown_parser import parse_message_entities
|
|||
# For sending and receiving requests
|
||||
from tl import MTProtoRequest
|
||||
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.help import GetConfigRequest
|
||||
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:
|
||||
|
@ -198,6 +208,66 @@ class TelegramClient:
|
|||
for msg in result.messages # ...from all the messages...
|
||||
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):
|
||||
"""Invokes an MTProtoRequest and returns its results"""
|
||||
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
|
||||
|
||||
elif 'bytes' == arg.type:
|
||||
builder.writeln('writer.write({})'.format(name))
|
||||
builder.writeln('writer.tgwrite_bytes({})'.format(name))
|
||||
|
||||
elif 'date' == arg.type: # Custom format
|
||||
builder.writeln('writer.tgwrite_date({})'.format(name))
|
||||
|
|
|
@ -106,6 +106,15 @@ class BinaryReader:
|
|||
constructor_id = self.read_int(signed=False)
|
||||
clazz = tlobjects.get(constructor_id, 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)
|
||||
|
||||
# Now we need to determine the number of parameters of the class, so we can
|
||||
|
|
Loading…
Reference in New Issue
Block a user