27
loading...
This website collects cookies to deliver better user experience
dotnet new worker -n DiscordBot
cd DiscordBot
dotnet add package DSharpPlus
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DSharpPlus;
using DSharpPlus.EventArgs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace DiscordBot
{
public class Worker : BackgroundService
{
private ILogger<Worker> logger;
private IConfiguration configuration;
private DiscordClient discordClient;
public Worker(ILogger<Worker> logger, IConfiguration configuration)
{
this.logger = logger;
this.configuration = configuration;
}
public override async Task StartAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Starting discord bot");
string discordBotToken = configuration["DiscordBotToken"];
discordClient = new DiscordClient(new DiscordConfiguration()
{
Token = discordBotToken,
TokenType = TokenType.Bot,
Intents = DiscordIntents.AllUnprivileged
});
discordClient.MessageCreated += OnMessageCreated;
await discordClient.ConnectAsync();
}
protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.CompletedTask;
public override async Task StopAsync(CancellationToken cancellationToken)
{
discordClient.MessageCreated -= OnMessageCreated;
await discordClient.DisconnectAsync();
discordClient.Dispose();
logger.LogInformation("Discord bot stopped");
}
private async Task OnMessageCreated(DiscordClient client, MessageCreateEventArgs e)
{
if (e.Message.Content.StartsWith("ping", StringComparison.OrdinalIgnoreCase))
{
logger.LogInformation("pinged, responding with pong!");
await e.Message.RespondAsync("pong!");
}
}
}
}
ILogger
and an IConfiguration
object into the constructor of the Worker
class. You can log information using the logger and retrieve external configuration from the configuration object.
The worker template has multiple configuration sources preconfigured such as JSON files, environment variables, user secrets, command-line arguments, and more.
Both constructor parameters are stored in private fields so the other parts of the class can use them.Worker
class inherits from the BackgroundService
abstract class. You have to implement the ExecuteAsync
method, but you can also override other methods like StartAsync
and StopAsync
.
For this application, you'll want to initialize the bidirectional connection with Discord in StartAsync
and disconnect from it in StopAsync
.
DiscordClient
requires the token you copied earlier. Instead of hardcoding the token, the token is pulled from the configuration object using configuration["DiscordBotToken"]
.
You'll configure the "DiscordBotToken" configuration later.discordBotToken
.MessageCreated
event to wire some code whenever a message is sent to the server.
When a message is sent, the OnMessageCreated
method will be invoked.discordClient.ConnectAsync()
Task.CompletedTask
.StopAsync
method will be called. Here you should clean up whatever resources you setup during StartAsync
:OnMessageCreated
is called whenever a message is sent to the Discord server. When the message starts with "ping", the bot will respond with "pong!"DiscordBotToken
user-secret:dotnet user-secrets init
dotnet user-secrets set DiscordBotToken [YOUR\_DISCORD\_BOT\_TOKEN]
dotnet run
FROM mcr.microsoft.com/dotnet/runtime:5.0 AS base
WORKDIR /app
# Creates a non-root user with an explicit UID and adds permission to access the /app folder
# For more info, please refer to https://aka.ms/vscode-docker-dotnet-configure-containers
RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuser
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["DiscordBot.csproj", "./"]
RUN dotnet restore "DiscordBot.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "DiscordBot.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "DiscordBot.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "DiscordBot.dll"]
dotnet restore
to restore the NuGet packages in "DiscordBot.csproj"dotnet build
in release mode and put the output at '/app/build'dotnet publish
in release mode and put the output at '/app/publish'dotnet DiscordBot.dll
when the container startsdocker build -t discord-bot-image:latest .
docker run -it discord-bot-image:latest
# Output:
# info: DiscordBot.Worker[0]
# Starting discord bot
# [2021-07-09 03:06:03 +00:00] [101 /Startup] [Info] DSharpPlus, version 4.0.1
# Unhandled exception. System.Exception: Authentication failed. Check your token and try again.
# ---> DSharpPlus.Exceptions.UnauthorizedException: Unauthorized: 401
# at DSharpPlus.Net.DiscordApiClient.GetGatewayInfoAsync()
# at DSharpPlus.BaseDiscordClient.GetGatewayInfoAsync(String token)
# at DSharpPlus.DiscordClient.InternalUpdateGatewayAsync()
# at DSharpPlus.DiscordClient.InternalConnectAsync()
# at DSharpPlus.DiscordClient.ConnectAsync(DiscordActivity activity, Nullable`1 status, Nullable`1 idlesince)
# --- End of inner exception stack trace ---
# at DSharpPlus.DiscordClient.ConnectAsync(DiscordActivity activity, Nullable`1 status, Nullable`1 idlesince)
# at DiscordBot.Worker.StartAsync(CancellationToken cancellationToken) in /src/Worker.cs:line 38
# at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
# at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
# at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
# at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
# at DiscordBot.Program.Main(String[] args) in /src/Program.cs:line 14
-e
argument:docker run -it discord-bot-image:latest -e DiscordBotToken=[YOUR\_DISCORD\_BOT\_TOKEN]
# Output:
# info: DiscordBot.Worker[0]
# Starting discord bot
# [2021-07-09 03:13:37 +00:00] [101 /Startup] [Info] DSharpPlus, version 4.0.1
# info: Microsoft.Hosting.Lifetime[0]
# Application started. Press Ctrl+C to shut down.
# info: Microsoft.Hosting.Lifetime[0]
# Hosting environment: Production
# info: Microsoft.Hosting.Lifetime[0]
# Content root path: /app
# info: DiscordBot.Worker[0]
# pinged, responding with pong!
az group create --location eastus --resource-group DiscordBotRg
az acr create --name DiscordBotAcr --resource-group DiscordBotRg \
--sku Basic \
--location eastus
az acr login --name DiscordBotAcr
docker tag discord-bot-image:latest discordbotacr.azurecr.io/discord-bot-image:latest
docker push discordbotacr.azurecr.io/discord-bot-image:latest
az acr build -r DiscordBotAcr -t discord-bot-image:latest .
# Obtain the full registry ID for subsequent command args
ACR_REGISTRY_ID=$(az acr show --name DiscordBotAcr --query id --output tsv)
SP_PASSWD=$(az ad sp create-for-rbac --name acr-service-principal --scopes $ACR_REGISTRY_ID --role acrpull --query password --output tsv)
SP_APP_ID=$(az ad sp list --display-name acr-service-principal --query [0].appId -o tsv)
SP_PASSWD
SP_APP_ID
SP_APP_ID
as the username and SP_PASSWD
as the password to log into your registry.az container create --resource-group DiscordBotRg \
--name discord-bot-container \
--image discordbotacr.azurecr.io/discord-bot-image:latest \
--registry-username $SP_APP_ID \
--registry-password $SP_PASSWD \
--secure-environment-variables DiscordBotToken=[YOUR\_DISCORD\_BOT\_TOKEN] \
--location eastus