commit 6ce8a777d4a0a22d0b829cf1504868a83d27eae4 Author: sam Date: Sun Jun 9 18:51:48 2024 +1200 Add project files. diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..faf85ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,364 @@ +## 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/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[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/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# 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 + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# 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/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd +/Test/Config.cs diff --git a/Discord.sln b/Discord.sln new file mode 100644 index 0000000..4d61ac0 --- /dev/null +++ b/Discord.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34728.123 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord", "Discord\Discord.csproj", "{E3BD2999-8A0F-493A-964B-52A1D840C18B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{9AB77E67-2EB7-4FB4-BB62-67F5A4FCDB09}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E3BD2999-8A0F-493A-964B-52A1D840C18B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3BD2999-8A0F-493A-964B-52A1D840C18B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3BD2999-8A0F-493A-964B-52A1D840C18B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3BD2999-8A0F-493A-964B-52A1D840C18B}.Release|Any CPU.Build.0 = Release|Any CPU + {9AB77E67-2EB7-4FB4-BB62-67F5A4FCDB09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AB77E67-2EB7-4FB4-BB62-67F5A4FCDB09}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AB77E67-2EB7-4FB4-BB62-67F5A4FCDB09}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AB77E67-2EB7-4FB4-BB62-67F5A4FCDB09}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4CDE1046-1571-4BC5-B362-674B74296AF5} + EndGlobalSection +EndGlobal diff --git a/Discord/Base.cs b/Discord/Base.cs new file mode 100644 index 0000000..92050bf --- /dev/null +++ b/Discord/Base.cs @@ -0,0 +1,7 @@ +namespace Discord +{ + public class Base + { + public Client? Client { get; set; } + } +} diff --git a/Discord/Client.cs b/Discord/Client.cs new file mode 100644 index 0000000..0893e5f --- /dev/null +++ b/Discord/Client.cs @@ -0,0 +1,34 @@ +using Discord.Managers; + +namespace Discord +{ + public class Client + { + public Action? Ready; + public Action? MessageCreate; + + public string Token; + public User? User; + public UserManager Users; + public ChannelManager Channels; + + public REST.Client Rest; + private Gateway.Client Gateway; + + public Client(string token) + { + this.Token = token; + this.Rest = new REST.Client(token); + this.Gateway = new Gateway.Client(this); + this.Users = new UserManager(this); + this.Channels = new ChannelManager(this); + } + + public async Task Login() + { + User = await Users.Get("@me"); + await Gateway.Connect(); + await Gateway.EventLoop(); + } + } +} \ No newline at end of file diff --git a/Discord/Discord.csproj b/Discord/Discord.csproj new file mode 100644 index 0000000..412d83d --- /dev/null +++ b/Discord/Discord.csproj @@ -0,0 +1,10 @@ + + + + Library + net8.0 + enable + enable + + + diff --git a/Discord/Gateway/GatewayBasicPayload.cs b/Discord/Gateway/GatewayBasicPayload.cs new file mode 100644 index 0000000..fa25627 --- /dev/null +++ b/Discord/Gateway/GatewayBasicPayload.cs @@ -0,0 +1,9 @@ +namespace Discord.Gateway +{ + internal class GatewayBasicPayload + { + public required GatewayOp Op { get; set; } + public required int? S { get; set; } + public required string? T { get; set; } + } +} diff --git a/Discord/Gateway/GatewayClient.cs b/Discord/Gateway/GatewayClient.cs new file mode 100644 index 0000000..d65c597 --- /dev/null +++ b/Discord/Gateway/GatewayClient.cs @@ -0,0 +1,82 @@ +using System.Reflection; + +namespace Discord.Gateway +{ + public class Client + { + private WebSocket ws; + private Discord.Client client; + private Timer heartbeat; + + public Client(Discord.Client client) + { + ws = new WebSocket(); + heartbeat = new Timer(async (s) => + { + await ws.Send(new GatewayPayload(GatewayOp.Heartbeat)); + }); + this.client = client; + } + + public async Task Connect() + { + await ws.Connect("wss://gateway.discord.gg/?v=10&encoding=json"); + } + + public async Task EventLoop() + { + while (true) + { + string json = await ws.Recieve(); + GatewayBasicPayload? basic = JSON.Deserialize(json); + Console.WriteLine(basic?.Op); + switch (basic?.Op) + { + case GatewayOp.Dispatch: + Console.WriteLine($"Event: {basic.T}"); + switch (basic.T) + { + case "READY": + client.Ready?.Invoke(client); + break; + + case "MESSAGE_CREATE": + Message? m = JSON.Deserialize>(json)?.D; + m!.Client = client; + client.MessageCreate?.Invoke(client, m); + break; + } + break; + + case GatewayOp.Hello: + GatewayPayload? helloPayload = GatewayPayload.Parse(json); + int interval = helloPayload?.D?.HeartbeatInterval ?? Timeout.Infinite; + heartbeat.Change(interval, interval); + + Console.WriteLine($"Heartbeat: {interval}"); + + string name = Assembly.GetExecutingAssembly().GetName().Name!; + + GatewayPayload identify = new(GatewayOp.Identify, new Payloads.Identify() + { + Token = client.Token, + Properties = new Payloads.IdentifyConnectionProperties() + { + Os = Environment.OSVersion.ToString(), + Browser = name, + Device = name + } + }); + + Console.WriteLine(identify); + + await ws.Send(identify); + + break; + case GatewayOp.HeartbeatACK: + break; + } + } + } + } +} diff --git a/Discord/Gateway/GatewayOps.cs b/Discord/Gateway/GatewayOps.cs new file mode 100644 index 0000000..e44a693 --- /dev/null +++ b/Discord/Gateway/GatewayOps.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord.Gateway +{ + internal enum GatewayOp + { + Dispatch = 0, + Heartbeat, + Identify, + PresenceUpdate, + VoiceStateUpdate, + Resume = 6, + Reconnect, + RequestGuildMembers, + InvalidSession, + Hello, + HeartbeatACK + } +} diff --git a/Discord/Gateway/GatewayPayload.cs b/Discord/Gateway/GatewayPayload.cs new file mode 100644 index 0000000..a6f7c2a --- /dev/null +++ b/Discord/Gateway/GatewayPayload.cs @@ -0,0 +1,34 @@ +namespace Discord.Gateway +{ + internal class GatewayPayload
+ { + public GatewayOp Op { get; set; } + public DT? D { get; set; } + + public int? S { get; set; } + public string? T { get; set; } + + public GatewayPayload(GatewayOp op, DT? d = default, int? s = null, string? t = null) + { + Op = op; + D = d; + S = s; + T = t; + } + + public static GatewayPayload
? Parse(string json) + { + return JSON.Deserialize>(json); + } + + public override string ToString() + { + return JSON.Serialize(this); + } + + public static implicit operator string(GatewayPayload
payload) + { + return payload.ToString(); + } + } +} diff --git a/Discord/Gateway/Payloads/HelloPayload.cs b/Discord/Gateway/Payloads/HelloPayload.cs new file mode 100644 index 0000000..2848c84 --- /dev/null +++ b/Discord/Gateway/Payloads/HelloPayload.cs @@ -0,0 +1,7 @@ +namespace Discord.Gateway.Payloads +{ + internal class Hello + { + public required int HeartbeatInterval { get; set; } + } +} diff --git a/Discord/Gateway/Payloads/IdentifyPayload.cs b/Discord/Gateway/Payloads/IdentifyPayload.cs new file mode 100644 index 0000000..1a9255c --- /dev/null +++ b/Discord/Gateway/Payloads/IdentifyPayload.cs @@ -0,0 +1,15 @@ +namespace Discord.Gateway.Payloads +{ + internal class Identify + { + public required string Token { get; set; } + public required IdentifyConnectionProperties Properties { get; set; } + } + + internal class IdentifyConnectionProperties + { + public required string Os { get; set; } + public required string Browser { get; set; } + public required string Device { get; set; } + } +} diff --git a/Discord/Gateway/WebSocket.cs b/Discord/Gateway/WebSocket.cs new file mode 100644 index 0000000..d0d7862 --- /dev/null +++ b/Discord/Gateway/WebSocket.cs @@ -0,0 +1,46 @@ +using System.Net.WebSockets; +using System.Text; + +namespace Discord.Gateway +{ + internal class WebSocket + { + private ClientWebSocket ws; + + public WebSocket() + { + ws = new ClientWebSocket(); + } + + public async Task Connect(string url) + { + await ws.ConnectAsync(new Uri(url), CancellationToken.None); + } + + public async Task Send(string text) + { + await ws.SendAsync(new ReadOnlyMemory(Encoding.UTF8.GetBytes(text)), WebSocketMessageType.Text, true, CancellationToken.None); + } + + public async Task Recieve() + { + Memory buffer = new Memory(new byte[1024]); + ValueWebSocketReceiveResult result; + int offset = 0; + do + { + result = await ws.ReceiveAsync(buffer[offset..], CancellationToken.None); + offset += result.Count; + + if (!result.EndOfMessage && offset >= buffer.Length) + { + ArraySegment newBuffer = new ArraySegment(new byte[buffer.Length * 2]); + buffer.CopyTo(newBuffer); + buffer = newBuffer; + } + } while (!result.EndOfMessage); + + return Encoding.UTF8.GetString(buffer.ToArray(), 0, offset); + } + } +} diff --git a/Discord/JSON.cs b/Discord/JSON.cs new file mode 100644 index 0000000..4e43f98 --- /dev/null +++ b/Discord/JSON.cs @@ -0,0 +1,28 @@ +using System.Text.Json; + +namespace Discord +{ + static internal class JSON + { + public static JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower + }; + + public static JsonSerializerOptions DeserializeOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower + }; + + public static string Serialize(T obj) + { + return JsonSerializer.Serialize(obj, SerializeOptions); + } + + public static T? Deserialize(string json) + { + return JsonSerializer.Deserialize(json, DeserializeOptions); + } + } +} diff --git a/Discord/Managers/ChannelManager.cs b/Discord/Managers/ChannelManager.cs new file mode 100644 index 0000000..4d95f34 --- /dev/null +++ b/Discord/Managers/ChannelManager.cs @@ -0,0 +1,25 @@ +namespace Discord.Managers +{ + public class ChannelManager : Manager + { + public ChannelManager(Client client) : base(client) { } + + public async Task Fetch(string id) + { + Channel? channel = await Client.Rest.Get(REST.Routes.Channel(id)); + if(channel != null) + { + channel.Client = Client; + channel.Messages = new MessageManager(Client, channel); + Cache[id] = channel; + } + return channel; + } + + public async Task Get(string id) + { + if (Cache.ContainsKey(id)) return Cache[id]; + return await Fetch(id); + } + } +} diff --git a/Discord/Managers/Manager.cs b/Discord/Managers/Manager.cs new file mode 100644 index 0000000..aebae24 --- /dev/null +++ b/Discord/Managers/Manager.cs @@ -0,0 +1,13 @@ +namespace Discord.Managers +{ + public class Manager where K : IEquatable + { + public Dictionary Cache; + public Client Client; + + public Manager(Client client) { + this.Client = client; + Cache = new Dictionary(); + } + } +} diff --git a/Discord/Managers/MessageManager.cs b/Discord/Managers/MessageManager.cs new file mode 100644 index 0000000..ceca8af --- /dev/null +++ b/Discord/Managers/MessageManager.cs @@ -0,0 +1,28 @@ +namespace Discord.Managers +{ + public class MessageManager : Manager + { + public Channel Channel; + + public MessageManager(Client client, Channel channel) : base(client) { + Channel = channel; + } + + public async Task Fetch(string id) + { + Message? msg = await Client.Rest.Get(REST.Routes.Message(Channel.Id, id)); + if(msg != null) + { + msg.Client = Client; + Cache[id] = msg; + } + return msg; + } + + public async Task Get(string id) + { + if (Cache.ContainsKey(id)) return Cache[id]; + return await Fetch(id); + } + } +} diff --git a/Discord/Managers/UserManager.cs b/Discord/Managers/UserManager.cs new file mode 100644 index 0000000..347de07 --- /dev/null +++ b/Discord/Managers/UserManager.cs @@ -0,0 +1,24 @@ +namespace Discord.Managers +{ + public class UserManager : Manager + { + public UserManager(Client client) : base(client) { } + + public async Task Fetch(string id) + { + User? user = await Client.Rest.Get(REST.Routes.User(id)); + if(user != null) + { + user.Client = Client; + Cache[id] = user; + } + return user; + } + + public async Task Get(string id) + { + if (Cache.ContainsKey(id)) return Cache[id]; + return await Fetch(id); + } + } +} diff --git a/Discord/REST/Payloads/MessagePayload.cs b/Discord/REST/Payloads/MessagePayload.cs new file mode 100644 index 0000000..e1a7491 --- /dev/null +++ b/Discord/REST/Payloads/MessagePayload.cs @@ -0,0 +1,7 @@ +namespace Discord.REST.Payloads +{ + internal class MessagePayload + { + public string? Content { get; set; } + } +} diff --git a/Discord/REST/RESTClient.cs b/Discord/REST/RESTClient.cs new file mode 100644 index 0000000..621f973 --- /dev/null +++ b/Discord/REST/RESTClient.cs @@ -0,0 +1,44 @@ +using System.Net.Http.Json; + +namespace Discord.REST +{ + public class Client + { + private string token; + private HttpClient client; + + public Client(string token) { + client = new HttpClient(); + this.token = token; + + SetHeaders(); + } + + public void SetHeaders() + { + client.DefaultRequestHeaders.Add("Authorization", token); + } + + public Task GetString(string url) + { + return client.GetStringAsync(url); + } + + public async Task PostString(string url, D data) + { + return await (await client.PostAsJsonAsync(url, data)).Content.ReadAsStringAsync(); + } + + public async Task Get(string url) + { + string text = await GetString(url); + return JSON.Deserialize(text); + } + + public async Task Post(string url, D data) + { + string text = await PostString(url, data); + return JSON.Deserialize(text); + } + } +} diff --git a/Discord/REST/Routes.cs b/Discord/REST/Routes.cs new file mode 100644 index 0000000..fb30ee2 --- /dev/null +++ b/Discord/REST/Routes.cs @@ -0,0 +1,33 @@ + + + +namespace Discord.REST +{ + public static class Routes + { + private static string Route(string path) + { + return $"https://discord.com/api/v10{path}"; + } + + public static string User(string id) + { + return Route($"/users/{id}"); + } + + internal static string Channel(string id) + { + return Route($"/channels/{id}"); + } + + internal static string Messages(string channelId) + { + return $"{Channel(channelId)}/messages"; + } + + internal static string Message(string channelId, string id) + { + return $"{Messages(channelId)}/{id}"; + } + } +} diff --git a/Discord/Structures/AvatarDecorationData.cs b/Discord/Structures/AvatarDecorationData.cs new file mode 100644 index 0000000..db67439 --- /dev/null +++ b/Discord/Structures/AvatarDecorationData.cs @@ -0,0 +1,13 @@ +namespace Discord +{ + public class AvatarDecorationData : Base + { + public required string Asset { get; set; } + public required string SkuId { get; set; } + + public override string ToString() + { + return JSON.Serialize(this); + } + } +} diff --git a/Discord/Structures/Channel.cs b/Discord/Structures/Channel.cs new file mode 100644 index 0000000..f191a7c --- /dev/null +++ b/Discord/Structures/Channel.cs @@ -0,0 +1,26 @@ +using Discord.Managers; +using Discord.REST.Payloads; + +namespace Discord +{ + public class Channel : Base + { + public required string Id { get; set; } + public required int Type { get; set; } + public string? Name { get; set; } + public MessageManager? Messages { get; set; } + + public async Task Send(string content) + { + return await Client!.Rest.Post(REST.Routes.Messages(Id), + new MessagePayload() { + Content = content + }); + } + + public override string ToString() + { + return JSON.Serialize(this); + } + } +} diff --git a/Discord/Structures/Message.cs b/Discord/Structures/Message.cs new file mode 100644 index 0000000..c89968f --- /dev/null +++ b/Discord/Structures/Message.cs @@ -0,0 +1,19 @@ +namespace Discord +{ + public class Message : Base + { + public required string Id { get; set; } + public required string ChannelId { get; set; } + public Channel Channel { get { return Client!.Channels.Get(ChannelId).Result!; } } + public required User Author { get; set; } + public required string Content { get; set; } + public required bool Tts { get; set; } + public required bool MentionEveryone { get; set; } + public required List Mentions { get; set; } + + public override string ToString() + { + return JSON.Serialize(this); + } + } +} diff --git a/Discord/Structures/User.cs b/Discord/Structures/User.cs new file mode 100644 index 0000000..9938812 --- /dev/null +++ b/Discord/Structures/User.cs @@ -0,0 +1,28 @@ +namespace Discord +{ + public class User : Base + { + public required string Id { get; set; } + public required string Username { get; set; } + public required string Discriminator { get; set; } + public required string? GlobalName { get; set; } + public required string? Avatar { get; set; } + public bool Bot { get; set; } + public bool System { get; set; } + public bool MfaEnabled { get; set; } + public string? Banner { get; set; } + public int? AccentColor { get; set; } + public string? Locale { get; set; } + public bool Verified { get; set; } + public string? Email { get; set; } + public int Flags { get; set; } + public int PremiumType { get; set; } + public int PublicFlags { get; set; } + public AvatarDecorationData? AvatarDecorationData { get; set; } + + public override string ToString() + { + return JSON.Serialize(this); + } + } +} diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..8aa2645 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Test/Program.cs b/Test/Program.cs new file mode 100644 index 0000000..9bbd7f8 --- /dev/null +++ b/Test/Program.cs @@ -0,0 +1,24 @@ +using Discord; +using Test; + +class Application +{ + public static async Task Main(string[] args) + { + Client client = new(Config.Token); + client.Ready += (client) => + { + Console.WriteLine(client.User); + }; + + client.MessageCreate += async (client, msg) => + { + if (msg.Content == "hi") + { + await msg.Channel.Send("hello"); + } + }; + + await client.Login(); + } +} \ No newline at end of file diff --git a/Test/TEMPLATE-Config.cs b/Test/TEMPLATE-Config.cs new file mode 100644 index 0000000..66b66df --- /dev/null +++ b/Test/TEMPLATE-Config.cs @@ -0,0 +1,7 @@ +namespace Test +{ + static internal class _Config // rename to Config + { + public static string Token = "enter token here"; + } +} diff --git a/Test/Test.csproj b/Test/Test.csproj new file mode 100644 index 0000000..378af26 --- /dev/null +++ b/Test/Test.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + +