mirror of
https://github.com/SoDOff-Project/sodoff-mmo.git
synced 2025-11-27 12:26:53 -08:00
Moved Discord bot configuration into appsettings and added moderation commands.
This commit is contained in:
parent
ec5621d904
commit
21e0a3f81a
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,4 +5,4 @@ src/bin
|
|||||||
src/obj
|
src/obj
|
||||||
src/Properties
|
src/Properties
|
||||||
src/sodoffmmo.csproj.user
|
src/sodoffmmo.csproj.user
|
||||||
/src/discord_bot.json
|
/src/discord_bot_token
|
||||||
|
|||||||
@ -12,7 +12,7 @@ class ChatMessageHandler : CommandHandler {
|
|||||||
if (ManagementCommandProcessor.ProcessCommand(message, client))
|
if (ManagementCommandProcessor.ProcessCommand(message, client))
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
if (CheckIllegalName(client)) return Task.CompletedTask;
|
if (CheckIllegalName(client)) return Task.CompletedTask;
|
||||||
if (client.TempMuted) {
|
if (client.Muted) {
|
||||||
ClientMuted(client);
|
ClientMuted(client);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,6 +88,8 @@ class LoginHandler : CommandHandler
|
|||||||
client.PlayerData.Role = info.Role;
|
client.PlayerData.Role = info.Role;
|
||||||
client.PlayerData.VikingId = info.Id;
|
client.PlayerData.VikingId = info.Id;
|
||||||
client.GameVersion = info.Version;
|
client.GameVersion = info.Version;
|
||||||
|
client.Muted = Client.MutedList.ContainsKey(info.Id);
|
||||||
|
client.Banned = Client.BannedList.ContainsKey(info.Id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
|
|||||||
@ -9,17 +9,23 @@ public class Client {
|
|||||||
static int id;
|
static int id;
|
||||||
static object lck = new();
|
static object lck = new();
|
||||||
|
|
||||||
|
// VikingId, Reason (nullable)
|
||||||
|
public static readonly Dictionary<int, string?> MutedList = new();
|
||||||
|
public static readonly Dictionary<int, string?> BannedList = new();
|
||||||
|
|
||||||
public int ClientID { get; private set; }
|
public int ClientID { get; private set; }
|
||||||
public PlayerData PlayerData { get; set; } = new();
|
public PlayerData PlayerData { get; set; } = new();
|
||||||
public Room? Room { get; private set; }
|
public Room? Room { get; private set; }
|
||||||
|
public Room? ReturnRoomOnPardon { get; private set; } = null;
|
||||||
public bool OldApi { get; set; } = false;
|
public bool OldApi { get; set; } = false;
|
||||||
public bool TempMuted { get; set; } = false;
|
public uint GameVersion { get; set; }
|
||||||
|
public bool Muted { get; set; } = false;
|
||||||
|
public bool Banned { get; set; } = false;
|
||||||
|
|
||||||
private readonly Socket socket;
|
private readonly Socket socket;
|
||||||
SocketBuffer socketBuffer = new();
|
SocketBuffer socketBuffer = new();
|
||||||
private volatile bool scheduledDisconnect = false;
|
private volatile bool scheduledDisconnect = false;
|
||||||
private readonly object clientLock = new();
|
private readonly object clientLock = new();
|
||||||
public uint GameVersion { get; set; }
|
|
||||||
|
|
||||||
public Client(Socket clientSocket) {
|
public Client(Socket clientSocket) {
|
||||||
socket = clientSocket;
|
socket = clientSocket;
|
||||||
@ -53,7 +59,12 @@ public class Client {
|
|||||||
// set variable player data as not valid, but do not reset all player data
|
// set variable player data as not valid, but do not reset all player data
|
||||||
PlayerData.IsValid = false;
|
PlayerData.IsValid = false;
|
||||||
|
|
||||||
if (Room != null) {
|
bool transfer = Configuration.DiscordBotConfig != null && Room != null && room != null &&
|
||||||
|
Configuration.DiscordBotConfig.Merges.Values
|
||||||
|
.Any(rooms => rooms.Contains(Room.Name) && rooms.Contains(room.Name));
|
||||||
|
|
||||||
|
ReturnRoomOnPardon = room; // This is needed so that users are put where they're supposed to when they're unbanned.
|
||||||
|
if (Room != null && (!Banned || !Room.Name.StartsWith("BannedUserRoom_"))) {
|
||||||
Console.WriteLine($"Leave room: {Room.Name} (id={Room.Id}, size={Room.ClientsCount}) IID: {ClientID}");
|
Console.WriteLine($"Leave room: {Room.Name} (id={Room.Id}, size={Room.ClientsCount}) IID: {ClientID}");
|
||||||
Room.RemoveClient(this);
|
Room.RemoveClient(this);
|
||||||
|
|
||||||
@ -61,19 +72,41 @@ public class Client {
|
|||||||
data.Add("r", Room.Id);
|
data.Add("r", Room.Id);
|
||||||
data.Add("u", ClientID);
|
data.Add("u", ClientID);
|
||||||
Room.Send(NetworkObject.WrapObject(0, 1004, data).Serialize());
|
Room.Send(NetworkObject.WrapObject(0, 1004, data).Serialize());
|
||||||
DiscordManager.SendPlayerBasedMessage("", ":red_square: {1} has left the room.", Room, PlayerData);
|
if (!transfer) DiscordManager.SendPlayerBasedMessage("", ":red_square: {1} has left the room.", Room, PlayerData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set new room (null when SetRoom is used as LeaveRoom)
|
if (Banned) {
|
||||||
Room = room;
|
if (room != null) {
|
||||||
|
Room = Room.GetOrAdd("BannedUserRoom_" + ClientID, autoRemove: true);
|
||||||
|
if (Room.Clients.Contains(this)) {
|
||||||
|
// Run all addclient things but no duplicate.
|
||||||
|
Room.AddClient(this); // Appends to end.
|
||||||
|
Room.RemoveClient(this); // Removes first instance.
|
||||||
|
} else {
|
||||||
|
Room.AddClient(this);
|
||||||
|
}
|
||||||
|
Send(Room.SubscribeRoom());
|
||||||
|
} else {
|
||||||
|
Room?.RemoveClient(this);
|
||||||
|
Room = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// set new room (null when SetRoom is used as LeaveRoom)
|
||||||
|
Room? oldRoom = Room;
|
||||||
|
Room = room;
|
||||||
|
|
||||||
if (Room != null) {
|
if (Room != null) {
|
||||||
Console.WriteLine($"Join room: {Room.Name} RoomID (id={Room.Id}, size={Room.ClientsCount}) IID: {ClientID}");
|
Console.WriteLine($"Join room: {Room.Name} RoomID (id={Room.Id}, size={Room.ClientsCount}) IID: {ClientID}");
|
||||||
Room.AddClient(this);
|
Room.AddClient(this);
|
||||||
|
|
||||||
Send(Room.SubscribeRoom());
|
Send(Room.SubscribeRoom());
|
||||||
if (Room.Name != "LIMBO") UpdatePlayerUserVariables(); // do not update user vars if room is limbo
|
if (Room.Name != "LIMBO") UpdatePlayerUserVariables(); // do not update user vars if room is limbo
|
||||||
DiscordManager.SendPlayerBasedMessage("", ":green_square: {1} has joined the room.", Room, PlayerData);
|
if (transfer) {
|
||||||
|
DiscordManager.SendPlayerBasedMessage("", ":cyclone: {1} transferred from "+$"{oldRoom!.Name} to {room!.Name}.", Room, PlayerData);
|
||||||
|
} else {
|
||||||
|
DiscordManager.SendPlayerBasedMessage("", ":green_square: {1} has joined the room.", Room, PlayerData);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ using System.Collections.Specialized;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ namespace sodoffmmo.Core;
|
|||||||
internal static class Configuration {
|
internal static class Configuration {
|
||||||
|
|
||||||
public static ServerConfiguration ServerConfiguration { get; private set; } = new ServerConfiguration();
|
public static ServerConfiguration ServerConfiguration { get; private set; } = new ServerConfiguration();
|
||||||
|
public static DiscordBotConfig? DiscordBotConfig { get; private set; } = null;
|
||||||
|
|
||||||
public static SortedDictionary<int, object> Emoticons = new();
|
public static SortedDictionary<int, object> Emoticons = new();
|
||||||
public static SortedDictionary<int, object> Actions = new();
|
public static SortedDictionary<int, object> Actions = new();
|
||||||
@ -33,15 +35,21 @@ internal static class Configuration {
|
|||||||
ServerConfiguration.Authentication = AuthenticationMode.Disabled;
|
ServerConfiguration.Authentication = AuthenticationMode.Disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emoticons (the emojis that float above your head)
|
DiscordBotConfig = config.GetSection("DiscordBot").Get<DiscordBotConfig>();
|
||||||
SortAndVersion(config.GetSection("Emoticons"), Emoticons);
|
if (DiscordBotConfig != null) {
|
||||||
// Actions (such as dances)
|
// Emoticons (the emojis that float above your head)
|
||||||
SortAndVersion(config.GetSection("Actions"), Actions);
|
SortAndVersion(config.GetSection("Emoticons"), Emoticons);
|
||||||
// In EMD, there are seperate actions when in car that share IDs with normal actions.
|
|
||||||
// Since this is easy to check MMO-side (SPM uservar), we can just store these seperate
|
// Actions (such as dances)
|
||||||
SortAndVersion(config.GetSection("EMDCarActions"), EMDCarActions);
|
SortAndVersion(config.GetSection("Actions"), Actions);
|
||||||
// Canned Chat options (which can vary game to game)
|
|
||||||
SortAndVersion(config.GetSection("CannedChat"), CannedChat);
|
// In EMD, there are seperate actions when in car that share IDs with normal actions.
|
||||||
|
// Since this is easy to check MMO-side (SPM uservar), we can just store these seperate
|
||||||
|
SortAndVersion(config.GetSection("EMDCarActions"), EMDCarActions);
|
||||||
|
|
||||||
|
// Canned Chat options (which can vary game to game)
|
||||||
|
SortAndVersion(config.GetSection("CannedChat"), CannedChat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// While version checking isn't exactly necessary for Actions when using vanilla files, it's nice to support it anyway.
|
// While version checking isn't exactly necessary for Actions when using vanilla files, it's nice to support it anyway.
|
||||||
@ -97,6 +105,15 @@ internal sealed class ServerConfiguration {
|
|||||||
public string BypassToken { get; set; } = "";
|
public string BypassToken { get; set; } = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal sealed class DiscordBotConfig {
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
public ulong Server { get; set; }
|
||||||
|
public ulong Channel { get; set; }
|
||||||
|
public ulong RoleModerator { get; set; }
|
||||||
|
public ulong RoleAdmin { get; set; }
|
||||||
|
public Dictionary<string, string[]> Merges { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public enum AuthenticationMode {
|
public enum AuthenticationMode {
|
||||||
Disabled, Optional, RequiredForChat, Required
|
Disabled, Optional, RequiredForChat, Required
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,10 +79,10 @@ public class Room {
|
|||||||
public static bool Exists(string name) => rooms.ContainsKey(name);
|
public static bool Exists(string name) => rooms.ContainsKey(name);
|
||||||
|
|
||||||
public static Room Get(string name) => rooms[name];
|
public static Room Get(string name) => rooms[name];
|
||||||
|
|
||||||
public static Room GetOrAdd(string name, bool autoRemove = false) {
|
public static Room GetOrAdd(string name, bool autoRemove = false) {
|
||||||
lock(RoomsListLock) {
|
lock(RoomsListLock) {
|
||||||
if (!Room.Exists(name))
|
if (!Room.Exists(name))
|
||||||
return new Room(name, autoRemove: autoRemove);
|
return new Room(name, autoRemove: autoRemove);
|
||||||
return rooms[name];
|
return rooms[name];
|
||||||
}
|
}
|
||||||
@ -104,8 +104,13 @@ public class Room {
|
|||||||
NetworkObject obj = new();
|
NetworkObject obj = new();
|
||||||
NetworkArray roomInfo = new();
|
NetworkArray roomInfo = new();
|
||||||
roomInfo.Add(Id);
|
roomInfo.Add(Id);
|
||||||
roomInfo.Add(Name); // Room Name
|
if (Name.StartsWith("BannedUserRoom_")) {
|
||||||
roomInfo.Add(Group); // Group Name
|
roomInfo.Add("BannedUserRoom"); // Room Name
|
||||||
|
roomInfo.Add("BannedUserRoom"); // Group Name
|
||||||
|
} else {
|
||||||
|
roomInfo.Add(Name); // Room Name
|
||||||
|
roomInfo.Add(Group); // Group Name
|
||||||
|
}
|
||||||
roomInfo.Add(true); // is game
|
roomInfo.Add(true); // is game
|
||||||
roomInfo.Add(false); // is hidden
|
roomInfo.Add(false); // is hidden
|
||||||
roomInfo.Add(false); // is password protected
|
roomInfo.Add(false); // is password protected
|
||||||
@ -136,8 +141,13 @@ public class Room {
|
|||||||
|
|
||||||
NetworkArray r1 = new();
|
NetworkArray r1 = new();
|
||||||
r1.Add(Id);
|
r1.Add(Id);
|
||||||
r1.Add(Name); // Room Name
|
if (Name.StartsWith("BannedUserRoom_")) {
|
||||||
r1.Add(Group); // Group Name
|
r1.Add("BannedUserRoom"); // Room Name
|
||||||
|
r1.Add("BannedUserRoom"); // Group Name
|
||||||
|
} else {
|
||||||
|
r1.Add(Name); // Room Name
|
||||||
|
r1.Add(Group); // Group Name
|
||||||
|
}
|
||||||
r1.Add(true);
|
r1.Add(true);
|
||||||
r1.Add(false);
|
r1.Add(false);
|
||||||
r1.Add(false);
|
r1.Add(false);
|
||||||
|
|||||||
@ -15,8 +15,8 @@ class TempMuteCommand : IManagementCommand {
|
|||||||
client.Send(Utils.BuildServerSideMessage($"TempMute: user {arguments[0]} not found", "Server"));
|
client.Send(Utils.BuildServerSideMessage($"TempMute: user {arguments[0]} not found", "Server"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
target.TempMuted = !target.TempMuted;
|
target.Muted = !target.Muted;
|
||||||
if (target.TempMuted)
|
if (target.Muted)
|
||||||
client.Send(Utils.BuildServerSideMessage($"TempMute: {arguments[0]} has been temporarily muted", "Server"));
|
client.Send(Utils.BuildServerSideMessage($"TempMute: {arguments[0]} has been temporarily muted", "Server"));
|
||||||
else
|
else
|
||||||
client.Send(Utils.BuildServerSideMessage($"TempMute: {arguments[0]} has been unmuted", "Server"));
|
client.Send(Utils.BuildServerSideMessage($"TempMute: {arguments[0]} has been unmuted", "Server"));
|
||||||
|
|||||||
@ -6,87 +6,77 @@ using System.Collections.Concurrent;
|
|||||||
using sodoffmmo.Core;
|
using sodoffmmo.Core;
|
||||||
using Discord.Rest;
|
using Discord.Rest;
|
||||||
using sodoffmmo.Data;
|
using sodoffmmo.Data;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
namespace sodoffmmo.Management;
|
namespace sodoffmmo.Management;
|
||||||
|
|
||||||
class DiscordManager {
|
class DiscordManager {
|
||||||
private static readonly string token_file;
|
private static readonly ConcurrentQueue<QueuedMessage> MessageQueue = new();
|
||||||
|
private static DiscordSocketClient BotClient;
|
||||||
private static readonly ConcurrentQueue<QueuedMessage> queue = new();
|
private static SocketTextChannel BotChannel;
|
||||||
private static DiscordSocketClient client;
|
|
||||||
private static BotConfig config;
|
|
||||||
private static SocketTextChannel channel;
|
|
||||||
|
|
||||||
static DiscordManager() {
|
|
||||||
DirectoryInfo dir = new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "src"));
|
|
||||||
while (dir.Parent != null) {
|
|
||||||
if (dir.Name == "src" && dir.Exists) break;
|
|
||||||
dir = dir.Parent;
|
|
||||||
}
|
|
||||||
if (dir.Parent == null) dir = new DirectoryInfo(Directory.GetCurrentDirectory());
|
|
||||||
token_file = Path.Combine(dir.FullName, "discord_bot.json");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Initialize() {
|
public static void Initialize() {
|
||||||
try {
|
if (Configuration.DiscordBotConfig?.Disabled == false) { // Bot config isn't null or disabled.
|
||||||
// This approach is taken because I don't want to accidentally
|
try {
|
||||||
// push my bot token because it was in appsettings. The
|
string botTokenPath = Path.Combine(Path.GetDirectoryName(typeof(Configuration).Assembly.Location), "discord_bot_token");
|
||||||
// json handled here has been added to gitignore.
|
if (File.Exists(botTokenPath)) {
|
||||||
if (File.Exists(token_file)) {
|
string? token = new StreamReader(botTokenPath).ReadLine();
|
||||||
Task.Run(() => RunBot(JsonSerializer.Deserialize<BotConfig>(File.ReadAllText(token_file))));
|
if (token != null) {
|
||||||
} else {
|
if (Configuration.DiscordBotConfig.Server != 0) {
|
||||||
Log("Discord Integration not started because the file containing the token was not found.\nPut the token in \""+token_file+"\" and restart the server to use Discord Integration.", ConsoleColor.Yellow);
|
if (Configuration.DiscordBotConfig.Channel != 0) {
|
||||||
File.WriteAllText(token_file, JsonSerializer.Serialize(new BotConfig {
|
Task.Run(() => RunBot(token));
|
||||||
Token = ""
|
return;
|
||||||
}, new JsonSerializerOptions {
|
}
|
||||||
WriteIndented = true
|
Log("Discord bot was not started because the ServerID isn't set. Configure it in appsettings.json or disable it altogether.", ConsoleColor.Yellow);
|
||||||
}));
|
return;
|
||||||
|
}
|
||||||
|
Log("Discord bot was not started because the ChannelID isn't set. Configure it in appsettings.json or disable it altogether.", ConsoleColor.Yellow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log("Discord bot was not started because the token wasn't found. Create the file containing the bot token (see instructions src/appsettings.json) or disable it altogether in appsettings.json.", ConsoleColor.Yellow);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log(e.ToString(), ConsoleColor.Red);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
Log(e.ToString(), ConsoleColor.Red);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task RunBot(BotConfig? config) {
|
private static async Task RunBot(string token) {
|
||||||
if (config == null) {
|
BotClient = new DiscordSocketClient(new DiscordSocketConfig {
|
||||||
Log("Bot config didn't deserialize correctly. Discord Integration is not active.", ConsoleColor.Yellow);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
client = new DiscordSocketClient(new DiscordSocketConfig {
|
|
||||||
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMembers | GatewayIntents.MessageContent,
|
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.GuildMembers | GatewayIntents.MessageContent,
|
||||||
AlwaysDownloadUsers = true
|
AlwaysDownloadUsers = true
|
||||||
});
|
});
|
||||||
client.Ready += async () => {
|
BotClient.Ready += async () => {
|
||||||
channel = client.GetGuild(config.Server).GetTextChannel(config.Channel);
|
BotChannel = BotClient.GetGuild(Configuration.DiscordBotConfig!.Server).GetTextChannel(Configuration.DiscordBotConfig!.Channel);
|
||||||
};
|
};
|
||||||
client.MessageReceived += OnDiscordMessage;
|
BotClient.MessageReceived += OnDiscordMessage;
|
||||||
await client.LoginAsync(TokenType.Bot, config.Token);
|
await BotClient.LoginAsync(TokenType.Bot, token);
|
||||||
DiscordManager.config = config;
|
await BotClient.StartAsync();
|
||||||
Log("Loaded bot config from "+token_file+" and bot is running.");
|
Log("Discord bot is running.");
|
||||||
await client.StartAsync();
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
if (!queue.IsEmpty && queue.TryDequeue(out QueuedMessage message)) {
|
if (!MessageQueue.IsEmpty && MessageQueue.TryDequeue(out QueuedMessage message)) {
|
||||||
SocketTextChannel? destination = channel;
|
SocketTextChannel? destination = BotChannel;
|
||||||
if (message.room != null) {
|
if (message.room != null) {
|
||||||
if (message.room.Name == "LIMBO") continue;
|
if (message.room.Name == "LIMBO" || message.room.Name.StartsWith("BannedUserRoom_")) continue; // Don't log stuff in LIMBO or Banned User.
|
||||||
string room = message.room.Name;
|
string room = message.roomName!;
|
||||||
destination = channel.Threads.SingleOrDefault(x => x.Name == room);
|
destination = BotChannel.Threads.SingleOrDefault(x => x.Name == room);
|
||||||
if (destination == null) {
|
if (destination == null) {
|
||||||
// A discord channel can have up to 1000 active threads
|
// A discord channel can have up to 1000 active threads
|
||||||
// and an unlimited number of archived threads.
|
// and an unlimited number of archived threads.
|
||||||
if (message.room.AutoRemove) {
|
if (message.room.AutoRemove) {
|
||||||
// Set temporary rooms (such as user rooms) to shorter archive times.
|
// Set temporary rooms (such as user rooms) to shorter archive times.
|
||||||
destination = await channel.CreateThreadAsync(room, autoArchiveDuration: ThreadArchiveDuration.OneHour);
|
destination = await BotChannel.CreateThreadAsync(room, autoArchiveDuration: ThreadArchiveDuration.OneHour);
|
||||||
} else {
|
} else {
|
||||||
// Persistent rooms should stay around for longer.
|
// Persistent rooms should stay around for longer.
|
||||||
destination = await channel.CreateThreadAsync(room, autoArchiveDuration: ThreadArchiveDuration.OneWeek);
|
destination = await BotChannel.CreateThreadAsync(room, autoArchiveDuration: ThreadArchiveDuration.OneWeek);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await destination.SendMessageAsync(message.msg);
|
await destination.SendMessageAsync(message.msg);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) { // In case something goes wrong for any reason, don't "kill" the bot.
|
||||||
Log(e.ToString(), ConsoleColor.Red);
|
Log(e.ToString(), ConsoleColor.Red);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,65 +85,331 @@ class DiscordManager {
|
|||||||
// For handling commands sent directly in the channel.
|
// For handling commands sent directly in the channel.
|
||||||
private static async Task OnDiscordMessage(SocketMessage arg) {
|
private static async Task OnDiscordMessage(SocketMessage arg) {
|
||||||
// Check that message is from a user in the correct place.
|
// Check that message is from a user in the correct place.
|
||||||
bool roomChannel = arg.Channel is SocketThreadChannel thr && thr.ParentChannel.Id == config.Channel;
|
bool roomChannel = arg.Channel is SocketThreadChannel thr && thr.ParentChannel.Id == BotChannel.Id;
|
||||||
if (!(
|
if (!(
|
||||||
arg.Channel.Id == config.Channel ||
|
roomChannel ||
|
||||||
roomChannel
|
arg.Channel.Id == BotChannel.Id
|
||||||
) ||
|
) ||
|
||||||
arg is not SocketUserMessage socketMessage ||
|
arg is not SocketUserMessage socketMessage ||
|
||||||
socketMessage.Author.IsBot
|
socketMessage.Author.IsBot
|
||||||
) return;
|
) return;
|
||||||
|
// Get the room if it exists.
|
||||||
|
Room? room = (roomChannel && Room.Exists(arg.Channel.Name)) ? Room.Get(arg.Channel.Name) : null;
|
||||||
|
|
||||||
|
// Perms
|
||||||
|
Role role = Role.User;
|
||||||
|
if (arg.Author is IGuildUser user) {
|
||||||
|
if (user.RoleIds.Contains(Configuration.DiscordBotConfig!.RoleAdmin)) {
|
||||||
|
role = Role.Admin;
|
||||||
|
} else if (user.RoleIds.Contains(Configuration.DiscordBotConfig!.RoleModerator)) {
|
||||||
|
role = Role.Admin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string[] message = socketMessage.Content.Split(' ');
|
string[] message = socketMessage.Content.Split(' ');
|
||||||
if (message.Length >= 1) {
|
if (message.Length >= 1) {
|
||||||
string[] everywhereCommands = new string[] {
|
string[] everywhereCommands = new string[] {
|
||||||
// Denotes commands that work identically regardless
|
// Denotes commands that work the same or similarly
|
||||||
// of whether you're in a room thread.
|
// regardless of whether you're in a room thread.
|
||||||
"!tempmute <int: vikingid> [string... reason] - Mute someone until they reconnect."
|
"!freechat <bool: enabled> - Enable/Disable free chat in all rooms.",
|
||||||
|
"!tempmute <int: vikingid> [string... reason] - Mute someone (until they reconnect or go elsewhere).",
|
||||||
|
"!untempmute <int: vikingid> - Revoke a tempmute.",
|
||||||
|
"!mute <int: vikingid> [string... reason] - Mute a vikingid (currently forgotten after server restart due to technical restrictions, but this will probably be fixed in the future).",
|
||||||
|
"!unmute <int: vikingid> - Revoke a mute.",
|
||||||
|
"!ban <int: vikingid> [string... reason] - Ban a vikingid from MMO (currently forgotten after server restart due to technical restrictions, but this will probably be fixed in the future).",
|
||||||
|
"!unban <int: vikingid> - Revoke a ban.",
|
||||||
|
"!pardon <int: vikingid> - Alias for !unban",
|
||||||
|
"!mutelist - Get a list of muted users.",
|
||||||
|
"!banlist - Get a list of banned users.",
|
||||||
};
|
};
|
||||||
bool un_command = message[0].StartsWith("!un");
|
if (message[0] == "!freechat") {
|
||||||
if (message[0] == "!tempmute" || message[0] == "!untempmute") {
|
if (role < Role.Moderator) {
|
||||||
if (message.Length > 1 && int.TryParse(message[1], out int id)) {
|
await arg.Channel.SendMessageAsync("You don't have permission to use this!", messageReference: arg.Reference);
|
||||||
foreach (Room room in Room.AllRooms()) {
|
return;
|
||||||
foreach (Client player in room.Clients) {
|
}
|
||||||
if (player.PlayerData.VikingId == id) {
|
// freechat command
|
||||||
if (player.TempMuted != un_command) {
|
if (message.Length > 1) {
|
||||||
await arg.Channel.SendMessageAsync($"This player is {(un_command ? "not" : "already")} tempmuted.", messageReference: arg.Reference);
|
bool enable;
|
||||||
} else {
|
if (!bool.TryParse(message[1], out enable)) {
|
||||||
player.TempMuted = !un_command;
|
if ((message[1][0] & 0b11011111) == 'Y') {
|
||||||
string reason = string.Empty;
|
enable = true;
|
||||||
string msg = un_command ? "Un-tempmuted" : "Tempmuted";
|
} else if ((message[1][0] & 0b11011111) == (int)'N') {
|
||||||
msg += $" {player.PlayerData.DiplayName}.";
|
enable = false;
|
||||||
if (message.Length >= 2) {
|
} else {
|
||||||
reason = string.Join(' ', message, 2, message.Length - 2);
|
await arg.Channel.SendMessageAsync("Input a boolean or yes/no value!", messageReference: arg.Reference);
|
||||||
msg += " Reason: "+reason;
|
return;
|
||||||
}
|
|
||||||
await arg.Channel.SendMessageAsync(msg, messageReference: arg.Reference);
|
|
||||||
}
|
|
||||||
goto tempmute_search_loop_end;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tempmute_search_loop_end:;// StackOverflow said to do it like this (no labeled break like in Java).
|
Configuration.ServerConfiguration.EnableChat = enable;
|
||||||
|
await arg.Channel.SendMessageAsync($"Chat {(enable?"en":"dis")}abled!", messageReference: arg.Reference);
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("Input a value!", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else if (message[0] == "!tempmute") {
|
||||||
|
if (role < Role.Moderator) {
|
||||||
|
await arg.Channel.SendMessageAsync("You don't have permission to use this!", messageReference: arg.Reference);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// tempmute command
|
||||||
|
if (message.Length > 1 && int.TryParse(message[1], out int id)) {
|
||||||
|
IEnumerable<Client> vikings = FindViking(id, room);
|
||||||
|
if (room != null && !vikings.Any()) vikings = FindViking(id); // Plan B
|
||||||
|
if (vikings.Any()) {
|
||||||
|
bool sent = false;
|
||||||
|
string? reason = null;
|
||||||
|
if (message.Length > 2) {
|
||||||
|
reason = string.Join(' ', message, 2, message.Length - 2);
|
||||||
|
}
|
||||||
|
foreach (Client player in vikings) {
|
||||||
|
if (player.Muted) {
|
||||||
|
if (Client.MutedList.TryGetValue(id, out string? rreason)) {
|
||||||
|
string msg = "This user is already muted.";
|
||||||
|
if (rreason != null && rreason.Length > 0) msg += " Reason: "+rreason;
|
||||||
|
if (!sent) await arg.Channel.SendMessageAsync(msg, messageReference: arg.Reference);
|
||||||
|
} else {
|
||||||
|
if (!sent) await arg.Channel.SendMessageAsync("This user is already tempmuted.", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
player.Muted = true;
|
||||||
|
string msg = $"Tempmuted {player.PlayerData.DiplayName}";
|
||||||
|
if (reason != null) {
|
||||||
|
msg += ". Reason: "+reason;
|
||||||
|
}
|
||||||
|
if (!sent) await arg.Channel.SendMessageAsync(msg, messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
sent = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("User with that ID is not online.", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await arg.Channel.SendMessageAsync("Input a valid vikingid!", messageReference: arg.Reference);
|
await arg.Channel.SendMessageAsync("Input a valid vikingid!", messageReference: arg.Reference);
|
||||||
}
|
}
|
||||||
} else if (roomChannel) {
|
} else if (message[0] == "!untempmute") {
|
||||||
if (message[0] == "!help") {
|
if (role < Role.Moderator) {
|
||||||
await arg.Channel.SendMessageAsync(string.Join(Environment.NewLine, new string[] {
|
await arg.Channel.SendMessageAsync("You don't have permission to use this!", messageReference: arg.Reference);
|
||||||
"### List of commands:",
|
return;
|
||||||
"!help - Prints the help message. That's this message right here!",
|
}
|
||||||
"!list - Lists all players in this room.",
|
// un-tempmute command
|
||||||
"!say <string... message> - Say something in this room."
|
if (message.Length > 1 && int.TryParse(message[1], out int id)) {
|
||||||
}.Concat(everywhereCommands)
|
IEnumerable<Client> vikings = FindViking(id, room);
|
||||||
));
|
if (room != null && !vikings.Any()) vikings = FindViking(id); // Plan B
|
||||||
} else if (message[0] == "!list") {
|
if (vikings.Any()) {
|
||||||
if (!Room.Exists(arg.Channel.Name)) {
|
bool sent = false;
|
||||||
await arg.Channel.SendMessageAsync("This room is currently unloaded.", messageReference: arg.Reference);
|
foreach (Client player in vikings) {
|
||||||
|
if (player.Muted) {
|
||||||
|
if (Client.MutedList.ContainsKey(id)) {
|
||||||
|
if (!sent) await arg.Channel.SendMessageAsync("This user is true muted. To unmute them, use !unmute", messageReference: arg.Reference);
|
||||||
|
} else {
|
||||||
|
player.Muted = false;
|
||||||
|
if (!sent) await arg.Channel.SendMessageAsync($"Un-tempmuted {player.PlayerData.DiplayName}.", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!sent) await arg.Channel.SendMessageAsync("This user is not muted.", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
sent = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Room room = Room.Get(arg.Channel.Name);
|
await arg.Channel.SendMessageAsync("User with that ID is not online.", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("Input a valid vikingid!", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else if (message[0] == "!mute") {
|
||||||
|
if (role < Role.Moderator) {
|
||||||
|
await arg.Channel.SendMessageAsync("You don't have permission to use this!", messageReference: arg.Reference);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// true mute command
|
||||||
|
if (message.Length > 1 && int.TryParse(message[1], out int id)) {
|
||||||
|
string? name = null;
|
||||||
|
IEnumerable<Client> vikings = FindViking(id, room);
|
||||||
|
if (room != null && !vikings.Any()) vikings = FindViking(id); // Plan B
|
||||||
|
foreach (Client player in vikings) {
|
||||||
|
name = player.PlayerData.DiplayName;
|
||||||
|
player.Muted = true;
|
||||||
|
}
|
||||||
|
if (Client.MutedList.TryGetValue(id, out string? rreason)) {
|
||||||
|
string msg = "This user is already muted.";
|
||||||
|
if (rreason != null && rreason.Length > 0) msg += " Reason: "+rreason;
|
||||||
|
await arg.Channel.SendMessageAsync(msg, messageReference: arg.Reference);
|
||||||
|
} else {
|
||||||
|
string? reason = null;
|
||||||
|
if (message.Length > 2) {
|
||||||
|
reason = string.Join(' ', message, 2, message.Length - 2);
|
||||||
|
}
|
||||||
|
Client.MutedList.Add(id, reason);
|
||||||
|
string msg = $"Muted *VikingID {id}*";
|
||||||
|
if (name != null) msg = $"Muted {name}";
|
||||||
|
if (reason != null) {
|
||||||
|
msg += ". Reason: " + reason;
|
||||||
|
}
|
||||||
|
await arg.Channel.SendMessageAsync(msg, messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("Input a valid vikingid!", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else if (message[0] == "!unmute") {
|
||||||
|
if (role < Role.Moderator) {
|
||||||
|
await arg.Channel.SendMessageAsync("You don't have permission to use this!", messageReference: arg.Reference);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// unmute command
|
||||||
|
if (message.Length > 1 && int.TryParse(message[1], out int id)) {
|
||||||
|
string? name = null;
|
||||||
|
IEnumerable<Client> vikings = FindViking(id, room);
|
||||||
|
if (room != null && !vikings.Any()) vikings = FindViking(id); // Plan B
|
||||||
|
if (vikings.Any()) {
|
||||||
|
foreach (Client player in vikings) {
|
||||||
|
if (player.Muted) {
|
||||||
|
name = player.PlayerData.DiplayName;
|
||||||
|
player.Muted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Client.MutedList.Remove(id) || name != null) { // name is only set if there is an unmute
|
||||||
|
if (name != null) {
|
||||||
|
await arg.Channel.SendMessageAsync($"Unmuted {name}.", messageReference: arg.Reference);
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync($"Unmuted *VikingID {id}*.", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("This user is not muted.", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("Input a valid vikingid!", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else if (message[0] == "!ban") {
|
||||||
|
if (role < Role.Moderator) {
|
||||||
|
await arg.Channel.SendMessageAsync("You don't have permission to use this!", messageReference: arg.Reference);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ban command
|
||||||
|
if (message.Length > 1 && int.TryParse(message[1], out int id)) {
|
||||||
|
string? name = null;
|
||||||
|
foreach (Client player in FindViking(id)) {
|
||||||
|
name = player.PlayerData.DiplayName;
|
||||||
|
player.Banned = true;
|
||||||
|
player.SetRoom(player.Room); // This will run the client through the 'if Banned' conditions.
|
||||||
|
}
|
||||||
|
if (Client.BannedList.TryGetValue(id, out string? rreason)) {
|
||||||
|
string msg = "This user is already banned.";
|
||||||
|
if (rreason != null && rreason.Length > 0) msg += " Reason: "+rreason;
|
||||||
|
await arg.Channel.SendMessageAsync(msg, messageReference: arg.Reference);
|
||||||
|
} else {
|
||||||
|
string? reason = null;
|
||||||
|
if (message.Length > 2) {
|
||||||
|
reason = string.Join(' ', message, 2, message.Length - 2);
|
||||||
|
}
|
||||||
|
Client.BannedList.Add(id, reason);
|
||||||
|
string msg = $"Banned *VikingID {id}*";
|
||||||
|
if (name != null) msg = $"Banned {name}";
|
||||||
|
if (reason != null) {
|
||||||
|
msg += ". Reason: " + reason;
|
||||||
|
}
|
||||||
|
await arg.Channel.SendMessageAsync(msg, messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("Input a valid vikingid!", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else if (message[0] == "!unban" || message[0] == "!pardon") {
|
||||||
|
if (role < Role.Moderator) {
|
||||||
|
await arg.Channel.SendMessageAsync("You don't have permission to use this!", messageReference: arg.Reference);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// unban command
|
||||||
|
if (message.Length > 1 && int.TryParse(message[1], out int id)) {
|
||||||
|
if (Client.BannedList.Remove(id)) {
|
||||||
|
string? playername = null;
|
||||||
|
foreach (Room rooom in Room.AllRooms()) {
|
||||||
|
foreach (Client player in rooom.Clients) {
|
||||||
|
if (player.PlayerData.VikingId == id) {
|
||||||
|
playername = player.PlayerData.DiplayName;
|
||||||
|
player.Banned = false;
|
||||||
|
player.SetRoom(player.ReturnRoomOnPardon); // Put the player back.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (playername != null) {
|
||||||
|
await arg.Channel.SendMessageAsync($"Unbanned {playername}.", messageReference: arg.Reference);
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync($"Unbanned *VikingID {id}*.", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("This user is not banned.", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("Input a valid vikingid!", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else if (message[0] == "!mutelist") {
|
||||||
|
// mutelist command
|
||||||
|
if (Client.MutedList.Count > 0) {
|
||||||
|
Dictionary<int, string> clients = new();
|
||||||
|
foreach (Room rooom in Room.AllRooms()) {
|
||||||
|
foreach (Client player in rooom.Clients) {
|
||||||
|
int id = player.PlayerData.VikingId;
|
||||||
|
if (Client.MutedList.ContainsKey(id)) {
|
||||||
|
clients.TryAdd(id, player.PlayerData.DiplayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string msg = "Muted Players:";
|
||||||
|
foreach (KeyValuePair<int, string?> play in Client.MutedList) {
|
||||||
|
if (clients.TryGetValue(play.Key, out string? name)) {
|
||||||
|
msg += $"\n* {SanitizeString(name ?? "")} ||VikingId: {play.Key}||";
|
||||||
|
} else {
|
||||||
|
msg += $"\n* *Offline Viking {play.Key}*";
|
||||||
|
}
|
||||||
|
if (play.Value != null) {
|
||||||
|
msg += $"; Reason: {play.Value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await arg.Channel.SendMessageAsync(msg, messageReference: arg.Reference);
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("No-one is muted.", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else if (message[0] == "!banlist") {
|
||||||
|
// mutelist command
|
||||||
|
if (Client.BannedList.Count > 0) {
|
||||||
|
Dictionary<int, string> clients = new();
|
||||||
|
foreach (Room rooom in Room.AllRooms()) {
|
||||||
|
foreach (Client player in rooom.Clients) {
|
||||||
|
int id = player.PlayerData.VikingId;
|
||||||
|
if (Client.BannedList.ContainsKey(id)) {
|
||||||
|
clients.TryAdd(id, player.PlayerData.DiplayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string msg = "Banned Players:";
|
||||||
|
foreach (KeyValuePair<int, string?> play in Client.BannedList) {
|
||||||
|
if (clients.TryGetValue(play.Key, out string? name)) {
|
||||||
|
msg += $"\n* {SanitizeString(name ?? "")} ||VikingId: {play.Key}||";
|
||||||
|
} else {
|
||||||
|
msg += $"\n* *Offline Viking {play.Key}*";
|
||||||
|
}
|
||||||
|
if (play.Value != null) {
|
||||||
|
msg += $"; Reason: {play.Value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await arg.Channel.SendMessageAsync(msg, messageReference: arg.Reference);
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("No-one is banned.", messageReference: arg.Reference);
|
||||||
|
}
|
||||||
|
} else if (roomChannel) {
|
||||||
|
if (room != null) {
|
||||||
|
if (message[0] == "!help") {
|
||||||
|
// help command (room ver.)
|
||||||
|
await arg.Channel.SendMessageAsync(string.Join(Environment.NewLine+"* ", new string[] {
|
||||||
|
"### List of commands:",
|
||||||
|
"!help - Prints the help message. That's this message right here!",
|
||||||
|
"!list - Lists all players in this room.",
|
||||||
|
"!say <string... message> - Say something in this room."
|
||||||
|
}.Concat(everywhereCommands)
|
||||||
|
));
|
||||||
|
} else if (message[0] == "!list") {
|
||||||
|
// list command (room ver.)
|
||||||
if (room.ClientsCount > 0) {
|
if (room.ClientsCount > 0) {
|
||||||
string msg = "Players in Room:\n";
|
string msg = "Players in Room:";
|
||||||
foreach (Client player in room.Clients) {
|
foreach (Client player in room.Clients) {
|
||||||
string vikingid;
|
string vikingid;
|
||||||
if (player.PlayerData.VikingId != 0) {
|
if (player.PlayerData.VikingId != 0) {
|
||||||
@ -161,19 +417,21 @@ class DiscordManager {
|
|||||||
} else {
|
} else {
|
||||||
vikingid = "||(Not Authenticated!)||";
|
vikingid = "||(Not Authenticated!)||";
|
||||||
}
|
}
|
||||||
msg += $"* {SanitizeString(player.PlayerData.DiplayName)} {vikingid}\n";
|
msg += $"\n* {SanitizeString(player.PlayerData.DiplayName)} {vikingid}";
|
||||||
|
if (player.Muted) {
|
||||||
|
msg += " (:mute: Muted)";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SendMessage(msg, room); // Sent like this in case it's longer than 2000 characters (handled by this method).
|
SendMessage(msg, room); // Sent like this in case it's longer than 2000 characters (handled by this method).
|
||||||
} else {
|
} else {
|
||||||
await arg.Channel.SendMessageAsync("This room is empty.", messageReference: arg.Reference);
|
await arg.Channel.SendMessageAsync("This room is empty.", messageReference: arg.Reference);
|
||||||
}
|
}
|
||||||
}
|
} else if (message[0] == "!say") {
|
||||||
} else if (message[0] == "!say") {
|
// say command
|
||||||
if (message.Length > 1) {
|
if (message.Length > 1) {
|
||||||
// Due to client-based restrictions, this probably only works in SoD and maybe MaM.
|
// Due to client-based restrictions, this probably only works in SoD and maybe MaM.
|
||||||
// Unless we want to create an MMOAvatar instance for the server "user".
|
// Unless we want to create an MMOAvatar instance for the server "user".
|
||||||
if (Room.Exists(arg.Channel.Name)) {
|
// This is because the code for chatlogging is tied to ChatBubble and only ChatBubble.
|
||||||
Room room = Room.Get(arg.Channel.Name);
|
|
||||||
if (room.ClientsCount > 0) {
|
if (room.ClientsCount > 0) {
|
||||||
room.Send(Utils.BuildChatMessage(Guid.Empty.ToString(), string.Join(' ', message, 1, message.Length-1), "Server"));
|
room.Send(Utils.BuildChatMessage(Guid.Empty.ToString(), string.Join(' ', message, 1, message.Length-1), "Server"));
|
||||||
await arg.AddReactionAsync(Emote.Parse(":white_check_mark:"));
|
await arg.AddReactionAsync(Emote.Parse(":white_check_mark:"));
|
||||||
@ -181,14 +439,15 @@ class DiscordManager {
|
|||||||
await arg.Channel.SendMessageAsync("There is no-one in this room to see your message.", messageReference: arg.Reference);
|
await arg.Channel.SendMessageAsync("There is no-one in this room to see your message.", messageReference: arg.Reference);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await arg.Channel.SendMessageAsync("This room is currently unloaded.", messageReference: arg.Reference);
|
await arg.Channel.SendMessageAsync("You didn't include any message content!", messageReference: arg.Reference);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
await arg.Channel.SendMessageAsync("You didn't include any message content!", messageReference: arg.Reference);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
await arg.Channel.SendMessageAsync("This room is currently unloaded.", messageReference: arg.Reference);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (message[0] == "!help") {
|
if (message[0] == "!help") {
|
||||||
|
// help command (global)
|
||||||
await arg.Channel.SendMessageAsync(string.Join(Environment.NewLine, new string[] {
|
await arg.Channel.SendMessageAsync(string.Join(Environment.NewLine, new string[] {
|
||||||
"### List of commands:",
|
"### List of commands:",
|
||||||
"!help - Prints the help message. That's this message right here!",
|
"!help - Prints the help message. That's this message right here!",
|
||||||
@ -199,7 +458,8 @@ class DiscordManager {
|
|||||||
.Append("*Using !help in a room thread yields a different set of commands. Try using !help in a room thread!*")
|
.Append("*Using !help in a room thread yields a different set of commands. Try using !help in a room thread!*")
|
||||||
));
|
));
|
||||||
} else if (message[0] == "!rooms") {
|
} else if (message[0] == "!rooms") {
|
||||||
IEnumerable<RestThreadChannel> threads = await channel.GetActiveThreadsAsync();
|
// rooms command
|
||||||
|
IEnumerable<RestThreadChannel> threads = await BotChannel.GetActiveThreadsAsync();
|
||||||
Console.WriteLine(threads.Count());
|
Console.WriteLine(threads.Count());
|
||||||
ulong uid = socketMessage.Author.Id;
|
ulong uid = socketMessage.Author.Id;
|
||||||
foreach (RestThreadChannel thread in threads) {
|
foreach (RestThreadChannel thread in threads) {
|
||||||
@ -211,10 +471,11 @@ class DiscordManager {
|
|||||||
await ping.DeleteAsync();
|
await ping.DeleteAsync();
|
||||||
}
|
}
|
||||||
} else if (message[0] == "!list") {
|
} else if (message[0] == "!list") {
|
||||||
|
// list command (global)
|
||||||
string msg = string.Empty;
|
string msg = string.Empty;
|
||||||
foreach (Room room in Room.AllRooms()) {
|
foreach (Room rooom in Room.AllRooms()) {
|
||||||
foreach (Client player in room.Clients) {
|
foreach (Client player in rooom.Clients) {
|
||||||
if (msg == string.Empty) msg = "Players Online:\n";
|
if (msg == string.Empty) msg = "Players Online:";
|
||||||
|
|
||||||
string vikingid;
|
string vikingid;
|
||||||
if (player.PlayerData.VikingId != 0) {
|
if (player.PlayerData.VikingId != 0) {
|
||||||
@ -222,7 +483,10 @@ class DiscordManager {
|
|||||||
} else {
|
} else {
|
||||||
vikingid = "||(Not Authenticated!)||";
|
vikingid = "||(Not Authenticated!)||";
|
||||||
}
|
}
|
||||||
msg += $"* {SanitizeString(player.PlayerData.DiplayName)} {vikingid} ({room.Name})\n";
|
msg += $"\n* {SanitizeString(player.PlayerData.DiplayName)} {vikingid} ({rooom.Name})";
|
||||||
|
if (player.Muted) {
|
||||||
|
msg += " (:mute: Muted)";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (msg == string.Empty) {
|
if (msg == string.Empty) {
|
||||||
@ -241,13 +505,14 @@ class DiscordManager {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="msg">Message to send</param>
|
/// <param name="msg">Message to send</param>
|
||||||
/// <param name="room">Room to send message in - can be null</param>
|
/// <param name="room">Room to send message in - can be null</param>
|
||||||
public static void SendMessage(string msg, Room? room=null) {
|
public static void SendMessage(string msg, Room? room=null, string? roomName=null) {
|
||||||
if (client != null) {
|
if (BotClient != null) {
|
||||||
while (msg.Length > 0) {
|
while (msg.Length > 0) {
|
||||||
string piece = msg;
|
string piece = msg;
|
||||||
if (msg.Length > 2000) { // Discord character limit is 2000.
|
if (msg.Length > 2000) { // Discord character limit is 2000.
|
||||||
// Find a good place to break the message, if possible.
|
// Find a good place to break the message, if possible. Otherwise revert to 2000.
|
||||||
int breakIndex = msg.LastIndexOfAny(new char[2] {' ','\n'}, 2000);
|
int breakIndex = msg.LastIndexOf('\n', 2000);
|
||||||
|
if (breakIndex <= 0) breakIndex = msg.LastIndexOf(' ', 2000);
|
||||||
if (breakIndex <= 0) breakIndex = 2000;
|
if (breakIndex <= 0) breakIndex = 2000;
|
||||||
// Split the first part of the message and recycle the rest.
|
// Split the first part of the message and recycle the rest.
|
||||||
piece = msg.Substring(0, breakIndex);
|
piece = msg.Substring(0, breakIndex);
|
||||||
@ -256,9 +521,10 @@ class DiscordManager {
|
|||||||
// Exit the loop when the time comes.
|
// Exit the loop when the time comes.
|
||||||
msg = string.Empty;
|
msg = string.Empty;
|
||||||
}
|
}
|
||||||
queue.Enqueue(new QueuedMessage {
|
MessageQueue.Enqueue(new QueuedMessage {
|
||||||
msg = piece,
|
msg = piece,
|
||||||
room = room
|
room = room,
|
||||||
|
roomName = roomName ?? room?.Name
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,29 +543,41 @@ class DiscordManager {
|
|||||||
/// <param name="player">PlayerData to associate the message with - can be null</param>
|
/// <param name="player">PlayerData to associate the message with - can be null</param>
|
||||||
public static void SendPlayerBasedMessage(string msg, string surround="{1} **-->** {0}", Room? room=null, PlayerData? player=null) {
|
public static void SendPlayerBasedMessage(string msg, string surround="{1} **-->** {0}", Room? room=null, PlayerData? player=null) {
|
||||||
try {
|
try {
|
||||||
if (client != null) {
|
if (BotClient != null) {
|
||||||
// We need to sanitize things first so that people can't
|
// We need to sanitize things first so that people can't
|
||||||
// use Discord's formatting features inside a chat message.
|
// use Discord's formatting features inside a chat message.
|
||||||
msg = SanitizeString(msg);
|
msg = SanitizeString(msg);
|
||||||
string name = SanitizeString(player?.DiplayName ?? "");
|
string name = SanitizeString(player?.DiplayName ?? "");
|
||||||
|
|
||||||
|
// Define for merged rooms.
|
||||||
|
string? roomName = room?.Name;
|
||||||
|
if (roomName != null) {
|
||||||
|
foreach (KeyValuePair<string, string[]> pair in Configuration.DiscordBotConfig!.Merges) {
|
||||||
|
if (pair.Value.Contains(roomName)) {
|
||||||
|
roomName = pair.Key;
|
||||||
|
if (roomName != room!.Name) surround = $"({room!.Name}) "+surround;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
if (player.VikingId != 0) {
|
if (player.VikingId != 0) {
|
||||||
surround = "||VikingId: "+player.VikingId+"||\n"+surround;
|
surround = $"||VikingId: {player.VikingId}||\n{surround}";
|
||||||
} else {
|
} else {
|
||||||
surround = "||(Not Authenticated!)||\n"+surround;
|
surround = $"||(Not Authenticated!)||\n{surround}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the sanitized message, headed with the viking id (if authenticated).
|
// Send the sanitized message, headed with the viking id (if authenticated).
|
||||||
SendMessage(string.Format(surround, msg, name), room);
|
SendMessage(string.Format(surround, msg, name), room, roomName);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log(e.ToString(), ConsoleColor.Red);
|
Log(e.ToString(), ConsoleColor.Red);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string SanitizeString(string str) {
|
private static string SanitizeString(string str) {
|
||||||
string sanitizedStr = "";
|
string sanitizedStr = "";
|
||||||
for (int i=0;i<str.Length;i++) {
|
for (int i=0;i<str.Length;i++) {
|
||||||
char c = str[i];
|
char c = str[i];
|
||||||
@ -327,23 +605,36 @@ class DiscordManager {
|
|||||||
return sanitizedStr;
|
return sanitizedStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BotConfig {
|
/// <summary>
|
||||||
[JsonPropertyName("// Token")]
|
/// Search all rooms for a vikingid, or null if not found.
|
||||||
private string __TokenComment { get; set; } = "This is your bot's token. DO NOT SHARE THIS WITH ANYBODY!!!";
|
/// This method currently returns a list because multiple clients can log in with the same viking at once. This should get fixed properly in the future.
|
||||||
public string Token { get; set; }
|
/// </summary>
|
||||||
|
/// <param name="id">Viking ID</param>
|
||||||
[JsonPropertyName("// Server")]
|
/// <returns>Clients for ID. If there are none then return an empty list.</returns>
|
||||||
private string __ServerComment { get; set; } = "This is the Server ID that the bot connects to.";
|
private static IEnumerable<Client> FindViking(int id, Room? room=null) {
|
||||||
public ulong Server { get; set; }
|
List<Client> clients = new();
|
||||||
|
if (room != null) {
|
||||||
[JsonPropertyName("// Channel")]
|
foreach (Client player in room.Clients) {
|
||||||
private string __ChannelComment { get; set; } = "This is the Channel ID where bot logs things and can run admin commands from.";
|
if (player.PlayerData.VikingId == id) {
|
||||||
public ulong Channel { get; set; }
|
clients.Add(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clients;
|
||||||
|
}
|
||||||
|
foreach (Room rooom in Room.AllRooms()) {
|
||||||
|
foreach (Client player in rooom.Clients) {
|
||||||
|
if (player.PlayerData.VikingId == id) {
|
||||||
|
clients.Add(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clients;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class QueuedMessage {
|
private class QueuedMessage {
|
||||||
public string msg;
|
public string msg;
|
||||||
public Room? room;
|
public Room? room;
|
||||||
|
public string? roomName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Log(string msg, ConsoleColor? color=null) {
|
private static void Log(string msg, ConsoleColor? color=null) {
|
||||||
|
|||||||
@ -68,5 +68,49 @@
|
|||||||
|
|
||||||
"// BypassToken": "Token allowed to connect without authentication",
|
"// BypassToken": "Token allowed to connect without authentication",
|
||||||
"BypassToken": ""
|
"BypassToken": ""
|
||||||
|
},
|
||||||
|
"DiscordBot": {
|
||||||
|
"// DiscordBot": "This allows you to set up a Discord bot that logs all MMO interactions to a Discord channel.",
|
||||||
|
"// DiscordBot_": "You are not required to set this up. The server will run without it. Set the value below to true to disable the warning (and the bot).",
|
||||||
|
"Disabled": false,
|
||||||
|
|
||||||
|
"// DiscordBot+": "In the bot settings, enable the 'Server Members' and 'Message Content' intents. Otherwise, (some/all) commands may not work properly/at all.",
|
||||||
|
|
||||||
|
"// BotToken": "DO NOT PUT YOUR BOT TOKEN HERE! If you push your bot token to GitHub it may endanger your Discord account!",
|
||||||
|
"// BotToken_": "Place your token in a file named 'discord_bot_token' in this directory. Git is configured here to not include that file. DO NOT SHARE YOUR BOT TOKEN!",
|
||||||
|
|
||||||
|
"// How To Get IDs": "The below options require IDs used by Discord's API. You can find these by enabling developer mode (User Settings > Advanced > Developer Mode) and then right clicking the thing and select 'Copy ____ ID' (bottom-most option).",
|
||||||
|
|
||||||
|
"// Server": "This is the ID of the server the bot will operate in. Make sure the bot is in this server.",
|
||||||
|
"Server": 846864309454897214,
|
||||||
|
"// Channel": "This is the ID of the channel that the bot logs activity and accepts moderation commands. Make sure the bot has access to this channel.",
|
||||||
|
"Channel": 1130548490041303070,
|
||||||
|
|
||||||
|
"// RoleModerator": "ID of the role that will be moderator. This role can perform moderative actions such as muting and banning players.",
|
||||||
|
"RoleModerator": 1378174063599550554,
|
||||||
|
|
||||||
|
"// RoleAdmin": "ID of the role that will be admin. This role can perform dangerous actions and moderative actions.",
|
||||||
|
"RoleAdmin": 1378174063599550554,
|
||||||
|
|
||||||
|
"// Merges": "Merge multi-zone rooms into one channel.",
|
||||||
|
"Merges": {
|
||||||
|
"MainStreet": [
|
||||||
|
"MainStreet",
|
||||||
|
"MainStreetFunZone"
|
||||||
|
],
|
||||||
|
"LoungeInt": [
|
||||||
|
"LoungeInt",
|
||||||
|
"LoungeIntUpper"
|
||||||
|
],
|
||||||
|
"DWMadEurope": [
|
||||||
|
"DWMadEuropeItaly",
|
||||||
|
"DWMadEuropeParis",
|
||||||
|
"DWMadEuropeAlps"
|
||||||
|
],
|
||||||
|
"DWMadNYCentralPark": [
|
||||||
|
"DWMadNYCentralPark",
|
||||||
|
"DWMadNYCentralParkFunZone"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,12 @@
|
|||||||
{
|
{
|
||||||
|
"// Usage": { "_": "(This example section isn't read. It can therefore be used for comments/documentation.)",
|
||||||
|
"// Desc": "Actions, emoticons, and canned chat messages are sent as IDs. This file maps these IDs to meaningful representations.",
|
||||||
|
"// Desc2": "If adding/changing any of these emotes, you should update this file accordingly.",
|
||||||
|
"450": { "_ID": "ID of the emote; this should be unique to the emote and must be a 32-bit integer.",
|
||||||
|
"a0000000": "I'm in School of Dragons!", "_Version": "Override for game with ClientVersion greater than or equal to this. For instance, this targets Min_SoD. Representation is in hexadecimal and is NOT case-sensitive. There can be multiple of these.",
|
||||||
|
"0": "I'm in a JumpStart Game!", "_Zero": "There should be something at 0 to handle all lower ClientVersions, lest it show up as unknown."
|
||||||
|
}
|
||||||
|
},
|
||||||
"Emoticons": {
|
"Emoticons": {
|
||||||
"1": {
|
"1": {
|
||||||
"04000001": ":grimacing:",
|
"04000001": ":grimacing:",
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
@ -24,5 +24,8 @@
|
|||||||
<None Update="emote_data.json">
|
<None Update="emote_data.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="discord_bot_token" Condition="Exists('discord_bot_token')">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user