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 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..c4aab25 100644 --- a/TLSharp.Core/TelegramClient.cs +++ b/TLSharp.Core/TelegramClient.cs @@ -2,14 +2,18 @@ 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; 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; +using TLSharp.Core.Requests; +using TLSharp.Core.Utils; namespace TLSharp.Core { @@ -53,7 +57,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 +177,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 +203,46 @@ 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 + }); + } + + 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/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 6c1d8d5..fe88804 100644 --- a/TLSharp.Tests/TLSharpTests.cs +++ b/TLSharp.Tests/TLSharpTests.cs @@ -8,7 +8,10 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using TeleSharp.TL; +using TeleSharp.TL.Upload; using TLSharp.Core; +using TLSharp.Core.Requests; +using TLSharp.Core.Utils; namespace TLSharp.Tests { @@ -86,42 +89,123 @@ namespace TLSharp.Tests Assert.IsTrue(client.IsUserAuthorized()); } - [TestMethod] - public async Task SendMessageTest() - { + [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() - { + } + + [TestMethod] + public async Task SendMessageToChannelTest() + { var client = new TelegramClient(ApiId, ApiHash); - await client.ConnectAsync(); + await client.ConnectAsync(); - var dialogs = await client.GetUserDialogsAsync(); - var chat = dialogs.chats.lists - .Where(c => c.GetType() == typeof(TLChannel)) - .Cast() - .FirstOrDefault(c => c.title == "TestGroup"); + 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"); - } + 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("")); + + await client.SendUploadedDocument( + new TLInputPeerUser() { user_id = user.id }, + fileResult, + "some zips", + "application/zip", + 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] public async Task SignUpNewUser() { var client = new TelegramClient(ApiId, ApiHash); @@ -147,6 +231,5 @@ namespace TLSharp.Tests var result = await client.IsPhoneRegisteredAsync(NumberToAuthenticate); Assert.IsTrue(result); } - } }