diff --git a/CLI/CliDocker.cs b/CLI/CliDocker.cs index bede28e..a455c7e 100644 --- a/CLI/CliDocker.cs +++ b/CLI/CliDocker.cs @@ -1,4 +1,5 @@ -using BetterSecondBotShared.IO; +using BetterSecondBot.DiscordSupervisor; +using BetterSecondBotShared.IO; using BetterSecondBotShared.Json; using BetterSecondBotShared.logs; using BetterSecondBotShared.Static; @@ -17,7 +18,7 @@ namespace BetterSecondBot LogFormater.Status("-> Getting config from Docker"); JsonConfig Config = MakeJsonConfig.FromENV(); Config = MakeJsonConfig.Http_config_check(Config); - new CliExitOnLogout(Config,true); + new DiscordTTS(Config,true); } } } diff --git a/CLI/CliExitOnLogout.cs b/CLI/CliExitOnLogout.cs index 2aa54ee..40fdc58 100644 --- a/CLI/CliExitOnLogout.cs +++ b/CLI/CliExitOnLogout.cs @@ -14,12 +14,35 @@ namespace BetterSecondBot { public class CliExitOnLogout { - public CliExitOnLogout(JsonConfig Config, bool as_docker) + public bool Exited { get { return exitBot; } } + protected bool exitBot = false; + public SecondBot Bot; + + protected void keep_alive() + { + while (Bot.KillMe == false) + { + string NewStatusMessage = Bot.GetStatus(); + if (NewStatusMessage != Bot.LastStatusMessage) + { + NewStatusMessage = NewStatusMessage.Trim(); + if (NewStatusMessage.Replace(" ", "") != "") + { + Bot.LastStatusMessage = NewStatusMessage; + Bot.Log2File(LogFormater.Status(Bot.LastStatusMessage, false), ConsoleLogLogLevel.Status); + } + } + Thread.Sleep(1000); + } + Bot.GetClient.Network.Logout(); + } + public CliExitOnLogout(JsonConfig Config, bool as_docker, bool use_self_keep_alive) { + exitBot = false; LogFormater.Status("Mode: Cli [Basic]"); if (helpers.botRequired(Config) == true) { - SecondBot Bot = new SecondBot(); + Bot = new SecondBot(); Bot.Setup(Config, AssemblyInfo.GetGitHash()); if(as_docker == true) { @@ -31,25 +54,18 @@ namespace BetterSecondBot SecondBotHttpServer my_http_server = new SecondBotHttpServer(); my_http_server.StartHttpServer(Bot, Config); } - while (Bot.KillMe == false) - { - string NewStatusMessage = Bot.GetStatus(); - if (NewStatusMessage != Bot.LastStatusMessage) - { - NewStatusMessage = NewStatusMessage.Trim(); - if (NewStatusMessage.Replace(" ", "") != "") - { - Bot.LastStatusMessage = NewStatusMessage; - Bot.Log2File(LogFormater.Status(Bot.LastStatusMessage, false), ConsoleLogLogLevel.Status); - } - } - Thread.Sleep(1000); + if(use_self_keep_alive == true) + { + keep_alive(); } - Bot.GetClient.Network.Logout(); } else { LogFormater.Warn("Required settings missing"); + } + if (use_self_keep_alive == true) + { + exitBot = true; } } } diff --git a/CLI/CliHardware.cs b/CLI/CliHardware.cs index 0f88b6f..6b9574f 100644 --- a/CLI/CliHardware.cs +++ b/CLI/CliHardware.cs @@ -5,6 +5,8 @@ using Newtonsoft.Json; using System; using static BetterSecondBot.Program; using BetterSecondBotShared.logs; +using BetterSecondBot.DiscordSupervisor; + namespace BetterSecondBot { public class CliHardware @@ -77,7 +79,7 @@ namespace BetterSecondBot if(ok_to_try_start == true) { Config = MakeJsonConfig.Http_config_check(Config); - new CliExitOnLogout(Config,false); + new DiscordTTS(Config, false); } } } diff --git a/CLI/DiscordSupervisor/ChannelControl.cs b/CLI/DiscordSupervisor/ChannelControl.cs new file mode 100644 index 0000000..28f06f8 --- /dev/null +++ b/CLI/DiscordSupervisor/ChannelControl.cs @@ -0,0 +1,264 @@ +using Discord; +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace BetterSecondBot.DiscordSupervisor +{ + public abstract class DiscordChannelControl : DiscordFunctions + { + + protected async Task SendMessageToChannelAsync(string channelname, string message, string catmapid, UUID sender_id, string TopicType) + { + if (AllowNewOutbound() == true) + { + + channelname = channelname.ToLowerInvariant(); + channelname = String.Concat(channelname.Where(char.IsLetterOrDigit)); + ITextChannel Channel = await FindTextChannel(channelname, catmap[catmapid], sender_id, TopicType).ConfigureAwait(false); + if (Channel != null) + { + return await Channel.SendMessageAsync(message); + } + } + return null; + } + + protected async Task FindTextChannel(string channelname, ICategoryChannel cat, UUID sender, string TopicType) + { + return await FindTextChannel(DiscordServer, channelname, cat, sender, TopicType); + } + + protected async Task FindTextChannel(IGuild onserver, string channelname) + { + return await FindTextChannel(onserver, channelname, null, UUID.Zero, "notUSED", false); + } + + protected async Task FindTextChannel(IGuild onserver,string channelname, ICategoryChannel cat, UUID sender, string TopicType,bool create_channel=true) + { + await WaitForUnlock().ConfigureAwait(false); + channelname = channelname.ToLowerInvariant(); + channelname = String.Concat(channelname.Where(char.IsLetterOrDigit)); + DiscordLock = true; + IReadOnlyCollection found_chans = await onserver.GetTextChannelsAsync(CacheMode.AllowDownload); + ITextChannel result = null; + foreach (ITextChannel ITC in found_chans) + { + if (create_channel == false) + { + if (ITC.Name == channelname) + { + result = ITC; + break; + } + } + else + { + if (ITC.CategoryId == cat.Id) + { + if (ITC.Name == channelname) + { + result = ITC; + break; + } + } + } + } + if (create_channel == true) + { + if (result == null) + { + result = await CreateChannel(onserver, channelname, TopicType, sender.ToString()).ConfigureAwait(false); + } + else + { + await CleanDiscordChannel(result, 24).ConfigureAwait(false); + } + } + DiscordLock = false; + return result; + } + + protected async static Task CleanDiscordChannel(ITextChannel chan) + { + await CleanDiscordChannel(chan, 48).ConfigureAwait(false); + } + protected async static Task CleanDiscordChannel(ITextChannel chan, int HistoryHours) + { + await CleanDiscordChannel(chan, HistoryHours, false).ConfigureAwait(false); + } + + protected async static Task CleanDiscordChannel(ITextChannel chan, int HistoryHours, bool forceempty) + { + long nowunix = DateTimeOffset.Now.ToUnixTimeSeconds(); + IEnumerable messages; + bool empty = false; + while (empty == false) + { + empty = true; + messages = await chan.GetMessagesAsync(50).FlattenAsync(); + List deleteMessages = new List(); + List slowDeleteMessages = new List(); + + foreach (IMessage mess in messages) + { + long messageunix = mess.Timestamp.ToUnixTimeSeconds(); + long dif = nowunix - messageunix; + var hours = (dif / 60)/60; + bool slowdel = false; + + if (hours > (24 * 13)) + { + slowdel = true; + } + if ((hours > HistoryHours) || (forceempty == true)) + { + empty = false; + if (slowdel == false) + { + deleteMessages.Add(mess.Id); + } + else + { + slowDeleteMessages.Add(mess); + } + } + } + if ((deleteMessages.Count > 0) || (slowDeleteMessages.Count > 0)) + { + try + { + if (deleteMessages.Count > 0) + { + await chan.DeleteMessagesAsync(deleteMessages).ConfigureAwait(true); + } + if (slowDeleteMessages.Count > 0) + { + foreach (IMessage slowdelmessage in slowDeleteMessages) + { + await chan.DeleteMessageAsync(slowdelmessage).ConfigureAwait(true); + } + } + } + catch + { + + } + empty = false; + } + } + } + + protected async Task CreateChannel(IGuild onserver, string channelname, string channeltopictype, string sender_id) + { + channelname = channelname.ToLowerInvariant(); + channelname = String.Concat(channelname.Where(char.IsLetterOrDigit)); + string display_topic = "" + channeltopictype + ":" + sender_id + ""; + if (channelname == "interface") + { + display_topic = "Actions -> !clear, !commands, !help command or command"; + } + else if (channelname == "status") + { + display_topic = "Actions -> !clear"; + } + else if (channelname == "localchat") + { + display_topic = "Actions -> !clear"; + } + else if (sender_id == UUID.Zero.ToString()) + { + if (controler.Bot != null) + { + display_topic = "" + myconfig.Basic_BotUserName + " #" + controler.Bot.MyVersion + ""; + } + } + else if (channeltopictype == "Group") + { + display_topic = "" + channeltopictype + ":" + sender_id + ": Actions -> !clear, !notice title|||message"; + } + else if (channeltopictype == "IM") + { + display_topic = "" + channeltopictype + ":" + sender_id + ": Actions -> !clear, !close"; + } + try + { + IGuildChannel channel = await onserver.CreateTextChannelAsync(channelname, X => DiscordGetNewChannelProperies(X, channelname, display_topic, channeltopictype.ToLowerInvariant())); + ITextChannel Txtchan = await onserver.GetTextChannelAsync(channel.Id); + return Txtchan; + } + catch + { + return null; + } + } + + protected void DiscordGetNewChannelProperies(TextChannelProperties C, string channelname, string channeltopic, string catname) + { + if (catname != null) + { + if (catmap.ContainsKey(catname) == true) + { + C.CategoryId = catmap[catname].Id; + } + } + C.Name = channelname; + C.Topic = channeltopic; + } + + protected async Task DiscordRebuildChannels() + { + List required_cats = new List() { "bot", "group", "im" }; + IReadOnlyCollection found_cats = await DiscordServer.GetCategoriesAsync(CacheMode.AllowDownload); + foreach (ICategoryChannel fcat in found_cats) + { + if (required_cats.Contains(fcat.Name) == true) + { + required_cats.Remove(fcat.Name); + catmap.Add(fcat.Name, fcat); + } + } + foreach (string A in required_cats) + { + ICategoryChannel newcat = await DiscordServer.CreateCategoryAsync(A).ConfigureAwait(true); + catmap.Add(A, newcat); + } + List required_channels = new List() { "status", "interface","localchat" }; + IReadOnlyCollection found_chans = await DiscordServer.GetTextChannelsAsync(CacheMode.AllowDownload); + List GroupChannels = new List(); + foreach (ITextChannel chan in found_chans) + { + if (chan.CategoryId == catmap["bot"].Id) + { + required_channels.Remove(chan.Name); + } + else + { + if (chan.CategoryId == catmap["group"].Id) + { + GroupChannels.Add(chan.Name); + } + } + } + foreach (string A in required_channels) + { + _ = await FindTextChannel(A, catmap["bot"], UUID.Zero, "bot").ConfigureAwait(false); + } + + if (HasBot() == true) + { + foreach (Group G in controler.Bot.MyGroups.Values) + { + string groupname = G.Name.ToLowerInvariant(); + groupname = String.Concat(groupname.Where(char.IsLetterOrDigit)); + if (GroupChannels.Contains(groupname) == false) + { + _ = await FindTextChannel(groupname, catmap["group"], G.ID, "Group").ConfigureAwait(false); + } + } + } + } + } +} diff --git a/CLI/DiscordSupervisor/Discord-super.cs b/CLI/DiscordSupervisor/Discord-super.cs new file mode 100644 index 0000000..60adcd5 --- /dev/null +++ b/CLI/DiscordSupervisor/Discord-super.cs @@ -0,0 +1,72 @@ +using BetterSecondBotShared.Json; +using BetterSecondBotShared.logs; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace BetterSecondBot.DiscordSupervisor +{ + public class Discord_super : DiscordEvents + { + protected int warmup = 0; + protected override void keep_alive() + { + while (exit_super == false) + { + string NewStatusMessage = GetStatus(); + if (NewStatusMessage != LastStatusMessage) + { + NewStatusMessage = NewStatusMessage.Trim(); + if (DiscordClientConnected == true) + { + DiscordStatusUpdate(); + } + if (NewStatusMessage.Replace(" ", "") != "") + { + if (LastStatusMessage != NewStatusMessage) + { + LastStatusMessage = NewStatusMessage; + if (HasBasicBot() == true) + { + controler.Bot.Log2File(LogFormater.Status(LastStatusMessage, false), ConsoleLogLogLevel.Status); + } + else + { + Console.WriteLine(LastStatusMessage); + } + } + } + } + Thread.Sleep(1000); + } + } + public Discord_super(JsonConfig configfile, bool as_docker) + { + myconfig = configfile; + running_as_docker = as_docker; + startService(); + } + + protected void startService() + { + if (myconfig.DiscordFull_Enable == true) + { + _ = StartDiscordClientService(); + login_bot(false); + keep_alive(); + } + else + { + login_bot(true); + } + } + + protected override void login_bot(bool self_keep_alive) + { + controler = new CliExitOnLogout(myconfig, running_as_docker, self_keep_alive); + attach_events(); + } + + } +} diff --git a/CLI/DiscordSupervisor/DiscordBot.cs b/CLI/DiscordSupervisor/DiscordBot.cs new file mode 100644 index 0000000..c531f68 --- /dev/null +++ b/CLI/DiscordSupervisor/DiscordBot.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using OpenMetaverse; +using Discord; +using BetterSecondBotShared.Static; +using Discord.WebSocket; +using System.Threading.Tasks; +using System.Linq; + +namespace BetterSecondBot.DiscordSupervisor +{ + public abstract class DiscordBot : DiscordInbound + { + protected virtual async Task StartDiscordClientService() + { + DiscordClient = new DiscordSocketClient(); + DiscordClient.Ready += DiscordClientReady; + DiscordClient.LoggedOut += DiscordClientLoggedOut; + DiscordClient.MessageReceived += DiscordClientMessageReceived; + await DiscordClient.LoginAsync(TokenType.Bot, myconfig.DiscordFull_Token); + _ = DiscordClient.StartAsync(); + } + + protected virtual async Task DiscordClientReady() + { + DiscordServer = DiscordClient.GetGuild(myconfig.DiscordFull_ServerID); + if (DiscordServer != null) + { + DiscordUnixTimeOnine = helpers.UnixTimeNow(); + DiscordClientConnected = true; + await DiscordRebuildChannels(); + } + else + { + DiscordClientConnected = false; + } + return Task.CompletedTask; + } + + } +} diff --git a/CLI/DiscordSupervisor/DiscordEvents.cs b/CLI/DiscordSupervisor/DiscordEvents.cs new file mode 100644 index 0000000..c6751b4 --- /dev/null +++ b/CLI/DiscordSupervisor/DiscordEvents.cs @@ -0,0 +1,20 @@ +using BSB.bottypes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BetterSecondBot.DiscordSupervisor +{ + public class DiscordEvents : DiscordBot + { + protected void attach_events() + { + controler.Bot.MessageEvent += BotChatControlerHandler; + } + + protected void BotChatControlerHandler(object sender, MessageEventArgs e) + { + BotChatControler(e.message, e.sender_name, e.sender_uuid, e.avatar, e.group, e.group_uuid, e.localchat, e.fromme); + } + } +} diff --git a/CLI/DiscordSupervisor/DiscordTTS/DiscordTTS.cs b/CLI/DiscordSupervisor/DiscordTTS/DiscordTTS.cs new file mode 100644 index 0000000..ff6e79e --- /dev/null +++ b/CLI/DiscordSupervisor/DiscordTTS/DiscordTTS.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using OpenMetaverse; +using Discord; +using BetterSecondBotShared.Static; +using Discord.WebSocket; +using System.Threading.Tasks; +using System.Linq; +using BetterSecondBotShared.Json; + +namespace BetterSecondBot.DiscordSupervisor +{ + public class DiscordTTS : Discord_super + { + public DiscordTTS(JsonConfig configfile, bool as_docker) : base(configfile, as_docker) + { + + } + protected ITextChannel TTSchannel; + protected IGuild TTSDiscordServer; + protected override async Task DiscordClientReady() + { + await base.DiscordClientReady(); + if (myconfig.DiscordTTS_Enable == true) + { + TTSDiscordServer = DiscordClient.GetGuild(myconfig.DiscordTTS_server_id); + if (TTSDiscordServer != null) + { + IGuildUser user = await TTSDiscordServer.GetCurrentUserAsync(); + await user.ModifyAsync(x => + { + x.Nickname = myconfig.DiscordTTS_Nickname; + }); + FindTTsChannel(); + } + } + return Task.CompletedTask; + } + + protected async void FindTTsChannel() + { + if (myconfig.DiscordTTS_Enable == true) + { + TTSchannel = await FindTextChannel(TTSDiscordServer, myconfig.DiscordTTS_channel_name); + } + } + + protected override async Task DiscordIMMessage(UUID sender_uuid, string sender_name, string message) + { + if (myconfig.DiscordTTS_Enable == true) + { + if ((helpers.notempty(sender_name) == true) && (helpers.notempty(message) == true)) + { + if (sender_uuid.ToString() == myconfig.DiscordTTS_avatar_uuid) + { + if (TTSchannel == null) + { + FindTTsChannel(); + } + if (TTSchannel != null) + { + await TTSchannel.SendMessageAsync(message, true); + } + } + } + } + } + } +} diff --git a/CLI/DiscordSupervisor/Functions.cs b/CLI/DiscordSupervisor/Functions.cs new file mode 100644 index 0000000..8ff9689 --- /dev/null +++ b/CLI/DiscordSupervisor/Functions.cs @@ -0,0 +1,42 @@ +using BetterSecondBotShared.Static; +using System.Threading; +using System.Threading.Tasks; + +namespace BetterSecondBot.DiscordSupervisor +{ + public abstract class DiscordFunctions : DiscordValues + { + protected Task WaitForUnlock() + { + while (DiscordLock == true) + { + Thread.Sleep(100); + } + return Task.CompletedTask; + } + protected bool AllowNewOutbound() + { + if (DiscordUnixTimeOnine != 0) + { + long dif = helpers.UnixTimeNow() - DiscordUnixTimeOnine; + if (dif > 8) + { + return true; + } + } + return false; + } + protected Task DiscordClientLoggedOut() + { + DiscordClientConnected = false; + return Task.CompletedTask; + } + + protected async Task DiscordKillMePlease() + { + DiscordUnixTimeOnine = 0; + await DiscordClient.LogoutAsync(); + } + + } +} diff --git a/CLI/DiscordSupervisor/Inbound.cs b/CLI/DiscordSupervisor/Inbound.cs new file mode 100644 index 0000000..0a50453 --- /dev/null +++ b/CLI/DiscordSupervisor/Inbound.cs @@ -0,0 +1,325 @@ +using Discord; +using Discord.WebSocket; +using OpenMetaverse; +using System; +using System.Text; +using System.Threading.Tasks; +using System.Linq; +using System.Threading; + +namespace BetterSecondBot.DiscordSupervisor +{ + public abstract class DiscordInbound : DiscordRelay + { + protected async Task InboundLocalchatMessage(IUserMessage message) + { + if (HasBot() == true) + { + IGuildUser user = (IGuildUser)message.Author; + controler.Bot.GetClient.Self.Chat(message.Content, 0, ChatType.Normal); + return await MarkMessage(message, "✅"); + } + return await MarkMessage(message, "❌"); + } + protected async Task InboundInterfaceMessage(IUserMessage message) + { + if (message.Content == "login") + { + if (HasBot() == true) + { + return await MarkMessage(message, "❌"); + } + login_bot(false); + return await MarkMessage(message, "✅").ConfigureAwait(false); + } + else if (message.Content == "logout") + { + if(HasBot() == false) + { + return await MarkMessage(message, "❌"); + } + controler.Bot.KillMePlease(); + return await MarkMessage(message, "✅").ConfigureAwait(false); + } + else if(message.Content == "exit") + { + if (HasBot() == true) + { + controler.Bot.KillMePlease(); + controler.Bot.GetClient.Network.Logout(); + } + exit_super = true; + return await MarkMessage(message, "✅").ConfigureAwait(false); + } + else + { + if (HasBot() == true) + { + StringBuilder output = new StringBuilder(); + if (message.Content.StartsWith("!")) + { + if (message.Content == "!commands") + { + int counter = 0; + string addon = ""; + foreach (string a in controler.Bot.GetCommandsInterface.GetCommandsList()) + { + output.Append(addon); + addon = " , "; + output.Append(a); + counter++; + if (counter == 5) + { + output.Append("\n"); + addon = ""; + counter = 0; + } + } + await SendMessageToChannelAsync("interface", output.ToString(), "bot", UUID.Zero, "bot"); + } + else if (message.Content.StartsWith("!help") == true) + { + string[] bits = message.Content.Split(' '); + if (bits.Length == 2) + { + string command = bits[1].ToLowerInvariant(); + + if (controler.Bot.GetCommandsInterface.GetCommandsList().Contains(command) == true) + { + output.Append("\n=========================\nCommand:"); + output.Append(command); + output.Append("\n"); + output.Append("Workspace: "); + output.Append(controler.Bot.GetCommandsInterface.GetCommandWorkspace(command)); + output.Append("\n"); + output.Append("Min args: "); + output.Append(controler.Bot.GetCommandsInterface.GetCommandArgs(command).ToString()); + output.Append("\n"); + output.Append("Arg types: "); + output.Append(String.Join(",", controler.Bot.GetCommandsInterface.GetCommandArgTypes(command))); + output.Append("\n"); + output.Append("\n"); + output.Append("About: "); + output.Append(controler.Bot.GetCommandsInterface.GetCommandHelp(command)); + await SendMessageToChannelAsync("interface", output.ToString(), "bot", UUID.Zero, "bot"); + } + else + { + output.Append("Unable to find command: "); + output.Append(command); + output.Append(" please use !commands for a full list"); + await SendMessageToChannelAsync("interface", output.ToString(), "bot", UUID.Zero, "bot"); + } + } + else + { + await SendMessageToChannelAsync("interface", "Please format help request as follows: !help commandname", "bot", UUID.Zero, "bot"); + } + } + else + { + output.Append("Unknown request: "); + output.Append(message.Content); + await SendMessageToChannelAsync("interface", output.ToString(), "bot", UUID.Zero, "bot"); + } + } + else + { + string[] bits = message.Content.Split("|||"); + string command = bits[0].ToLowerInvariant(); + bool known_command = controler.Bot.GetCommandsInterface.GetCommandsList().Contains(command); + if (known_command == false) + { + known_command = controler.Bot.getCustomCommands.ContainsKey(command); + } + if (known_command == true) + { + bool process_status = false; + if (bits.Length == 1) + { + bits = new string[] { bits[0], "" }; + } + if (controler.Bot.getCustomCommands.ContainsKey(command) == false) + { + process_status = controler.Bot.GetCommandsInterface.Call(command, bits[1], UUID.Zero); + } + else + { + process_status = true; + Thread t = new Thread(() => controler.Bot.custom_commands_loop(command, bits[1], UUID.Zero)); + t.Start(); + } + if (process_status == true) + { + await MarkMessage(message, "✅").ConfigureAwait(false); + } + else + { + await MarkMessage(message, "❌").ConfigureAwait(false); + } + } + else + { + output.Append("Unable to find command: "); + output.Append(command); + output.Append(" please use !commands for a full list"); + await SendMessageToChannelAsync("interface", output.ToString(), "bot", UUID.Zero, "bot"); + } + } + } + } + return Task.CompletedTask; + } + protected virtual async Task InboundImMessage(ITextChannel Chan, IUserMessage message) + { + if (HasBot() == true) + { + if (message.Content.StartsWith("!close")) + { + await Chan.DeleteAsync(); + } + else if (message.Content.StartsWith("!clear")) + { + await CleanDiscordChannel(Chan, 0, true); + } + else + { + string[] bits = Chan.Topic.Split(':'); + if (bits.Length >= 2) + { + if (bits[0] == "IM") + { + if (UUID.TryParse(bits[1], out UUID avatar) == true) + { + IGuildUser user = (IGuildUser)message.Author; + controler.Bot.GetClient.Self.InstantMessage(avatar, message.Content); + await MarkMessage(message, "✅"); + } + } + } + } + } + return Task.CompletedTask; + } + + protected async Task InboundGroupImMessage(ITextChannel Chan, IUserMessage message) + { + if (HasBot() == true) + { + string[] bits = Chan.Topic.Split(':'); + if (bits.Length >= 2) + { + if (bits[0] == "Group") + { + if (UUID.TryParse(bits[1], out UUID group) == true) + { + if (controler.Bot.MyGroups.ContainsKey(group) == true) + { + if (message.Content == "!clear") + { + await CleanDiscordChannel(Chan, 0, true).ConfigureAwait(false); + } + else if (message.Content.StartsWith("!notice") == true) + { + string Noticetitle = "Notice"; + string Noticemessage; + bits = message.Content.Split("|||", StringSplitOptions.None); + if (bits.Length == 2) + { + Noticetitle = bits[0]; + Noticemessage = bits[1]; + } + else + { + Noticemessage = bits[0]; + } + Noticetitle = Noticetitle.Replace("!notice ", ""); + Noticetitle = Noticetitle.Trim(); + controler.Bot.GetCommandsInterface.Call("GroupNotice", "" + group.ToString() + "~#~" + Noticetitle + "~#~" + Noticemessage, UUID.Zero); + await MarkMessage(message, "✅"); + } + else + { + IGuildUser user = (IGuildUser)message.Author; + controler.Bot.GetCommandsInterface.Call("Groupchat", "" + group.ToString() + "~#~" + message.Content, UUID.Zero); + await MarkMessage(message, "✅"); + } + } + } + } + } + } + else + { + await MarkMessage(message, "❌").ConfigureAwait(false); + } + return Task.CompletedTask; + } + + protected async Task MarkMessage(IUserMessage message,string emote) + { + var Tickmark = new Emoji(emote); + await message.AddReactionAsync(Tickmark, RequestOptions.Default); + return Task.CompletedTask; + } + + + + protected async Task DiscordClientMessageReceived(SocketMessage message) + { + if (AllowNewOutbound() == true) + { + if (message.Author.IsBot == false) + { + ITextChannel Chan = (ITextChannel)message.Channel; + if (message.Content.StartsWith("!clear") == true) + { + await CleanDiscordChannel(Chan, 0, true).ConfigureAwait(false); + } + else if (Chan.CategoryId == catmap["bot"].Id) + { + if (Chan.Name == "interface") + { + await InboundInterfaceMessage((IUserMessage)message).ConfigureAwait(false); + } + else if (Chan.Name == "localchat") + { + await InboundLocalchatMessage((IUserMessage)message).ConfigureAwait(false); + } + else if(Chan.Name == "status") + { + IUserMessage Message = await SendMessageToChannelAsync("interface", message.Content, "bot", UUID.Zero, "bot").ConfigureAwait(true); + if (Message == null) + { + await MarkMessage((IUserMessage)message, "❌").ConfigureAwait(false); + } + else + { + ITextChannel channel = await FindTextChannel(DiscordServer, "interface"); + if (channel != null) + { + await message.DeleteAsync().ConfigureAwait(false); + await InboundInterfaceMessage(Message).ConfigureAwait(false); + } + else + { + await MarkMessage((IUserMessage)message, "❌").ConfigureAwait(false); + } + } + + } + } + else if (Chan.CategoryId == catmap["im"].Id) + { + await InboundImMessage(Chan, (IUserMessage)message).ConfigureAwait(false); + } + else if (Chan.CategoryId == catmap["group"].Id) + { + await InboundGroupImMessage(Chan, (IUserMessage)message).ConfigureAwait(false); + } + } + } + return Task.CompletedTask; + } + } +} diff --git a/CLI/DiscordSupervisor/Status.cs b/CLI/DiscordSupervisor/Status.cs new file mode 100644 index 0000000..be0652c --- /dev/null +++ b/CLI/DiscordSupervisor/Status.cs @@ -0,0 +1,72 @@ +using OpenMetaverse; + +namespace BetterSecondBot.DiscordSupervisor +{ + public abstract class DiscordStatus : DiscordChannelControl + { + protected void DiscordStatusUpdate() + { + if (AllowNewOutbound() == true) + { + if (LastSendDiscordStatus != LastStatusMessage) + { + LastSendDiscordStatus = LastStatusMessage; + _ = SendMessageToChannelAsync("status", LastSendDiscordStatus, "bot", UUID.Zero, "bot"); + } + } + } + string DiscordBotStatus_laststatus = ""; + public string GetStatus() + { + string reply = ""; + if (myconfig.DiscordFull_Enable == true) + { + if (DiscordClientConnected == true) + { + reply = "[Discord-V: connected]"; + } + else + { + reply = "[Discord-V: Disconnected]"; + } + } + if (HasBasicBot() == false) + { + reply = reply + " (Not logged in)"; + } + if (reply != "") + { + reply = " " + reply; + } + if(reply == DiscordBotStatus_laststatus) + { + reply = ""; + } + else + { + DiscordBotStatus_laststatus = reply; + } + if(HasBasicBot() == true) + { + controler.Bot.LastStatusMessage = controler.Bot.GetStatus(); + reply = controler.Bot.LastStatusMessage + reply; + if (controler.Bot.KillMe == true) + { + controler.Bot.GetClient.Network.Logout(); + controler = null; + if (DiscordClientConnected == true) + { + reply = "[Discord-V: connected] (Logging out)"; + } + else + { + reply = "[Discord-V: Disconnected] (Logging out)"; + } + } + + } + return reply; + } + + } +} diff --git a/CLI/DiscordSupervisor/Values.cs b/CLI/DiscordSupervisor/Values.cs new file mode 100644 index 0000000..ce98fba --- /dev/null +++ b/CLI/DiscordSupervisor/Values.cs @@ -0,0 +1,57 @@ +using BetterSecondBotShared.Json; +using Discord; +using Discord.WebSocket; +using System.Collections.Generic; + +namespace BetterSecondBot.DiscordSupervisor +{ + public abstract class DiscordValues + { + protected JsonConfig myconfig; + protected bool exit_super = false; + protected CliExitOnLogout controler; + protected bool running_as_docker = false; + + protected bool discord_group_relay; + protected DiscordSocketClient DiscordClient; + protected bool DiscordClientConnected; + protected IGuild DiscordServer; + protected string LastSendDiscordStatus = ""; + protected long DiscordUnixTimeOnine; + protected bool DiscordLock; + protected string LastStatusMessage = ""; + + protected Dictionary catmap = new Dictionary(); + + protected virtual void login_bot(bool self_keepalive) + { + + } + + protected virtual void keep_alive() + { + + } + + protected bool HasBot() + { + if (HasBasicBot() == true) + { + return controler.Bot.GetClient.Network.Connected; + } + return false; + } + + protected bool HasBasicBot() + { + if (controler != null) + { + if (controler.Bot != null) + { + return true; + } + } + return false; + } + } +} diff --git a/CLI/DiscordSupervisor/relay.cs b/CLI/DiscordSupervisor/relay.cs new file mode 100644 index 0000000..3ef95cc --- /dev/null +++ b/CLI/DiscordSupervisor/relay.cs @@ -0,0 +1,129 @@ +using BetterSecondBotShared.Static; +using Discord.Webhook; +using OpenMetaverse; +using System.Threading.Tasks; + +namespace BetterSecondBot.DiscordSupervisor +{ + public abstract class DiscordRelay : DiscordStatus + { + public void SendIM(UUID avatar, string message) + { + if (HasBot() == true) + { + controler.Bot.SendIM(avatar, message); + if (controler.Bot.AvatarKey2Name.ContainsKey(avatar) == true) + { + if (DiscordClientConnected == true) + { + _ = SendMessageToChannelAsync(controler.Bot.AvatarKey2Name[avatar], "Bot/Script: " + message + "", "im", avatar, "IM").ConfigureAwait(false); + } + } + } + } + protected async Task DiscordGroupMessage(UUID group_uuid, string sender_name, string message) + { + if (HasBot() == true) + { + if ((helpers.notempty(sender_name) == true) && (helpers.notempty(message) == true)) + { + if (controler.Bot.MyGroups.ContainsKey(group_uuid) == true) + { + await SendMessageToChannelAsync(controler.Bot.MyGroups[group_uuid].Name, "" + sender_name + ": " + message + "", "group", group_uuid, "Group").ConfigureAwait(false); + } + } + } + } + protected virtual async Task DiscordIMMessage(UUID sender_uuid, string sender_name, string message) + { + if ((helpers.notempty(sender_name) == true) && (helpers.notempty(message) == true)) + { + await SendMessageToChannelAsync(sender_name, "" + sender_name + ": " + message + "", "im", sender_uuid, "IM").ConfigureAwait(false); + } + } + + protected async void BotChatControler(string message, string sender_name, UUID sender_uuid, bool avatar, bool group, UUID group_uuid, bool localchat, bool fromme) + { + await DiscordBotChatControler(message, sender_name, sender_uuid, avatar, group, group_uuid, localchat, fromme).ConfigureAwait(false); + } + + public string CommandHistoryAdd(string command, string arg, bool status, string WhyFailed) + { + if (HasBot() == true) + { + string output = controler.Bot.CommandHistoryAdd(command, arg, status, WhyFailed); + if (output != "") + { + _ = SendMessageToChannelAsync("interface", output, "bot", UUID.Zero, "bot"); + } + return output; + } + return ""; + } + + protected async Task DiscordBotChatControler(string message, string sender_name, UUID sender_uuid, bool avatar, bool group, UUID group_uuid, bool localchat, bool fromme) + { + if (HasBot() == true) + { + if (fromme == false) + { + if (discord_group_relay == true) + { + // GroupChat Relay only + if (group == true) + { + if (myconfig.DiscordRelay_URL != "") + { + if ((myconfig.DiscordRelay_GroupUUID == group_uuid.ToString()) || (myconfig.DiscordRelay_GroupUUID == "*") || (myconfig.DiscordRelay_GroupUUID == "all")) + { + Group Gr = controler.Bot.MyGroups[group_uuid]; + try + { + using var DWHclient = new DiscordWebhookClient(myconfig.DiscordRelay_URL); + string SendMessage = "(" + Gr.Name + ") @" + sender_name + ":" + message + ""; + await DWHclient.SendMessageAsync(text: SendMessage).ConfigureAwait(false); + DWHclient.Dispose(); + } + catch + { + controler.Bot.Info("Discord web hook has failed - marking as disabled untill next restart!"); + myconfig.DiscordRelay_URL = ""; + } + } + } + } + } + if (DiscordClientConnected == true) + { + if (avatar == true) + { + if (localchat == false) + { + if (group == true) + { + controler.Bot.Debug("DiscordBotChatControler - GroupChat:(" + group_uuid.ToString() + " -> " + sender_name + ":" + message); + await DiscordGroupMessage(group_uuid, sender_name, message).ConfigureAwait(false); + } + else + { + controler.Bot.Debug("DiscordBotChatControler - IM:" + sender_name + ":" + message); + await DiscordIMMessage(sender_uuid, sender_name, message).ConfigureAwait(false); + } + } + else + { + controler.Bot.Debug("DiscordBotChatControler - Localchat:" + sender_name + ":" + message); + await SendMessageToChannelAsync("localchat", "" + sender_name + ":" + message, "bot", UUID.Zero, "bot").ConfigureAwait(false); + } + } + else + { + controler.Bot.Debug("DiscordBotChatControler - Not from an avatar"); + } + } + } + } + return Task.CompletedTask; + } + } +}