commit f2bf47806ae65719dabf3be0caf9e3ad75f2e513 Author: Laura Hausmann Date: Fri Dec 6 23:34:40 2019 +0100 Initial flattened commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f14cfaa --- /dev/null +++ b/.gitignore @@ -0,0 +1,352 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +tdlib.log* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4f01f49 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tdsharp"] + path = tdsharp + url = https://github.com/egramtel/tdsharp diff --git a/.idea/.idea.tgcli/.idea/contentModel.xml b/.idea/.idea.tgcli/.idea/contentModel.xml new file mode 100644 index 0000000..f3bb765 --- /dev/null +++ b/.idea/.idea.tgcli/.idea/contentModel.xml @@ -0,0 +1,1134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.tgcli/.idea/encodings.xml b/.idea/.idea.tgcli/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.tgcli/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.tgcli/.idea/indexLayout.xml b/.idea/.idea.tgcli/.idea/indexLayout.xml new file mode 100644 index 0000000..27ba142 --- /dev/null +++ b/.idea/.idea.tgcli/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.tgcli/.idea/modules.xml b/.idea/.idea.tgcli/.idea/modules.xml new file mode 100644 index 0000000..e288666 --- /dev/null +++ b/.idea/.idea.tgcli/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.tgcli/.idea/projectSettingsUpdater.xml b/.idea/.idea.tgcli/.idea/projectSettingsUpdater.xml new file mode 100644 index 0000000..7515e76 --- /dev/null +++ b/.idea/.idea.tgcli/.idea/projectSettingsUpdater.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.tgcli/.idea/vcs.xml b/.idea/.idea.tgcli/.idea/vcs.xml new file mode 100644 index 0000000..b8ec2a0 --- /dev/null +++ b/.idea/.idea.tgcli/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.tgcli/.idea/workspace.xml b/.idea/.idea.tgcli/.idea/workspace.xml new file mode 100644 index 0000000..4307084 --- /dev/null +++ b/.idea/.idea.tgcli/.idea/workspace.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1575363296065 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.tgcli/riderModule.iml b/.idea/.idea.tgcli/riderModule.iml new file mode 100644 index 0000000..1a4e0d9 --- /dev/null +++ b/.idea/.idea.tgcli/riderModule.iml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/tdsharp b/tdsharp new file mode 160000 index 0000000..45f1281 --- /dev/null +++ b/tdsharp @@ -0,0 +1 @@ +Subproject commit 45f128104467b249bddccaa91ea8a6c9fac05d66 diff --git a/tgcli.core/.DS_Store b/tgcli.core/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/tgcli.core/.DS_Store differ diff --git a/tgcli.core/Command.cs b/tgcli.core/Command.cs new file mode 100644 index 0000000..312820c --- /dev/null +++ b/tgcli.core/Command.cs @@ -0,0 +1,64 @@ +using System.Linq; +using static tgcli.core.Util; + +namespace tgcli.core +{ + public class Command + { + public static void ParseCommand(string command) + { + var split = command.Split(" "); + if (split[0].Equals("open") || split[0].Equals("o")) + { + if (split.Length != 2) + return; + var chatId = searchChatId(split[1]); + if (chatId == 0) + return; + var chat = getChat(chatId); + Program.currentChatId = chat.Id; + Program.prefix = $"[{chat.Title}]"; + markRead(chat.Id, getHistory(chat.Id).First().Id); + } + else if (split[0].Equals("close") || split[0].Equals("c")) + { + if (split.Length != 1) + return; + Program.currentChatId = 0; + Program.prefix = "[tgcli]"; + + + lock (Program._lock) + { + var count = Program.missedMessages.Count; + if (count == 0) + return; + Program.messageQueue.Add($"{Ansi.Bold}{Ansi.Yellow}" + + $"[tgcli] You have {count} missed message" + + $"{(count == 1 ? "." : "s.")}"); + Program.messageQueue.AddRange(Program.missedMessages); + Program.missedMessages.Clear(); + } + } + else if (split[0].Equals("history") || split[0].Equals("h")) + { + if (split.Length != 1 && split.Length != 2 || Program.currentChatId == 0) + { + lock(Program._lock) + Program.messageQueue.Add($"{Ansi.Bold}{Ansi.Red}[tgcli] " + + $"No chat selected. Select a chat with /open "); + return; + } + var history = split.Length > 1 && int.TryParse(split[1], out var limit) + ? getHistory(Program.currentChatId, limit) + : getHistory(Program.currentChatId); + history.Reverse(); + + foreach (var msg in history) + { + Program.AddMessageToQueue(msg); + } + } + } + } +} \ No newline at end of file diff --git a/tgcli.core/Program.cs b/tgcli.core/Program.cs new file mode 100644 index 0000000..f86c608 --- /dev/null +++ b/tgcli.core/Program.cs @@ -0,0 +1,328 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Td = TdLib; +using static tgcli.core.Util; + +namespace tgcli.core +{ + public class Program + { + public static Td.TdClient client = new Td.TdClient(); + public static string dbdir = ""; + public static bool authorized; + public static long currentChatId = 0; + public static volatile string currentInputLine = ""; + public static volatile List messageQueue = new List(); + public static volatile List missedMessages = new List(); + public static string prefix = "[tgcli]"; + public static object _lock = new object(); + + 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 + }; + private static void Main() + { + dbdir = + $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}{Path.DirectorySeparatorChar}.tgcli"; + if (!Directory.Exists(dbdir)) + Directory.CreateDirectory(dbdir); + + client.Send(new Td.TdApi.SetLogStream + { + LogStream = new Td.TdApi.LogStream.LogStreamFile + { + Path = "tdlib.log", + MaxFileSize = 10000000 + } + }); + + Console.Clear(); + ClearCurrentConsoleLine(); + + client.UpdateReceived += HandleUpdate; + + OnAuthUpdate("authorizationStateWaitTdlibParameters"); + while (!authorized) + { + Thread.Sleep(1); + } + + ScreenUpdate(); + while (true) + MainLoop(); + } + + private static void MainLoop() + { + var key = Console.ReadKey(true); + OnKeyPressed(key); + } + + private static void HandleUpdate(object sender, Td.TdApi.Update e) + { + switch (e) + { + case Td.TdApi.Update.UpdateAuthorizationState state: + OnAuthUpdate(state.AuthorizationState.DataType); + break; + case Td.TdApi.Update.UpdateNewMessage message: + { + var msg = message.Message; + Task.Run(() => AddMessageToQueue(msg)); + + break; + } + } + } + + public static void ScreenUpdate() + { + lock (_lock) + { + ClearCurrentConsoleLine(); + messageQueue.ForEach(p => Console.WriteLine(p + Ansi.ResetAll)); + messageQueue.Clear(); + Console.Write(prefix + " > " + currentInputLine); + } + } + + private static void OnKeyPressed(ConsoleKeyInfo key) + { + if (key.Key == ConsoleKey.Enter) + { + if (currentInputLine.StartsWith("/")) + { + var command = currentInputLine.Substring(1); + currentInputLine = ""; + Command.ParseCommand(command); + ScreenUpdate(); + return; + } + + if (currentChatId == 0) + { + lock(_lock) + messageQueue.Add($"{Ansi.Bold}{Ansi.Red}[tgcli] " + + $"No chat selected. Select a chat with /open "); + ScreenUpdate(); + return; + } + + sendMessage(currentInputLine, currentChatId); + currentInputLine = ""; + ScreenUpdate(); + } + else if (key.Key == ConsoleKey.Backspace && currentInputLine.Length >= 1) + { + currentInputLine = currentInputLine.Substring(0, currentInputLine.Length - 1); + ScreenUpdate(); + } + else if (!specialKeys.Contains(key.Key)) + { + currentInputLine += key.KeyChar; + ScreenUpdate(); + } + } + + private static void OnAuthUpdate(string state) + { + if (state == "authorizationStateWaitTdlibParameters") + { + client.Send(new Td.TdApi.SetTdlibParameters + { + Parameters = new Td.TdApi.TdlibParameters + { + ApiId = 600606, + ApiHash = "c973f46778be4b35481ce45e93271e82", + DatabaseDirectory = dbdir, + UseMessageDatabase = true, + SystemLanguageCode = "en_US", + DeviceModel = Environment.MachineName, + SystemVersion = ".NET Core CLR " + Environment.Version, + ApplicationVersion = "0.1a", + EnableStorageOptimizer = true + } + }); + } + + else if (state == "authorizationStateWaitEncryptionKey") + { + client.Send(new Td.TdApi.CheckDatabaseEncryptionKey()); + } + + else if (state == "authorizationStateWaitPhoneNumber") + { + Console.Write("[tgcli] login> "); + var phone = Console.ReadLine(); + client.Send(new Td.TdApi.SetAuthenticationPhoneNumber + { + PhoneNumber = phone + }); + } + + else if (state == "authorizationStateWaitCode") + { + Console.Write("[tgcli] code> "); + var code = Console.ReadLine(); + client.Send(new Td.TdApi.CheckAuthenticationCode + { + Code = code + }); + } + + else if (state == "authorizationStateWaitPassword") + { + Console.Write("[tgcli] 2fa password> "); + var pass = ReadConsolePassword(); + client.Send(new Td.TdApi.CheckAuthenticationPassword + { + Password = pass + }); + } + + else if (state == "authorizationStateReady") + { + Console.WriteLine("[tgcli] logged in."); + authorized = true; + } + + else + { + Console.WriteLine($"unknown state: {state}"); + Environment.Exit(1); + } + } + + public static string FormatMessage(Td.TdApi.Message msg) + { + string text; + if (msg.Content is Td.TdApi.MessageContent.MessageText messageText) + text = messageText.Text.Text; + else + text = $"[unsupported {msg.Content.DataType}]"; + var sender = getUser(msg.SenderUserId); + var chat = getChat(msg.ChatId); + var username = getFormattedUsername(sender); + var time = formatTime(msg.Date); + var isPrivate = chat.Type is Td.TdApi.ChatType.ChatTypePrivate; + + return $"{Ansi.Bold}{Ansi.Green}[{time}] {Ansi.Cyan}{chat.Title} " + + $"{(isPrivate ? "" : $"{Ansi.Yellow}{username} ")}" + + $"{(msg.IsOutgoing ? $"{Ansi.Blue}»»»" : $"{Ansi.Magenta}«««")} " + + $"{text}"; + } + public static void AddMessageToQueue(Td.TdApi.Message msg) + { + //handle muted + if (getChat(msg.ChatId).NotificationSettings.MuteFor > 0 && currentChatId != msg.ChatId) + return; + + var formattedMessage = FormatMessage(msg); + + if (currentChatId != 0 && msg.ChatId != currentChatId) + lock (_lock) + missedMessages.Add(formattedMessage); + else + lock (_lock) + messageQueue.Add(formattedMessage); + + if (msg.ChatId == currentChatId) + markRead(msg.ChatId, msg.Id); + ScreenUpdate(); + } + } +} \ No newline at end of file diff --git a/tgcli.core/Util.cs b/tgcli.core/Util.cs new file mode 100644 index 0000000..ceabccf --- /dev/null +++ b/tgcli.core/Util.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using TdLib; + +namespace tgcli.core +{ + 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 TdApi.User getUser(int uid) + { + try + { + var uinfo = Program.client.ExecuteAsync(new TdApi.GetUser + { + UserId = uid + }).Result; + return uinfo; + } + catch + { + var user = new TdApi.User(); + user.FirstName = "null"; + user.LastName = "null"; + return user; + } + } + + public static TdApi.Chat getChat(long chatId) + { + return Program.client.ExecuteAsync(new TdApi.GetChat + { + ChatId = chatId + }).Result; + } + + public static TdApi.User getMe() + { + return Program.client.ExecuteAsync(new TdApi.GetMe()).Result; + } + + public static List getHistory(long chatId, int limit = 5 ,long fromMessageId = 0) + { + var history = new List(); + if (limit <= 0) + { + lock(Program._lock) + Program.messageQueue.Add($"{Ansi.Bold}{Ansi.Red}[tgcli] " + + $"Limit cannot be less than one. Usage: /history "); + return history; + } + var response = Program.client.ExecuteAsync(new TdApi.GetChatHistory + { + ChatId = chatId, + FromMessageId = fromMessageId, + Limit = limit, + }).Result; + history.AddRange(response.Messages_); + return history; + } + + public static void ClearCurrentConsoleLine() + { + Console.SetCursorPosition(0, Console.WindowHeight); + Console.Write(new string(' ', Console.WindowWidth)); + Console.SetCursorPosition(0, Console.WindowHeight); + } + + public static string ReadConsolePassword() + { + string pass = ""; + do + { + ConsoleKeyInfo 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) + { + Program.client.ExecuteAsync(new TdApi.SendMessage + { + ChatId = chatId, + InputMessageContent = new TdApi.InputMessageContent.InputMessageText + { + Text = new TdApi.FormattedText() + { + Text = message + } + } + }); + } + + public static void markRead(long chatId, long messageId) + { + Program.client.ExecuteAsync(new TdApi.ViewMessages + { + ChatId = chatId, + MessageIds = new [] + { + messageId + }, + ForceRead = true + }); + } + + public static long searchChatId(string query) + { + try + { + var results = Program.client.ExecuteAsync(new TdApi.SearchChatsOnServer + { + Query = query, + Limit = 5 + }).Result; + + return results.ChatIds.First(); + } + catch + { + lock (Program._lock) + Program.messageQueue.Add($"{Ansi.Red}{Ansi.Bold}[tgcli] No results found."); + return 0; + } + } + + public static object getFormattedUsername(TdApi.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"); + } + } +} \ No newline at end of file diff --git a/tgcli.core/libtdjson.dylib b/tgcli.core/libtdjson.dylib new file mode 100755 index 0000000..ec14e86 Binary files /dev/null and b/tgcli.core/libtdjson.dylib differ diff --git a/tgcli.core/libtdjson.so b/tgcli.core/libtdjson.so new file mode 100755 index 0000000..7611a0e Binary files /dev/null and b/tgcli.core/libtdjson.so differ diff --git a/tgcli.core/tgcli.core.csproj b/tgcli.core/tgcli.core.csproj new file mode 100644 index 0000000..c622da5 --- /dev/null +++ b/tgcli.core/tgcli.core.csproj @@ -0,0 +1,21 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + Always + + + Always + + + + diff --git a/tgcli.sln b/tgcli.sln new file mode 100644 index 0000000..08156c3 --- /dev/null +++ b/tgcli.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tgcli.core", "tgcli.core\tgcli.core.csproj", "{26C5A85E-DDBB-4358-84A7-4A6A577428CB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TDLib", "tdsharp\TDLib\TDLib.csproj", "{9134FECE-FD08-418D-B3FF-E1FB135A98C8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TDLib.Api", "tdsharp\TDLib.Api\TDLib.Api.csproj", "{3BCC90D7-1303-42EE-ACF6-11DA6251A52F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {26C5A85E-DDBB-4358-84A7-4A6A577428CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {26C5A85E-DDBB-4358-84A7-4A6A577428CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {26C5A85E-DDBB-4358-84A7-4A6A577428CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {26C5A85E-DDBB-4358-84A7-4A6A577428CB}.Release|Any CPU.Build.0 = Release|Any CPU + {9134FECE-FD08-418D-B3FF-E1FB135A98C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9134FECE-FD08-418D-B3FF-E1FB135A98C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9134FECE-FD08-418D-B3FF-E1FB135A98C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9134FECE-FD08-418D-B3FF-E1FB135A98C8}.Release|Any CPU.Build.0 = Release|Any CPU + {3BCC90D7-1303-42EE-ACF6-11DA6251A52F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3BCC90D7-1303-42EE-ACF6-11DA6251A52F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BCC90D7-1303-42EE-ACF6-11DA6251A52F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3BCC90D7-1303-42EE-ACF6-11DA6251A52F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal