private void OnCtrlEvent(CtrlType type) { BotHelper.LogDebugToConsole("[CORE] ProcessExit triggered."); _ctrlEventHandler.Invoke(type); _mre.Set(); }
private void LoadConfig() { BotHelper.LogDebugToConsole($"[GUILDBOT] _configPath: {_configPath}"); BotHelper.LogDebugToConsole($"[GUILDBOT] Exists(_configPath): {File.Exists(_configPath)}"); if (File.Exists(_configPath)) { BotHelper.LogDebugToConsole($"[GUILDBOT] Full Path: {Path.GetFullPath(_configPath)}"); } using (var stream = File.Open(_configPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) _config = XDocument.Load(stream); if (_config.Root == null || _config.Root.Name != "guildbotconfig") { throw new ApplicationException("Configuration file not found."); } XElement prefixEl = _config.Root.Element("coreprefix"); if (prefixEl == null || String.IsNullOrEmpty(prefixEl.Value)) { throw new ApplicationException("Configuration file is missing prefix data."); } _prefix = prefixEl.Value; }
// Private methods section private async Task MainAsync() { try { BotHelper.LogDebugToConsole(String.Join(Environment.NewLine, $"[CORE] Current working dir :{Directory.GetCurrentDirectory()}", $"Current AppDomain BaseDirectory :{AppDomain.CurrentDomain.BaseDirectory}")); _guildBotsDict = new Dictionary <ulong, GuildBot>(); _client = new DiscordSocketClient(); _client.Log += Log; await LoadCoreConfig().ConfigureAwait(false); await _client.LoginAsync(TokenType.Bot, _token).ConfigureAwait(false); await _client.StartAsync().ConfigureAwait(false); _client.Ready += () => Task.Run(ConfigGuilds); AppDomain.CurrentDomain.ProcessExit += (o, e) => OnCtrlEvent(CtrlType.CTRL_C_EVENT); _client.MessageReceived += MessageReceived; _mre.WaitOne(); } catch (Exception ex) { ex.LogToConsole("[FATAL][CORE] Unrecoverable error occurred."); throw; } }
private async Task ConfigGuilds() { // move this to the config later const ulong serviceGuildId = 910122574527234058UL; var guilds = _client.Guilds.Where(g => g.Id != serviceGuildId); var serviceGuild = _client.Guilds.First(g => g.Id == serviceGuildId); foreach (var guild in guilds) { ulong guildID = guild.Id; string guildPath = Path.Combine(_appBaseDirectory, guildID.ToString()); string guildConfigPath = Path.Combine(guildPath, "config.xml"); await Task.Run(() => { BotHelper.LogDebugToConsole($"[CORE] guildPath:{guildPath}"); if (!Directory.Exists(guildPath)) { BotHelper.LogDebugToConsole($"[CORE] Creating guildPath:{guildPath}"); Directory.CreateDirectory(guildPath); } if (!File.Exists(guildConfigPath)) { BotHelper.LogDebugToConsole($"[CORE] Creating guild config:{guildConfigPath}"); GetDefaultGuildConfig().Save(guildConfigPath); } }).ConfigureAwait(false); GuildBot newBot = new GuildBot(guild, serviceGuild, guildPath, _client.CurrentUser.Id); _ctrlEventHandler += newBot.OnCtrlEvent; _guildBotsDict.TryAdd(guildID, newBot); } }
private async Task LoadConfig() { await Task.Run(() => { BotHelper.LogDebugToConsole($"[CORE] Load Config Path :{Path.Combine(_appBaseDirectory, _configFileName)}"); if (File.Exists(Path.Combine(_appBaseDirectory, _configFileName))) { using (var stream = File.Open(Path.Combine(_appBaseDirectory, _configFileName), FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) _config = XDocument.Load(stream); } else { throw new ApplicationException("Configuration file not found!"); } }).ConfigureAwait(false); if (_config.Root.Name != "botconfig") { throw new ApplicationException("Configuration file not found."); } XElement tokenEl = _config.Root.Element("token"); if (tokenEl == null || String.IsNullOrEmpty(tokenEl.Value)) { throw new ApplicationException("Configuration file is missing token data."); } _token = tokenEl.Value; XElement prefixEl = _config.Root.Element("coreprefix"); if (prefixEl == null || String.IsNullOrEmpty(prefixEl.Value)) { throw new ApplicationException("Configuration file is missing prefix data."); } _prefix = prefixEl.Value; }
private async Task EmojiStatsCmd(SocketMessage msg) { string logPrefix = "[GUILDBOT][EMOJISTATS]"; try { var sw = new System.Diagnostics.Stopwatch(); sw.Start(); var emotes = _guild.Emotes; var emojiStrings = emotes.Select(e => $"{e}"); var channels = _guild.TextChannels; var timestamp = DateTime.UtcNow; var timeDiff = TimeSpan.FromDays(60.0d); var timeBoundary = timestamp - timeDiff; var emoteTextDict = new Dictionary <string, int>(); var emoteReacDict = new Dictionary <IEmote, int>(); var emoteTextDictLock = new object(); var emoteReacDictLock = new object(); foreach (var e in emojiStrings) { emoteTextDict.TryAdd(e, 0); } foreach (var e in emotes) { emoteReacDict.TryAdd(e, 0); } foreach (var textChannel in channels) { IMessage lastSeenMsg = null; int q = 0; BotHelper.LogDebugToConsole($"{logPrefix} Started processing of #{textChannel.Name}."); var asyncMessages = textChannel.GetMessagesAsync(); var msgList = new List <IMessage>(); try { await foreach (var messages in asyncMessages) { foreach (var message in messages) { lastSeenMsg = message; if (message.Timestamp < timeBoundary) { break; } msgList.Add(message); } if (lastSeenMsg == null || lastSeenMsg.Timestamp < timeBoundary) { break; } } } catch (Exception ex) { ex.LogToConsole($"{logPrefix} Fetch Messages failed : #{textChannel.Name}"); continue; } BotHelper.LogDebugToConsole($"{logPrefix} Downloaded {msgList.Count} messages from #{textChannel.Name}."); while (lastSeenMsg.Timestamp > timeBoundary) { int listCount; try { listCount = msgList.Count; await foreach (var messages in textChannel.GetMessagesAsync(lastSeenMsg, Direction.Before)) { foreach (var message in messages) { lastSeenMsg = message; if (message.Timestamp < timeBoundary) { break; } msgList.Add(message); } if (lastSeenMsg.Timestamp < timeBoundary) { break; } } } catch (Exception ex) { ex.LogToConsole($"{logPrefix} Fetch Messages failed : #{textChannel.Name}"); continue; } BotHelper.LogDebugToConsole($"{logPrefix} Downloaded {msgList.Count} messages from #{textChannel.Name}."); if (msgList.Count == listCount) { break; // boundary is earlier than channel creation } } int threads = Environment.ProcessorCount < 3 ? 1 : Environment.ProcessorCount - 1; int count = msgList.Count / threads; int remainder = msgList.Count % threads; List <List <IMessage> > splitList = new List <List <IMessage> >(); int j = 0; for (int i = 0; i < threads; i++) { int curCount = (i < remainder) ? count + 1 : count; splitList.Add(msgList.GetRange(j, curCount)); j += curCount; } List <Task> tasklist = new List <Task>(); foreach (var messagesPerCore in splitList) { tasklist.Add(Task.Run(() => { try { foreach (var message in messagesPerCore) { if (message.Author.IsBot || message.Author.IsWebhook) { continue; } if (message is IUserMessage um) { foreach (var reaction in um.Reactions) { if (emoteReacDict.ContainsKey(reaction.Key)) { lock (emoteReacDictLock) emoteReacDict[reaction.Key] += reaction.Value.ReactionCount; } } } if (String.IsNullOrEmpty(message.Content)) { continue; // skip empty messages } foreach (var emote in emoteTextDict.Keys.ToList()) { int n = (message.Content.Length - message.Content.Replace(emote, String.Empty, StringComparison.InvariantCulture).Length) / emote.Length; lock (emoteTextDictLock) emoteTextDict[emote] += n; } Interlocked.Increment(ref q); } } catch (Exception ex) { ex.LogToConsole($"{logPrefix} Processing Messages failed : #{textChannel.Name}"); } })); } await Task.WhenAll(tasklist).ConfigureAwait(false); BotHelper.LogDebugToConsole($"{logPrefix} Processed messages from #{textChannel.Name}."); } var emoteTotalDict = new Dictionary <Discord.IEmote, int>(emoteReacDict); foreach (var e in emoteTotalDict.Keys.ToList()) { emoteTotalDict[e] += emoteTextDict[$"{e}"]; } var outputs = new List <string>(); var str = $"**=== Emoji Usage Stats ===**\n\n`{"Emoji",4} \u2502 {"Total",10} \u2502 {"Reactions",10} \u2502 {"Text",10}`\n"; foreach (var e in emoteTotalDict.OrderByDescending(kvp => kvp.Value)) { str += $"{e.Key,4}` \u2502 {e.Value,10} \u2502 {emoteReacDict[e.Key],10} \u2502 {emoteTextDict[$"{e.Key}"],10}`{Environment.NewLine}"; if (str.Length > 1900) { outputs.Add(str); str = String.Empty; } } outputs.Add(str); sw.Stop(); foreach (var output in outputs) { await msg.Channel.SendMessageAsyncSafe(output).ConfigureAwait(false); await Task.Delay(500).ConfigureAwait(false); } await msg.Channel.SendMessageAsyncSafe($"Total time spent: {sw.Elapsed}").ConfigureAwait(false); } catch (Exception ex) { ex.LogToConsole($"{logPrefix}. Command failed:"); throw; } }
protected override async Task SearchCommand(SocketMessage msg) { string cmdPrefix = $"[{LogPref}][SEARCH-FILES]"; BotHelper.LogDebugToConsole($"{cmdPrefix} Entering."); var regexStr = msg.Content.Replace($"{_prefix}search", String.Empty, StringComparison.InvariantCulture).TrimStart(); try { if (regexStr.Length == 0) { return; } if (regexStr.Length > 200) // unreasonably long regex { await msg.Channel.SendMessageAsyncSafe($"Занадто довгий запит: `{regexStr}` {EmojiCodes.WaitWhat}").ConfigureAwait(false); return; } // init regex before starting waiter, as regex can be malformed Regex regex; try { regex = new Regex(regexStr, RegexOptions.CultureInvariant | RegexOptions.Multiline | RegexOptions.IgnoreCase); } catch (ArgumentException ex) { ex.LogToConsole($"[WARNING]{cmdPrefix} Malformed regex \"{regexStr}\"."); await msg.Channel.SendMessageAsyncSafe($"Що це за хуйня?? {EmojiCodes.Tomas} `{regexStr}`").ConfigureAwait(false); return; } using ManualResetEventSlim mres = new ManualResetEventSlim(false); var waitTask = Task.Run(async() => { BotHelper.LogDebugToConsole($"{cmdPrefix} Entering waiter."); var waitMsg = await msg.Channel.SendMessageAsyncSafe($"Шукаю `{regexStr}` в цитатах {EmojiCodes.Bumagi}").ConfigureAwait(false); if (waitMsg == null) { return; } string content = String.Empty; while (mres != null && !mres.IsSet) { await Task.Delay(5000).ConfigureAwait(false); content = (await msg.Channel.GetMessageAsync(waitMsg.Id).ConfigureAwait(false)).Content; await waitMsg.ModifyAsync(p => { p.Content = $"{content}{EmojiCodes.Bumagi}"; }).ConfigureAwait(false); } content = (await msg.Channel.GetMessageAsync(waitMsg.Id).ConfigureAwait(false)).Content; await waitMsg.ModifyAsync(p => { p.Content = $"{content}{Environment.NewLine}Пошук закінчено. {EmojiCodes.Picardia}"; }).ConfigureAwait(false); }); var itemsDict = new Dictionary <string, XElement>(); var matchesDict = new Dictionary <string, string>(); RPItemDictGenerator(GetRootByKey(String.Empty), String.Empty, itemsDict); BotHelper.LogDebugToConsole($"{cmdPrefix} {itemsDict.Count} entries in dictionary."); foreach (var kvp in itemsDict) { string citation; string citationFileName = kvp.Value.Value; try { citation = await File.ReadAllTextAsync(Path.Combine(_guildPath, ModuleFolder, citationFileName)).ConfigureAwait(false); } catch (Exception ex) { ex.LogToConsole($"[WARNING]{cmdPrefix} Citation loading failed: {kvp.Key} - {kvp.Value.Value}"); continue; } if (citation.Length > _msgLengthLimit || !regex.IsMatch(citation)) { continue; } matchesDict.Add(kvp.Key, citation); } BotHelper.LogDebugToConsole($"{cmdPrefix} {matchesDict.Count} matches."); mres.Set(); await Task.WhenAll(waitTask).ConfigureAwait(false); if (matchesDict.Count == 0) { await msg.Channel.SendMessageAsyncSafe($"Не знайдено **цитат** за запитом `{regexStr}` {EmojiCodes.Pepe}").ConfigureAwait(false); return; } await msg.Channel.SendMessageAsyncSafe($"Знайдено {matchesDict.Count} **цитат** за запитом `{regexStr}` {EmojiCodes.DankPepe}").ConfigureAwait(false); string output = $"Результати пошуку **цитат** за запитом: `{regexStr}`:{Environment.NewLine}"; var dm = await msg.Author.GetOrCreateDMChannelAsync().ConfigureAwait(false); await dm.GenerateAndSendOutputMessages(output, matchesDict, kvp => $"`{kvp.Key}`{Environment.NewLine}{kvp.Value}{Environment.NewLine}", s => s, s => s).ConfigureAwait(false); } catch (Exception ex) { ex.LogToConsole($"[WARNING]{cmdPrefix} Search failed for regex \"{regexStr}\"."); throw; } finally { BotHelper.LogDebugToConsole($"{cmdPrefix} Passing control to keys search."); await base.SearchCommand(msg).ConfigureAwait(false); } }
protected virtual async Task SearchCommand(SocketMessage msg) { string cmdPrefix = $"[{LogPref}][SEARCH]"; BotHelper.LogDebugToConsole($"{cmdPrefix} Entered search."); var regexStr = msg.Content.Replace($"{_prefix}search", String.Empty, StringComparison.InvariantCulture).TrimStart(); try { if (regexStr.Length == 0) { return; } if (regexStr.Length > 200) // unreasonably long regex { await msg.Channel.SendMessageAsyncSafe($"Занадто довгий запит: `{regexStr}` {EmojiCodes.WaitWhat}").ConfigureAwait(false); return; } Regex regex; try { regex = new Regex(regexStr, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); } catch (ArgumentException ex) { ex.LogToConsole($"[WARNING]{cmdPrefix} Malformed regex \"{regexStr}\"."); await msg.Channel.SendMessageAsyncSafe($"Що це за хуйня?? {EmojiCodes.Tomas} `{regexStr}`").ConfigureAwait(false); return; } var matchedKeysList = RPKeyListGenerator(GetRootByKey(String.Empty), String.Empty, true).Where(key => regex.IsMatch(key)).ToList(); BotHelper.LogDebugToConsole($"{cmdPrefix} Number of matches: {matchedKeysList.Count} for regex \"{regexStr}\"."); if (!matchedKeysList.Any()) { await msg.Channel.SendMessageAsyncSafe($"Не знайдено **ключів** за запитом `{regexStr}` {EmojiCodes.Pepe}").ConfigureAwait(false); return; } await msg.Channel.SendMessageAsyncSafe($"Знайдено {matchedKeysList.Count} **ключів** за запитом `{regexStr}` {EmojiCodes.DankPepe}").ConfigureAwait(false); string output = $"Результати пошуку **ключів** за запитом `{regexStr}`:{Environment.NewLine}```{Environment.NewLine}"; var dm = await msg.Author.GetOrCreateDMChannelAsync().ConfigureAwait(false); await dm.GenerateAndSendOutputMessages(output, matchedKeysList, s => $"{s}{Environment.NewLine}", s => $"```{Environment.NewLine}{s}", s => $"{s}```").ConfigureAwait(false); } catch (Exception ex) { ex.LogToConsole($"[WARNING]{cmdPrefix} Command failed. Query=\"{regexStr}\""); throw; } }