This commit is contained in:
2026-03-18 03:01:05 +00:00
parent 5b9362ddd6
commit fbf0fb92cd
9 changed files with 311 additions and 8 deletions

View File

@@ -7,7 +7,7 @@ using Valour.Shared;
namespace SkyBot.Commands
{
public class Test : ICommand
public class Edit : ICommand
{
public string Name => "edit";
public string[] Aliases => [];
@@ -29,6 +29,7 @@ namespace SkyBot.Commands
if(!PermissionHelper.IsOwner(member))
{
await MessageHelper.ReplyAsync(ctx, channel, "This is a Dev only command.");
return;
}
if (message.ReplyToId == null)

View File

@@ -0,0 +1,103 @@
using System.Collections.Concurrent;
using SkyBot.Helpers;
using SkyBot.Models;
using SkyBot.Services;
using Valour.Sdk.Models;
using Valour.Shared.Authorization;
using Valour.Shared.Models;
namespace SkyBot.Commands
{
public class SetWelcome : ICommand
{
public string Name => "setwelcome";
public string[] Aliases => [];
public string Description => "Sets the welcome channel, message or active.";
public string Section => "Mod";
public string Usage => "set <channel|message|active [value]";
public async Task Execute(CommandContext ctx)
{
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
long channelId = ctx.ChannelId;
Message message = ctx.Message;
PlanetMember member = ctx.Member;
Planet planet = ctx.Planet;
string[] args = ctx.Args;
if (!channelCache.TryGetValue(channelId, out var channel)) return;
if (!PermissionHelper.HasPerm(member, [PlanetPermissions.Manage]) && !PermissionHelper.IsOwner(member))
{
await MessageHelper.ReplyAsync(ctx, channel, "You don't have permission to use this command.");
return;
}
if (args.Length == 0)
{
await MessageHelper.ReplyAsync(ctx, channel, "Please specify `channel` or `message`.");
return;
}
switch (args[0].ToLower())
{
case "channel":
case "c":
long targetChannelId;
if (message.Mentions != null && message.Mentions.Any(m => m.Type == MentionType.Channel)) {targetChannelId = message.Mentions.First(m => m.Type == MentionType.Channel).TargetId;}
else if (args.Length > 1 && long.TryParse(args[1], out long parsedChannelId)) {targetChannelId = parsedChannelId;}
else {targetChannelId = channelId;}
if (!channelCache.ContainsKey(targetChannelId)) {await MessageHelper.ReplyAsync(ctx, channel, "Could not find that channel."); return;}
await WelcomeService.SetWelcomeChannel(planet.Id, targetChannelId);
await MessageHelper.ReplyAsync(ctx, channel, $"Welcome channel set to «@c-{targetChannelId}».");
break;
case "message":
case "m":
if (args.Length < 2)
{
await MessageHelper.ReplyAsync(ctx, channel, "Please provide a message. Valid variables: {username} {nickname} {fulluser} {mention} {id}");
return;
}
string msg = string.Join(" ", args[1..]);
await WelcomeService.SetWelcomeMessage( planet.Id, msg);
await MessageHelper.ReplyAsync(ctx, channel, $"Welcome message set to: `{msg}`");
break;
case "active":
case "a":
if (args.Length < 2)
{
await MessageHelper.ReplyAsync(ctx, channel, "Please provide a value. Use `true`, `false`, or `toggle`.");
return;
}
string value = args[1].ToLower();
if (value != "toggle" && value != "true" && value != "false")
{
await MessageHelper.ReplyAsync(ctx, channel, "Invalid value. Use `true`, `false`, `toggle`");
return;
}
if (value == "toggle")
{
var toggle = await WelcomeService.SetActive(planet.Id);
await MessageHelper.ReplyAsync(ctx, channel, toggle.Value ? "Welcome messages enabled." : "Welcome messages disabled.");
return;
}
bool.TryParse(value, out var active);
await WelcomeService.SetActive(planet.Id, active);
await MessageHelper.ReplyAsync(ctx, channel, active ? "Welcome messages enabled." : "Welcome messages disabled.");
break;
default:
await MessageHelper.ReplyAsync(ctx, channel, "Invalid option. Use `channel`, `message` or `active`.");
break;
}
}
}
}

View File

@@ -0,0 +1,32 @@
using Microsoft.Data.Sqlite;
namespace SkyBot.Helpers
{
public static class DatabaseHelper
{
private const string ConnectionString = "Data Source=database.db";
public static SqliteConnection GetConnection()
{
SqliteConnection connection = new SqliteConnection(ConnectionString);
connection.Open();
return connection;
}
public static async Task InitializeAsync()
{
using SqliteConnection connection = GetConnection();
using SqliteCommand cmd = connection.CreateCommand();
cmd.CommandText = @"
CREATE TABLE IF NOT EXISTS WelcomeConfigs (
PlanetId INTEGER PRIMARY KEY,
ChannelId INTEGER NOT NULL DEFAULT 0,
Message TEXT NOT NULL DEFAULT 'Welcome to the planet, {username}!',
Active INTEGER NOT NULL DEFAULT 0
);
";
await cmd.ExecuteNonQueryAsync();
Console.WriteLine("Database initialized.");
}
}
}

View File

@@ -0,0 +1,7 @@
public class WelcomeConfig
{
public long PlanetId { get; set; }
public long ChannelId { get; set; }
public string Message { get; set; } = "Welcome to the planet, {username}!";
public bool Active { get; set; } = false;
}

View File

@@ -5,10 +5,14 @@ using SkyBot.Models;
using Valour.Sdk.Client;
using Valour.Sdk.Models;
namespace SkyBot.Services.Messages
{
public static class Create
{
private static readonly ConcurrentDictionary<long, DateTime> _cooldowns = new();
private static readonly TimeSpan _cooldown = TimeSpan.FromSeconds(2);
public static async Task MessageAsync(
ValourClient client,
ConcurrentDictionary<long, Channel> channelCache,
@@ -40,6 +44,11 @@ namespace SkyBot.Services.Messages
Client = client
};
if (_cooldowns.TryGetValue(message.AuthorUserId, out var lastUsed) && DateTime.UtcNow - lastUsed < _cooldown)
return;
_cooldowns[message.AuthorUserId] = DateTime.UtcNow;
if (CommandRegistry.Commands.TryGetValue(command, out var handler))
{
await handler.Execute(ctx);

View File

@@ -1,14 +1,13 @@
using System.Collections.Concurrent;
using SkyBot.Services;
using Valour.Sdk.Client;
using Valour.Sdk.ModelLogic;
using Valour.Sdk.Models;
using Valour.Sdk.Models.Messages.Embeds;
namespace SkyBot.Services
{
public static class PlanetService
{
private static readonly DateTime _startTime = DateTime.UtcNow;
public static async Task InitializePlanetsAsync(
ValourClient client,
ConcurrentDictionary<long, Channel> channelCache,
@@ -19,15 +18,27 @@ namespace SkyBot.Services
.Select(async planet =>
{
Console.WriteLine($"Initializing Planet: {planet.Name}");
await planet.EnsureReadyAsync();
await planet.FetchInitialDataAsync();
await ChannelService.InitializeChannelsAsync(channelCache, planet);
planet.Channels.Changed += async (channelEvent) => {
planet.Channels.Changed += async _ =>
{
await ChannelService.InitializeChannelsAsync(channelCache, planet);
};
});
planet.Members.Changed += async memberEvent =>
{
if ((DateTime.UtcNow - _startTime).TotalSeconds < 10) return;
if (memberEvent is ModelAddedEvent<PlanetMember> addedEvent)
{
await WelcomeService.OnMemberJoin(addedEvent.Model, channelCache);
}
};
initializedPlanets.TryAdd(planet.Id, true);
});
await Task.WhenAll(tasks);
}
}

View File

@@ -0,0 +1,137 @@
using System.Collections.Concurrent;
using Microsoft.Data.Sqlite;
using SkyBot.Helpers;
using Valour.Sdk.Models;
namespace SkyBot.Services
{
public static class WelcomeService
{
private static readonly ConcurrentDictionary<long, WelcomeConfig> _cache = new();
public static async Task InitializeAsync()
{
using SqliteConnection connection = DatabaseHelper.GetConnection();
using SqliteCommand cmd = connection.CreateCommand();
cmd.CommandText = "SELECT * FROM WelcomeConfigs";
using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
var config = new WelcomeConfig
{
PlanetId = (long)reader["PlanetId"],
ChannelId = (long)reader["ChannelId"],
Message = (string)reader["Message"],
Active = (long)reader["Active"] == 1
};
_cache[config.PlanetId] = config;
}
Console.WriteLine("WelcomeService initialized.");
Console.WriteLine($"Loaded {_cache.Count} welcome configs from database.");
}
public static async Task OnMemberJoin(PlanetMember member, ConcurrentDictionary<long, Channel> channelCache)
{
if (!_cache.TryGetValue(member.PlanetId, out var config)) { Console.WriteLine("No config found"); return; }
if (!config.Active) { Console.WriteLine("Not active"); return; }
Channel? channel = null;
if (config.ChannelId != 0 && channelCache.TryGetValue(config.ChannelId, out var configChannel))
{
channel = configChannel;
}
else
{
channel = channelCache.Values.FirstOrDefault(c => c.PlanetId == member.PlanetId && c.IsDefault);
}
if (channel == null) { Console.WriteLine("No channel found"); return; }
string message = config.Message
.Replace("{username}", member.Name)
.Replace("{fulluser}", member.User.NameAndTag)
.Replace("{nickname}", string.IsNullOrWhiteSpace(member.Nickname) ? member.Name : member.Nickname)
.Replace("{mention}", MessageHelper.Mention(member))
.Replace("{id}", $"{member.Id}");
await channel.SendMessageAsync(message);
}
public static async Task SetWelcomeChannel(long planetId, long channelId)
{
using SqliteConnection connection = DatabaseHelper.GetConnection();
using SqliteCommand cmd = connection.CreateCommand();
cmd.CommandText = @"
INSERT INTO WelcomeConfigs (PlanetId, ChannelId) VALUES ($planetId, $channelId)
ON CONFLICT(PlanetId) DO UPDATE SET ChannelId = $channelId;
";
cmd.Parameters.AddWithValue("$planetId", planetId);
cmd.Parameters.AddWithValue("$channelId", channelId);
await cmd.ExecuteNonQueryAsync();
if (_cache.TryGetValue(planetId, out var config))
{
config.ChannelId = channelId;
}
else
{
_cache[planetId] = new WelcomeConfig{PlanetId = planetId, ChannelId = channelId};
}
}
public static async Task SetWelcomeMessage(long planetId, string message)
{
using SqliteConnection connection = DatabaseHelper.GetConnection();
using SqliteCommand cmd = connection.CreateCommand();
cmd.CommandText = @"
INSERT INTO WelcomeConfigs (PlanetId, Message) VALUES ($planetId, $message)
ON CONFLICT(PlanetId) DO UPDATE SET Message = $message;
";
cmd.Parameters.AddWithValue("$planetId", planetId);
cmd.Parameters.AddWithValue("$message", message);
await cmd.ExecuteNonQueryAsync();
if (_cache.TryGetValue(planetId, out var config))
{
config.Message = message;
}
else
{
_cache[planetId] = new WelcomeConfig{PlanetId = planetId, Message = message};
}
}
public static async Task SetActive(long planetId, bool active)
{
using SqliteConnection connection = DatabaseHelper.GetConnection();
using SqliteCommand cmd = connection.CreateCommand();
cmd.CommandText = @"
INSERT INTO WelcomeConfigs (PlanetId, Active) VALUES ($planetId, $active)
ON CONFLICT(PlanetId) DO UPDATE SET Active = $active;
";
cmd.Parameters.AddWithValue("$planetId", planetId);
cmd.Parameters.AddWithValue("$active", active ? 1 : 0);
await cmd.ExecuteNonQueryAsync();
if (_cache.TryGetValue(planetId, out var config))
{
config.Active = active;
}
else
{
_cache[planetId] = new WelcomeConfig{PlanetId = planetId, Active = active};
}
}
public static async Task<bool?> SetActive(long planetId)
{
if (!_cache.TryGetValue(planetId, out var config)) return null;
bool newActive = !config.Active;
await SetActive(planetId, newActive);
return newActive;
}
}
}

View File

@@ -2,6 +2,7 @@ using Valour.Sdk.Client;
using Valour.Sdk.Models;
using SkyBot.Services;
using System.Collections.Concurrent;
using SkyBot.Helpers;
namespace SkyBot
{
@@ -21,6 +22,8 @@ namespace SkyBot
public async Task StartAsync()
{
StartTime = DateTime.UtcNow;
await DatabaseHelper.InitializeAsync();
await WelcomeService.InitializeAsync();
await BotService.InitializeBotAsync(_client, _channelCache, _initializedPlanets);
}
}
@@ -34,7 +37,6 @@ namespace SkyBot
try
{
await new SkyBot().StartAsync();
Console.WriteLine("Ready and listening...");
await Task.Delay(Timeout.Infinite);

View File

@@ -5,11 +5,12 @@
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>0.2.0.0</Version>
<Version>0.2.1.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNetEnv" Version="3.1.1" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.5" />
<PackageReference Include="Valour.Sdk" Version="0.5.19" />
</ItemGroup>