Added documenation and improved discord bot for local development. Parameterized guild and context.

This commit is contained in:
Vanessa
2026-06-08 10:11:08 -05:00
parent 78dd74e5c1
commit c776571860
8 changed files with 93 additions and 27 deletions
+3
View File
@@ -312,3 +312,6 @@ Resources/MapImages
# Changelog package lock
package-lock.json
# Discord Bot Config
link-config.json
+5 -5
View File
@@ -10,14 +10,14 @@ using Microsoft.EntityFrameworkCore;
namespace Content.DiscordBot;
public sealed class CommandHandler(DiscordSocketClient client, CommandService commands, InteractionService interaction, ServerDbContext db)
public sealed class CommandHandler(DiscordSocketClient client, CommandService commands, InteractionService interaction, ServerDbContext db, Config config)
{
private const ulong Guild = 1168210010233376858UL;
private ImmutableDictionary<ulong, DeltaVPatronTier>? _patronTiers;
private ImmutableArray<DeltaVPatronTier> _tierPriority;
private Task? _refreshPatronsTask;
private readonly Config _config = config;
public int Running = 1;
public async Task InstallCommandsAsync()
@@ -229,7 +229,7 @@ public sealed class CommandHandler(DiscordSocketClient client, CommandService co
{
try
{
var user = await client.Rest.GetGuildUserAsync(Guild, linked.DiscordId);
var user = await client.Rest.GetGuildUserAsync(_config.Guild, linked.DiscordId);
if (user == null)
{
if (linked.Player.Patron != null)
@@ -271,7 +271,7 @@ public sealed class CommandHandler(DiscordSocketClient client, CommandService co
}
await db.SaveChangesAsync();
await Task.Delay(60000);
await Task.Delay(60000); // 60 seconds
}
catch (Exception e)
{
+7
View File
@@ -5,4 +5,11 @@ public sealed class Config
public string Token { get; set; } = string.Empty;
public string DatabaseString { get; set; } = string.Empty;
public string DatabaseContext { get; set; } = "postgres";
/// <summary>
/// Discord "guild" ID (which is just the server ID). Default is DeltaV's server ID.
/// </summary>
public ulong Guild { get; set; } = 1513310351490289834UL;
}
@@ -2,6 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<OutputPath>..\bin\Content.DiscordBot\</OutputPath>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
@@ -1,12 +1,13 @@
using Discord;
using Discord.Commands;
using Discord.Interactions;
namespace Content.DiscordBot.Modules;
public sealed class AccountLinkingModule : ModuleBase<SocketCommandContext>
{
[Command("create")]
[RequireOwner]
[SlashCommand("create", "Creates the message with the linking popup. Only has to be run once.")]
[Discord.Interactions.RequireOwner]
public Task CreateAsync()
{
var component = new ComponentBuilder()
@@ -4,9 +4,9 @@ namespace Content.DiscordBot.Modules;
public class LinkAccountModal : IModal
{
public string Title => "Link SS14 account";
public string Title => "Link SS14 Account";
[InputLabel("SS14 Linking Code (top left in the lobby)")]
[InputLabel("SS14 Linking Code (Top Left in the Lobby)")]
[RequiredInput]
[ModalTextInput("account_code")]
public string Code { get; set; } = string.Empty;
+40 -18
View File
@@ -11,53 +11,75 @@ using Microsoft.EntityFrameworkCore;
var client = new DiscordSocketClient(new DiscordSocketConfig { GatewayIntents = GatewayIntents.All });
client.Log += Logger.Log;
string? token = null;
string? connectionString = null;
if (File.Exists("config.json"))
Console.Out.WriteLine($"[INIT] Working Directory: {Directory.GetCurrentDirectory()}");
var config = new Config();
if (File.Exists("link-config.json"))
{
var config = await JsonSerializer.DeserializeAsync<Config>(File.OpenRead("config.json")) ?? new Config();
token = config.Token;
Console.Out.WriteLine("[INIT] link-config.json found. Deserializing.");
config = await JsonSerializer.DeserializeAsync<Config>(File.OpenRead("link-config.json")) ?? new Config();
}
#if DEBUG
if (Environment.GetEnvironmentVariable("DISCORD_TOKEN") is { } envToken)
token = envToken;
config.Token = envToken;
if (Environment.GetEnvironmentVariable("DATABASE_STRING") is { } dbString)
connectionString = dbString;
config.DatabaseString = dbString;
if (Environment.GetEnvironmentVariable("DATABASE_CONTEXT") is { } context)
config.DatabaseContext = context;
if (Environment.GetEnvironmentVariable("GUILD") is { } guildId)
{
try
{
config.Guild = ulong.Parse("guildId");
}
catch (Exception ex)
{
Console.Out.WriteLine($"[INIT] Unable to parse guild ID. Defaulting to {config.Guild}. Exception: {ex.GetType()} {ex.Message}");
}
}
#endif
if (string.IsNullOrWhiteSpace(token))
throw new ArgumentException("No token found.");
if (string.IsNullOrWhiteSpace(config.Token))
throw new ArgumentException("[INIT] No Discord Bot token found.");
if (string.IsNullOrWhiteSpace(config.DatabaseString))
throw new ArgumentException("[INIT] No database connection string found.");
// Override for local sqlite development
var usePostgres = true;
if (string.IsNullOrWhiteSpace(connectionString))
{
if (!string.IsNullOrWhiteSpace(config.DatabaseContext) && config.DatabaseContext.Equals("sqlite"))
usePostgres = false;
//throw new ArgumentException("No database connection string found. Assuming local development.");
}
await client.LoginAsync(TokenType.Bot, token);
Console.Out.WriteLine($"[INIT] Guild ID: {config.Guild}");
await client.LoginAsync(TokenType.Bot, config.Token);
await client.StartAsync();
ServerDbContext db;
if (usePostgres)
{
Console.Out.WriteLine("[INIT] Connecting to postgres...");
var builder = new DbContextOptionsBuilder<PostgresServerDbContext>();
builder.UseNpgsql(connectionString);
builder.UseNpgsql(config.DatabaseString);
db = new PostgresServerDbContext(builder.Options);
// await db.Database.MigrateAsync();
}
else
{
var relativePath = "FIX"; // Gross but whatever, I'm just testing
Console.Out.WriteLine("[INIT] Connecting to sqlite...");
var builder = new DbContextOptionsBuilder<SqliteServerDbContext>();
builder.UseSqlite(new SqliteConnection($"Data Source={relativePath}"));
builder.UseSqlite(new SqliteConnection(config.DatabaseString));
db = new SqliteServerDbContext(builder.Options);
}
Console.Out.WriteLine("[INIT] Database connection successful.");
var interaction = new InteractionService(client);
var handler = new CommandHandler(client, new CommandService(), interaction, db);
var handler = new CommandHandler(client, new CommandService(), interaction, db, config);
AppDomain.CurrentDomain.ProcessExit += (_, _) => Interlocked.Decrement(ref handler.Running);
+32
View File
@@ -0,0 +1,32 @@
# Content.DiscordBot
### Configuration (Debug)
* `DISCORD_TOKEN`: Discord application token provided @ `https://discord.com/developers/applications/<APP_ID>/bot`
* `DATABASE_STRING`: Connection string to the database
* For local development, you'll want it to be `Data Source=./bin/Content.Server/data/preferences.db` if you're using sqlite.
* `DATABASE_CONTEXT`: Default is `postgres`. If testing locally with sqlite, use `sqlite`.
* `GUILD`: The Discord Server ID. DeltaV's discord is `1513310351490289834`.
* You can find this by turning Developer mode on in Discord, right-clicking on a server, and copying the server ID.
### Configuration (Production)
Requires a `link-config.json` file in the working directory.
```json
{
"Token": "DISCORD_BOT_TOKEN",
"DatabaseString": "DB_CONNECTION_STRING"
}
```
Here's a quick bash command to create it in your current working directory. Windows users will have to fend for themselves.
```bash
cat << EOF > link-config.json
{
"Token": "DISCORD_BOT_TOKEN",
"DatabaseString": "DB_CONNECTION_STRING"
}
EOF
```
You can also override `Guild` and `DatabaseContext` if you wanted to by including those JSON properties.