Restructured folders and project names, ci should be fixed

This commit is contained in:
Kwoth
2021-06-17 23:40:48 +02:00
parent 7aca29ae8a
commit 91ecf9ca41
788 changed files with 204 additions and 146 deletions

View File

@@ -0,0 +1,149 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Extensions;
using NadekoBot.Modules.Permissions.Common;
using NadekoBot.Modules.Permissions.Services;
using NadekoBot.Core.Services;
using NadekoBot.Modules.Games.Common.ChatterBot;
using System.Net.Http;
using Serilog;
namespace NadekoBot.Modules.Games.Services
{
public class ChatterBotService : IEarlyBehavior, INService
{
private readonly DiscordSocketClient _client;
private readonly PermissionService _perms;
private readonly CommandHandler _cmd;
private readonly IBotStrings _strings;
private readonly IBotCredentials _creds;
private readonly IHttpClientFactory _httpFactory;
public ConcurrentDictionary<ulong, Lazy<IChatterBotSession>> ChatterBotGuilds { get; }
public int Priority => -1;
public ModuleBehaviorType BehaviorType => ModuleBehaviorType.Executor;
public ChatterBotService(DiscordSocketClient client, PermissionService perms,
NadekoBot bot, CommandHandler cmd, IBotStrings strings, IHttpClientFactory factory,
IBotCredentials creds)
{
_client = client;
_perms = perms;
_cmd = cmd;
_strings = strings;
_creds = creds;
_httpFactory = factory;
ChatterBotGuilds = new ConcurrentDictionary<ulong, Lazy<IChatterBotSession>>(
bot.AllGuildConfigs
.Where(gc => gc.CleverbotEnabled)
.ToDictionary(gc => gc.GuildId, gc => new Lazy<IChatterBotSession>(() => CreateSession(), true)));
}
public IChatterBotSession CreateSession()
{
if (!string.IsNullOrWhiteSpace(_creds.CleverbotApiKey))
return new OfficialCleverbotSession(_creds.CleverbotApiKey, _httpFactory);
else
return new CleverbotIOSession("GAh3wUfzDCpDpdpT", "RStKgqn7tcO9blbrv4KbXM8NDlb7H37C", _httpFactory);
}
public string PrepareMessage(IUserMessage msg, out IChatterBotSession cleverbot)
{
var channel = msg.Channel as ITextChannel;
cleverbot = null;
if (channel == null)
return null;
if (!ChatterBotGuilds.TryGetValue(channel.Guild.Id, out Lazy<IChatterBotSession> lazyCleverbot))
return null;
cleverbot = lazyCleverbot.Value;
var nadekoId = _client.CurrentUser.Id;
var normalMention = $"<@{nadekoId}> ";
var nickMention = $"<@!{nadekoId}> ";
string message;
if (msg.Content.StartsWith(normalMention, StringComparison.InvariantCulture))
{
message = msg.Content.Substring(normalMention.Length).Trim();
}
else if (msg.Content.StartsWith(nickMention, StringComparison.InvariantCulture))
{
message = msg.Content.Substring(nickMention.Length).Trim();
}
else
{
return null;
}
return message;
}
public static async Task<bool> TryAsk(IChatterBotSession cleverbot, ITextChannel channel, string message)
{
await channel.TriggerTypingAsync().ConfigureAwait(false);
var response = await cleverbot.Think(message).ConfigureAwait(false);
try
{
await channel.SendConfirmAsync(response.SanitizeMentions(true)).ConfigureAwait(false);
}
catch
{
await channel.SendConfirmAsync(response.SanitizeMentions(true)).ConfigureAwait(false); // try twice :\
}
return true;
}
public async Task<bool> RunBehavior(DiscordSocketClient client, IGuild guild, IUserMessage usrMsg)
{
if (!(guild is SocketGuild sg))
return false;
try
{
var message = PrepareMessage(usrMsg, out IChatterBotSession cbs);
if (message == null || cbs == null)
return false;
var pc = _perms.GetCacheFor(guild.Id);
if (!pc.Permissions.CheckPermissions(usrMsg,
"cleverbot",
"Games".ToLowerInvariant(),
out int index))
{
if (pc.Verbose)
{
var returnMsg = _strings.GetText("trigger", guild.Id, index + 1, Format.Bold(pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), (SocketGuild)guild)));
try { await usrMsg.Channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { }
Log.Information(returnMsg);
}
return true;
}
var cleverbotExecuted = await TryAsk(cbs, (ITextChannel)usrMsg.Channel, message).ConfigureAwait(false);
if (cleverbotExecuted)
{
Log.Information($@"CleverBot Executed
Server: {guild.Name} [{guild.Id}]
Channel: {usrMsg.Channel?.Name} [{usrMsg.Channel?.Id}]
UserId: {usrMsg.Author} [{usrMsg.Author.Id}]
Message: {usrMsg.Content}");
return true;
}
}
catch (Exception ex)
{
Log.Warning(ex,"Error in cleverbot");
}
return false;
}
}
}

View File

@@ -0,0 +1,173 @@
using NadekoBot.Core.Common;
using NadekoBot.Core.Common.Configs;
using NadekoBot.Core.Services;
using NadekoBot.Modules.Games.Common;
namespace NadekoBot.Modules.Games.Services
{
public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
{
public override string Name { get; } = "games";
private const string FilePath = "data/games.yml";
private static TypedKey<GamesConfig> changeKey = new TypedKey<GamesConfig>("config.games.updated");
public GamesConfigService(IConfigSeria serializer, IPubSub pubSub)
: base(FilePath, serializer, pubSub, changeKey)
{
AddParsedProp("trivia.min_win_req", gs => gs.Trivia.MinimumWinReq, int.TryParse,
ConfigPrinters.ToString, val => val > 0);
AddParsedProp("trivia.currency_reward", gs => gs.Trivia.CurrencyReward, long.TryParse,
ConfigPrinters.ToString, val => val >= 0);
}
}
// public sealed class GamesConfigMigrator : IConfigMigrator
// {
// // private readonly DbService _db;
// private readonly GamesConfigService _gss;
//
// public GamesConfigMigrator(DbService dbService, GamesConfigService gss)
// {
// _log = LogManager.GetCurrentClassLogger();
// _db = dbService;
// _gss = gss;
// }
//
// public void EnsureMigrated()
// {
// using var uow = _db.GetDbContext();
// using var conn = uow._context.Database.GetDbConnection();
// MigrateRaceAnimals(conn);
// MigrateEightBall(conn);
// }
//
// private void MigrateTrivia(DbConnection conn)
// {
// using (var checkTableCommand = conn.CreateCommand())
// {
// // make sure table still exists
// checkTableCommand.CommandText =
// "SELECT name FROM sqlite_master WHERE type='table' AND name='BotConfig';";
// var checkReader = checkTableCommand.ExecuteReader();
// if (!checkReader.HasRows)
// return;
// }
//
// Log.Information("Migrating trivia...");
//
// using var com = conn.CreateCommand();
// com.CommandText = $@"SELECT MinimumTriviaWinReq, TriviaCurrencyReward FROM BotConfig";
// using var reader = com.ExecuteReader();
//
// if (!reader.Read())
// return;
//
// _gss.ModifyConfig(ModifyTriviaAction(reader));
//
// Log.Information("Trivia config migrated to data/games.yml");
// }
//
// private static Action<GamesConfig> ModifyTriviaAction(DbDataReader reader)
// => realConfig =>
// {
// var val = (int) (long) reader["MinimumTriviaWinReq"];
// realConfig.Trivia.MinimumWinReq = val <= 0 ? 1 : val;
// realConfig.Trivia.CurrencyReward = (long) reader["TriviaCurrencyReward"];
// };
//
// private void MigrateEightBall(DbConnection conn)
// {
// using (var checkTableCommand = conn.CreateCommand())
// {
// // make sure table still exists
// checkTableCommand.CommandText =
// "SELECT name FROM sqlite_master WHERE type='table' AND name='EightBallResponses';";
// var checkReader = checkTableCommand.ExecuteReader();
// if (!checkReader.HasRows)
// return;
// }
//
// try
// {
// using (var com = conn.CreateCommand())
// {
// com.CommandText = $@"SELECT Text FROM EightBallResponses";
// using var reader = com.ExecuteReader();
//
// if (!reader.Read())
// return;
//
// Log.Information("Migrating eightball...");
// _gss.ModifyConfig(Modify8ballAction(reader));
// }
//
// Log.Information("Eightball migrated to data/games.yml");
// MigrateTrivia(conn);
// }
// finally
// {
//
// using var deleteEightBallCommand = conn.CreateCommand();
// deleteEightBallCommand.CommandText = "DROP TABLE IF EXISTS EightBallResponses";
// deleteEightBallCommand.ExecuteNonQuery();
// }
//
// }
//
// private static Action<GamesConfig> Modify8ballAction(DbDataReader reader)
// => realConfig =>
// {
// realConfig.EightBallResponses.Clear();
// do
// {
// realConfig.EightBallResponses.Add((string) reader["Text"]);
// } while (reader.Read());
// };
//
// private void MigrateRaceAnimals(DbConnection conn)
// {
// using (var checkTableCommand = conn.CreateCommand())
// {
// // make sure table still exists
// checkTableCommand.CommandText =
// "SELECT name FROM sqlite_master WHERE type='table' AND name='RaceAnimals';";
// var checkReader = checkTableCommand.ExecuteReader();
// if (!checkReader.HasRows)
// return;
// }
//
// using (var com = conn.CreateCommand())
// {
// com.CommandText = $@"SELECT Name, Icon FROM RaceAnimals";
// using var reader = com.ExecuteReader();
//
// if (!reader.Read())
// return;
//
// Log.Information("Migrating race animals...");
// _gss.ModifyConfig(ModifyRaceAnimalsAction(reader));
// }
//
// Log.Information("Race animals migrated to data/games.yml");
//
// using var deleteRaceAnimalsCommand = conn.CreateCommand();
// deleteRaceAnimalsCommand.CommandText = "DROP TABLE IF EXISTS RaceAnimals";
// deleteRaceAnimalsCommand.ExecuteNonQuery();
// }
//
// private static Action<GamesConfig> ModifyRaceAnimalsAction(DbDataReader reader)
// => realConfig =>
// {
// realConfig.RaceAnimals.Clear();
//
// do
// {
// realConfig.RaceAnimals.Add(new RaceAnimal()
// {
// Icon = (string) reader["Icon"],
// Name = (string) reader["Name"]
// });
// } while (reader.Read());
// };
// }
}

View File

@@ -0,0 +1,159 @@
using Discord;
using NadekoBot.Common;
using NadekoBot.Core.Services;
using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Common;
using NadekoBot.Modules.Games.Common.Acrophobia;
using NadekoBot.Modules.Games.Common.Hangman;
using NadekoBot.Modules.Games.Common.Nunchi;
using NadekoBot.Modules.Games.Common.Trivia;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Serilog;
namespace NadekoBot.Modules.Games.Services
{
public class GamesService : INService, IUnloadableService
{
private readonly GamesConfigService _gamesConfig;
public ConcurrentDictionary<ulong, GirlRating> GirlRatings { get; } = new ConcurrentDictionary<ulong, GirlRating>();
public IReadOnlyList<string> EightBallResponses => _gamesConfig.Data.EightBallResponses;
private readonly Timer _t;
private readonly IHttpClientFactory _httpFactory;
private readonly IMemoryCache _8BallCache;
private readonly Random _rng;
private const string TypingArticlesPath = "data/typing_articles3.json";
public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
//channelId, game
public ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>();
public ConcurrentDictionary<ulong, Hangman> HangmanGames { get; } = new ConcurrentDictionary<ulong, Hangman>();
public TermPool TermPool { get; } = new TermPool();
public ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>();
public Dictionary<ulong, TicTacToe> TicTacToeGames { get; } = new Dictionary<ulong, TicTacToe>();
public ConcurrentDictionary<ulong, TypingGame> RunningContests { get; } = new ConcurrentDictionary<ulong, TypingGame>();
public ConcurrentDictionary<ulong, NunchiGame> NunchiGames { get; } = new ConcurrentDictionary<ulong, NunchiGame>();
public AsyncLazy<RatingTexts> Ratings { get; }
public class RatingTexts
{
public string Nog { get; set; }
public string Tra { get; set; }
public string Fun { get; set; }
public string Uni { get; set; }
public string Wif { get; set; }
public string Dat { get; set; }
public string Dan { get; set; }
}
public GamesService(GamesConfigService gamesConfig, IHttpClientFactory httpFactory)
{
_gamesConfig = gamesConfig;
_httpFactory = httpFactory;
_8BallCache = new MemoryCache(new MemoryCacheOptions()
{
SizeLimit = 500_000
});
Ratings = new AsyncLazy<RatingTexts>(GetRatingTexts);
_rng = new NadekoRandom();
//girl ratings
_t = new Timer((_) =>
{
GirlRatings.Clear();
}, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1));
try
{
TypingArticles = JsonConvert.DeserializeObject<List<TypingArticle>>(File.ReadAllText(TypingArticlesPath));
}
catch (Exception ex)
{
Log.Warning("Error while loading typing articles {0}", ex.ToString());
TypingArticles = new List<TypingArticle>();
}
}
private async Task<RatingTexts> GetRatingTexts()
{
using (var http = _httpFactory.CreateClient())
{
var text = await http.GetStringAsync("https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/rategirl/rates.json");
return JsonConvert.DeserializeObject<RatingTexts>(text);
}
}
public async Task Unload()
{
_t.Change(Timeout.Infinite, Timeout.Infinite);
AcrophobiaGames.ForEach(x => x.Value.Dispose());
AcrophobiaGames.Clear();
HangmanGames.ForEach(x => x.Value.Dispose());
HangmanGames.Clear();
await Task.WhenAll(RunningTrivias.Select(x => x.Value.StopGame())).ConfigureAwait(false);
RunningTrivias.Clear();
TicTacToeGames.Clear();
await Task.WhenAll(RunningContests.Select(x => x.Value.Stop()))
.ConfigureAwait(false);
RunningContests.Clear();
NunchiGames.ForEach(x => x.Value.Dispose());
NunchiGames.Clear();
}
public void AddTypingArticle(IUser user, string text)
{
TypingArticles.Add(new TypingArticle
{
Source = user.ToString(),
Extra = $"Text added on {DateTime.UtcNow} by {user}.",
Text = text.SanitizeMentions(true),
});
File.WriteAllText(TypingArticlesPath, JsonConvert.SerializeObject(TypingArticles));
}
public string GetEightballResponse(ulong userId, string question)
{
return _8BallCache.GetOrCreate($"8ball:{userId}:{question}", e =>
{
e.Size = question.Length;
e.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
return EightBallResponses[_rng.Next(0, EightBallResponses.Count)];;
});
}
public TypingArticle RemoveTypingArticle(int index)
{
var articles = TypingArticles;
if (index < 0 || index >= articles.Count)
return null;
var removed = articles[index];
TypingArticles.RemoveAt(index);
File.WriteAllText(TypingArticlesPath, JsonConvert.SerializeObject(articles));
return removed;
}
}
}

View File

@@ -0,0 +1,127 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Modules.Games.Common;
using NadekoBot.Core.Services;
using NadekoBot.Core.Services.Database.Models;
using NadekoBot.Common.Collections;
using NadekoBot.Extensions;
using Serilog;
namespace NadekoBot.Modules.Games.Services
{
public class PollService : IEarlyBehavior, INService
{
public ConcurrentDictionary<ulong, PollRunner> ActivePolls { get; } = new ConcurrentDictionary<ulong, PollRunner>();
public int Priority => -5;
public ModuleBehaviorType BehaviorType => ModuleBehaviorType.Executor;
private readonly DbService _db;
private readonly IBotStrings _strs;
public PollService(DbService db, IBotStrings strs)
{
_db = db;
_strs = strs;
using (var uow = db.GetDbContext())
{
ActivePolls = uow.Polls.GetAllPolls()
.ToDictionary(x => x.GuildId, x =>
{
var pr = new PollRunner(db, x);
pr.OnVoted += Pr_OnVoted;
return pr;
})
.ToConcurrent();
}
}
public Poll CreatePoll(ulong guildId, ulong channelId, string input)
{
if (string.IsNullOrWhiteSpace(input) || !input.Contains(";"))
return null;
var data = input.Split(';');
if (data.Length < 3)
return null;
var col = new IndexedCollection<PollAnswer>(data.Skip(1)
.Select(x => new PollAnswer() { Text = x }));
return new Poll()
{
Answers = col,
Question = data[0],
ChannelId = channelId,
GuildId = guildId,
Votes = new System.Collections.Generic.HashSet<PollVote>()
};
}
public bool StartPoll(Poll p)
{
var pr = new PollRunner(_db, p);
if (ActivePolls.TryAdd(p.GuildId, pr))
{
using (var uow = _db.GetDbContext())
{
uow.Polls.Add(p);
uow.SaveChanges();
}
pr.OnVoted += Pr_OnVoted;
return true;
}
return false;
}
public Poll StopPoll(ulong guildId)
{
if (ActivePolls.TryRemove(guildId, out var pr))
{
pr.OnVoted -= Pr_OnVoted;
using var uow = _db.GetDbContext();
uow.Polls.RemovePoll(pr.Poll.Id);
uow.SaveChanges();
return pr.Poll;
}
return null;
}
private async Task Pr_OnVoted(IUserMessage msg, IGuildUser usr)
{
var toDelete = await msg.Channel.SendConfirmAsync(_strs.GetText("poll_voted",
usr.Guild.Id, Format.Bold(usr.ToString())))
.ConfigureAwait(false);
toDelete.DeleteAfter(5);
try { await msg.DeleteAsync().ConfigureAwait(false); } catch { }
}
public async Task<bool> RunBehavior(DiscordSocketClient client, IGuild guild, IUserMessage msg)
{
if (guild == null)
return false;
if (!ActivePolls.TryGetValue(guild.Id, out var poll))
return false;
try
{
return await poll.TryVote(msg).ConfigureAwait(false);
}
catch (Exception ex)
{
Log.Warning(ex, "Error voting");
}
return false;
}
}
}