using System; using System.Collections.Generic; using System.Linq; using System.Threading; using NeoSmart.Unicode; using static TdLib.TdApi; using static telegram.tgcli; namespace telegram { public class Util { public static class Ansi { public const string ResetAll = "\x1B[0m"; public const string Red = "\x1b[31m"; public const string Green = "\x1b[32m"; public const string Yellow = "\x1b[33m"; public const string Blue = "\x1b[34m"; public const string Magenta = "\x1b[35m"; public const string Cyan = "\x1b[36m"; public const string Bold = "\x1b[1m"; public const string BoldOff = "\x1b[22m"; } public static User GetUser(int uid) { try { var uinfo = client.ExecuteAsync(new GetUser {UserId = uid}).Result; return uinfo; } catch { var user = new User(); user.FirstName = "null"; user.LastName = "null"; return user; } } public static Chat GetChat(long chatId) { try { return client.ExecuteAsync(new GetChat {ChatId = chatId}).Result; } catch { return null; } } public static User GetMe() { return client.ExecuteAsync(new GetMe()).Result; } public static Message GetMessage(long chatId, long messageId) { return client.ExecuteAsync(new GetMessage {ChatId = chatId, MessageId = messageId}).Result; } public static int GetTotalMessages(long chatId) { try { var response = client.ExecuteAsync(new SearchChatMessages {ChatId = chatId, Query = "+", Limit = 1}); return response.Result.TotalCount; } catch { return 9999; } } public static List GetHistory(long chatId, int limit = 5, long fromMessageId = 0, int offset = 0, bool isSecret = false, bool skipTotal = false) { var history = new List(); var total = GetTotalMessages(chatId); var chat = GetChat(chatId); if (chat.Type is ChatType.ChatTypeSupergroup || isSecret) skipTotal = true; if (limit > total && !skipTotal) limit = total; for (var i = 5; i > 0; i--) { if (limit <= 0) { if (total == 0) return history; lock (@lock) messageQueue.Add($"{Ansi.Red}[tgcli] " + $"Limit cannot be less than one. Usage: /history "); return history; } var response = client.ExecuteAsync(new GetChatHistory { ChatId = chatId, FromMessageId = fromMessageId, Limit = limit, Offset = offset, OnlyLocal = false }) .Result; if (response.Messages_.Length < limit && i > 1 && !isSecret) { Thread.Sleep(100); continue; } history.AddRange(response.Messages_); history.Reverse(); return history; } return history; } public static List GetUnreadChats(bool all = false) { var output = new List(); var offset = 0L; while (true) { if (offset == 0) { var response = client.ExecuteAsync(new GetChats {OffsetOrder = long.MaxValue, Limit = int.MaxValue}).Result; offset = GetChat(response.ChatIds.Last()).Order; output.AddRange(all ? response.ChatIds.Select(GetChat).Where(c => c.UnreadCount > 0 || c.IsMarkedAsUnread).ToList() : response.ChatIds.Select(GetChat) .Where(c => (c.UnreadCount > 0 || c.IsMarkedAsUnread) && c.NotificationSettings.MuteFor == 0) .ToList()); } else { var response = client.ExecuteAsync(new GetChats {OffsetOrder = offset, Limit = int.MaxValue}).Result; if (response.ChatIds.Length == 0) break; offset = GetChat(response.ChatIds.Last()).Order; output.AddRange(all ? response.ChatIds.Select(GetChat).Where(c => c.UnreadCount > 0 || c.IsMarkedAsUnread).ToList() : response.ChatIds.Select(GetChat) .Where(c => (c.UnreadCount > 0 || c.IsMarkedAsUnread) && c.NotificationSettings.MuteFor == 0) .ToList()); } } return output; } public static List GetChats() { var response = client.ExecuteAsync(new GetChats {OffsetOrder = long.MaxValue, Limit = int.MaxValue}).Result; return response.ChatIds.Select(GetChat).ToList(); } public static List SearchChatsGlobal(string query) { if (query.TrimStart('@').Length < 5) { return new List(); } var response = client.ExecuteAsync(new SearchPublicChats {Query = query}).Result; var chats = response.ChatIds.Select(GetChat).ToList(); chats.AddRange(client.ExecuteAsync(new SearchChats {Query = query, Limit = int.MaxValue}).Result.ChatIds.Select(GetChat)); return chats; } public static Chat GetChatByUsernameGlobal(string username) { try { var response = client.ExecuteAsync(new SearchPublicChat {Username = username}).Result; return response; } catch { return null; } } public static int GetUserIdByUsername(string username) { try { var response = client.ExecuteAsync(new SearchPublicChat {Username = username}).Result; if (response.Type is ChatType.ChatTypePrivate priv) return priv.UserId; return 0; } catch { return 0; } } public static void AddUserToContacts(int userId, string name) { //TODO implement when TDLib 1.6 is released } public static List GetSecretChats() { var response = client.ExecuteAsync(new GetChats {OffsetOrder = long.MaxValue, Limit = int.MaxValue}).Result; return response.ChatIds.Select(GetChat).Where(c => c.Type is ChatType.ChatTypeSecret).ToList(); } public static void CloseSecretChat(int secretChatId) { client.ExecuteAsync(new CloseSecretChat() {SecretChatId = secretChatId}).Wait(); } public static Chat CreateSecretChat(int userId) { return client.ExecuteAsync(new CreateNewSecretChat {UserId = userId}).Result; } public static void DeleteChatHistory(long chatId) { client.ExecuteAsync(new DeleteChatHistory {ChatId = chatId, RemoveFromChatList = true, Revoke = true}).Wait(); } public static SecretChat GetSecretChat(int secretChatId) { var response = client.ExecuteAsync(new GetSecretChat {SecretChatId = secretChatId}).Result; return response; } public static void ClearCurrentConsoleLine() { Console.Write("\u001b[2K\r"); //Console.SetCursorPosition(0, Console.WindowHeight); //Console.Write(new string(' ', Console.WindowWidth)); //Console.SetCursorPosition(0, Console.WindowHeight); } public static string ReadConsolePassword() { var pass = ""; do { var key = Console.ReadKey(true); if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter) { pass += key.KeyChar; Console.Write("*"); } else { if (key.Key == ConsoleKey.Backspace && pass.Length > 0) { pass = pass.Substring(0, (pass.Length - 1)); Console.Write("\b \b"); } else if (key.Key == ConsoleKey.Enter) { break; } } } while (true); Console.WriteLine(); return pass; } public static void SendMessage(string message, long chatId, long replyTo = 0) { if (string.IsNullOrWhiteSpace(message)) return; Emojis.ForEach(em => message = message.Replace(em.Item1, em.Item2)); client.ExecuteAsync(new SendMessage { ChatId = chatId, InputMessageContent = new InputMessageContent.InputMessageText {Text = new FormattedText() {Text = message}}, ReplyToMessageId = replyTo, }); currentUserRead = false; } public static Message EditMessage(string newText, Message message) { Emojis.ForEach(em => newText = newText.Replace(em.Item1, em.Item2)); var msg = client.ExecuteAsync(new EditMessageText { ChatId = message.ChatId, MessageId = message.Id, InputMessageContent = new InputMessageContent.InputMessageText {Text = new FormattedText() {Text = newText}} }) .Result; return msg; } public static void MarkRead(long chatId, long messageId) { client.ExecuteAsync(new ViewMessages {ChatId = chatId, MessageIds = new[] {messageId}, ForceRead = true}); } public static void MarkUnread(long chatId) { client.ExecuteAsync(new ToggleChatIsMarkedAsUnread {ChatId = chatId, IsMarkedAsUnread = true,}); } public static long SearchChatId(string query) { try { var results = client.ExecuteAsync(new SearchChats {Query = query, Limit = 5}).Result; if (query.StartsWith("@")) return results.ChatIds.First(p => GetChat(p).Type is ChatType.ChatTypePrivate type && GetUser(type.UserId).Username == query.Substring(1)); return results.ChatIds.First(p => !(GetChat(p).Type is ChatType.ChatTypeSecret)); } catch { lock (@lock) messageQueue.Add($"{Ansi.Red}[tgcli] No results found."); return 0; } } public static int SearchUserInChats(string query) { var results = client.ExecuteAsync(new SearchChatsOnServer {Query = query, Limit = 5}).Result; if (results.ChatIds.Length == 0) return 0; var output = results.ChatIds.Select(GetChat).Where(p => p.Type is ChatType.ChatTypePrivate).Select(p => ((ChatType.ChatTypePrivate) p.Type).UserId); return output.Any() ? output.First() : 0; } public static int SearchContacts(string query) { //TODO implement when TDLib 1.6 is released try { var results = client.ExecuteAsync(new SearchContacts {Query = query, Limit = 5}).Result; return query.StartsWith("@") ? results.UserIds.First(p => GetUser(p).Username == query.Substring(1)) : results.UserIds.First(); } catch { lock (@lock) messageQueue.Add($"{Ansi.Red}[tgcli] No results found."); return 0; } } public static void LogOut() { lock (@lock) messageQueue.Add($"{Ansi.Yellow}[tgcli] Logging out..."); client.ExecuteAsync(new LogOut()).Wait(); } public static string GetFormattedUsername(User sender) { var username = sender.Username; if (string.IsNullOrWhiteSpace(username)) username = sender.FirstName + " " + sender.LastName; else username = "@" + username; return username; } public static string FormatTime(long unix) { var time = DateTimeOffset.FromUnixTimeSeconds(unix).DateTime.ToLocalTime(); var currentTime = DateTime.Now.ToLocalTime(); return time.ToString(time.Date.Ticks == currentTime.Date.Ticks ? "HH:mm" : "yyyy-MM-dd HH:mm"); } public static bool IsMessageRead(long chatId, long messageId) { var chat = GetChat(chatId); return chat.LastReadOutboxMessageId >= messageId; } public static int GetActualStringWidth(string input) { input = input.Replace(Ansi.Blue, ""); input = input.Replace(Ansi.Bold, ""); input = input.Replace(Ansi.Cyan, ""); input = input.Replace(Ansi.Green, ""); input = input.Replace(Ansi.Magenta, ""); input = input.Replace(Ansi.Red, ""); input = input.Replace(Ansi.Yellow, ""); input = input.Replace(Ansi.BoldOff, ""); input = input.Replace(Ansi.ResetAll, ""); return input.Length; } public static string GetFormattedStatus(bool isRead) { var output = " "; output += (isRead ? Ansi.Green : Ansi.Red) + "r"; return output + $"{Ansi.ResetAll}]"; } public static string TruncateString(string input, int maxLen) { if (maxLen < 2) maxLen = 2; return input.Length <= maxLen ? input : input.Substring(0, maxLen - 1) + "~"; } public static string TruncateMessageStart(string input, int maxLen) { if (maxLen < 2) maxLen = 2; if (input.Contains("⏎ ")) input = "⏎ " + input.Split("⏎ ").Last(); return input.Length < maxLen ? input : "<" + input.Substring(input.Length - maxLen + 2); } public static readonly List> Emojis = new List> { new Tuple("⏎ ", "\n"), new Tuple(":xd:", Emoji.FaceWithTearsOfJoy.Sequence.AsString), new Tuple(":check:", Emoji.WhiteHeavyCheckMark.Sequence.AsString), new Tuple(":thinking:", Emoji.ThinkingFace.Sequence.AsString), new Tuple(":eyes:", Emoji.Eyes.Sequence.AsString), new Tuple(":heart:", Emoji.RedHeart.Sequence.AsString), new Tuple(":shrug:", Emoji.PersonShrugging.Sequence.AsString), new Tuple(":shrugf:", Emoji.WomanShrugging.Sequence.AsString), new Tuple(":shrugm:", Emoji.ManShrugging.Sequence.AsString) }; public static readonly List SpecialKeys = new List { ConsoleKey.Backspace, ConsoleKey.Tab, ConsoleKey.Clear, ConsoleKey.Enter, ConsoleKey.Pause, ConsoleKey.Escape, ConsoleKey.PageUp, ConsoleKey.PageDown, ConsoleKey.End, ConsoleKey.Home, ConsoleKey.LeftArrow, ConsoleKey.UpArrow, ConsoleKey.RightArrow, ConsoleKey.DownArrow, ConsoleKey.Select, ConsoleKey.Print, ConsoleKey.Execute, ConsoleKey.PrintScreen, ConsoleKey.Insert, ConsoleKey.Delete, ConsoleKey.Help, ConsoleKey.LeftWindows, ConsoleKey.RightWindows, ConsoleKey.Applications, ConsoleKey.Sleep, ConsoleKey.F1, ConsoleKey.F2, ConsoleKey.F3, ConsoleKey.F4, ConsoleKey.F5, ConsoleKey.F6, ConsoleKey.F7, ConsoleKey.F8, ConsoleKey.F9, ConsoleKey.F10, ConsoleKey.F11, ConsoleKey.F12, ConsoleKey.F13, ConsoleKey.F14, ConsoleKey.F15, ConsoleKey.F16, ConsoleKey.F17, ConsoleKey.F18, ConsoleKey.F19, ConsoleKey.F20, ConsoleKey.F21, ConsoleKey.F22, ConsoleKey.F23, ConsoleKey.F24, ConsoleKey.BrowserBack, ConsoleKey.BrowserForward, ConsoleKey.BrowserRefresh, ConsoleKey.BrowserStop, ConsoleKey.BrowserSearch, ConsoleKey.BrowserFavorites, ConsoleKey.BrowserHome, ConsoleKey.VolumeMute, ConsoleKey.VolumeDown, ConsoleKey.VolumeUp, ConsoleKey.MediaNext, ConsoleKey.MediaPrevious, ConsoleKey.MediaStop, ConsoleKey.MediaPlay, ConsoleKey.LaunchMail, ConsoleKey.LaunchMediaSelect, ConsoleKey.LaunchApp1, ConsoleKey.LaunchApp2, ConsoleKey.Oem1, ConsoleKey.OemPlus, ConsoleKey.OemComma, ConsoleKey.OemMinus, ConsoleKey.OemPeriod, ConsoleKey.Oem2, ConsoleKey.Oem3, ConsoleKey.Oem4, ConsoleKey.Oem5, ConsoleKey.Oem6, ConsoleKey.Oem7, ConsoleKey.Oem8, ConsoleKey.Oem102, ConsoleKey.Process, ConsoleKey.Packet, ConsoleKey.Attention, ConsoleKey.CrSel, ConsoleKey.ExSel, ConsoleKey.EraseEndOfFile, ConsoleKey.Play, ConsoleKey.Zoom, ConsoleKey.NoName, ConsoleKey.Pa1, ConsoleKey.OemClear }; } }