diff --git a/.gitignore b/.gitignore
index 44c3b430e..060c1385e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -364,4 +364,8 @@ FodyWeavers.xsd
# Settings file
appsettings.json
-!Documentation/**/appsettings.json
\ No newline at end of file
+!Documentation/**/appsettings.json
+!ProjectTemplates/**/appsettings.json
+
+# Rider settings folder
+.idea/
\ No newline at end of file
diff --git a/NetCord.slnx b/NetCord.slnx
index c9a17d42b..2af6b471e 100644
--- a/NetCord.slnx
+++ b/NetCord.slnx
@@ -93,4 +93,5 @@
+
diff --git a/ProjectTemplates/NetCordTemplates.csproj b/ProjectTemplates/NetCordTemplates.csproj
new file mode 100644
index 000000000..73a2b1eee
--- /dev/null
+++ b/ProjectTemplates/NetCordTemplates.csproj
@@ -0,0 +1,29 @@
+
+
+
+ Template
+ NetCord.Templates
+
+ $(NoWarn);NU5128
+
+ true
+ false
+ content
+
+
+
+ false
+
+
+
+ NetCord Project Templates
+ NetCord
+ Starting templates to use with NetCord - The modern and fully customizable C# Discord library.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ProjectTemplates/Templates.md b/ProjectTemplates/Templates.md
new file mode 100644
index 000000000..2ef250b98
--- /dev/null
+++ b/ProjectTemplates/Templates.md
@@ -0,0 +1,62 @@
+### Convenient TL;DR one-liner
+
+> [!WARNING]
+> TODO: Add actual command to install from nuget
+
+Minimal template
+```bash
+dotnet new install NetCord.Templates && \
+dotnet new netcord --name MyNewBot --framework net9.0
+```
+Or if you want complete template with all features
+```bash
+dotnet new install NetCord.Templates && \
+dotnet new netcord --add-text-commands --add-application-commands --add-component-interactions --name MyNewBot --framework net9.0
+```
+
+
+> [!IMPORTANT]
+> In case of local testing,
+> all commands should be run from the root of the repository.
+
+### Packing
+```bash
+dotnet pack ./ProjectTemplates/ -c Release -o ./nupkgs
+```
+
+### Installing
+Nuget
+
+> [!WARNING]
+> TODO: Add actual command to install from nuget
+
+```bash
+dotnet new install NetCord.Templates
+```
+Local
+```bash
+dotnet new install ./nupkgs/*
+```
+
+### Uninstalling
+```bash
+dotnet new uninstall NetCord.Templates
+```
+
+### Updating
+```bash
+dotnet new update
+```
+
+### Usage
+`dotnet new netcord [options] [template options]`
+Use `dotnet new netcord --help` to see all available options.
+Example command:
+```bash
+dotnet new netcord \
+ --add-text-commands \
+ --add-application-commands \
+ --add-component-interactions \
+ --name MyNewBot \
+ --framework net9.0
+```
\ No newline at end of file
diff --git a/ProjectTemplates/templates/NetCord.GenericHost/.template.config/dotnetcli.host.json b/ProjectTemplates/templates/NetCord.GenericHost/.template.config/dotnetcli.host.json
new file mode 100644
index 000000000..41a9c5a99
--- /dev/null
+++ b/ProjectTemplates/templates/NetCord.GenericHost/.template.config/dotnetcli.host.json
@@ -0,0 +1,37 @@
+{
+ "$schema": "https://json.schemastore.org/dotnetcli.host",
+ "symbolInfo": {
+ "kestrelHttpPort": {
+ "isHidden": true
+ },
+ "kestrelHttpsPort": {
+ "isHidden": true
+ },
+ "framework": {
+ "longName": "framework"
+ },
+ "skipRestore": {
+ "longName": "no-restore",
+ "shortName": ""
+ },
+ "excludeLaunchSettings": {
+ "longName": "exclude-launch-settings",
+ "shortName": ""
+ },
+ "addApplicationCommands": {
+ "longName": "add-application-commands",
+ "shortName": ""
+ },
+ "addTextCommands": {
+ "longName": "add-text-commands",
+ "shortName": ""
+ },
+ "addComponentInteractions": {
+ "longName": "add-component-interactions",
+ "shortName": ""
+ }
+ },
+ "usageExamples": [
+ "--add-text-commands --add-interactions --add-application-commands --name MyNewBot --framework net9.0"
+ ]
+}
\ No newline at end of file
diff --git a/ProjectTemplates/templates/NetCord.GenericHost/.template.config/template.json b/ProjectTemplates/templates/NetCord.GenericHost/.template.config/template.json
new file mode 100644
index 000000000..feca32e28
--- /dev/null
+++ b/ProjectTemplates/templates/NetCord.GenericHost/.template.config/template.json
@@ -0,0 +1,180 @@
+{
+ "$schema": "http://json.schemastore.org/template",
+ "author": "NetCord",
+ "classifications": [
+ "Web",
+ "NetCord",
+ "AspNetCore"
+ ],
+ "identity": "NetCord.Hosting.AspNetCore",
+ "groupIdentity": "NetCord",
+ "name": "NetCord Project",
+ "defaultName": "NetCordBot",
+ "sourceName": "NetCord.Template.Bot",
+ "shortName": "netcord",
+ "description": "NetCord Bot with module-based approach",
+ "preferNameDirectory": true,
+ "tags": {
+ "language": "C#",
+ "type": "project"
+ },
+ "primaryOutputs": [
+ {
+ "path": "NetCord.Template.Bot.csproj"
+ }
+ ],
+ "sources": [
+ {
+ "source": "./",
+ "target": "./",
+ "exclude": [
+ ".template.config/**",
+ "**/*.idea/**"
+ ],
+ "modifiers": [
+ {
+ "condition": "(excludeLaunchSettings)",
+ "exclude": [ "Properties/launchSettings.json" ]
+ },
+ {
+ "condition": "(!addApplicationCommands)",
+ "exclude": [
+ "SlashCommands/HelloSlashModule.cs"
+ ]
+ },
+ {
+ "condition": "(!addTextCommands)",
+ "exclude": [
+ "TextCommands/HelloTextModule.cs"
+ ]
+ },
+ {
+ "condition": "(!addComponentInteractions)",
+ "exclude": [
+ "Interactions/ButtonInteractionModule.cs",
+ "SlashCommands/ButtonSlashModule.cs"
+ ]
+ }
+ ]
+ }
+ ],
+ "symbols": {
+ "framework": {
+ "type": "parameter",
+ "description": "The target framework for the project.",
+ "datatype": "choice",
+ "choices": [
+ {
+ "choice": "net8.0",
+ "description": "Targets .NET 8"
+ },
+ {
+ "choice": "net9.0",
+ "description": "Targets .NET 9"
+ }
+ ],
+ "defaultValue": "net8.0",
+ "replaces": "net8.0"
+ },
+ "addApplicationCommands": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "Add Application Commands",
+ "description": "Whether to add application commands with example module",
+ "isRequired": false
+ },
+ "addTextCommands": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "Add Text Commands",
+ "description": "Whether to add text commands with example module",
+ "isRequired": false
+ },
+ "addComponentInteractions": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "Add Interactions",
+ "description": "Whether to add interactions with example module",
+ "isRequired": false
+ },
+ "hostIdentifier": {
+ "type": "bind",
+ "binding": "HostIdentifier"
+ },
+ "skipRestore": {
+ "type": "parameter",
+ "datatype": "bool",
+ "description": "Whether to skip automatic restore of the project on create.",
+ "defaultValue": "false",
+ "displayName": "Skip restore",
+ "isRequired": false
+ },
+ "excludeLaunchSettings": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "description": "Whether to exclude launchSettings.json from the generated template."
+ },
+ "kestrelHttpPort": {
+ "type": "parameter",
+ "datatype": "integer",
+ "description": "Port number to use for the HTTP endpoint in launchSettings.json."
+ },
+ "kestrelHttpPortGenerated": {
+ "type": "generated",
+ "generator": "port",
+ "parameters": {
+ "low": 5000,
+ "high": 5300
+ }
+ },
+ "kestrelHttpPortReplacer": {
+ "type": "generated",
+ "generator": "coalesce",
+ "parameters": {
+ "sourceVariableName": "kestrelHttpPort",
+ "fallbackVariableName": "kestrelHttpPortGenerated"
+ },
+ "replaces": "5500"
+ },
+ "kestrelHttpsPort": {
+ "type": "parameter",
+ "datatype": "integer",
+ "description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if Individual auth is used)."
+ },
+ "kestrelHttpsPortGenerated": {
+ "type": "generated",
+ "generator": "port",
+ "parameters": {
+ "low": 7000,
+ "high": 7300
+ }
+ },
+ "kestrelHttpsPortReplacer": {
+ "type": "generated",
+ "generator": "coalesce",
+ "parameters": {
+ "sourceVariableName": "kestrelHttpsPort",
+ "fallbackVariableName": "kestrelHttpsPortGenerated"
+ },
+ "replaces": "5501"
+ }
+ },
+ "postActions": [
+ {
+ "id": "restore",
+ "condition": "(!skipRestore)",
+ "description": "Restore NuGet packages.",
+ "manualInstructions": [
+ {
+ "text": "Run 'dotnet restore'"
+ }
+ ],
+ "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
+ "continueOnError": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ProjectTemplates/templates/NetCord.GenericHost/Interactions/ButtonInteractionModule.cs b/ProjectTemplates/templates/NetCord.GenericHost/Interactions/ButtonInteractionModule.cs
new file mode 100644
index 000000000..deb9a6724
--- /dev/null
+++ b/ProjectTemplates/templates/NetCord.GenericHost/Interactions/ButtonInteractionModule.cs
@@ -0,0 +1,46 @@
+using NetCord.Rest;
+using NetCord.Services.ComponentInteractions;
+
+namespace NetCord.Template.Bot.Interactions;
+
+///
+/// Example Button interaction module.
+/// To learn more about interactions, check out docs.
+///
+/// Logger injected via DI
+public class ButtonModule(ILogger logger) : ComponentInteractionModule
+{
+ ///
+ /// Replies with deferred message to the button click.
+ /// Don't forget that CustomId must match the one in the button.
+ ///
+ ///
+ [ComponentInteraction("pong-interaction")]
+ public async Task PingButton()
+ {
+ logger.LogInformation("Received {Interaction} from {User}", Context.Interaction.Data.CustomId, Context.User.Username);
+ var received = DateTime.Now;
+ var callback = InteractionCallback.DeferredModifyMessage;
+ await Context.Interaction.SendResponseAsync(callback).ConfigureAwait(false);
+
+ await Task.Delay(10 * 1_000).ConfigureAwait(true); // Simulate some work
+
+ await Context.Interaction.ModifyResponseAsync(m =>
+ {
+ m.Components =
+ [
+ new ComponentContainerProperties
+ {
+ AccentColor = new Color(0, 255, 0),
+ Components =
+ [
+ new TextDisplayProperties("# Pong!"),
+ new TextDisplayProperties($"I've received your interaction at `{received:T}` and replied at `{DateTime.Now:T}`"),
+ new TextDisplayProperties("-# This was example of deferred component interaction response"),
+ new TextDisplayProperties("-# Which also used [ComponentsV2](https://discord.com/developers/docs/components/overview) over regular embeds."),
+ ]
+ }
+ ];
+ }).ConfigureAwait(false);
+ }
+}
diff --git a/ProjectTemplates/templates/NetCord.GenericHost/NetCord.Template.Bot.csproj b/ProjectTemplates/templates/NetCord.GenericHost/NetCord.Template.Bot.csproj
new file mode 100644
index 000000000..90bb7ecdf
--- /dev/null
+++ b/ProjectTemplates/templates/NetCord.GenericHost/NetCord.Template.Bot.csproj
@@ -0,0 +1,26 @@
+
+
+
+
+ net9.0
+
+ net8.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ProjectTemplates/templates/NetCord.GenericHost/Program.cs b/ProjectTemplates/templates/NetCord.GenericHost/Program.cs
new file mode 100644
index 000000000..12177307b
--- /dev/null
+++ b/ProjectTemplates/templates/NetCord.GenericHost/Program.cs
@@ -0,0 +1,54 @@
+using NetCord.Gateway;
+using NetCord.Hosting.Gateway;
+using NetCord.Hosting.Services;
+#if (addApplicationCommands)
+using NetCord.Hosting.Services.ApplicationCommands;
+#endif
+#if (addTextCommands)
+using NetCord.Hosting.Services.Commands;
+#endif
+#if (addComponentInteractions)
+using NetCord.Hosting.Services.ComponentInteractions;
+#endif
+#if (addComponentInteractions)
+using NetCord.Services.ComponentInteractions;
+#endif
+
+namespace NetCord.Template.Bot;
+
+public static class Program
+{
+ public static async Task Main(string[] args)
+ {
+ var builder = Host.CreateApplicationBuilder(args);
+ builder.Services
+ // Use intents suitable for your needs.
+ // See: https://netcord.dev/guides/events/intents.html?tabs=generic-host
+ .AddDiscordGateway(op => op.Intents = GatewayIntents.All)
+#if (addTextCommands)
+ .AddApplicationCommands()
+#endif
+#if (addTextCommands)
+ .AddCommands(options => options.IgnoreCase = true) // IgnoreCase makes commands case-insensitive. "!hello" and "!Hello" will be treated the same.
+#endif
+#if (addComponentInteractions)
+ .AddComponentInteractions()
+ .AddComponentInteractions()
+ .AddComponentInteractions()
+ .AddComponentInteractions()
+ .AddComponentInteractions()
+ .AddComponentInteractions()
+ .AddComponentInteractions()
+#endif
+ .AddGatewayEventHandlers(typeof(Program).Assembly);
+
+ var host = builder
+ .Build()
+ // Adds application commands, text commands and interactions from the assembly.
+ // All "Modules" must be public in order to be discovered and registered properly.
+ .AddModules(typeof(Program).Assembly)
+ .UseGatewayEventHandlers();
+
+ await host.RunAsync().ConfigureAwait(false);
+ }
+}
diff --git a/ProjectTemplates/templates/NetCord.GenericHost/Properties/launchSettings.json b/ProjectTemplates/templates/NetCord.GenericHost/Properties/launchSettings.json
new file mode 100644
index 000000000..aa7b214f6
--- /dev/null
+++ b/ProjectTemplates/templates/NetCord.GenericHost/Properties/launchSettings.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "applicationUrl": "http://localhost:5500",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "applicationUrl": "https://localhost:5501;http://localhost:5500",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/ProjectTemplates/templates/NetCord.GenericHost/SlashCommands/ButtonSlashModule.cs b/ProjectTemplates/templates/NetCord.GenericHost/SlashCommands/ButtonSlashModule.cs
new file mode 100644
index 000000000..7f3d73484
--- /dev/null
+++ b/ProjectTemplates/templates/NetCord.GenericHost/SlashCommands/ButtonSlashModule.cs
@@ -0,0 +1,45 @@
+using NetCord.Rest;
+using NetCord.Services;
+using NetCord.Services.ApplicationCommands;
+
+namespace NetCord.Template.Bot.SlashCommands;
+
+///
+/// Example App (Slash) command module with buttons and componentsV2.
+/// To learn more about slash commands, check out docs.
+///
+/// Logger injected via DI
+[RequireBotPermissions(Permissions.SendMessages)]
+public class ButtonSlashModule(ILogger logger) : ApplicationCommandModule
+{
+ [SlashCommand(name: "generate-button", description: "Generates message with button", DefaultGuildUserPermissions = Permissions.UseApplicationCommands)]
+ public InteractionMessageProperties GenerateButton()
+ {
+ logger.LogInformation("Received {Command} slash command from {User}", Context.Interaction.Data.Name, Context.User.Username);
+ return new InteractionMessageProperties
+ {
+ Components =
+ [
+ new ComponentContainerProperties
+ {
+ AccentColor = new Color(255, 0, 0),
+ Spoiler = false,
+ Components =
+ [
+ new ComponentSectionProperties(new ComponentSectionThumbnailProperties(new ComponentMediaProperties("https://netcord.dev/images/SmallSquare.png")))
+ {
+ new TextDisplayProperties($"<@{Context.Interaction.User.Id}> Click the button to trigger HTTP Interaction"), new TextDisplayProperties("-# Or don't. It's up to you 🤖"),
+ },
+ new ComponentSeparatorProperties { Divider = true, Spacing = ComponentSeparatorSpacingSize.Small },
+ new ActionRowProperties
+ {
+ new LinkButtonProperties("https://github.com/NetCordDev/NetCord", "Check out NetCord!"), new ButtonProperties("pong-interaction", "Click for pong!", ButtonStyle.Primary)
+ }
+ ]
+ }
+ ],
+ Flags = MessageFlags.IsComponentsV2,
+ AllowedMentions = AllowedMentionsProperties.All
+ };
+ }
+}
diff --git a/ProjectTemplates/templates/NetCord.GenericHost/SlashCommands/HelloSlashModule.cs b/ProjectTemplates/templates/NetCord.GenericHost/SlashCommands/HelloSlashModule.cs
new file mode 100644
index 000000000..1be94b950
--- /dev/null
+++ b/ProjectTemplates/templates/NetCord.GenericHost/SlashCommands/HelloSlashModule.cs
@@ -0,0 +1,23 @@
+using NetCord.Rest;
+using NetCord.Services;
+using NetCord.Services.ApplicationCommands;
+
+namespace NetCord.Template.Bot.SlashCommands;
+
+///
+/// Example App (Slash) command module.
+/// Usable only within guilds where bot has '' permission.
+/// To learn more about slash commands, check out docs.
+///
+/// Logger injected via DI
+[RequireBotPermissions(Permissions.SendMessages)]
+[RequireContext(RequiredContext.Guild)]
+public class HelloSlashModule(ILogger logger) : ApplicationCommandModule
+{
+ [SlashCommand(name: "hello", description: "Hello, NetCord!", DefaultGuildUserPermissions = Permissions.UseApplicationCommands)]
+ public InteractionMessageProperties Hello()
+ {
+ logger.LogInformation("Received {Command} slash command from {User}", Context.Interaction.Data.Name, Context.User.Username);
+ return new InteractionMessageProperties { Content = $"<@{Context.User.Id}> Hello from NetCord Slash commands!", AllowedMentions = AllowedMentionsProperties.All };
+ }
+}
diff --git a/ProjectTemplates/templates/NetCord.GenericHost/TextCommands/HelloTextModule.cs b/ProjectTemplates/templates/NetCord.GenericHost/TextCommands/HelloTextModule.cs
new file mode 100644
index 000000000..998e154af
--- /dev/null
+++ b/ProjectTemplates/templates/NetCord.GenericHost/TextCommands/HelloTextModule.cs
@@ -0,0 +1,18 @@
+using NetCord.Services.Commands;
+
+namespace NetCord.Template.Bot.TextCommands;
+
+///
+/// Example Text command module.
+/// To learn more about text commands, check out docs.
+///
+/// Logger injected via DI
+public class HelloTextModule(ILogger logger) : CommandModule
+{
+ [Command("hello")]
+ public string Hello()
+ {
+ logger.LogInformation("Received hello command from {User}", Context.User.Username);
+ return "Hello from NetCord text commands!";
+ }
+}
diff --git a/ProjectTemplates/templates/NetCord.GenericHost/appsettings.json b/ProjectTemplates/templates/NetCord.GenericHost/appsettings.json
new file mode 100644
index 000000000..9e1d75de9
--- /dev/null
+++ b/ProjectTemplates/templates/NetCord.GenericHost/appsettings.json
@@ -0,0 +1,14 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "Discord": {
+ "Token": "Your_Discord_Token_Here",
+ "Prefixes": [
+ "Your_First_Prefix_Here (eg. !)"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/ProjectTemplates/templates/NetCord.GenericHost/example.appsettings.jsonc b/ProjectTemplates/templates/NetCord.GenericHost/example.appsettings.jsonc
new file mode 100644
index 000000000..b18d6c8db
--- /dev/null
+++ b/ProjectTemplates/templates/NetCord.GenericHost/example.appsettings.jsonc
@@ -0,0 +1,20 @@
+// Example "appsettings.json" for a NetCord Generic Host project.
+// All changes should be made in the "appsettings.json" file, or it's corresponding environment file.
+// This file can be removed. It's purpose is purely to show the user what the default settings are.
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "Discord": {
+ // See: https://netcord.dev/guides/getting-started/making-a-bot.html?tabs=generic-host
+ "Token": "Your_Discord_Token_Here",
+ // See https://netcord.dev/guides/services/text-commands/introduction.html?tabs=generic-host#specifying-a-prefix
+ "Prefixes": [
+ "Your_First_Prefix_Here (eg. !)",
+ "Your_Second_Another_Prefix_Here (eg. ?)"
+ ]
+ }
+}
\ No newline at end of file