init commit
This commit is contained in:
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Copy this file to .env and set your real values.
|
||||||
|
DISCORD_BOT_TOKEN=YOUR_DISCORD_BOT_TOKEN_HERE
|
||||||
45
.github/copilot-instructions.md
vendored
Normal file
45
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# TentacleBot - Discord .NET Bot
|
||||||
|
|
||||||
|
A Discord bot project built with Discord.Net and .NET 8+, featuring dependency injection and slash commands.
|
||||||
|
|
||||||
|
## Setup Instructions
|
||||||
|
|
||||||
|
1. Verify .NET SDK is installed (version 8.0 or higher)
|
||||||
|
2. Install project dependencies: `dotnet restore`
|
||||||
|
3. Configure bot token in `appsettings.json`
|
||||||
|
4. Build the project: `dotnet build`
|
||||||
|
5. Run the bot: `dotnet run`
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
- **Program.cs**: Bot initialization and dependency injection setup
|
||||||
|
- **Services/BotService.cs**: Core bot service managing connection and event handlers
|
||||||
|
- **Commands/GeneralCommands.cs**: Example slash command module
|
||||||
|
- **appsettings.json**: Configuration file for bot token and prefix
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
- Dependency injection for services
|
||||||
|
- Slash command support
|
||||||
|
- Event handling for bot lifecycle
|
||||||
|
- Discord.Net integration
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Before running, update `appsettings.json` with your Discord bot token:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Discord": {
|
||||||
|
"BotToken": "YOUR_BOT_TOKEN_HERE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Bot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet run
|
||||||
|
```
|
||||||
|
|
||||||
|
The bot will connect to Discord and respond to slash commands.
|
||||||
50
.gitignore
vendored
Normal file
50
.gitignore
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# 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/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio cache/options
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode/settings.json
|
||||||
|
.vscode/launch.json
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Configuration files with sensitive data
|
||||||
|
appsettings.json
|
||||||
|
!appsettings.*.json
|
||||||
|
|
||||||
|
# NuGet
|
||||||
|
.nuget/
|
||||||
|
*.nupkg
|
||||||
|
*.snupkg
|
||||||
|
|
||||||
|
# dotnet
|
||||||
|
project.assets.json
|
||||||
|
project.lock.json
|
||||||
|
|
||||||
|
# secrets
|
||||||
|
.env
|
||||||
|
|
||||||
|
# OS generated files
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*.DS_Store
|
||||||
44
.vscode/tasks.json
vendored
Normal file
44
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "shell",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--configuration",
|
||||||
|
"Debug"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile",
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "run",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "shell",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"--no-build"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile",
|
||||||
|
"group": {
|
||||||
|
"kind": "test",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"isBackground": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "clean",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "shell",
|
||||||
|
"args": [
|
||||||
|
"clean"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
32
AGENTS.md
Normal file
32
AGENTS.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# TentacleBot - Agent Instructions
|
||||||
|
|
||||||
|
## Quick Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet restore && dotnet run # Run the bot locally
|
||||||
|
docker compose up --build -d # Run with Docker
|
||||||
|
docker compose logs -f # View Docker logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Set `DISCORD_BOT_TOKEN` env var (preferred) or edit `appsettings.json`
|
||||||
|
2. Copy `.env.example` to `.env` for Docker: `cp .env.example .env`
|
||||||
|
3. `appsettings.json` is gitignored — never commit it
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **`Program.cs`** — Entry point, DI setup, registers command modules from the assembly
|
||||||
|
- **`Services/BotService.cs`** — Core bot lifecycle (login, ready handler, interaction routing)
|
||||||
|
- **`Commands/GeneralCommands.cs`** — Slash commands (`/general ping`, `/general hello`, `/botinfo`)
|
||||||
|
|
||||||
|
Add new commands by creating a class in `Commands/` that extends `InteractionModuleBase<SocketInteractionContext>`. Commands are auto-discovered via `AddModulesAsync(typeof(Program).Assembly)`.
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
|
||||||
|
- **Two `DiscordSocketClient` instances** are created in `Program.cs` — one for the bot client and one passed into `InteractionService`. They are separate instances sharing the same config.
|
||||||
|
- **Token priority**: `DISCORD_BOT_TOKEN` env var is checked first, then `appsettings.json` → `Discord:BotToken`.
|
||||||
|
|
||||||
|
## No tests, no lint config
|
||||||
|
|
||||||
|
This project has no test framework, no linting, and no CI. `dotnet build` is the only verification step.
|
||||||
71
Commands/GeneralCommands.cs
Normal file
71
Commands/GeneralCommands.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.Interactions;
|
||||||
|
|
||||||
|
namespace TentacleBot.Commands;
|
||||||
|
|
||||||
|
[Group("general", "General commands")]
|
||||||
|
public class GeneralCommands : InteractionModuleBase<SocketInteractionContext>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Example ping command that responds with pong
|
||||||
|
/// </summary>
|
||||||
|
[SlashCommand("ping", "Responds with pong and latency")]
|
||||||
|
public async Task HandlePingAsync()
|
||||||
|
{
|
||||||
|
await DeferAsync();
|
||||||
|
|
||||||
|
var latency = Context.Client.Latency;
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithTitle("Pong!")
|
||||||
|
.WithDescription($"Latency: {latency}ms")
|
||||||
|
.WithColor(Color.Green)
|
||||||
|
.WithTimestamp(DateTimeOffset.UtcNow)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await FollowupAsync(embed: embed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Example hello command with a user parameter
|
||||||
|
/// </summary>
|
||||||
|
[SlashCommand("hello", "Greet a user")]
|
||||||
|
public async Task HandleHelloAsync(
|
||||||
|
[Summary("user", "The user to greet")] IUser? user = null)
|
||||||
|
{
|
||||||
|
await DeferAsync();
|
||||||
|
|
||||||
|
var targetUser = user ?? Context.User;
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithTitle($"Hello, {targetUser.Username}!")
|
||||||
|
.WithDescription($"Nice to meet you! ?")
|
||||||
|
.WithThumbnailUrl(targetUser.GetAvatarUrl() ?? targetUser.GetDefaultAvatarUrl())
|
||||||
|
.WithColor(Color.Blue)
|
||||||
|
.WithTimestamp(DateTimeOffset.UtcNow)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await FollowupAsync(embed: embed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Example command that shows bot information
|
||||||
|
/// </summary>
|
||||||
|
[SlashCommand("botinfo", "Display bot information")]
|
||||||
|
public async Task HandleBotInfoAsync()
|
||||||
|
{
|
||||||
|
await DeferAsync();
|
||||||
|
|
||||||
|
var bot = Context.Client.CurrentUser;
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithTitle("Bot Information")
|
||||||
|
.AddField("Name", bot?.Username, true)
|
||||||
|
.AddField("ID", bot?.Id, true)
|
||||||
|
.AddField("Created", bot?.CreatedAt.ToString("g"), true)
|
||||||
|
.AddField("Guilds", Context.Client.Guilds.Count, true)
|
||||||
|
.WithColor(Color.Purple)
|
||||||
|
.WithThumbnailUrl(bot?.GetAvatarUrl())
|
||||||
|
.WithTimestamp(DateTimeOffset.UtcNow)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await FollowupAsync(embed: embed);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Dockerfile
Normal file
18
Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY TentacleBot.csproj ./
|
||||||
|
RUN dotnet restore
|
||||||
|
|
||||||
|
COPY . ./
|
||||||
|
RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/runtime:8.0 AS final
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=build /app/publish ./
|
||||||
|
|
||||||
|
ENV DOTNET_RUNNING_IN_CONTAINER=true
|
||||||
|
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
|
||||||
|
|
||||||
|
ENTRYPOINT ["dotnet", "TentacleBot.dll"]
|
||||||
50
Program.cs
Normal file
50
Program.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using Discord.Interactions;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using TentacleBot.Services;
|
||||||
|
using TentacleBot.Commands;
|
||||||
|
|
||||||
|
var configuration = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(Directory.GetCurrentDirectory())
|
||||||
|
.AddJsonFile("appsettings.json")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var discordConfig = new DiscordSocketConfig
|
||||||
|
{
|
||||||
|
GatewayIntents = GatewayIntents.All,
|
||||||
|
LogLevel = LogSeverity.Info,
|
||||||
|
UseInteractionSnowflakeDate = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
var client = new DiscordSocketClient(discordConfig);
|
||||||
|
|
||||||
|
var services = new ServiceCollection()
|
||||||
|
.AddLogging(builder => builder.AddConsole())
|
||||||
|
.AddSingleton<IConfiguration>(configuration)
|
||||||
|
.AddSingleton(client)
|
||||||
|
.AddSingleton(new InteractionService(client, new InteractionServiceConfig
|
||||||
|
{
|
||||||
|
LogLevel = LogSeverity.Info,
|
||||||
|
UseCompiledLambda = true,
|
||||||
|
}))
|
||||||
|
.AddSingleton<BotService>();
|
||||||
|
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
|
var botService = serviceProvider.GetRequiredService<BotService>();
|
||||||
|
var interactionService = serviceProvider.GetRequiredService<InteractionService>();
|
||||||
|
|
||||||
|
// Register command modules
|
||||||
|
await interactionService.AddModulesAsync(
|
||||||
|
typeof(Program).Assembly,
|
||||||
|
serviceProvider);
|
||||||
|
|
||||||
|
// Initialize the bot
|
||||||
|
await botService.InitializeAsync();
|
||||||
|
|
||||||
|
// Keep the bot running
|
||||||
|
await Task.Delay(Timeout.Infinite);
|
||||||
157
README.md
Normal file
157
README.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# TentacleBot - Discord .NET Bot
|
||||||
|
|
||||||
|
A Discord bot project built with Discord.Net and .NET 8+, featuring dependency injection and slash commands.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- ? Built with Discord.Net 3.14+
|
||||||
|
- ? Dependency Injection for services
|
||||||
|
- ? Slash commands support
|
||||||
|
- ? Rich embed messages
|
||||||
|
- ? Example commands included
|
||||||
|
- ? .NET 8 or higher
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- .NET 8.0 or higher ([download here](https://dotnet.microsoft.com/download))
|
||||||
|
- A Discord Bot Token (create one at [Discord Developer Portal](https://discord.com/developers/applications))
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Clone and Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet restore
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Configure Your Bot Token
|
||||||
|
|
||||||
|
Edit `appsettings.json` and replace `YOUR_BOT_TOKEN_HERE` with your actual Discord bot token:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Discord": {
|
||||||
|
"BotToken": "YOUR_ACTUAL_TOKEN_HERE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Run the Bot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet run
|
||||||
|
```
|
||||||
|
|
||||||
|
The bot will connect to Discord and register its slash commands globally.
|
||||||
|
|
||||||
|
## Run With Docker Compose
|
||||||
|
|
||||||
|
1. Copy the example env file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Edit `.env` and set your Discord token:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
DISCORD_BOT_TOKEN=YOUR_ACTUAL_TOKEN_HERE
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Build and run with Docker Compose:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up --build -d
|
||||||
|
```
|
||||||
|
|
||||||
|
4. View logs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Stop the bot:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
TentacleBot/
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Program.cs # Entry point and DI setup
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Services/
|
||||||
|
<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> BotService.cs # Core bot service
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Commands/
|
||||||
|
<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> GeneralCommands.cs # Example slash commands
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> appsettings.json # Configuration
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Dockerfile # Container image definition
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> docker-compose.yml # Local compose setup
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> .env.example # Environment variable template
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> TentacleBot.csproj # Project file
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> README.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Commands
|
||||||
|
|
||||||
|
The bot includes example slash commands:
|
||||||
|
|
||||||
|
- `/general ping` - Shows bot latency
|
||||||
|
- `/general hello [user]` - Greet a user
|
||||||
|
- `/general botinfo` - Display bot information
|
||||||
|
|
||||||
|
## Adding New Commands
|
||||||
|
|
||||||
|
Create a new class in the `Commands` folder that inherits from `InteractionModuleBase<SocketInteractionContext>`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Discord.Interactions;
|
||||||
|
|
||||||
|
namespace TentacleBot.Commands;
|
||||||
|
|
||||||
|
public class MyCommands : InteractionModuleBase<SocketInteractionContext>
|
||||||
|
{
|
||||||
|
[SlashCommand("mycommand", "My custom command")]
|
||||||
|
public async Task HandleMyCommandAsync()
|
||||||
|
{
|
||||||
|
await RespondAsync("Hello from my command!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Commands are automatically discovered and registered when the bot starts.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### appsettings.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Discord": {
|
||||||
|
"BotToken": "YOUR_BOT_TOKEN_HERE"
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **"Bot token not found"**: Set `DISCORD_BOT_TOKEN` in `.env` (Docker) or in your shell. The app falls back to `appsettings.json` if the env var is not set
|
||||||
|
- **"Insufficient permissions"**: Ensure your bot has the necessary permissions in Discord Developer Portal
|
||||||
|
- **Commands not appearing**: The bot needs to connect first. Wait a few seconds after starting, then try `/` in Discord
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Discord.Net Documentation](https://discordnet.dev/)
|
||||||
|
- [Discord Developer Portal](https://discord.com/developers/applications)
|
||||||
|
- [.NET Documentation](https://docs.microsoft.com/dotnet)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
102
Services/BotService.cs
Normal file
102
Services/BotService.cs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using Discord.Interactions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace TentacleBot.Services;
|
||||||
|
|
||||||
|
public class BotService
|
||||||
|
{
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly InteractionService _interactionService;
|
||||||
|
private readonly ILogger<BotService> _logger;
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public BotService(
|
||||||
|
DiscordSocketClient client,
|
||||||
|
InteractionService interactionService,
|
||||||
|
ILogger<BotService> logger,
|
||||||
|
IConfiguration configuration,
|
||||||
|
IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
_interactionService = interactionService;
|
||||||
|
_logger = logger;
|
||||||
|
_configuration = configuration;
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
_client.Log += LogAsync;
|
||||||
|
_interactionService.Log += LogAsync;
|
||||||
|
|
||||||
|
_client.Ready += ClientReadyAsync;
|
||||||
|
_client.InteractionCreated += InteractionCreatedAsync;
|
||||||
|
|
||||||
|
var token = Environment.GetEnvironmentVariable("DISCORD_BOT_TOKEN");
|
||||||
|
if (string.IsNullOrWhiteSpace(token))
|
||||||
|
{
|
||||||
|
token = _configuration["Discord:BotToken"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(token))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Discord bot token not found. Set DISCORD_BOT_TOKEN or Discord:BotToken in appsettings.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _client.LoginAsync(TokenType.Bot, token);
|
||||||
|
await _client.StartAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("Bot service initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ClientReadyAsync()
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Bot connected as {BotName}#{Discriminator}", _client.CurrentUser?.Username, _client.CurrentUser?.Discriminator);
|
||||||
|
|
||||||
|
// Register slash commands
|
||||||
|
await _interactionService.RegisterCommandsGloballyAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("Slash commands registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InteractionCreatedAsync(SocketInteraction interaction)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ctx = new SocketInteractionContext(_client, interaction);
|
||||||
|
await _interactionService.ExecuteCommandAsync(ctx, _serviceProvider);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error handling interaction");
|
||||||
|
|
||||||
|
if (interaction.Type == InteractionType.ApplicationCommand)
|
||||||
|
{
|
||||||
|
await interaction.GetOriginalResponseAsync().ContinueWith(async msg =>
|
||||||
|
{
|
||||||
|
if (msg.IsCompletedSuccessfully)
|
||||||
|
{
|
||||||
|
await interaction.FollowupAsync("An error occurred while executing the command.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task LogAsync(LogMessage message)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[{message.Severity}] {message.Source}: {message.Message}");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StopAsync()
|
||||||
|
{
|
||||||
|
await _client.StopAsync();
|
||||||
|
_client.Dispose();
|
||||||
|
_logger.LogInformation("Bot service stopped");
|
||||||
|
}
|
||||||
|
}
|
||||||
27
TentacleBot.csproj
Normal file
27
TentacleBot.csproj
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<RootNamespace>TentacleBot</RootNamespace>
|
||||||
|
<AssemblyName>TentacleBot</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Discord.Net" Version="3.14.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
9
docker-compose.yml
Normal file
9
docker-compose.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
services:
|
||||||
|
tentaclebot:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: tentaclebot
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
Reference in New Issue
Block a user