Archived
0
0
Fork 0
This repository has been archived on 2024-02-06. You can view files and clone it, but cannot push or open issues or pull requests.
chord/Program.cs

499 lines
14 KiB
C#
Raw Normal View History

2021-12-15 01:37:03 +00:00
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
2021-12-15 01:37:03 +00:00
using Discord;
using Discord.WebSocket;
2021-12-23 05:02:06 +00:00
using DiscordRPC;
2021-12-15 01:37:03 +00:00
using Microsoft.Extensions.Configuration;
using RestSharp;
using Terminal.Gui;
2021-12-16 22:58:32 +00:00
using Terminal.Gui.Trees;
2021-12-15 01:37:03 +00:00
using Attribute = Terminal.Gui.Attribute;
2021-12-23 05:02:06 +00:00
using Button = Terminal.Gui.Button;
2021-12-15 01:37:03 +00:00
using Color = Terminal.Gui.Color;
namespace chord
{
public class Program
{
private ListView chatBoxList;
private List<string> currentChannelMessages;
private ulong currentSelectedChannel;
private ulong currentSelectedGuild;
private SortedDictionary<ulong, List<ulong>> guilds;
private DiscordRpcClient rpcClient;
2021-12-15 01:37:03 +00:00
private Settings settings;
private Window window;
public static void Main()
{
new Program().Start();
}
private async void Start()
{
try {
2021-12-15 01:37:03 +00:00
#if DEBUG
var config = new ConfigurationBuilder()
.AddJsonFile("config.json")
2021-12-15 01:37:03 +00:00
.Build();
#else
var config = new ConfigurationBuilder()
.AddJsonFile($"{Environment.GetEnvironmentVariable("HOME")}/.config/chord/config.json")
.Build();
2021-12-15 01:37:03 +00:00
#endif
settings = config.Get<Settings>();
2021-12-15 01:37:03 +00:00
var client = new DiscordSocketClient(new DiscordSocketConfig
{ AlwaysDownloadUsers = true, GatewayIntents = GatewayIntents.All });
2021-12-15 01:37:03 +00:00
await client.LoginAsync(TokenType.Bot, settings.Token);
await client.StartAsync();
2021-12-15 01:37:03 +00:00
if (settings.EnableRichPresence) {
rpcClient = new DiscordRpcClient("923436807297859625");
rpcClient.Initialize();
rpcClient.SetPresence(new RichPresence());
}
2021-12-23 05:02:06 +00:00
Application.Init();
2021-12-15 01:37:03 +00:00
window = new Window("chord")
2021-12-15 01:37:03 +00:00
{
X = 0,
Y = 1,
Width = Dim.Fill(),
Height = Dim.Fill(),
ColorScheme =
{
Normal = new Attribute(Color.White, Color.Black)
}
};
2021-12-15 01:37:03 +00:00
2021-12-23 06:56:13 +00:00
var menuBar = buildMenu();
var loadingLabel = new Label("Loading...")
{
X = 0,
Y = 0,
Width = Dim.Fill(),
Height = Dim.Fill(),
TextAlignment = TextAlignment.Centered,
VerticalTextAlignment = VerticalTextAlignment.Middle,
};
client.Ready += () =>
2021-12-15 01:37:03 +00:00
{
guilds = new SortedDictionary<ulong, List<ulong>>();
2021-12-15 01:37:03 +00:00
foreach (var guild in client.Guilds)
2021-12-15 01:37:03 +00:00
{
guilds.Add(guild.Id, new List<ulong>());
2021-12-15 01:37:03 +00:00
foreach (var channel in guild.TextChannels)
{
var findGuild = guilds.GetValueOrDefault(guild.Id);
2021-12-15 01:37:03 +00:00
if (channel.Users.ToList().Find(user => user.Id == client.CurrentUser.Id) == null) continue;
if (findGuild == null) continue;
findGuild.Add(channel.Id);
findGuild.Sort();
}
2021-12-15 01:37:03 +00:00
}
var serverList = buildServerList();
var channelList = buildChannelList(serverList);
var chatBox = buildChatBox(serverList);
var messageBox = buildMessageBox(serverList, chatBox);
var userList = buildUserList(chatBox);
2021-12-15 01:37:03 +00:00
var serverListList = new ListView
{
X = 0,
Y = 0,
Width = Dim.Fill(),
Height = Dim.Fill()
};
2021-12-15 01:37:03 +00:00
var guildNames = guilds.Select(guild => client.GetGuild(guild.Key).Name).ToList();
2021-12-15 01:37:03 +00:00
serverListList.SetSource(guildNames);
2021-12-15 01:37:03 +00:00
var channelListTree = new TreeView
{
X = 0,
Y = 0,
Width = Dim.Fill(),
Height = Dim.Fill()
};
2021-12-15 01:37:03 +00:00
chatBoxList = new ListView
{
X = 0,
Y = 0,
Width = Dim.Fill(),
Height = Dim.Fill()
};
2021-12-15 01:37:03 +00:00
var messageBoxText = new TextView
{
X = 0,
Y = 0,
Width = Dim.Percent(95),
Height = Dim.Fill()
};
2021-12-15 01:37:03 +00:00
var messageBoxSend = new Button("Send", true)
{
X = Pos.Right(messageBoxText) - 2,
Y = 0
};
2021-12-15 01:37:03 +00:00
var userListTree = new TreeView
{
X = 0,
Y = 0,
Width = Dim.Fill(),
Height = Dim.Fill()
};
serverListList.OpenSelectedItem += args =>
{
channelListTree.ClearObjects();
2021-12-15 01:37:03 +00:00
currentSelectedGuild = guilds.Keys.ToList()[args.Item];
2021-12-15 01:37:03 +00:00
var channelNames = guilds.GetValueOrDefault(currentSelectedGuild)!
.Select(channel => client.GetGuild(currentSelectedGuild).GetTextChannel(channel).Name).ToList();
2021-12-16 22:58:32 +00:00
var categoryDict = new SortedDictionary<ulong, TreeNode>();
2021-12-16 22:58:32 +00:00
foreach (var categories in client.GetGuild(currentSelectedGuild).CategoryChannels)
2021-12-16 22:58:32 +00:00
{
var categoryName = Regex.Replace(categories.Name, @"[^\u0000-\u007F]+", string.Empty);
var node = new TreeNode(categoryName);
foreach(var channel in categories.Channels)
2021-12-16 22:58:32 +00:00
{
if (channel.GetType().Name == "SocketTextChannel")
{
var channelName = Regex.Replace(channel.Name, @"[^\u0000-\u007F]+", string.Empty);
var channelNode = new TreeNode(channelName);
channelNode.Tag = new { Id = channel.Id };
2021-12-16 22:58:32 +00:00
node.Children.Add(channelNode);
}
2021-12-16 22:58:32 +00:00
}
categoryDict.Add(categories.Id, node);
2021-12-16 22:58:32 +00:00
}
foreach (var category in categoryDict)
{
channelListTree.AddObject(category.Value);
}
2021-12-23 05:02:06 +00:00
if (settings.EnableRichPresence) {
rpcClient.UpdateDetails($"Chatting in {client.GetGuild(currentSelectedGuild).Name}");
}
};
2021-12-16 22:58:32 +00:00
channelListTree.SelectionChanged += async (arg1, arg2) =>
2021-12-16 22:58:32 +00:00
{
userListTree.ClearObjects();
2021-12-15 01:37:03 +00:00
try {
currentSelectedChannel = (ulong)arg2.NewValue.Tag.GetType().GetProperty("Id").GetValue(arg2.NewValue.Tag);
} catch {
return;
}
try {
var restSharpClient = new RestClient("https://discord.com/api/v9");
var restSharpReq = new RestRequest($"channels/{currentSelectedChannel}/messages");
restSharpClient.AddDefaultHeader("Authorization", settings.Token);
2021-12-15 01:37:03 +00:00
var response = await restSharpClient.GetAsync<List<GetMessagesResponse>>(restSharpReq);
2021-12-15 01:37:03 +00:00
var messages = new List<string>();
2021-12-15 01:37:03 +00:00
foreach (var msg in response)
{
var msgNewLines = msg.content.Split("\n").ToList();
2021-12-15 01:37:03 +00:00
var firstMsg = msgNewLines[0];
2021-12-15 01:37:03 +00:00
msgNewLines.RemoveAt(0);
msgNewLines.Reverse();
2021-12-15 01:37:03 +00:00
messages.AddRange(msgNewLines.Select(message => $"{message}"));
2021-12-15 01:37:03 +00:00
messages.Add($"{msg.author.username}#{msg.author.discriminator} | {firstMsg}");
2021-12-15 01:37:03 +00:00
if (msg.embeds.Count != 0)
messages.Add($"{msg.author.username}#{msg.author.discriminator} | [Unable to display embed]");
2021-12-15 01:37:03 +00:00
if (msg.attachments.Count == 0) continue;
messages.AddRange(msg.attachments
.Select(attachment => $"{msg.author.username}#{msg.author.discriminator} | {attachment.url}")
.Select(dummy => dummy));
}
2021-12-15 01:37:03 +00:00
messages.Reverse();
2021-12-15 01:37:03 +00:00
await chatBoxList.SetSourceAsync(messages);
currentChannelMessages = messages;
2021-12-15 01:37:03 +00:00
chatBoxList.ScrollDown(currentChannelMessages.Count - chatBoxList.Bounds.Height);
chatBoxList.SelectedItem = currentChannelMessages.Count - 1;
2021-12-15 01:37:03 +00:00
var rolesDict = new SortedDictionary<int, TreeNode>();
2021-12-17 01:44:44 +00:00
foreach (var roles in client.GetGuild(currentSelectedGuild).Roles)
{
2021-12-24 03:24:13 +00:00
if (roles.IsHoisted) {
if (roles.Members.Count() != 0) {
var roleName = Regex.Replace(roles.Name, @"[^\u0000-\u007F]+", string.Empty);
var node = new TreeNode(roleName);
2021-12-17 01:44:44 +00:00
2021-12-24 03:24:13 +00:00
foreach (var users in roles.Members)
{
var userName = Regex.Replace(users.Username, @"[^\u0000-\u007F]+", string.Empty);
2021-12-24 03:24:13 +00:00
string userNick = string.Empty;
if (users.Nickname != null)
userNick = Regex.Replace(users.Nickname, @"[^\u0000-\u007F]+", string.Empty);
2021-12-24 03:14:38 +00:00
2021-12-24 03:24:13 +00:00
node.Children.Add(new TreeNode($"{userName}#{users.Discriminator} ({userNick})"));
}
2021-12-17 01:44:44 +00:00
2021-12-24 03:24:13 +00:00
rolesDict.TryAdd(Math.Abs(roles.Position - client.GetGuild(currentSelectedGuild).Roles.Count()), node);
}
}
}
2021-12-17 01:44:44 +00:00
foreach (var role in rolesDict)
{
userListTree.AddObject(role.Value);
}
2021-12-23 05:02:06 +00:00
if (settings.EnableRichPresence) {
rpcClient.UpdateState($"In channel {client.GetGuild(currentSelectedGuild).GetTextChannel(currentSelectedChannel).Name}");
rpcClient.UpdateStartTime();
}
2021-12-24 03:14:38 +00:00
} catch(Exception err) {
Console.WriteLine(err);
await chatBoxList.SetSourceAsync(new List<string>());
}
};
messageBoxSend.Clicked += async () =>
{
var restSharpClient = new RestClient("https://discord.com/api/v9");
var restSharpReq = new RestRequest($"channels/{currentSelectedChannel}/messages");
restSharpClient.AddDefaultHeader("Authorization", settings.Token);
restSharpReq.AddJsonBody(new { content = Encoding.UTF8.GetString(messageBoxText.Text.ToByteArray()) });
2021-12-15 01:37:03 +00:00
await restSharpClient.PostAsync<List<object>>(restSharpReq);
2021-12-15 01:37:03 +00:00
messageBoxText.Text = "";
};
2021-12-15 01:37:03 +00:00
chatBoxList.OpenSelectedItem += args =>
{
if (currentChannelMessages[args.Item].Split(" | ")[1].Contains("http"))
for (var i = 0; i < currentChannelMessages[args.Item].Split(" | ")[1].Split(" ").Count(); i++)
if (currentChannelMessages[args.Item].Split(" | ")[1].Split(" ")[i].Contains("http"))
2021-12-15 01:37:03 +00:00
{
var procStartInfo = new ProcessStartInfo("/bin/sh",
$"-c \"xdg-open {currentChannelMessages[args.Item].Split(" | ")[1].Split(" ")[i]}\"")
{
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
UseShellExecute = false
};
var proc = new Process();
proc.StartInfo = procStartInfo;
proc.Start();
}
};
Application.Top.KeyPress += async args =>
{
switch (ShortcutHelper.GetModifiersKey(args.KeyEvent)) {
case Key.CtrlMask | Key.S:
serverListList.SetFocus();
break;
case Key.CtrlMask | Key.C:
channelListTree.SetFocus();
break;
case Key.CtrlMask | Key.AltMask | Key.C:
chatBoxList.SetFocus();
break;
case Key.CtrlMask | Key.U:
userListTree.SetFocus();
break;
case Key.CtrlMask | Key.Enter:
var restSharpClient = new RestClient("https://discord.com/api/v9");
var restSharpReq = new RestRequest($"channels/{currentSelectedChannel}/messages");
restSharpClient.AddDefaultHeader("Authorization", settings.Token);
restSharpReq.AddJsonBody(new { content = Encoding.UTF8.GetString(messageBoxText.Text.ToByteArray()) });
await restSharpClient.PostAsync<List<object>>(restSharpReq);
messageBoxText.Text = "";
break;
}
};
2021-12-16 19:23:54 +00:00
2021-12-23 06:56:13 +00:00
window.RemoveAll();
window.Add(serverList, channelList, messageBox, chatBox, userList);
serverList.Add(serverListList);
channelList.Add(channelListTree);
chatBox.Add(chatBoxList);
messageBox.Add(messageBoxText, messageBoxSend);
userList.Add(userListTree);
2021-12-16 19:23:54 +00:00
return Task.CompletedTask;
2021-12-16 19:23:54 +00:00
};
client.MessageReceived += async msg =>
{
if (msg.Channel.Id != currentSelectedChannel) return;
2021-12-15 01:37:03 +00:00
var restSharpClient = new RestClient("https://discord.com/api/v9");
var restSharpReq = new RestRequest($"channels/{currentSelectedChannel}/messages");
restSharpReq.AddQueryParameter("limit", "1");
restSharpClient.AddDefaultHeader("Authorization", settings.Token);
2021-12-15 01:37:03 +00:00
var response = await restSharpClient.GetAsync<List<GetMessagesResponse>>(restSharpReq);
2021-12-15 01:37:03 +00:00
currentChannelMessages.Add(
$"{response.First().author.username}#{response.First().author.discriminator} | {response.First().content}");
await chatBoxList.SetSourceAsync(currentChannelMessages);
2021-12-15 01:37:03 +00:00
chatBoxList.ScrollDown(currentChannelMessages.Count - chatBoxList.Bounds.Height);
chatBoxList.SelectedItem = currentChannelMessages.Count - 1;
};
2021-12-15 01:37:03 +00:00
2021-12-23 05:02:06 +00:00
Application.MainLoop.AddTimeout(TimeSpan.FromMilliseconds(100), caller => {
if (settings.EnableRichPresence)
rpcClient.Invoke();
2021-12-23 05:02:06 +00:00
return true;
});
2021-12-15 01:37:03 +00:00
2021-12-23 06:56:13 +00:00
Application.Top.Add(window, menuBar);
window.Add(loadingLabel);
Application.Run();
Application.Shutdown();
} catch (System.Exception err) {
Console.WriteLine(err);
2021-12-23 06:56:13 +00:00
Application.Shutdown();
}
2021-12-15 01:37:03 +00:00
}
private Window buildChatBox(Window serverList)
{
return new Window("Chat Box")
{
X = Pos.Right(serverList),
Y = 0,
Width = Dim.Percent(60),
Height = Dim.Percent(90)
};
}
private Window buildChannelList(Window serverList)
{
return new Window("Channel List")
{
X = 0,
Y = Pos.Bottom(serverList),
Width = Dim.Percent(30),
Height = Dim.Percent(50)
};
}
private MenuBar buildMenu()
{
return new MenuBar(new[]
{
new MenuBarItem("File", new[]
{
2021-12-16 20:56:44 +00:00
new MenuItem("_Quit", "Quit the Application", () => { Application.Top.Running = false; })
}),
new MenuBarItem("Help", new[]
{
new MenuItem("_License", "See the Application License", () => {
MessageBox.Query("License", "chord Copyright (C) 2021 Daryl Ronningen\nThis program comes with ABSOLUTELY NO WARRANTY; for details see section `15` of the GPL license\nThis is free software, and you are welcome to redistribute it under certain conditions; for details see section `2` of the GPL license", "Close");
})
2021-12-15 01:37:03 +00:00
})
});
}
private Window buildMessageBox(Window serverList, Window chatBox)
{
return new Window("Message Box")
{
X = Pos.Right(serverList),
Y = Pos.Bottom(chatBox),
Width = Dim.Percent(60),
Height = Dim.Percent(10)
};
}
private Window buildUserList(Window chatBox)
{
return new Window("User List")
{
X = Pos.Right(chatBox),
Y = 0,
Width = Dim.Percent(10),
Height = Dim.Fill()
};
}
private Window buildServerList()
{
return new Window("Server List")
{
X = 0,
Y = 0,
Width = Dim.Percent(30),
Height = Dim.Percent(50)
};
}
}
}
public class Settings
{
public string Token { get; set; }
public bool EnableRichPresence { get; set; }
2021-12-15 01:37:03 +00:00
}
public class GetMessagesResponse
{
public string content { get; set; }
public MessageAuthor author { get; set; }
public List<object> embeds { get; set; }
public List<MessageAttachments> attachments { get; set; }
}
public class MessageAuthor
{
public string username { get; set; }
public string discriminator { get; set; }
}
public class MessageAttachments
{
public string url { get; set; }
}