8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
bin/
|
|
||||||
obj/
|
|
||||||
SkyBot.sln
|
|
||||||
.env
|
.env
|
||||||
Program.cs.old
|
.gitignore
|
||||||
|
SkyBot/bin/
|
||||||
|
SkyBot/obj/
|
||||||
|
SkyBot/SkyBot.sln
|
||||||
|
|||||||
33
SkyBot/Commands/CommandRegistry.cs
Normal file
33
SkyBot/Commands/CommandRegistry.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using SkyBot.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public static class CommandRegistry
|
||||||
|
{
|
||||||
|
public static readonly Dictionary<string, ICommand> Commands = new();
|
||||||
|
public static readonly Dictionary<string, List<ICommand>> Sections = new();
|
||||||
|
|
||||||
|
static CommandRegistry()
|
||||||
|
{
|
||||||
|
var allCommands = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
.SelectMany(a => a.GetTypes())
|
||||||
|
.Where(t => typeof(ICommand).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
|
||||||
|
.Select(t => (ICommand?)Activator.CreateInstance(t))
|
||||||
|
.Select(c => c!);
|
||||||
|
|
||||||
|
foreach (var cmd in allCommands)
|
||||||
|
{
|
||||||
|
Commands[cmd.Name.ToLower()] = cmd;
|
||||||
|
foreach (var alias in cmd.Aliases)
|
||||||
|
{
|
||||||
|
Commands[alias.ToLower()] = cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sections = Commands.Values
|
||||||
|
.Distinct()
|
||||||
|
.GroupBy(c => c.Section.ToLower())
|
||||||
|
.ToDictionary(g => g.Key, g => g.ToList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
SkyBot/Commands/CommandTemplate.cs
Normal file
29
SkyBot/Commands/CommandTemplate.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class CommandTemplate : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "template";
|
||||||
|
public string[] Aliases => [];
|
||||||
|
public string Description => "";
|
||||||
|
public string Section => "template";
|
||||||
|
public string Usage => "";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
string message = $"";
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
SkyBot/Commands/Dev/Test.cs
Normal file
32
SkyBot/Commands/Dev/Test.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Helpers;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Client;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class Test : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "test";
|
||||||
|
public string[] Aliases => [];
|
||||||
|
public string Description => "Just a test command";
|
||||||
|
public string Section => "Dev";
|
||||||
|
public string Usage => "test";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
ValourClient client = ctx.Client;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
Message message = ctx.Message;
|
||||||
|
Planet planet = ctx.Planet;
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, "This is a test message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
SkyBot/Commands/Fun/Echo.cs
Normal file
42
SkyBot/Commands/Fun/Echo.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using SkyBot.Helpers;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class Echo : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "echo";
|
||||||
|
public string[] Aliases => [];
|
||||||
|
public string Description => "Echos what you said through the bot.";
|
||||||
|
public string Section => "Fun";
|
||||||
|
public string Usage => "echo <message>";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
String[] args = ctx.Args;
|
||||||
|
Message message = ctx.Message;
|
||||||
|
|
||||||
|
string reply = string.Join(" ", args);
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(reply)) await channel.SendMessageAsync($"{MentionHelper.Mention(member)} Enter a message to echo.");
|
||||||
|
|
||||||
|
reply = $"{member.Name} » {reply}";
|
||||||
|
|
||||||
|
if (reply.Length > 2048)
|
||||||
|
{
|
||||||
|
reply = reply.Substring(0, 2048);
|
||||||
|
}
|
||||||
|
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, reply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
SkyBot/Commands/Info/Devcentral.cs
Normal file
29
SkyBot/Commands/Info/Devcentral.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class Devcentral : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "devcentral";
|
||||||
|
public string[] Aliases => ["dev"];
|
||||||
|
public string Description => "Sends an invite link to the Dev Central Planet.";
|
||||||
|
public string Section => "Info";
|
||||||
|
public string Usage => "devcentral|dev";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
string message = $"you can join the Dev Central (ID: 42439954653511681) planet here: https://app.valour.gg/I/k2tz9c4i";
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
SkyBot/Commands/Info/Help.cs
Normal file
92
SkyBot/Commands/Info/Help.cs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Text;
|
||||||
|
using SkyBot.Helpers;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
using Valour.Shared.Authorization;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class Help : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "help";
|
||||||
|
public string[] Aliases => ["h"];
|
||||||
|
public string Description => "Shows all the commands and their descriptions.";
|
||||||
|
public string Section => "Info";
|
||||||
|
public string Usage => "help|h [section] [page]";
|
||||||
|
private const int PageSize = 5;
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
string[] args = ctx.Args;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
bool isOwner = await PermissionHelper.IsOwner(member);
|
||||||
|
|
||||||
|
if (!channelCache.TryGetValue(channelId, out var channel)) return;
|
||||||
|
|
||||||
|
// Show all sections.
|
||||||
|
if (args.Length == 0)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine("**Available Categories**");
|
||||||
|
foreach (var section in CommandRegistry.Sections.Keys)
|
||||||
|
{
|
||||||
|
if (section == "template") continue;
|
||||||
|
if (section == "dev" && !isOwner) continue;
|
||||||
|
if (section == "mod" && !PermissionHelper.HasPermAsync(member, [PlanetPermissions.Kick, PlanetPermissions.Ban, PlanetPermissions.ManageRoles]).Result) continue;
|
||||||
|
sb.AppendLine($"- `{section.ToTitleCase()}` ({CommandRegistry.Sections[section].Count})");
|
||||||
|
}
|
||||||
|
sb.AppendLine($"\nUse `{Config.Prefix}help <category>` to see commands in a category.");
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, sb.ToString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// section [page]
|
||||||
|
string sectionName = args[0].ToLower();
|
||||||
|
if (!CommandRegistry.Sections.TryGetValue(sectionName, out var commands))
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, $"Unknown category `{sectionName}`.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectionName == "dev" && !isOwner)
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, $"Unknown category `{sectionName}`.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectionName == "mod" && !PermissionHelper.HasPermAsync(member, [PlanetPermissions.Kick, PlanetPermissions.Ban, PlanetPermissions.ManageRoles]).Result)
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, $"Unknown category `{sectionName}`.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int page = 1;
|
||||||
|
if (args.Length >= 2 && int.TryParse(args[1], out int parsedPage))
|
||||||
|
{
|
||||||
|
page = parsedPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalPages = (int)Math.Ceiling(commands.Count / (double)PageSize);
|
||||||
|
page = Math.Clamp(page, 1, totalPages);
|
||||||
|
|
||||||
|
var pageCommands = commands.Skip((page - 1) * PageSize).Take(PageSize);
|
||||||
|
|
||||||
|
var sb2 = new StringBuilder();
|
||||||
|
sb2.AppendLine($"**{sectionName.ToTitleCase()} commands** (Page {page}/{totalPages}):");
|
||||||
|
foreach (var cmd in pageCommands)
|
||||||
|
{
|
||||||
|
var name = cmd.Aliases.Length > 0
|
||||||
|
? $"{cmd.Name}|{string.Join("|", cmd.Aliases)}"
|
||||||
|
: cmd.Name;
|
||||||
|
sb2.AppendLine($"`{Config.Prefix}{name}` - {cmd.Description}");
|
||||||
|
}
|
||||||
|
sb2.AppendLine($"\nUse `{Config.Prefix}help {sectionName} <page>` to see more.");
|
||||||
|
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, sb2.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
SkyBot/Commands/Info/JoinSite.cs
Normal file
29
SkyBot/Commands/Info/JoinSite.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class JoinSite : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "joinsite";
|
||||||
|
public string[] Aliases => [];
|
||||||
|
public string Description => "Links to a site to help your bots join a planet.";
|
||||||
|
public string Section => "Info";
|
||||||
|
public string Usage => "joinsite";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
string message = $"You can use this website to easily add your bot to a planet: https://skyjoshua.xyz/planetjoiner";
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
SkyBot/Commands/Info/Minecraft.cs
Normal file
31
SkyBot/Commands/Info/Minecraft.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class Minecraft : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "minecraft";
|
||||||
|
public string[] Aliases => ["mc"];
|
||||||
|
public string Description => "Sends the Unofficial ValourSMP IPs";
|
||||||
|
public string Section => "Info";
|
||||||
|
public string Usage => "minecraft|mc";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
string message = @$"you can join the Unofficial ValourSMP Minecraft Server by using this ip:
|
||||||
|
Java: `valour.sxsc.xyz`, Bedrock: `valourbr.sxsc.xyz` Both with the default ports.
|
||||||
|
Cool features can be found here: https://sxsc.xyz/servers/valour/";
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
SkyBot/Commands/Info/Source.cs
Normal file
29
SkyBot/Commands/Info/Source.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class Source : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "source";
|
||||||
|
public string[] Aliases => ["src"];
|
||||||
|
public string Description => "Shows the source code for this bot.";
|
||||||
|
public string Section => "Info";
|
||||||
|
public string Usage => "source";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
string message = $"You can find my source code here: {Config.SourceLink}";
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
SkyBot/Commands/Info/Suggest.cs
Normal file
29
SkyBot/Commands/Info/Suggest.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class Suggest : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "suggest";
|
||||||
|
public string[] Aliases => [];
|
||||||
|
public string Description => "Shows the source code for this bot.";
|
||||||
|
public string Section => "Info";
|
||||||
|
public string Usage => "source";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
string message = $"You can suggest a command to be added here: https://docs.google.com/spreadsheets/d/1CzcpLAuMiPL_RODrZ5x25cPj8yE-rR3mEnqrd_2Fbmk";
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
SkyBot/Commands/Info/SwaggerAPI.cs
Normal file
29
SkyBot/Commands/Info/SwaggerAPI.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class SwaggerAPI : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "swagger";
|
||||||
|
public string[] Aliases => ["api"];
|
||||||
|
public string Description => "Sends a link to the Valour.gg Swagger API.";
|
||||||
|
public string Section => "Info";
|
||||||
|
public string Usage => "swagger|api";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
string message = $"Here is a link to the Swagger API: https://api.valour.gg/swagger";
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
SkyBot/Commands/Info/UserCount.cs
Normal file
31
SkyBot/Commands/Info/UserCount.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Helpers;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class UserCount : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "usercount";
|
||||||
|
public string[] Aliases => ["users"];
|
||||||
|
public string Description => "Shows the user count of Valour.";
|
||||||
|
public string Section => "Info";
|
||||||
|
public string Usage => "usercount|users";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
string message = @$"Current Valour user count is: {ValourUsercountHelper.ValourUsercount:N0}
|
||||||
|
You can see a graph of the user count here: /meow";
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
SkyBot/Commands/Info/Version.cs
Normal file
30
SkyBot/Commands/Info/Version.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class Version : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "version";
|
||||||
|
public string[] Aliases => [];
|
||||||
|
public string Description => "Shows the current version of the Bot and Valour.";
|
||||||
|
public string Section => "Info";
|
||||||
|
public string Usage => "";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
string message = @$"Bot Version: {typeof(Version).Assembly.GetName().Version}
|
||||||
|
Valour Version: {typeof(Channel).Assembly.GetName().Version}";
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
SkyBot/Commands/Mod/Ban.cs
Normal file
36
SkyBot/Commands/Mod/Ban.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Helpers;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
using Valour.Shared.Authorization;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class Ban : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "ban";
|
||||||
|
public string[] Aliases => [];
|
||||||
|
public string Description => "Bans a user from the planet.";
|
||||||
|
public string Section => "mod";
|
||||||
|
public string Usage => "ban <user> [reason]";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
if (!PermissionHelper.HasPermAsync(member, [PlanetPermissions.Ban]).Result)
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, $"You don't have permission to use this command.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string message = $"Work in progress...";
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
SkyBot/Commands/Mod/Kick.cs
Normal file
37
SkyBot/Commands/Mod/Kick.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Helpers;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
using Valour.Shared.Authorization;
|
||||||
|
|
||||||
|
namespace SkyBot.Commands
|
||||||
|
{
|
||||||
|
public class Kick : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "kick";
|
||||||
|
public string[] Aliases => [];
|
||||||
|
public string Description => "Kicks a user from the planet.";
|
||||||
|
public string Section => "mod";
|
||||||
|
public string Usage => "kick <user> [reason]";
|
||||||
|
|
||||||
|
public async Task Execute(CommandContext ctx)
|
||||||
|
{
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache = ctx.ChannelCache;
|
||||||
|
long channelId = ctx.ChannelId;
|
||||||
|
PlanetMember member = ctx.Member;
|
||||||
|
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
if (!PermissionHelper.HasPermAsync(member, [PlanetPermissions.Kick]).Result)
|
||||||
|
{
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, $"You don't have permission to use this command.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string message = $"Work in progress...";
|
||||||
|
|
||||||
|
await MessageHelper.ReplyAsync(ctx, channel, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
SkyBot/Config.cs
Normal file
10
SkyBot/Config.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace SkyBot
|
||||||
|
{
|
||||||
|
|
||||||
|
public static class Config {
|
||||||
|
public static readonly long OwnerId = 15652354820931584;
|
||||||
|
public static readonly string Prefix = "sd/";
|
||||||
|
public static readonly string SourceLink = "https://github.com/SkyJoshua/SkyBot";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
10
SkyBot/Helpers/MentionHelper.cs
Normal file
10
SkyBot/Helpers/MentionHelper.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Helpers
|
||||||
|
{
|
||||||
|
public static class MentionHelper
|
||||||
|
{
|
||||||
|
public static string Mention(this PlanetMember member) => $"«@m-{member.Id}»";
|
||||||
|
public static string Mention(this User user) => $"«@u-{user.Id}»";
|
||||||
|
}
|
||||||
|
}
|
||||||
24
SkyBot/Helpers/MessageHelper.cs
Normal file
24
SkyBot/Helpers/MessageHelper.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
public static class MessageHelper
|
||||||
|
{
|
||||||
|
public static async Task ReplyAsync(CommandContext ctx, Channel channel, string content)
|
||||||
|
{
|
||||||
|
long? replyToId = ctx.Message.ReplyToId.HasValue ? ctx.Message.ReplyToId : ctx.Message.Id;
|
||||||
|
|
||||||
|
var msg = new Message(ctx.Client)
|
||||||
|
{
|
||||||
|
Content = content,
|
||||||
|
ChannelId = channel.Id,
|
||||||
|
PlanetId = ctx.Planet.Id,
|
||||||
|
AuthorUserId = ctx.Client.Me.Id,
|
||||||
|
AuthorMemberId = channel.Planet?.MyMember.Id,
|
||||||
|
ReplyToId = replyToId,
|
||||||
|
Fingerprint = Guid.NewGuid().ToString()
|
||||||
|
};
|
||||||
|
await ctx.Client.MessageService.SendMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToTitleCase(this string str) => System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(str);
|
||||||
|
}
|
||||||
27
SkyBot/Helpers/PermissionHelper.cs
Normal file
27
SkyBot/Helpers/PermissionHelper.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Valour.Sdk.Models;
|
||||||
|
using Valour.Shared.Authorization;
|
||||||
|
|
||||||
|
namespace SkyBot.Helpers
|
||||||
|
{
|
||||||
|
public static class PermissionHelper
|
||||||
|
{
|
||||||
|
public static async Task<bool> HasPermAsync(PlanetMember member, PlanetPermission[] permissions, bool requireAll = false)
|
||||||
|
{
|
||||||
|
if (member == null) return false;
|
||||||
|
if (member.HasPermission(PlanetPermissions.FullControl)) return true;
|
||||||
|
if (member.Roles.Any(r => r.IsAdmin)) return true;
|
||||||
|
|
||||||
|
return requireAll
|
||||||
|
? permissions.All(permission => member.HasPermission(permission))
|
||||||
|
: permissions.Any(permission => member.HasPermission(permission));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<bool> IsOwner(PlanetMember member)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (member == null) return false;
|
||||||
|
if (member.UserId == Config.OwnerId) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
SkyBot/Helpers/ValourUsercountHelper.cs
Normal file
32
SkyBot/Helpers/ValourUsercountHelper.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace SkyBot.Helpers
|
||||||
|
{
|
||||||
|
public static class ValourUsercountHelper {
|
||||||
|
private static readonly HttpClient _http = new HttpClient();
|
||||||
|
private static long _valourUsercount;
|
||||||
|
public static long ValourUsercount => _valourUsercount;
|
||||||
|
public static async Task UpdateUsercount()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _http.GetStringAsync("https://api.valour.gg/api/users/count");
|
||||||
|
|
||||||
|
_valourUsercount = JsonSerializer.Deserialize<long>(response);
|
||||||
|
|
||||||
|
Console.WriteLine($"Valour user count updated: {_valourUsercount}");
|
||||||
|
} catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to update valour user count: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StartUpdater()
|
||||||
|
{
|
||||||
|
var timer = new System.Timers.Timer(300_000);
|
||||||
|
timer.Elapsed += async (_, _) => await UpdateUsercount();
|
||||||
|
timer.AutoReset = true;
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
SkyBot/Models/CommandContext.cs
Normal file
18
SkyBot/Models/CommandContext.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Valour.Sdk.Client;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Models
|
||||||
|
{
|
||||||
|
public class CommandContext
|
||||||
|
{
|
||||||
|
public required ValourClient Client{ get; set; }
|
||||||
|
public required ConcurrentDictionary<long, Channel> ChannelCache { get; set; }
|
||||||
|
public required PlanetMember Member { get; set; }
|
||||||
|
public required Message Message { get; set; }
|
||||||
|
public required Planet Planet { get; set; }
|
||||||
|
public required long ChannelId { get; set; }
|
||||||
|
public required string[] Args { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
14
SkyBot/Models/ICommand.cs
Normal file
14
SkyBot/Models/ICommand.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace SkyBot.Models
|
||||||
|
{
|
||||||
|
public interface ICommand
|
||||||
|
{
|
||||||
|
string Name { get; }
|
||||||
|
string[] Aliases { get; }
|
||||||
|
string Description { get; }
|
||||||
|
string Section { get; }
|
||||||
|
string Usage { get; }
|
||||||
|
Task Execute(CommandContext ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
SkyBot/Services/BotService.cs
Normal file
40
SkyBot/Services/BotService.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using DotNetEnv;
|
||||||
|
using SkyBot.Helpers;
|
||||||
|
using Valour.Sdk.Client;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Services
|
||||||
|
{
|
||||||
|
public static class BotService
|
||||||
|
{
|
||||||
|
public static async Task InitializeBotAsync(
|
||||||
|
ValourClient client,
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache,
|
||||||
|
ConcurrentDictionary<long, bool> initalizedPlanets)
|
||||||
|
{
|
||||||
|
Env.Load();
|
||||||
|
|
||||||
|
var token = Environment.GetEnvironmentVariable("TOKEN");
|
||||||
|
if (string.IsNullOrWhiteSpace(token)) {Console.WriteLine("TOKEN not set."); return;}
|
||||||
|
|
||||||
|
var loginResult = await client.InitializeUser(token);
|
||||||
|
if (!loginResult.Success) {Console.WriteLine($"Login Failed: {loginResult.Message}"); return;}
|
||||||
|
Console.WriteLine($"Logged in as {client.Me.Name} (ID: {client.Me.Id})");
|
||||||
|
|
||||||
|
await ValourUsercountHelper.UpdateUsercount();
|
||||||
|
ValourUsercountHelper.StartUpdater();
|
||||||
|
|
||||||
|
await PlanetService.InitializePlanetsAsync(client, channelCache, initalizedPlanets);
|
||||||
|
client.PlanetService.JoinedPlanetsUpdated += async () =>
|
||||||
|
{
|
||||||
|
await PlanetService.InitializePlanetsAsync(client, channelCache, initalizedPlanets);
|
||||||
|
};
|
||||||
|
|
||||||
|
client.MessageService.MessageReceived += async (message) =>
|
||||||
|
{
|
||||||
|
await Messages.Create.MessageAsync(client, channelCache, message);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
SkyBot/Services/ChannelService.cs
Normal file
36
SkyBot/Services/ChannelService.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
using Valour.Shared.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Services
|
||||||
|
{
|
||||||
|
public static class ChannelService
|
||||||
|
{
|
||||||
|
public static async Task InitializeChannelsAsync(
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache,
|
||||||
|
Planet planet)
|
||||||
|
{
|
||||||
|
foreach (var channel in planet.Channels)
|
||||||
|
{
|
||||||
|
channelCache[channel.Id] = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
foreach (var channel in planet.Channels.Where(c => c.ChannelType == ChannelTypeEnum.PlanetChat)){
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await channel.OpenWithResult("SkyBot");
|
||||||
|
Console.WriteLine($"Realtime opened for: {planet.Name} (ID: {planet.Id}) -> {channel.Name} (ID: {channel.Id})");
|
||||||
|
} catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error opening realtime for {channel.Id}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"All channels opened for {planet.Name}.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
SkyBot/Services/Messages/Create.cs
Normal file
53
SkyBot/Services/Messages/Create.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Commands;
|
||||||
|
using SkyBot.Helpers;
|
||||||
|
using SkyBot.Models;
|
||||||
|
using Valour.Sdk.Client;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
|
||||||
|
namespace SkyBot.Services.Messages
|
||||||
|
{
|
||||||
|
public static class Create
|
||||||
|
{
|
||||||
|
public static async Task MessageAsync(
|
||||||
|
ValourClient client,
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache,
|
||||||
|
Message message
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (message.AuthorUserId == client.Me.Id) return;
|
||||||
|
string prefix = Config.Prefix;
|
||||||
|
string content = message.Content ?? "";
|
||||||
|
if (string.IsNullOrWhiteSpace(content)) return;
|
||||||
|
if (!content.ToLower().StartsWith(prefix)) return;
|
||||||
|
|
||||||
|
long channelId = message.ChannelId;
|
||||||
|
PlanetMember member = await message.FetchAuthorMemberAsync();
|
||||||
|
var parts = content.Substring(prefix.Length).Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (parts.Length == 0) return;
|
||||||
|
|
||||||
|
string command = parts[0].ToLower();
|
||||||
|
string[] args = parts[1..];
|
||||||
|
|
||||||
|
if (CommandRegistry.Commands.TryGetValue(command, out var handler))
|
||||||
|
{
|
||||||
|
await handler.Execute(new CommandContext
|
||||||
|
{
|
||||||
|
ChannelCache = channelCache,
|
||||||
|
ChannelId = channelId,
|
||||||
|
Member = member,
|
||||||
|
Planet = message.Planet,
|
||||||
|
Args = args,
|
||||||
|
Message = message,
|
||||||
|
Client = client
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
if (channelCache.TryGetValue(channelId, out var channel))
|
||||||
|
{
|
||||||
|
await channel.SendMessageAsync($"{MentionHelper.Mention(member)} Unknown command.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
SkyBot/Services/PlanetService.cs
Normal file
34
SkyBot/Services/PlanetService.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SkyBot.Services;
|
||||||
|
using Valour.Sdk.Client;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
using Valour.Sdk.Models.Messages.Embeds;
|
||||||
|
|
||||||
|
|
||||||
|
namespace SkyBot.Services
|
||||||
|
{
|
||||||
|
public static class PlanetService
|
||||||
|
{
|
||||||
|
public static async Task InitializePlanetsAsync(
|
||||||
|
ValourClient client,
|
||||||
|
ConcurrentDictionary<long, Channel> channelCache,
|
||||||
|
ConcurrentDictionary<long, bool> initializedPlanets)
|
||||||
|
{
|
||||||
|
var tasks = client.PlanetService.JoinedPlanets
|
||||||
|
.Where(planet => !initializedPlanets.ContainsKey(planet.Id))
|
||||||
|
.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) => {
|
||||||
|
await ChannelService.InitializeChannelsAsync(channelCache, planet);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
SkyBot/SkyBot.cs
Normal file
47
SkyBot/SkyBot.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using Valour.Sdk.Client;
|
||||||
|
using Valour.Sdk.Models;
|
||||||
|
using SkyBot.Services;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace SkyBot
|
||||||
|
{
|
||||||
|
public class SkyBot
|
||||||
|
{
|
||||||
|
private readonly ValourClient _client;
|
||||||
|
private readonly ConcurrentDictionary<long, Channel> _channelCache = new();
|
||||||
|
private readonly ConcurrentDictionary<long, bool> _initializedPlanets = new();
|
||||||
|
|
||||||
|
public SkyBot()
|
||||||
|
{
|
||||||
|
_client = new ValourClient("https://api.valour.gg/");
|
||||||
|
_client.SetupHttpClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartAsync()
|
||||||
|
{
|
||||||
|
await BotService.InitializeBotAsync(_client, _channelCache, _initializedPlanets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await new SkyBot().StartAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("Ready and listening...");
|
||||||
|
await Task.Delay(Timeout.Infinite);
|
||||||
|
} catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Fatal error: {ex.Message}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
SkyBot/SkyBot.csproj
Normal file
16
SkyBot/SkyBot.csproj
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<Version>0.2.0.0</Version>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="DotNetEnv" Version="3.1.1" />
|
||||||
|
<PackageReference Include="Valour.Sdk" Version="0.5.19" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user