From 1d6035aeb7ba7060ece2a14a5aa524fa69715160 Mon Sep 17 00:00:00 2001 From: Ilya P Date: Sun, 23 Oct 2016 13:02:44 +0300 Subject: [PATCH 1/3] - fix file Uploader - refactor file uploader - added test for file uploading --- TLSharp.Core/Requests/UploadHelper.cs | 214 ++++++++++++++------------ TLSharp.Core/TelegramClient.cs | 48 +++++- TLSharp.Tests/TLSharp.Tests.csproj | 7 +- TLSharp.Tests/TLSharpTests.cs | 50 +++++- 4 files changed, 213 insertions(+), 106 deletions(-) diff --git a/TLSharp.Core/Requests/UploadHelper.cs b/TLSharp.Core/Requests/UploadHelper.cs index 5397e67..f7d14fe 100644 --- a/TLSharp.Core/Requests/UploadHelper.cs +++ b/TLSharp.Core/Requests/UploadHelper.cs @@ -7,112 +7,130 @@ using System.Text; using System.Threading.Tasks; using TeleSharp.TL; using TeleSharp.TL.Upload; +using TLSharp.Core.Utils; + namespace TLSharp.Core.Requests { - public class UploadHelper + public static class UploadHelper { - public static async Task Uploader(string name,StreamReader reader,TelegramClient client) + private static string GetFileHash(byte[] data) { - if (reader.BaseStream.Length < 10 * 1024 * 1024) - return await SmallFileUpload(name, reader, client); + string md5_checksum; + using (var md5 = MD5.Create()) + { + var hash = md5.ComputeHash(data); + var hashResult = new StringBuilder(hash.Length * 2); + + foreach (byte t in hash) + hashResult.Append(t.ToString("x2")); + + md5_checksum = hashResult.ToString(); + } + + return md5_checksum; + } + + public static async Task UploadFile(this TelegramClient client, string name, StreamReader reader) + { + const long tenMb = 10 * 1024 * 1024; + return await UploadFile(name, reader, client, reader.BaseStream.Length >= tenMb); + } + + private static byte[] GetFile(StreamReader reader) + { + var file = new byte[reader.BaseStream.Length]; + + using (reader) + { + reader.BaseStream.Read(file, 0, (int)reader.BaseStream.Length); + } + + return file; + } + + private static Queue GetFileParts(byte[] file) + { + var fileParts = new Queue(); + + const int maxFilePart = 512 * 1024; + + using (var stream = new MemoryStream(file)) + { + while (stream.Position != stream.Length) + { + if ((stream.Length - stream.Position) > maxFilePart) + { + var temp = new byte[maxFilePart]; + stream.Read(temp, 0, maxFilePart); + fileParts.Enqueue(temp); + } + else + { + var length = stream.Length - stream.Position; + var temp = new byte[length]; + stream.Read(temp, 0, (int)(length)); + fileParts.Enqueue(temp); + } + } + } + + return fileParts; + } + + private static async Task UploadFile(string name, StreamReader reader, + TelegramClient client, bool isBigFileUpload) + { + var file = GetFile(reader); + var fileParts = GetFileParts(file); + + int partNumber = 0; + int partsCount = fileParts.Count; + long file_id = BitConverter.ToInt64(Helpers.GenerateRandomBytes(8), 0); + while (fileParts.Count != 0) + { + var part = fileParts.Dequeue(); + + if (isBigFileUpload) + { + await client.SendRequestAsync(new TLRequestSaveBigFilePart + { + file_id = file_id, + file_part = partNumber, + bytes = part, + file_total_parts = partsCount + }); + } + else + { + await client.SendRequestAsync(new TLRequestSaveFilePart + { + file_id = file_id, + file_part = partNumber, + bytes = part + }); + } + partNumber++; + } + + if (isBigFileUpload) + { + return new TLInputFileBig + { + id = file_id, + name = name, + parts = partsCount + }; + } else - return await BigFileUpload(name, reader, client); - } - private static async Task SmallFileUpload(string name, StreamReader reader, TelegramClient client) - { - var file = new byte[reader.BaseStream.Length]; - reader.BaseStream.Read(file, 0, (int)reader.BaseStream.Length); - string hash; - using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider()) { - hash = Convert.ToBase64String(md5.ComputeHash(file)); - } - reader = null; - var stream = new MemoryStream(file); - Queue parts = new Queue(); - while (!(stream.Position == stream.Length)) - { - if ((stream.Length - stream.Position) > 512 *1024) + return new TLInputFile { - byte[] temp = new byte[512]; - stream.Read(temp, 0, 512 * 1024); - parts.Enqueue(temp); - } - else - { - byte[] temp = new byte[512]; - stream.Read(temp, 0, (int)(stream.Length - stream.Position)); - parts.Enqueue(temp); - } + id = file_id, + name = name, + parts = partsCount, + md5_checksum = GetFileHash(file) + }; } - stream = null; - int partnumber = 0; - long file_id = BitConverter.ToInt64(RandomByteArray(8), 0); - while (parts.Count != 0) - { - var part = parts.Dequeue(); - TLRequestSaveFilePart save = new TLRequestSaveFilePart(); - save.file_id = file_id; - save.file_part = partnumber; - save.bytes = part; - await client.SendRequestAsync(save); - partnumber++; - } - TLInputFile returnFile = new TLInputFile(); - returnFile.id = file_id; - returnFile.name = name; - returnFile.parts = parts.Count; - returnFile.md5_checksum = hash; - return returnFile; - } - private static async Task BigFileUpload(string name, StreamReader reader, TelegramClient client) - { - var file = new byte[reader.BaseStream.Length]; - reader.BaseStream.Read(file, 0, (int)reader.BaseStream.Length); - reader = null; - var stream = new MemoryStream(file); - Queue parts = new Queue(); - while (!(stream.Position == stream.Length)) - { - if ((stream.Length - stream.Position) > 512 * 1024) - { - byte[] temp = new byte[512]; - stream.Read(temp, 0, 512 * 1024); - parts.Enqueue(temp); - } - else - { - byte[] temp = new byte[512]; - stream.Read(temp, 0, (int)(stream.Length - stream.Position)); - parts.Enqueue(temp); - } - } - stream = null; - int partnumber = 0; - long file_id = BitConverter.ToInt64(RandomByteArray(8), 0); - while (parts.Count != 0) - { - var part = parts.Dequeue(); - TLRequestSaveBigFilePart save = new TLRequestSaveBigFilePart(); - save.file_id = file_id; - save.file_part = partnumber; - save.bytes = part; - save.file_total_parts = parts.Count; - await client.SendRequestAsync(save); - partnumber++; - } - TLInputFileBig returnFile = new TLInputFileBig(); - returnFile.id = file_id; - returnFile.name = name; - returnFile.parts = parts.Count; - return returnFile; - } - private static byte[] RandomByteArray(int count) - { - var temp = new byte[count]; - Random random = new Random(); - random.NextBytes(temp); - return temp; } } } diff --git a/TLSharp.Core/TelegramClient.cs b/TLSharp.Core/TelegramClient.cs index 40eefaf..587c679 100644 --- a/TLSharp.Core/TelegramClient.cs +++ b/TLSharp.Core/TelegramClient.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using System.Web; using TeleSharp.TL; using TeleSharp.TL.Auth; using TeleSharp.TL.Contacts; @@ -10,6 +11,8 @@ using TeleSharp.TL.Messages; using TLSharp.Core.Auth; using TLSharp.Core.MTProto.Crypto; using TLSharp.Core.Network; +using TLSharp.Core.Requests; +using TLSharp.Core.Utils; namespace TLSharp.Core { @@ -53,7 +56,15 @@ namespace TLSharp.Core //set-up layer var config = new TLRequestGetConfig(); - var request = new TLRequestInitConnection() { api_id = _apiId, app_version = "1.0.0", device_model = "PC", lang_code = "en", query = config, system_version = "Win 10.0" }; + var request = new TLRequestInitConnection() + { + api_id = _apiId, + app_version = "1.0.0", + device_model = "PC", + lang_code = "en", + query = config, + system_version = "Win 10.0" + }; var invokewithLayer = new TLRequestInvokeWithLayer() { layer = 57, query = request }; await _sender.Send(invokewithLayer); await _sender.Receive(invokewithLayer); @@ -165,14 +176,12 @@ namespace TLSharp.Core if (!IsUserAuthorized()) throw new InvalidOperationException("Authorize user first!"); - long uniqueId = Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds); - return await SendRequestAsync( new TLRequestSendMessage() { peer = peer, message = message, - random_id = uniqueId + random_id = Helpers.GenerateRandomLong() }); } @@ -193,6 +202,37 @@ namespace TLSharp.Core new TLRequestGetDialogs() { offset_date = 0, offset_peer = peer, limit = 100 }); } + public async Task SendUploadedPhoto(TLAbsInputPeer peer, TLAbsInputFile file, string caption) + { + return await SendRequestAsync(new TLRequestSendMedia() + { + random_id = Helpers.GenerateRandomLong(), + background = false, + clear_draft = false, + media = new TLInputMediaUploadedPhoto() { file = file, caption = caption }, + peer = peer + }); + } + + public async Task SendUploadedDocument( + TLAbsInputPeer peer, TLAbsInputFile file, string caption, string mimeType, TLVector attributes) + { + return await SendRequestAsync(new TLRequestSendMedia() + { + random_id = Helpers.GenerateRandomLong(), + background = false, + clear_draft = false, + media = new TLInputMediaUploadedDocument() + { + file = file, + caption = caption, + mime_type = mimeType, + attributes = attributes + }, + peer = peer + }); + } + private void OnUserAuthenticated(TLUser TLUser) { _session.TLUser = TLUser; diff --git a/TLSharp.Tests/TLSharp.Tests.csproj b/TLSharp.Tests/TLSharp.Tests.csproj index a30c84a..f215a0c 100644 --- a/TLSharp.Tests/TLSharp.Tests.csproj +++ b/TLSharp.Tests/TLSharp.Tests.csproj @@ -1,4 +1,4 @@ - + Debug @@ -68,6 +68,11 @@ TLSharp.Core + + + Always + + diff --git a/TLSharp.Tests/TLSharpTests.cs b/TLSharp.Tests/TLSharpTests.cs index 59607e4..00f1cc7 100644 --- a/TLSharp.Tests/TLSharpTests.cs +++ b/TLSharp.Tests/TLSharpTests.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using System.Web; using Microsoft.VisualStudio.TestTools.UnitTesting; using TeleSharp.TL; using TeleSharp.TL.Channels; @@ -15,6 +16,8 @@ using TLSharp.Core; using TLSharp.Core.Auth; using TLSharp.Core.MTProto; using TLSharp.Core.Network; +using TLSharp.Core.Requests; +using TLSharp.Core.Utils; namespace TLSharp.Tests { @@ -37,7 +40,7 @@ namespace TLSharp.Tests private int apiId = 0; - [TestInitialize] + [TestInitialize] public void Init() { // Setup your phone numbers in app.config @@ -117,7 +120,49 @@ namespace TLSharp.Tests await client.SendMessageAsync(new TLInputPeerChannel() { channel_id = chat.id, access_hash = chat.access_hash.Value }, "TEST MSG"); } - [TestMethod] + [TestMethod] + public async Task SendPhotoToContactTest() + { + var client = new TelegramClient(apiId, apiHash); + + await client.ConnectAsync(); + + var result = await client.GetContactsAsync(); + + var user = result.users.lists + .Where(x => x.GetType() == typeof(TLUser)) + .Cast() + .FirstOrDefault(x => x.phone == NumberToSendMessage); + + var fileResult = (TLInputFile)await client.UploadFile("cat.jpg", new StreamReader("data/cat.jpg")); + await client.SendUploadedPhoto(new TLInputPeerUser() {user_id = user.id}, fileResult, "kitty"); + } + + [TestMethod] + public async Task SendBigFileToContactTest() + { + var client = new TelegramClient(apiId, apiHash); + + await client.ConnectAsync(); + + var result = await client.GetContactsAsync(); + + var user = result.users.lists + .Where(x => x.GetType() == typeof(TLUser)) + .Cast() + .FirstOrDefault(x => x.phone == NumberToSendMessage); + + var fileResult = (TLInputFileBig)await client.UploadFile("some.zip", new StreamReader("C:\\PetProjects\\TelegramBotSample.zip")); + + await client.SendUploadedDocument( + new TLInputPeerUser() {user_id = user.id}, + fileResult, + "some zips", + "application/zip", + new TLVector()); + } + + [TestMethod] public async Task SignUpNewUser() { var client = new TelegramClient(apiId, apiHash); @@ -143,6 +188,5 @@ namespace TLSharp.Tests var result = await client.IsPhoneRegisteredAsync(NumberToAuthenticate); Assert.IsTrue(result); } - } } From f6ad50ce94e949d237f97ce0021df2de67f800cd Mon Sep 17 00:00:00 2001 From: Ilya P Date: Sun, 23 Oct 2016 13:29:18 +0300 Subject: [PATCH 2/3] - added getFile method --- TLSharp.Core/TelegramClient.cs | 10 ++++ TLSharp.Tests/TLSharpTests.cs | 104 +++++++++++++++++++++++---------- 2 files changed, 82 insertions(+), 32 deletions(-) diff --git a/TLSharp.Core/TelegramClient.cs b/TLSharp.Core/TelegramClient.cs index 587c679..c4aab25 100644 --- a/TLSharp.Core/TelegramClient.cs +++ b/TLSharp.Core/TelegramClient.cs @@ -8,6 +8,7 @@ using TeleSharp.TL.Auth; using TeleSharp.TL.Contacts; using TeleSharp.TL.Help; using TeleSharp.TL.Messages; +using TeleSharp.TL.Upload; using TLSharp.Core.Auth; using TLSharp.Core.MTProto.Crypto; using TLSharp.Core.Network; @@ -233,6 +234,15 @@ namespace TLSharp.Core }); } + public async Task GetFile(TLAbsInputFileLocation location, int filePartSize) + { + return await SendRequestAsync(new TLRequestGetFile() + { + location = location, + limit = filePartSize + }); + } + private void OnUserAuthenticated(TLUser TLUser) { _session.TLUser = TLUser; diff --git a/TLSharp.Tests/TLSharpTests.cs b/TLSharp.Tests/TLSharpTests.cs index 00f1cc7..c65ba68 100644 --- a/TLSharp.Tests/TLSharpTests.cs +++ b/TLSharp.Tests/TLSharpTests.cs @@ -12,6 +12,7 @@ using TeleSharp.TL; using TeleSharp.TL.Channels; using TeleSharp.TL.Contacts; using TeleSharp.TL.Messages; +using TeleSharp.TL.Upload; using TLSharp.Core; using TLSharp.Core.Auth; using TLSharp.Core.MTProto; @@ -40,7 +41,7 @@ namespace TLSharp.Tests private int apiId = 0; - [TestInitialize] + [TestInitialize] public void Init() { // Setup your phone numbers in app.config @@ -85,40 +86,40 @@ namespace TLSharp.Tests Assert.IsTrue(client.IsUserAuthorized()); } - [TestMethod] - public async Task SendMessageTest() - { - var client = new TelegramClient(apiId, apiHash); + [TestMethod] + public async Task SendMessageTest() + { + var client = new TelegramClient(apiId, apiHash); - await client.ConnectAsync(); + await client.ConnectAsync(); - var result = await client.GetContactsAsync(); + var result = await client.GetContactsAsync(); - var user = result.users.lists - .Where(x => x.GetType() == typeof (TLUser)) - .Cast() - .FirstOrDefault(x => x.phone == NumberToSendMessage); - await client.SendTypingAsync(new TLInputPeerUser() {user_id = user.id}); - Thread.Sleep(3000); - await client.SendMessageAsync(new TLInputPeerUser() {user_id = user.id}, "TEST"); - - } + var user = result.users.lists + .Where(x => x.GetType() == typeof(TLUser)) + .Cast() + .FirstOrDefault(x => x.phone == NumberToSendMessage); + await client.SendTypingAsync(new TLInputPeerUser() { user_id = user.id }); + Thread.Sleep(3000); + await client.SendMessageAsync(new TLInputPeerUser() { user_id = user.id }, "TEST"); - [TestMethod] - public async Task SendMessageToChannelTest() - { - var client = new TelegramClient(apiId, apiHash); + } - await client.ConnectAsync(); + [TestMethod] + public async Task SendMessageToChannelTest() + { + var client = new TelegramClient(apiId, apiHash); - var dialogs = await client.GetUserDialogsAsync(); - var chat = dialogs.chats.lists - .Where(c => c.GetType() == typeof(TLChannel)) - .Cast() - .FirstOrDefault(c => c.title == "TestGroup"); + await client.ConnectAsync(); - await client.SendMessageAsync(new TLInputPeerChannel() { channel_id = chat.id, access_hash = chat.access_hash.Value }, "TEST MSG"); - } + var dialogs = await client.GetUserDialogsAsync(); + var chat = dialogs.chats.lists + .Where(c => c.GetType() == typeof(TLChannel)) + .Cast() + .FirstOrDefault(c => c.title == "TestGroup"); + + await client.SendMessageAsync(new TLInputPeerChannel() { channel_id = chat.id, access_hash = chat.access_hash.Value }, "TEST MSG"); + } [TestMethod] public async Task SendPhotoToContactTest() @@ -135,7 +136,7 @@ namespace TLSharp.Tests .FirstOrDefault(x => x.phone == NumberToSendMessage); var fileResult = (TLInputFile)await client.UploadFile("cat.jpg", new StreamReader("data/cat.jpg")); - await client.SendUploadedPhoto(new TLInputPeerUser() {user_id = user.id}, fileResult, "kitty"); + await client.SendUploadedPhoto(new TLInputPeerUser() { user_id = user.id }, fileResult, "kitty"); } [TestMethod] @@ -152,14 +153,53 @@ namespace TLSharp.Tests .Cast() .FirstOrDefault(x => x.phone == NumberToSendMessage); - var fileResult = (TLInputFileBig)await client.UploadFile("some.zip", new StreamReader("C:\\PetProjects\\TelegramBotSample.zip")); + var fileResult = (TLInputFileBig)await client.UploadFile("some.zip", new StreamReader("")); await client.SendUploadedDocument( - new TLInputPeerUser() {user_id = user.id}, + new TLInputPeerUser() { user_id = user.id }, fileResult, "some zips", "application/zip", - new TLVector()); + new TLVector()); + } + + [TestMethod] + public async Task DownloadFileFromContactTest() + { + var client = new TelegramClient(apiId, apiHash); + + await client.ConnectAsync(); + + var result = await client.GetContactsAsync(); + + var user = result.users.lists + .Where(x => x.GetType() == typeof(TLUser)) + .Cast() + .FirstOrDefault(x => x.phone == NumberToSendMessage); + + var inputPeer = new TLInputPeerUser() { user_id = user.id }; + var res = await client.SendRequestAsync(new TLRequestGetHistory() { peer = inputPeer }); + var document = res.messages.lists + .Where(m => m.GetType() == typeof(TLMessage)) + .Cast() + .Where(m => m.media != null && m.media.GetType() == typeof(TLMessageMediaDocument)) + .Select(m => m.media) + .Cast() + .Where(md => md.document.GetType() == typeof(TLDocument)) + .Select(md => md.document) + .Cast() + .First(); + + var resFile = await client.GetFile( + new TLInputDocumentFileLocation() + { + access_hash = document.access_hash, + id = document.id, + version = document.version + }, + document.size); + + Assert.IsTrue(resFile.bytes.Length > 0); } [TestMethod] From b7f3da175665085bbf4b67c96e647187cf96c6aa Mon Sep 17 00:00:00 2001 From: Ilya Pirozhenko Date: Sun, 23 Oct 2016 13:46:28 +0300 Subject: [PATCH 3/3] Update README.md --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8afdbc7..bc3ff4c 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ It's a perfect fit for any developer who would like to send data directly to Tel - [Starter Guide](#starter-guide) - [Quick configuration](#quick-configuration) - [First requests](#first-requests) + - [Working with files](#working-with-files) - [Available Methods](#available-methods) - [Contributing](#contributing) - [FAQ](#faq) @@ -104,8 +105,41 @@ To send message to channel you could use the following code: await client.SendMessageAsync(new TLInputPeerChannel() { channel_id = chat.id, access_hash = chat.access_hash.Value }, "OUR_MESSAGE"); ``` Full code you can see at [SendMessageToChannel test](https://github.com/sochix/TLSharp/blob/master/TLSharp.Tests/TLSharpTests.cs#L107) +## Working with files +Telegram separate files to two categories -> big file and small file. File is Big if its size more than 10 Mb. TLSharp tries to hide this complexity from you, thats why we provide one method to upload files **UploadFile**. -## Available Methods +```csharp + var fileResult = await client.UploadFile("cat.jpg", new StreamReader("data/cat.jpg")); +``` + +TLSharp provides two wrappers for sending photo and document + +```csharp + await client.SendUploadedPhoto(new TLInputPeerUser() { user_id = user.id }, fileResult, "kitty"); + await client.SendUploadedDocument( + new TLInputPeerUser() { user_id = user.id }, + fileResult, + "some zips", //caption + "application/zip", //mime-type + new TLVector()); //document attributes, such as file name +``` +Full code you can see at [SendPhotoToContactTest](https://github.com/sochix/TLSharp/blob/master/TLSharp.Tests/TLSharpTests.cs#L125) and [SendBigFileToContactTest](https://github.com/sochix/TLSharp/blob/master/TLSharp.Tests/TLSharpTests.cs#L143) + +To download file you should call **GetFile** method +```csharp + await client.GetFile( + new TLInputDocumentFileLocation() + { + access_hash = document.access_hash, + id = document.id, + version = document.version + }, + document.size); //size of fileChunk you want to retrieve +``` + +Full code you can see at [DownloadFileFromContactTest](https://github.com/sochix/TLSharp/blob/master/TLSharp.Tests/TLSharpTests.cs#L167) + +# Available Methods For your convenience TLSharp have wrappers for several Telegram API methods. You could add your own, see details below. @@ -117,6 +151,10 @@ For your convenience TLSharp have wrappers for several Telegram API methods. You 1. SendMessageAsync 1. SendTypingAsync 1. GetUserDialogsAsync +1. SendUploadedPhoto +1. SendUploadedDocument +1. GetFile +1. UploadFile **What if you can't find needed method at the list?** @@ -140,18 +178,19 @@ Don't panic. You can call any method with help of `SendRequestAsync` function. F The only way is [Telegram API docs](https://core.telegram.org/methods). Yes, it's outdated. But there is no other source. Latest scheme in JSON format you can find [here](https://gist.github.com/aarani/b22b7cda024973dff68e1672794b0298) -## Contributing +# Contributing Contributing is highly appreciated! -### What things can I Implement (Project Roadmap)? +## What things can I Implement (Project Roadmap)? -#### Release 1.0.0 +### Release 1.0.0 * [DONE] Add PHONE_MIGRATE handling +* Add FILE_MIGRATE handling * Add Updates handling * Add NuGet package -* [WIP] Add wrappers for media uploading +* [DONE] Add wrappers for media uploading and downloading * Store user session as JSON # FAQ