tgcli/telegram/Command.cs

785 lines
26 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using NeoSmart.Unicode;
using TdLib;
using static telegram.tgcli;
using static telegram.Util;
namespace telegram
{
public abstract class Command
{
public string Trigger;
public string Shortcut;
public string Description;
public string Syntax;
public int ParamCount;
public abstract void Handler(List<string> inputParams);
protected Command(string trigger, string shortcut, string description, string syntax, int paramCount)
{
Trigger = trigger;
Shortcut = shortcut;
Description = description;
ParamCount = paramCount;
Syntax = syntax;
}
}
public static class CommandManager
{
public static List<Command> Commands = new List<Command>
{
new ClearCommand(),
new CloseCommand(),
new EditCommand(),
new ReplyCommand(),
new HistoryCommand(),
new OpenCommand(),
new UnreadsCommand(),
new CloseUnreadCommand(),
new ListChatsCommand(),
new NewChatCommand(),
new ListSecretChatsCommand(),
new OpenSecretCommand(),
new OpenSecretDirectCommand(),
new NewSecretChatCommand(),
new CloseSecretChatCommand(),
new SearchUserCommand(),
//new AddContactCommand(),
new QuitCommand(),
new HelpCommand(),
new LogoutCommand(),
};
public static void HandleCommand(string input)
{
var split = input.Split(" ").ToList();
var trigger = split.First();
var command = Commands.Find(p => p.Trigger == trigger || p.Shortcut == trigger);
if (command == null)
{
//TODO Console.WriteLine("do something");
return;
}
split.RemoveAt(0);
if (command.ParamCount == -1)
{
command.Handler(split);
}
else if (split.Count == command.ParamCount)
{
command.Handler(split);
}
else
{
//TODO Console.WriteLine("do something");
}
}
}
public class OpenCommand : Command
{
public OpenCommand() : base("o", "^O", "opens a chat. queries chat list", "<query>", -1)
{
}
public override void Handler(List<string> inputParams)
{
if (inputParams.Count == 0)
return; //TODO do something
var query = inputParams.Aggregate((current, param) => current + " " + param).Trim();
var chatId = SearchChatId(query);
if (chatId == 0) return;
currentChatId = 0;
currentChatUserId = 0;
currentUserRead = false;
var chat = GetChat(chatId);
if (chat.Type is TdApi.ChatType.ChatTypePrivate privChat)
{
currentChatUserId = privChat.UserId;
}
currentChatId = chat.Id;
chat.Title = TruncateString(chat.Title, 20);
prefix = $"[{chat.Title}";
lock (@lock)
{
messageQueue.Add($"{Ansi.Yellow}[tgcli] Opening chat: {chat.Title}");
messageQueue.Add($"{Ansi.Yellow}" + $"[tgcli] You have {chat.UnreadCount} unread message" +
$"{(chat.UnreadCount == 1 ? "." : "s.")}");
if (chat.UnreadCount >= 5)
{
var capped = chat.UnreadCount > 50;
GetHistory(chatId, capped ? 50 : chat.UnreadCount).ForEach(AddMessageToQueue);
if (capped)
messageQueue.Add(
$"{Ansi.Yellow}[tgcli] " + $"Showing 50 of {chat.UnreadCount} unread messages.");
}
else if (chat.UnreadCount > 0)
{
var unreads = GetHistory(chatId, chat.UnreadCount);
Console.WriteLine(unreads.Count);
var rest = GetHistory(chatId, 5 - unreads.Count, unreads.First().Id);
rest.ForEach(AddMessageToQueue);
messageQueue.Add($"{Ansi.Yellow}[tgcli] ---UNREAD---");
unreads.ForEach(AddMessageToQueue);
}
else
{
GetHistory(chatId).ForEach(AddMessageToQueue);
}
}
var history = GetHistory(currentChatId, 50);
if (history.Count != 0)
MarkRead(chat.Id, history.First().Id);
var last = history.LastOrDefault(p => p.IsOutgoing);
if (last == null)
{
currentUserRead = true;
return;
}
lastMessage = last;
currentUserRead = IsMessageRead(last.ChatId, last.Id);
}
}
public class NewChatCommand : Command
{
public NewChatCommand() : base("n", "", "starts a new chat.", "<username>", 1)
{
}
public override void Handler(List<string> inputParams)
{
var chat = GetChatByUsernameGlobal(inputParams[0]);
if (chat == null) return; //TODO do something
currentChatId = 0;
currentChatUserId = 0;
currentUserRead = false;
if (chat.Type is TdApi.ChatType.ChatTypePrivate privChat)
{
currentChatUserId = privChat.UserId;
}
currentChatId = chat.Id;
chat.Title = TruncateString(chat.Title, 20);
prefix = $"[{chat.Title}";
lock (@lock)
{
messageQueue.Add($"{Ansi.Yellow}[tgcli] Opening chat: {chat.Title}");
messageQueue.Add($"{Ansi.Yellow}" + $"[tgcli] You have {chat.UnreadCount} unread message" +
$"{(chat.UnreadCount == 1 ? "." : "s.")}");
if (chat.UnreadCount >= 5)
{
var capped = chat.UnreadCount > 50;
GetHistory(chat.Id, capped ? 50 : chat.UnreadCount).ForEach(AddMessageToQueue);
if (capped)
messageQueue.Add(
$"{Ansi.Yellow}[tgcli] " + $"Showing 50 of {chat.UnreadCount} unread messages.");
}
else if (chat.UnreadCount > 0)
{
var unreads = GetHistory(chat.Id, chat.UnreadCount);
Console.WriteLine(unreads.Count);
var rest = GetHistory(chat.Id, 5 - unreads.Count, unreads.First().Id);
rest.ForEach(AddMessageToQueue);
messageQueue.Add($"{Ansi.Yellow}[tgcli] ---UNREAD---");
unreads.ForEach(AddMessageToQueue);
}
else
{
GetHistory(chat.Id).ForEach(AddMessageToQueue);
}
}
var history = GetHistory(currentChatId, 50);
if (history.Count != 0)
MarkRead(chat.Id, history.First().Id);
var last = history.LastOrDefault(p => p.IsOutgoing);
if (last == null)
{
currentUserRead = true;
return;
}
lastMessage = last;
currentUserRead = IsMessageRead(last.ChatId, last.Id);
}
}
public class CloseSecretChatCommand : Command
{
public CloseSecretChatCommand() : base("cs", "", "closes a secret chat (permanently)", "", 0)
{
}
public override void Handler(List<string> _)
{
if (currentChatId == 0)
return; //TODO do something
if (GetChat(currentChatId).Type is TdApi.ChatType.ChatTypeSecret type)
{
CloseSecretChat(type.SecretChatId);
DeleteChatHistory(currentChatId);
CommandManager.HandleCommand("c");
}
else
{
//TODO do something
}
}
}
public class NewSecretChatCommand : Command
{
public NewSecretChatCommand() : base("ns", "", "creates a new secret chat.", "<username>", 1)
{
}
public override void Handler(List<string> inputParams)
{
var userId = GetUserIdByUsername(inputParams[0]);
if (userId == 0)
return; //TODO do something
if (GetSecretChats().Count(p => ((TdApi.ChatType.ChatTypeSecret) p.Type).UserId == userId) > 0)
return; //TODO do something
var chat = CreateSecretChat(userId);
CommandManager.HandleCommand("osd " + chat.Id);
}
}
public class OpenSecretDirectCommand : Command
{
public OpenSecretDirectCommand() : base("osd", "", "opens a secret chat by chat id", "<chat_id>", 1)
{
}
public override void Handler(List<string> inputParams)
{
var id = inputParams[0];
if (!long.TryParse(id, out var chatId))
{
return; //todo do something
}
var chat = GetChat(chatId);
if (chat == null)
return; //TODO do something
TdApi.SecretChat secChat;
if (chat.Type is TdApi.ChatType.ChatTypeSecret secretChat)
{
currentChatUserId = secretChat.UserId;
currentChatId = chat.Id;
currentUserRead = false;
secChat = GetSecretChat(secretChat.SecretChatId);
}
else
{
return; //TODO do something
}
chat.Title = TruncateString(chat.Title, 20);
prefix = $"[{Ansi.Red}sec {Ansi.ResetAll}{chat.Title}]";
lock (@lock)
{
messageQueue.Add($"{Ansi.Yellow}[tgcli] Opening secret chat: {chat.Title}");
messageQueue.Add($"{Ansi.Yellow}" + $"[tgcli] You have {chat.UnreadCount} unread message" +
$"{(chat.UnreadCount == 1 ? "." : "s.")}");
if (secChat.State is TdApi.SecretChatState.SecretChatStateClosed)
{
messageQueue.Add($"{Ansi.Red}[tgcli] Secret chat has ended. No messages can be sent.");
}
else if (secChat.State is TdApi.SecretChatState.SecretChatStatePending)
{
messageQueue.Add($"{Ansi.Red}[tgcli] Secret chat is pending. No messages can be sent.");
}
if (chat.UnreadCount >= 5)
{
var capped = chat.UnreadCount > 50;
GetHistory(chatId, capped ? 50 : chat.UnreadCount, isSecret: true).ForEach(AddMessageToQueue);
if (capped)
messageQueue.Add(
$"{Ansi.Yellow}[tgcli] " + $"Showing 50 of {chat.UnreadCount} unread messages.");
}
else if (chat.UnreadCount > 0)
{
var unreads = GetHistory(chatId, chat.UnreadCount, isSecret: true);
var rest = GetHistory(chatId, 5 - unreads.Count, unreads.First().Id, isSecret: true);
rest.ForEach(AddMessageToQueue);
messageQueue.Add($"{Ansi.Yellow}[tgcli] ---UNREAD---");
unreads.ForEach(AddMessageToQueue);
}
else
{
GetHistory(chatId, isSecret: true).ForEach(AddMessageToQueue);
}
}
var history = GetHistory(currentChatId, 50, isSecret: true);
if (history.Count != 0)
MarkRead(chat.Id, history.First().Id);
var last = history.LastOrDefault(p => p.IsOutgoing);
if (last == null)
{
currentUserRead = true;
return;
}
lastMessage = last;
currentUserRead = IsMessageRead(last.ChatId, last.Id);
}
}
public class OpenSecretCommand : Command
{
public OpenSecretCommand() : base("os", "", "opens a secret chat. queries chat list.", "<query>", -1)
{
}
public override void Handler(List<string> inputParams)
{
var query = inputParams.Aggregate((current, param) => current + " " + param).Trim();
var userId = SearchUserInChats(query);
if (userId == 0 || query.Length == 0)
return; //TODO do something;
var chat = GetSecretChats().Find(p => ((TdApi.ChatType.ChatTypeSecret) p.Type).UserId == userId);
if (chat == null)
{
return; //TODO do something
}
TdApi.SecretChat secChat;
if (chat.Type is TdApi.ChatType.ChatTypeSecret secretChat)
{
currentChatUserId = secretChat.UserId;
currentChatId = chat.Id;
currentUserRead = false;
secChat = GetSecretChat(secretChat.SecretChatId);
}
else
{
return; //TODO do something
}
chat.Title = TruncateString(chat.Title, 20);
prefix = $"[{Ansi.Red}sec {Ansi.ResetAll}{chat.Title}]";
lock (@lock)
{
messageQueue.Add($"{Ansi.Yellow}[tgcli] Opening secret chat: {chat.Title}");
messageQueue.Add($"{Ansi.Yellow}" + $"[tgcli] You have {chat.UnreadCount} unread message" +
$"{(chat.UnreadCount == 1 ? "." : "s.")}");
if (secChat.State is TdApi.SecretChatState.SecretChatStateClosed)
{
messageQueue.Add($"{Ansi.Red}[tgcli] Secret chat has ended. No messages can be sent.");
}
else if (secChat.State is TdApi.SecretChatState.SecretChatStatePending)
{
messageQueue.Add($"{Ansi.Red}[tgcli] Secret chat is pending. No messages can be sent.");
}
if (chat.UnreadCount >= 5)
{
var capped = chat.UnreadCount > 50;
GetHistory(chat.Id, capped ? 50 : chat.UnreadCount, isSecret: true).ForEach(AddMessageToQueue);
if (capped)
messageQueue.Add(
$"{Ansi.Yellow}[tgcli] " + $"Showing 50 of {chat.UnreadCount} unread messages.");
}
else if (chat.UnreadCount > 0)
{
var unreads = GetHistory(chat.Id, chat.UnreadCount, isSecret: true);
var rest = GetHistory(chat.Id, 5 - unreads.Count, unreads.First().Id, isSecret: true);
rest.ForEach(AddMessageToQueue);
messageQueue.Add($"{Ansi.Yellow}[tgcli] ---UNREAD---");
unreads.ForEach(AddMessageToQueue);
}
else
{
GetHistory(chat.Id, isSecret: true).ForEach(AddMessageToQueue);
}
}
var history = GetHistory(currentChatId, 50, isSecret: true);
if (history.Count != 0)
MarkRead(chat.Id, history.First().Id);
var last = history.LastOrDefault(p => p.IsOutgoing);
if (last == null)
{
currentUserRead = true;
return;
}
lastMessage = last;
currentUserRead = IsMessageRead(last.ChatId, last.Id);
}
}
public class CloseUnreadCommand : Command
{
public CloseUnreadCommand() : base("cu", "", "closes a chat, marking it as unread", "", 0)
{
}
public override void Handler(List<string> inputParams)
{
if (currentChatId == 0) return; //TODO: do something
MarkUnread(currentChatId);
CommandManager.HandleCommand("c");
}
}
public class CloseCommand : Command
{
public CloseCommand() : base("c", "^E", "closes a chat", "", 0)
{
}
public override void Handler(List<string> inputParams)
{
if (currentChatId == 0) return; //TODO: do something
currentChatId = 0;
currentChatUserId = 0;
currentUserRead = false;
lastMessage = null;
prefix = "[tgcli";
lock (@lock)
{
messageQueue.Add($"{Ansi.Yellow}[tgcli] Closing chat.");
var count = missedMessages.Count;
if (count == 0) return;
messageQueue.Add($"{Ansi.Yellow}" + $"[tgcli] You have {count} missed message" +
$"{(count == 1 ? "." : "s.")}");
messageQueue.AddRange(missedMessages);
missedMessages.Clear();
}
}
}
public class HistoryCommand : Command
{
public HistoryCommand() : base("h", "", "shows chat history. default limit is 5", "[1-50]", -1)
{
}
public override void Handler(List<string> inputParams)
{
if (currentChatId == 0) return; //TODO: do something
var history = inputParams.Count == 1 && int.TryParse(inputParams[0], out var limit)
? GetHistory(currentChatId, limit)
: GetHistory(currentChatId);
lock (@lock)
{
messageQueue.Add($"{Ansi.Yellow}[tgcli] Last {history.Count} messages in " +
$"{GetChat(currentChatId).Title}");
}
foreach (var msg in history)
{
AddMessageToQueue(msg);
}
}
}
public class ClearCommand : Command
{
public ClearCommand() : base("cl", "^L", "clears console", "", 0)
{
}
public override void Handler(List<string> inputParams)
{
lock (@lock)
{
Console.Clear();
}
}
}
public class UnreadsCommand : Command
{
public UnreadsCommand() : base("u", "^U", "displays unread chat", "[all]", -1)
{
}
public override void Handler(List<string> inputParams)
{
var unreads = GetUnreadChats(inputParams.Count == 1 && inputParams[0].Equals("all"));
lock (@lock)
{
messageQueue.Add($"{Ansi.Yellow}[tgcli] You have {unreads.Count} unread chats.");
unreads.ForEach(chat =>
{
string line;
if (chat.UnreadCount == 0)
line = $"{Ansi.Bold}{Ansi.Yellow}[M] {chat.Title}";
else if (chat.Type is TdApi.ChatType.ChatTypeSecret)
line = $"{Ansi.Bold}{Ansi.Red}[{chat.UnreadCount}] [sec] {chat.Title}";
else
line = $"{Ansi.Bold}{Ansi.Green}[{chat.UnreadCount}] {chat.Title}";
messageQueue.Add(line);
});
}
}
}
public class ListChatsCommand : Command
{
public ListChatsCommand() : base("lc", "", "lists all chats, optionally filtered", "[query]", -1)
{
}
public override void Handler(List<string> inputParams)
{
var chats = GetChats();
lock (@lock)
{
if (inputParams.Count > 0)
{
var query = inputParams.Aggregate((current, param) => current + " " + param).Trim();
chats = chats.FindAll(p => p.Title.ToLower().Contains(query.ToLower()));
}
messageQueue.Add($"{Ansi.Yellow}[tgcli] Listing {chats.Count} chats.");
chats.ForEach(chat =>
{
string line;
if (chat.UnreadCount == 0)
line = $"{Ansi.Bold}{Ansi.Blue}[0] {chat.Title}";
else
line = $"{Ansi.Bold}{Ansi.Green}[{chat.UnreadCount}] {chat.Title}";
messageQueue.Add(line);
});
}
}
}
public class SearchUserCommand : Command
{
public SearchUserCommand() : base("s", "", "searches for users globally", "<query>", -1)
{
}
public override void Handler(List<string> inputParams)
{
if (inputParams.Count == 0)
return; //TODO do something
var query = inputParams.Aggregate((current, param) => current + " " + param).Trim();
var chats = SearchChatsGlobal(query);
chats = chats.FindAll(p => p.Type is TdApi.ChatType.ChatTypePrivate);
lock (@lock)
{
messageQueue.Add($"{Ansi.Yellow}[tgcli] Listing {chats.Count} chats.");
chats.ForEach(chat =>
{
string line;
var type = (TdApi.ChatType.ChatTypePrivate) chat.Type;
var user = GetUser(type.UserId);
line = $"{Ansi.Bold}{Ansi.Yellow}@{user.Username} {Ansi.Magenta}{chat.Title}";
messageQueue.Add(line);
});
}
}
}
public class AddContactCommand : Command
{
public AddContactCommand() : base("ac", "", "adds user to contact list", "<username>", 1)
{
}
public override void Handler(List<string> inputParams)
{
var query = inputParams[0];
var chat = GetChatByUsernameGlobal(query);
if (chat.Type is TdApi.ChatType.ChatTypePrivate type)
{
//TODO implement when TDLib 1.6 is released
}
else
return; //TODO do something
}
}
public class ListSecretChatsCommand : Command
{
public ListSecretChatsCommand() : base("ls", "", "displays all open secret chats", "", 0)
{
}
public override void Handler(List<string> inputParams)
{
var secretChats = GetSecretChats();
lock (@lock)
{
messageQueue.Add($"{Ansi.Yellow}[tgcli] Listing {secretChats.Count} secret chats:");
secretChats.ForEach(chat =>
{
messageQueue.Add($"{Ansi.Bold}{Ansi.Red}[sec] {chat.Title} -> {chat.Id}");
});
}
}
}
public class HelpCommand : Command
{
public HelpCommand() : base("help", "", "lists all commands", "", 0)
{
}
public override void Handler(List<string> inputParams)
{
lock (@lock)
{
messageQueue.Add($"{Ansi.Yellow}[tgcli] Listing {CommandManager.Commands.Count} commands:");
CommandManager.Commands.ForEach(command =>
{
var commandText = $"/{command.Trigger}";
if (!string.IsNullOrWhiteSpace(command.Syntax))
commandText += $" {command.Syntax}";
commandText += $": {command.Description}";
if (!string.IsNullOrWhiteSpace(command.Shortcut))
commandText += $" ({command.Shortcut})";
messageQueue.Add($"{Ansi.Yellow}{commandText}");
});
}
}
}
public class QuitCommand : Command
{
public QuitCommand() : base("q", "^D", "quits the program", "", 0)
{
}
public override void Handler(List<string> inputParams)
{
quitting = true;
}
}
public class EditCommand : Command
{
public EditCommand() : base("e", "", "edits last message. param empty adds last message to inputline",
"[message]", -1)
{
}
public override void Handler(List<string> inputParams)
{
try
{
if (inputParams.Count == 0)
{
currentInputLine = "/e " + ((TdApi.MessageContent.MessageText) lastMessage?.Content)?.Text?.Text;
Emojis.ForEach(em => currentInputLine = currentInputLine.Replace(em.Item2, em.Item1));
return;
}
var message = inputParams.Aggregate((current, param) => current + " " + param).Trim();
if (currentChatId == 0) return; //TODO do something
if (lastMessage == null)
{
//try to find last message
var history = GetHistory(currentChatId, 50);
var last = history.LastOrDefault(p => p.IsOutgoing);
if (last == null) return; //TODO do something
lastMessage = last;
}
if (string.IsNullOrWhiteSpace(message))
return;
EditMessage(message, lastMessage);
}
catch
{
//TODO do something
}
}
}
public class ReplyCommand : Command
{
public ReplyCommand() : base("r", "", "replies to message", "<offset> <message>", -1)
{
}
public override void Handler(List<string> inputParams)
{
try
{
if (inputParams.Count < 2)
{
return; //TODO do something
}
var history = GetHistory(currentChatId, 50);
var parsed = int.TryParse(inputParams[0], out var offset);
inputParams.RemoveAt(0);
history.Reverse();
var message = inputParams.Aggregate((current, param) => current + " " + param).Trim();
if (!parsed || string.IsNullOrWhiteSpace(message) || history.Count < offset)
{
return; //TODO do something
}
var replyMessage = history[offset - 1];
if (currentChatId == 0) return; //TODO do something
SendMessage(message, currentChatId, replyMessage.Id);
}
catch
{
//TODO do something
}
}
}
public class LogoutCommand : Command
{
public LogoutCommand() : base("logout", "", "log out this session (destroys all local data)", "", 0)
{
}
public override void Handler(List<string> inputParams)
{
LogOut();
}
}
}