Games done, admin half done
This commit is contained in:
parent
3797fbd439
commit
355425bf80
@ -7,82 +7,54 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Attributes;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using static NadekoBot.Modules.Permissions.Permissions;
|
||||
using System.Collections.Concurrent;
|
||||
using NLog;
|
||||
using NadekoBot.Modules.Permissions;
|
||||
using NadekoBot.Services.Administration;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
[NadekoModule("Administration", ".")]
|
||||
public partial class Administration : NadekoTopLevelModule
|
||||
{
|
||||
private static readonly ConcurrentHashSet<ulong> deleteMessagesOnCommand;
|
||||
private IGuild _nadekoSupportServer;
|
||||
private readonly DbHandler _db;
|
||||
private readonly AdministrationService _admin;
|
||||
|
||||
private new static readonly Logger _log;
|
||||
|
||||
static Administration()
|
||||
public Administration(DbHandler db, AdministrationService admin)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler;
|
||||
|
||||
deleteMessagesOnCommand = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
|
||||
|
||||
_db = db;
|
||||
_admin = admin;
|
||||
}
|
||||
|
||||
private static Task DelMsgOnCmd_Handler(IUserMessage msg, CommandInfo cmd)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var channel = msg.Channel as SocketTextChannel;
|
||||
if (channel == null)
|
||||
return;
|
||||
if (deleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune" && cmd.Name != "pick")
|
||||
await msg.DeleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex, "Delmsgoncmd errored...");
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
////todo permissions
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//[RequireContext(ContextType.Guild)]
|
||||
//[RequireUserPermission(GuildPermission.Administrator)]
|
||||
//public async Task ResetPermissions()
|
||||
//{
|
||||
// using (var uow = _db.UnitOfWork)
|
||||
// {
|
||||
// var config = uow.GuildConfigs.GcWithPermissionsv2For(Context.Guild.Id);
|
||||
// config.Permissions = Permissionv2.GetDefaultPermlist;
|
||||
// await uow.CompleteAsync();
|
||||
// UpdateCache(config);
|
||||
// }
|
||||
// await ReplyConfirmLocalized("perms_reset").ConfigureAwait(false);
|
||||
//}
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//[OwnerOnly]
|
||||
//public async Task ResetGlobalPermissions()
|
||||
//{
|
||||
// using (var uow = _db.UnitOfWork)
|
||||
// {
|
||||
// var gc = uow.BotConfig.GetOrCreate();
|
||||
// gc.BlockedCommands.Clear();
|
||||
// gc.BlockedModules.Clear();
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.Administrator)]
|
||||
public async Task ResetPermissions()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.GuildConfigs.GcWithPermissionsv2For(Context.Guild.Id);
|
||||
config.Permissions = Permissionv2.GetDefaultPermlist;
|
||||
await uow.CompleteAsync();
|
||||
UpdateCache(config);
|
||||
}
|
||||
await ReplyConfirmLocalized("perms_reset").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ResetGlobalPermissions()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var gc = uow.BotConfig.GetOrCreate();
|
||||
gc.BlockedCommands.Clear();
|
||||
gc.BlockedModules.Clear();
|
||||
|
||||
GlobalPermissionCommands.BlockedCommands.Clear();
|
||||
GlobalPermissionCommands.BlockedModules.Clear();
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
await ReplyConfirmLocalized("global_perms_reset").ConfigureAwait(false);
|
||||
}
|
||||
// GlobalPermissionCommands.BlockedCommands.Clear();
|
||||
// GlobalPermissionCommands.BlockedModules.Clear();
|
||||
// await uow.CompleteAsync();
|
||||
// }
|
||||
// await ReplyConfirmLocalized("global_perms_reset").ConfigureAwait(false);
|
||||
//}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@ -91,7 +63,7 @@ namespace NadekoBot.Modules.Administration
|
||||
public async Task Delmsgoncmd()
|
||||
{
|
||||
bool enabled;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
enabled = conf.DeleteMessageOnCommand = !conf.DeleteMessageOnCommand;
|
||||
@ -100,12 +72,12 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
if (enabled)
|
||||
{
|
||||
deleteMessagesOnCommand.Add(Context.Guild.Id);
|
||||
_admin.DeleteMessagesOnCommand.Add(Context.Guild.Id);
|
||||
await ReplyConfirmLocalized("delmsg_on").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
deleteMessagesOnCommand.TryRemove(Context.Guild.Id);
|
||||
_admin.DeleteMessagesOnCommand.TryRemove(Context.Guild.Id);
|
||||
await ReplyConfirmLocalized("delmsg_off").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@ -454,19 +426,18 @@ namespace NadekoBot.Modules.Administration
|
||||
await Context.Channel.SendMessageAsync(send).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private IGuild _nadekoSupportServer;
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Donators()
|
||||
{
|
||||
IEnumerable<Donator> donatorsOrdered;
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
donatorsOrdered = uow.Donators.GetDonatorsOrdered();
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync(GetText("donators"), string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false);
|
||||
|
||||
_nadekoSupportServer = _nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850);
|
||||
_nadekoSupportServer = _nadekoSupportServer ?? (await Context.Client.GetGuildAsync(117523346618318850));
|
||||
|
||||
var patreonRole = _nadekoSupportServer?.GetRole(236667642088259585);
|
||||
if (patreonRole == null)
|
||||
@ -482,7 +453,7 @@ namespace NadekoBot.Modules.Administration
|
||||
public async Task Donadd(IUser donator, int amount)
|
||||
{
|
||||
Donator don;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
don = uow.Donators.AddOrUpdateDonator(donator.Id, donator.Username, amount);
|
||||
await uow.CompleteAsync();
|
||||
|
@ -4,6 +4,7 @@ using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Administration;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
@ -19,232 +20,13 @@ namespace NadekoBot.Modules.Administration
|
||||
[Group]
|
||||
public class MuteCommands : NadekoSubmodule
|
||||
{
|
||||
private static ConcurrentDictionary<ulong, string> GuildMuteRoles { get; }
|
||||
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; }
|
||||
private static ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, Timer>> UnmuteTimers { get; }
|
||||
= new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, Timer>>();
|
||||
private readonly MuteService _service;
|
||||
private readonly DbHandler _db;
|
||||
|
||||
public static event Action<IGuildUser, MuteType> UserMuted = delegate { };
|
||||
public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { };
|
||||
|
||||
|
||||
public enum MuteType {
|
||||
Voice,
|
||||
Chat,
|
||||
All
|
||||
}
|
||||
private static readonly OverwritePermissions denyOverwrite = new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny);
|
||||
|
||||
private static readonly new Logger _log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
static MuteCommands()
|
||||
public MuteCommands(MuteService service, DbHandler db)
|
||||
{
|
||||
var configs = NadekoBot.AllGuildConfigs;
|
||||
GuildMuteRoles = new ConcurrentDictionary<ulong, string>(configs
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
|
||||
.ToDictionary(c => c.GuildId, c => c.MuteRoleName));
|
||||
|
||||
MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(configs.ToDictionary(
|
||||
k => k.GuildId,
|
||||
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
|
||||
));
|
||||
|
||||
foreach (var conf in configs)
|
||||
{
|
||||
foreach (var x in conf.UnmuteTimers)
|
||||
{
|
||||
TimeSpan after;
|
||||
if (x.UnmuteAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
|
||||
{
|
||||
after = TimeSpan.FromMinutes(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
after = x.UnmuteAt - DateTime.UtcNow;
|
||||
}
|
||||
StartUnmuteTimer(conf.GuildId, x.UserId, after);
|
||||
}
|
||||
}
|
||||
|
||||
NadekoBot.Client.UserJoined += Client_UserJoined;
|
||||
}
|
||||
|
||||
private static async Task Client_UserJoined(IGuildUser usr)
|
||||
{
|
||||
try
|
||||
{
|
||||
MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted);
|
||||
|
||||
if (muted == null || !muted.Contains(usr.Id))
|
||||
return;
|
||||
await MuteUser(usr).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.GetCurrentClassLogger().Warn(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static async Task MuteUser(IGuildUser usr)
|
||||
{
|
||||
await usr.ModifyAsync(x => x.Mute = true).ConfigureAwait(false);
|
||||
var muteRole = await GetMuteRole(usr.Guild);
|
||||
if (!usr.RoleIds.Contains(muteRole.Id))
|
||||
await usr.AddRoleAsync(muteRole).ConfigureAwait(false);
|
||||
StopUnmuteTimer(usr.GuildId, usr.Id);
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.GuildConfigs.For(usr.Guild.Id,
|
||||
set => set.Include(gc => gc.MutedUsers)
|
||||
.Include(gc => gc.UnmuteTimers));
|
||||
config.MutedUsers.Add(new MutedUserId()
|
||||
{
|
||||
UserId = usr.Id
|
||||
});
|
||||
if (MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted))
|
||||
muted.Add(usr.Id);
|
||||
|
||||
config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id);
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
UserMuted(usr, MuteType.All);
|
||||
}
|
||||
|
||||
public static async Task UnmuteUser(IGuildUser usr)
|
||||
{
|
||||
StopUnmuteTimer(usr.GuildId, usr.Id);
|
||||
try { await usr.ModifyAsync(x => x.Mute = false).ConfigureAwait(false); } catch { }
|
||||
try { await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild)).ConfigureAwait(false); } catch { /*ignore*/ }
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.GuildConfigs.For(usr.Guild.Id, set => set.Include(gc => gc.MutedUsers)
|
||||
.Include(gc => gc.UnmuteTimers));
|
||||
config.MutedUsers.Remove(new MutedUserId()
|
||||
{
|
||||
UserId = usr.Id
|
||||
});
|
||||
if (MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted))
|
||||
muted.TryRemove(usr.Id);
|
||||
|
||||
config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id);
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
UserUnmuted(usr, MuteType.All);
|
||||
}
|
||||
|
||||
public static async Task<IRole>GetMuteRole(IGuild guild)
|
||||
{
|
||||
const string defaultMuteRoleName = "nadeko-mute";
|
||||
|
||||
var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName);
|
||||
|
||||
var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
|
||||
if (muteRole == null)
|
||||
{
|
||||
|
||||
//if it doesn't exist, create it
|
||||
try { muteRole = await guild.CreateRoleAsync(muteRoleName, GuildPermissions.None).ConfigureAwait(false); }
|
||||
catch
|
||||
{
|
||||
//if creations fails, maybe the name is not correct, find default one, if doesn't work, create default one
|
||||
muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName) ??
|
||||
await guild.CreateRoleAsync(defaultMuteRoleName, GuildPermissions.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var toOverwrite in (await guild.GetTextChannelsAsync()))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!toOverwrite.PermissionOverwrites.Select(x => x.Permissions).Contains(denyOverwrite))
|
||||
{
|
||||
await toOverwrite.AddPermissionOverwriteAsync(muteRole, denyOverwrite)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
return muteRole;
|
||||
}
|
||||
|
||||
public static async Task TimedMute(IGuildUser user, TimeSpan after)
|
||||
{
|
||||
await MuteUser(user).ConfigureAwait(false); // mute the user. This will also remove any previous unmute timers
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.GuildConfigs.For(user.GuildId, set => set.Include(x => x.UnmuteTimers));
|
||||
config.UnmuteTimers.Add(new UnmuteTimer()
|
||||
{
|
||||
UserId = user.Id,
|
||||
UnmuteAt = DateTime.UtcNow + after,
|
||||
}); // add teh unmute timer to the database
|
||||
uow.Complete();
|
||||
}
|
||||
StartUnmuteTimer(user.GuildId, user.Id, after); // start the timer
|
||||
}
|
||||
|
||||
public static void StartUnmuteTimer(ulong guildId, ulong userId, TimeSpan after)
|
||||
{
|
||||
//load the unmute timers for this guild
|
||||
var userUnmuteTimers = UnmuteTimers.GetOrAdd(guildId, new ConcurrentDictionary<ulong, Timer>());
|
||||
|
||||
//unmute timer to be added
|
||||
var toAdd = new Timer(async _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var guild = NadekoBot.Client.GetGuild(guildId); // load the guild
|
||||
if (guild == null)
|
||||
{
|
||||
RemoveUnmuteTimerFromDb(guildId, userId);
|
||||
return; // if guild can't be found, just remove the timer from db
|
||||
}
|
||||
// unmute the user, this will also remove the timer from the db
|
||||
await UnmuteUser(guild.GetUser(userId)).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RemoveUnmuteTimerFromDb(guildId, userId); // if unmute errored, just remove unmute from db
|
||||
Administration._log.Warn("Couldn't unmute user {0} in guild {1}", userId, guildId);
|
||||
Administration._log.Warn(ex);
|
||||
}
|
||||
}, null, after, Timeout.InfiniteTimeSpan);
|
||||
|
||||
//add it, or stop the old one and add this one
|
||||
userUnmuteTimers.AddOrUpdate(userId, (key) => toAdd, (key, old) =>
|
||||
{
|
||||
old.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
return toAdd;
|
||||
});
|
||||
}
|
||||
|
||||
public static void StopUnmuteTimer(ulong guildId, ulong userId)
|
||||
{
|
||||
if (!UnmuteTimers.TryGetValue(guildId, out ConcurrentDictionary<ulong, Timer> userUnmuteTimers)) return;
|
||||
|
||||
if (userUnmuteTimers.TryRemove(userId, out Timer removed))
|
||||
{
|
||||
removed.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveUnmuteTimerFromDb(ulong guildId, ulong userId)
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.GuildConfigs.For(guildId, set => set.Include(x => x.UnmuteTimers));
|
||||
config.UnmuteTimers.RemoveWhere(x => x.UserId == userId);
|
||||
uow.Complete();
|
||||
}
|
||||
_service = service;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -257,11 +39,11 @@ namespace NadekoBot.Modules.Administration
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return;
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
config.MuteRoleName = name;
|
||||
GuildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name);
|
||||
_service.GuildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await ReplyConfirmLocalized("mute_role_set").ConfigureAwait(false);
|
||||
@ -283,7 +65,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
try
|
||||
{
|
||||
await MuteUser(user).ConfigureAwait(false);
|
||||
await _service.MuteUser(user).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_muted", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
@ -303,7 +85,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
try
|
||||
{
|
||||
await TimedMute(user, TimeSpan.FromMinutes(minutes)).ConfigureAwait(false);
|
||||
await _service.TimedMute(user, TimeSpan.FromMinutes(minutes)).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_muted_time", Format.Bold(user.ToString()), minutes).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -321,7 +103,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
try
|
||||
{
|
||||
await UnmuteUser(user).ConfigureAwait(false);
|
||||
await _service.UnmuteUser(user).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_unmuted", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
@ -337,8 +119,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
try
|
||||
{
|
||||
await user.AddRoleAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
||||
UserMuted(user, MuteType.Chat);
|
||||
await _service.MuteUser(user, MuteType.Chat).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_chat_mute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
@ -354,8 +135,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
try
|
||||
{
|
||||
await user.RemoveRoleAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
||||
UserUnmuted(user, MuteType.Chat);
|
||||
await _service.UnmuteUser(user, MuteType.Chat).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_chat_unmute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
@ -367,12 +147,11 @@ namespace NadekoBot.Modules.Administration
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.MuteMembers)]
|
||||
public async Task VoiceMute(IGuildUser user)
|
||||
public async Task VoiceMute([Remainder] IGuildUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
|
||||
UserMuted(user, MuteType.Voice);
|
||||
await _service.MuteUser(user, MuteType.Voice).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_voice_mute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
@ -384,12 +163,11 @@ namespace NadekoBot.Modules.Administration
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.MuteMembers)]
|
||||
public async Task VoiceUnmute(IGuildUser user)
|
||||
public async Task VoiceUnmute([Remainder] IGuildUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
|
||||
UserUnmuted(user, MuteType.Voice);
|
||||
await _service.UnmuteUser(user, MuteType.Voice).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_voice_unmute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
|
@ -15,6 +15,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using System.Collections.Immutable;
|
||||
using NadekoBot.DataStructures;
|
||||
using NLog;
|
||||
using NadekoBot.Services.Administration;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
@ -23,34 +24,20 @@ namespace NadekoBot.Modules.Administration
|
||||
[Group]
|
||||
public class SelfCommands : NadekoSubmodule
|
||||
{
|
||||
private static volatile bool _forwardDMs;
|
||||
private static volatile bool _forwardDMsToAllOwners;
|
||||
private readonly DbHandler _db;
|
||||
|
||||
private static readonly object _locker = new object();
|
||||
private readonly SelfService _service;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly IImagesService _images;
|
||||
|
||||
private new static readonly Logger _log;
|
||||
|
||||
static SelfCommands()
|
||||
public SelfCommands(DbHandler db, SelfService service, DiscordShardedClient client,
|
||||
IImagesService images)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
_forwardDMs = config.ForwardMessages;
|
||||
_forwardDMsToAllOwners = config.ForwardToAllOwners;
|
||||
}
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
while (!NadekoBot.Ready)
|
||||
await Task.Delay(1000);
|
||||
|
||||
foreach (var cmd in NadekoBot.BotConfig.StartupCommands)
|
||||
{
|
||||
await NadekoBot.CommandHandler.ExecuteExternal(cmd.GuildId, cmd.ChannelId, cmd.CommandText);
|
||||
await Task.Delay(400).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
_db = db;
|
||||
_service = service;
|
||||
_client = client;
|
||||
_images = images;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -69,7 +56,7 @@ namespace NadekoBot.Modules.Administration
|
||||
VoiceChannelId = guser.VoiceChannel?.Id,
|
||||
VoiceChannelName = guser.VoiceChannel?.Name,
|
||||
};
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
uow.BotConfig
|
||||
.GetOrCreate(set => set.Include(x => x.StartupCommands))
|
||||
@ -96,7 +83,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
page -= 1;
|
||||
IEnumerable<StartupCommand> scmds;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
scmds = uow.BotConfig
|
||||
.GetOrCreate(set => set.Include(x => x.StartupCommands))
|
||||
@ -148,7 +135,7 @@ namespace NadekoBot.Modules.Administration
|
||||
public async Task StartupCommandRemove([Remainder] string cmdText)
|
||||
{
|
||||
StartupCommand cmd;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var cmds = uow.BotConfig
|
||||
.GetOrCreate(set => set.Include(x => x.StartupCommands))
|
||||
@ -174,7 +161,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task StartupCommandsClear()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
uow.BotConfig
|
||||
.GetOrCreate(set => set.Include(x => x.StartupCommands))
|
||||
@ -190,14 +177,13 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task ForwardMessages()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
lock (_locker)
|
||||
_forwardDMs = config.ForwardMessages = !config.ForwardMessages;
|
||||
_service.ForwardDMs = config.ForwardMessages = !config.ForwardMessages;
|
||||
uow.Complete();
|
||||
}
|
||||
if (_forwardDMs)
|
||||
if (_service.ForwardDMs)
|
||||
await ReplyConfirmLocalized("fwdm_start").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalized("fwdm_stop").ConfigureAwait(false);
|
||||
@ -207,83 +193,83 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task ForwardToAll()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
lock (_locker)
|
||||
_forwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners;
|
||||
_service.ForwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners;
|
||||
uow.Complete();
|
||||
}
|
||||
if (_forwardDMsToAllOwners)
|
||||
if (_service.ForwardDMsToAllOwners)
|
||||
await ReplyConfirmLocalized("fwall_start").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalized("fwall_stop").ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
public static async Task HandleDmForwarding(IUserMessage msg, ImmutableArray<AsyncLazy<IDMChannel>> ownerChannels)
|
||||
{
|
||||
if (_forwardDMs && ownerChannels.Length > 0)
|
||||
{
|
||||
var title = GetTextStatic("dm_from",
|
||||
NadekoBot.Localization.DefaultCultureInfo,
|
||||
typeof(Administration).Name.ToLowerInvariant()) +
|
||||
$" [{msg.Author}]({msg.Author.Id})";
|
||||
//todo dm forwarding
|
||||
//public async Task HandleDmForwarding(IUserMessage msg, ImmutableArray<AsyncLazy<IDMChannel>> ownerChannels)
|
||||
//{
|
||||
// if (_service.ForwardDMs && ownerChannels.Length > 0)
|
||||
// {
|
||||
// var title = _strings.GetText("dm_from",
|
||||
// NadekoBot.Localization.DefaultCultureInfo,
|
||||
// typeof(Administration).Name.ToLowerInvariant()) +
|
||||
// $" [{msg.Author}]({msg.Author.Id})";
|
||||
|
||||
var attachamentsTxt = GetTextStatic("attachments",
|
||||
NadekoBot.Localization.DefaultCultureInfo,
|
||||
typeof(Administration).Name.ToLowerInvariant());
|
||||
// var attachamentsTxt = GetTextStatic("attachments",
|
||||
// NadekoBot.Localization.DefaultCultureInfo,
|
||||
// typeof(Administration).Name.ToLowerInvariant());
|
||||
|
||||
var toSend = msg.Content;
|
||||
// var toSend = msg.Content;
|
||||
|
||||
if (msg.Attachments.Count > 0)
|
||||
{
|
||||
toSend += $"\n\n{Format.Code(attachamentsTxt)}:\n" +
|
||||
string.Join("\n", msg.Attachments.Select(a => a.ProxyUrl));
|
||||
}
|
||||
// if (msg.Attachments.Count > 0)
|
||||
// {
|
||||
// toSend += $"\n\n{Format.Code(attachamentsTxt)}:\n" +
|
||||
// string.Join("\n", msg.Attachments.Select(a => a.ProxyUrl));
|
||||
// }
|
||||
|
||||
if (_forwardDMsToAllOwners)
|
||||
{
|
||||
var allOwnerChannels = await Task.WhenAll(ownerChannels
|
||||
.Select(x => x.Value))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (var ownerCh in allOwnerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id))
|
||||
{
|
||||
try
|
||||
{
|
||||
await ownerCh.SendConfirmAsync(title, toSend).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_log.Warn("Can't contact owner with id {0}", ownerCh.Recipient.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstOwnerChannel = await ownerChannels[0];
|
||||
if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await firstOwnerChannel.SendConfirmAsync(title, toSend).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (_service.ForwardDMsToAllOwners)
|
||||
// {
|
||||
// var allOwnerChannels = await Task.WhenAll(ownerChannels
|
||||
// .Select(x => x.Value))
|
||||
// .ConfigureAwait(false);
|
||||
|
||||
// foreach (var ownerCh in allOwnerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id))
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// await ownerCh.SendConfirmAsync(title, toSend).ConfigureAwait(false);
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// _log.Warn("Can't contact owner with id {0}", ownerCh.Recipient.Id);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// var firstOwnerChannel = await ownerChannels[0];
|
||||
// if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// await firstOwnerChannel.SendConfirmAsync(title, toSend).ConfigureAwait(false);
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// // ignored
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ConnectShard(int shardid)
|
||||
{
|
||||
var shard = NadekoBot.Client.GetShard(shardid);
|
||||
var shard = _client.GetShard(shardid);
|
||||
|
||||
if (shard == null)
|
||||
{
|
||||
@ -307,15 +293,15 @@ namespace NadekoBot.Modules.Administration
|
||||
public async Task Leave([Remainder] string guildStr)
|
||||
{
|
||||
guildStr = guildStr.Trim().ToUpperInvariant();
|
||||
var server = NadekoBot.Client.Guilds.FirstOrDefault(g => g.Id.ToString() == guildStr) ??
|
||||
NadekoBot.Client.Guilds.FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == guildStr);
|
||||
var server = _client.Guilds.FirstOrDefault(g => g.Id.ToString() == guildStr) ??
|
||||
_client.Guilds.FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == guildStr);
|
||||
|
||||
if (server == null)
|
||||
{
|
||||
await ReplyErrorLocalized("no_server").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (server.OwnerId != NadekoBot.Client.CurrentUser.Id)
|
||||
if (server.OwnerId != _client.CurrentUser.Id)
|
||||
{
|
||||
await server.LeaveAsync().ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("left_server", Format.Bold(server.Name)).ConfigureAwait(false);
|
||||
@ -351,7 +337,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (string.IsNullOrWhiteSpace(newName))
|
||||
return;
|
||||
|
||||
await NadekoBot.Client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false);
|
||||
await _client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalized("bot_name", Format.Bold(newName)).ConfigureAwait(false);
|
||||
}
|
||||
@ -360,7 +346,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task SetStatus([Remainder] SettableUserStatus status)
|
||||
{
|
||||
await NadekoBot.Client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false);
|
||||
await _client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalized("bot_status", Format.Bold(status.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
@ -380,7 +366,7 @@ namespace NadekoBot.Modules.Administration
|
||||
await sr.CopyToAsync(imgStream);
|
||||
imgStream.Position = 0;
|
||||
|
||||
await NadekoBot.Client.CurrentUser.ModifyAsync(u => u.Avatar = new Image(imgStream)).ConfigureAwait(false);
|
||||
await _client.CurrentUser.ModifyAsync(u => u.Avatar = new Image(imgStream)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -391,7 +377,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task SetGame([Remainder] string game = null)
|
||||
{
|
||||
await NadekoBot.Client.SetGameAsync(game).ConfigureAwait(false);
|
||||
await _client.SetGameAsync(game).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalized("set_game").ConfigureAwait(false);
|
||||
}
|
||||
@ -402,7 +388,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
name = name ?? "";
|
||||
|
||||
await NadekoBot.Client.SetGameAsync(name, url, StreamType.Twitch).ConfigureAwait(false);
|
||||
await _client.SetGameAsync(name, url, StreamType.Twitch).ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalized("set_stream").ConfigureAwait(false);
|
||||
}
|
||||
@ -418,7 +404,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (ids.Length != 2)
|
||||
return;
|
||||
var sid = ulong.Parse(ids[0]);
|
||||
var server = NadekoBot.Client.Guilds.FirstOrDefault(s => s.Id == sid);
|
||||
var server = _client.Guilds.FirstOrDefault(s => s.Id == sid);
|
||||
|
||||
if (server == null)
|
||||
return;
|
||||
@ -455,7 +441,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task Announce([Remainder] string message)
|
||||
{
|
||||
var channels = NadekoBot.Client.Guilds.Select(g => g.DefaultChannel).ToArray();
|
||||
var channels = _client.Guilds.Select(g => g.DefaultChannel).ToArray();
|
||||
if (channels == null)
|
||||
return;
|
||||
await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync(GetText("message_from_bo", Context.User.ToString()), message)))
|
||||
@ -468,7 +454,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task ReloadImages()
|
||||
{
|
||||
var time = await NadekoBot.Images.Reload().ConfigureAwait(false);
|
||||
var time = _images.Reload();
|
||||
await ReplyConfirmLocalized("images_loaded", time.TotalSeconds.ToString("F3")).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,9 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.DataStructures;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
@ -18,158 +13,13 @@ namespace NadekoBot.Modules.Administration
|
||||
[Group]
|
||||
public class ServerGreetCommands : NadekoSubmodule
|
||||
{
|
||||
//make this to a field in the guildconfig table
|
||||
private readonly GreetSettingsService _greetService;
|
||||
private readonly DbHandler _db;
|
||||
|
||||
private new static Logger _log { get; }
|
||||
private static readonly GreetSettingsService greetService;
|
||||
|
||||
static ServerGreetCommands()
|
||||
public ServerGreetCommands(GreetSettingsService greetService, DbHandler db)
|
||||
{
|
||||
NadekoBot.Client.UserJoined += UserJoined;
|
||||
NadekoBot.Client.UserLeft += UserLeft;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
//todo di
|
||||
greetService = NadekoBot.GreetSettingsService;
|
||||
}
|
||||
|
||||
private static Task UserLeft(IGuildUser user)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var conf = greetService.GetOrAddSettingsForGuild(user.GuildId);
|
||||
|
||||
if (!conf.SendChannelByeMessage) return;
|
||||
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
|
||||
|
||||
if (channel == null) //maybe warn the server owner that the channel is missing
|
||||
return;
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.ChannelByeMessageText, out embedData))
|
||||
{
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
if (conf.AutoDeleteByeMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
return;
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
||||
if (conf.AutoDeleteByeMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static Task UserJoined(IGuildUser user)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var conf = greetService.GetOrAddSettingsForGuild(user.GuildId);
|
||||
|
||||
if (conf.SendChannelGreetMessage)
|
||||
{
|
||||
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId);
|
||||
if (channel != null) //maybe warn the server owner that the channel is missing
|
||||
{
|
||||
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData))
|
||||
{
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
if (conf.AutoDeleteGreetMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
||||
if (conf.AutoDeleteGreetMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.SendDmGreetMessage)
|
||||
{
|
||||
var channel = await user.CreateDMChannelAsync();
|
||||
|
||||
if (channel != null)
|
||||
{
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData))
|
||||
{
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
try
|
||||
{
|
||||
await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.DmGreetMessageText.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
await channel.SendConfirmAsync(msg).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
_greetService = greetService;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -180,7 +30,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (timer < 0 || timer > 600)
|
||||
return;
|
||||
|
||||
await ServerGreetCommands.SetGreetDel(Context.Guild.Id, timer).ConfigureAwait(false);
|
||||
await _greetService.SetGreetDel(Context.Guild.Id, timer).ConfigureAwait(false);
|
||||
|
||||
if (timer > 0)
|
||||
await ReplyConfirmLocalized("greetdel_on", timer).ConfigureAwait(false);
|
||||
@ -188,29 +38,12 @@ namespace NadekoBot.Modules.Administration
|
||||
await ReplyConfirmLocalized("greetdel_off").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task SetGreetDel(ulong id, int timer)
|
||||
{
|
||||
if (timer < 0 || timer > 600)
|
||||
return;
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var conf = uow.GuildConfigs.For(id, set => set);
|
||||
conf.AutoDeleteGreetMessagesTimer = timer;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
greetService.GuildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd);
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageGuild)]
|
||||
public async Task Greet()
|
||||
{
|
||||
var enabled = await greetService.SetGreet(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
|
||||
var enabled = await _greetService.SetGreet(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
|
||||
|
||||
if (enabled)
|
||||
await ReplyConfirmLocalized("greet_on").ConfigureAwait(false);
|
||||
@ -226,7 +59,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
string channelGreetMessageText;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
channelGreetMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelGreetMessageText;
|
||||
}
|
||||
@ -234,7 +67,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
}
|
||||
|
||||
var sendGreetEnabled = greetService.SetGreetMessage(Context.Guild.Id, ref text);
|
||||
var sendGreetEnabled = _greetService.SetGreetMessage(Context.Guild.Id, ref text);
|
||||
|
||||
await ReplyConfirmLocalized("greetmsg_new").ConfigureAwait(false);
|
||||
if (!sendGreetEnabled)
|
||||
@ -246,7 +79,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireUserPermission(GuildPermission.ManageGuild)]
|
||||
public async Task GreetDm()
|
||||
{
|
||||
var enabled = await greetService.SetGreetDm(Context.Guild.Id).ConfigureAwait(false);
|
||||
var enabled = await _greetService.SetGreetDm(Context.Guild.Id).ConfigureAwait(false);
|
||||
|
||||
if (enabled)
|
||||
await ReplyConfirmLocalized("greetdm_on").ConfigureAwait(false);
|
||||
@ -262,7 +95,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
GuildConfig config;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
config = uow.GuildConfigs.For(Context.Guild.Id);
|
||||
}
|
||||
@ -270,7 +103,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
}
|
||||
|
||||
var sendGreetEnabled = greetService.SetGreetDmMessage(Context.Guild.Id, ref text);
|
||||
var sendGreetEnabled = _greetService.SetGreetDmMessage(Context.Guild.Id, ref text);
|
||||
|
||||
await ReplyConfirmLocalized("greetdmmsg_new").ConfigureAwait(false);
|
||||
if (!sendGreetEnabled)
|
||||
@ -282,7 +115,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireUserPermission(GuildPermission.ManageGuild)]
|
||||
public async Task Bye()
|
||||
{
|
||||
var enabled = await greetService.SetBye(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
|
||||
var enabled = await _greetService.SetBye(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
|
||||
|
||||
if (enabled)
|
||||
await ReplyConfirmLocalized("bye_on").ConfigureAwait(false);
|
||||
@ -298,7 +131,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
string byeMessageText;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
byeMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelByeMessageText;
|
||||
}
|
||||
@ -306,7 +139,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
}
|
||||
|
||||
var sendByeEnabled = greetService.SetByeMessage(Context.Guild.Id, ref text);
|
||||
var sendByeEnabled = _greetService.SetByeMessage(Context.Guild.Id, ref text);
|
||||
|
||||
await ReplyConfirmLocalized("byemsg_new").ConfigureAwait(false);
|
||||
if (!sendByeEnabled)
|
||||
@ -318,7 +151,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireUserPermission(GuildPermission.ManageGuild)]
|
||||
public async Task ByeDel(int timer = 30)
|
||||
{
|
||||
await greetService.SetByeDel(Context.Guild.Id, timer).ConfigureAwait(false);
|
||||
await _greetService.SetByeDel(Context.Guild.Id, timer).ConfigureAwait(false);
|
||||
|
||||
if (timer > 0)
|
||||
await ReplyConfirmLocalized("byedel_on", timer).ConfigureAwait(false);
|
||||
|
@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Administration;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -18,6 +19,16 @@ namespace NadekoBot.Modules.Administration
|
||||
[Group]
|
||||
public class UserPunishCommands : NadekoSubmodule
|
||||
{
|
||||
private readonly DbHandler _db;
|
||||
private readonly MuteService _muteService;
|
||||
|
||||
public UserPunishCommands(DbHandler db, MuteService muteService)
|
||||
{
|
||||
_db = db;
|
||||
|
||||
_muteService = muteService;
|
||||
}
|
||||
|
||||
private async Task<PunishmentAction?> InternalWarn(IGuild guild, ulong userId, string modName, string reason)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(reason))
|
||||
@ -36,7 +47,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
int warnings = 1;
|
||||
List<WarningPunishment> ps;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
ps = uow.GuildConfigs.For(guildId, set => set.Include(x => x.WarnPunishments))
|
||||
.WarnPunishments;
|
||||
@ -62,9 +73,9 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
case PunishmentAction.Mute:
|
||||
if (p.Time == 0)
|
||||
await MuteCommands.MuteUser(user).ConfigureAwait(false);
|
||||
await _muteService.MuteUser(user).ConfigureAwait(false);
|
||||
else
|
||||
await MuteCommands.TimedMute(user, TimeSpan.FromMinutes(p.Time)).ConfigureAwait(false);
|
||||
await _muteService.TimedMute(user, TimeSpan.FromMinutes(p.Time)).ConfigureAwait(false);
|
||||
break;
|
||||
case PunishmentAction.Kick:
|
||||
await user.KickAsync().ConfigureAwait(false);
|
||||
@ -147,7 +158,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (page < 0)
|
||||
return;
|
||||
Warning[] warnings;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
warnings = uow.Warnings.For(Context.Guild.Id, userId);
|
||||
}
|
||||
@ -192,7 +203,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireUserPermission(GuildPermission.BanMembers)]
|
||||
public async Task Warnclear(ulong userId)
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
await uow.Warnings.ForgiveAll(Context.Guild.Id, userId, Context.User.ToString()).ConfigureAwait(false);
|
||||
uow.Complete();
|
||||
@ -212,7 +223,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (number <= 0)
|
||||
return;
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var ps = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
|
||||
ps.RemoveAll(x => x.Count == number);
|
||||
@ -239,7 +250,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (number <= 0)
|
||||
return;
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var ps = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
|
||||
var p = ps.FirstOrDefault(x => x.Count == number);
|
||||
@ -260,7 +271,7 @@ namespace NadekoBot.Modules.Administration
|
||||
public async Task WarnPunishList()
|
||||
{
|
||||
WarningPunishment[] ps;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
ps = uow.GuildConfigs.For(Context.Guild.Id, gc => gc.Include(x => x.WarnPunishments))
|
||||
.WarnPunishments
|
||||
|
@ -10,6 +10,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Services.Administration;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
@ -18,86 +19,13 @@ namespace NadekoBot.Modules.Administration
|
||||
[Group]
|
||||
public class VcRoleCommands : NadekoSubmodule
|
||||
{
|
||||
private static ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; }
|
||||
private readonly VcRoleService _service;
|
||||
private readonly DbHandler _db;
|
||||
|
||||
static VcRoleCommands()
|
||||
public VcRoleCommands(VcRoleService service, DbHandler db)
|
||||
{
|
||||
NadekoBot.Client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated;
|
||||
VcRoles = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>>();
|
||||
foreach (var gconf in NadekoBot.AllGuildConfigs)
|
||||
{
|
||||
var g = NadekoBot.Client.GetGuild(gconf.GuildId);
|
||||
if (g == null)
|
||||
continue; //todo delete everything from db if guild doesn't exist?
|
||||
|
||||
var infos = new ConcurrentDictionary<ulong, IRole>();
|
||||
VcRoles.TryAdd(gconf.GuildId, infos);
|
||||
foreach (var ri in gconf.VcRoleInfos)
|
||||
{
|
||||
var role = g.GetRole(ri.RoleId);
|
||||
if (role == null)
|
||||
continue; //todo remove this entry from db
|
||||
|
||||
infos.TryAdd(ri.VoiceChannelId, role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState,
|
||||
SocketVoiceState newState)
|
||||
{
|
||||
|
||||
var gusr = usr as SocketGuildUser;
|
||||
if (gusr == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var oldVc = oldState.VoiceChannel;
|
||||
var newVc = newState.VoiceChannel;
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (oldVc != newVc)
|
||||
{
|
||||
ulong guildId;
|
||||
guildId = newVc?.Guild.Id ?? oldVc.Guild.Id;
|
||||
|
||||
if (VcRoles.TryGetValue(guildId, out ConcurrentDictionary<ulong, IRole> guildVcRoles))
|
||||
{
|
||||
//remove old
|
||||
if (oldVc != null && guildVcRoles.TryGetValue(oldVc.Id, out IRole role))
|
||||
{
|
||||
if (gusr.Roles.Contains(role))
|
||||
{
|
||||
try
|
||||
{
|
||||
await gusr.RemoveRoleAsync(role).ConfigureAwait(false);
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
await gusr.RemoveRoleAsync(role).ConfigureAwait(false);
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
//add new
|
||||
if (newVc != null && guildVcRoles.TryGetValue(newVc.Id, out role))
|
||||
{
|
||||
if (!gusr.Roles.Contains(role))
|
||||
await gusr.AddRoleAsync(role).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Administration._log.Warn(ex);
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
_service = service;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -118,14 +46,14 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
}
|
||||
|
||||
var guildVcRoles = VcRoles.GetOrAdd(user.GuildId, new ConcurrentDictionary<ulong, IRole>());
|
||||
var guildVcRoles = _service.VcRoles.GetOrAdd(user.GuildId, new ConcurrentDictionary<ulong, IRole>());
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
if (guildVcRoles.TryRemove(vc.Id, out role))
|
||||
{
|
||||
await ReplyConfirmLocalized("vcrole_removed", Format.Bold(vc.Name)).ConfigureAwait(false);
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.VcRoleInfos));
|
||||
conf.VcRoleInfos.RemoveWhere(x => x.VoiceChannelId == vc.Id);
|
||||
@ -136,7 +64,7 @@ namespace NadekoBot.Modules.Administration
|
||||
else
|
||||
{
|
||||
guildVcRoles.AddOrUpdate(vc.Id, role, (key, old) => role);
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.VcRoleInfos));
|
||||
conf.VcRoleInfos.RemoveWhere(x => x.VoiceChannelId == vc.Id); // remove old one
|
||||
@ -157,7 +85,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
var guild = (SocketGuild) Context.Guild;
|
||||
string text;
|
||||
if (VcRoles.TryGetValue(Context.Guild.Id, out ConcurrentDictionary<ulong, IRole> roles))
|
||||
if (_service.VcRoles.TryGetValue(Context.Guild.Id, out ConcurrentDictionary<ulong, IRole> roles))
|
||||
{
|
||||
if (!roles.Any())
|
||||
{
|
||||
|
@ -4,6 +4,7 @@ using Discord.WebSocket;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Administration;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
@ -21,140 +22,15 @@ namespace NadekoBot.Modules.Administration
|
||||
[Group]
|
||||
public class VoicePlusTextCommands : NadekoSubmodule
|
||||
{
|
||||
private new static readonly Logger _log;
|
||||
private readonly VplusTService _service;
|
||||
private readonly DbHandler _db;
|
||||
|
||||
private static readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);
|
||||
|
||||
private static readonly ConcurrentHashSet<ulong> _voicePlusTextCache;
|
||||
private static readonly ConcurrentDictionary<ulong, SemaphoreSlim> _guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>();
|
||||
static VoicePlusTextCommands()
|
||||
public VoicePlusTextCommands(VplusTService service, DbHandler db)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
_voicePlusTextCache = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
|
||||
NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler;
|
||||
|
||||
sw.Stop();
|
||||
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||
_service = service;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
private static Task UserUpdatedEventHandler(SocketUser iuser, SocketVoiceState before, SocketVoiceState after)
|
||||
{
|
||||
var user = (iuser as SocketGuildUser);
|
||||
var guild = user?.Guild;
|
||||
|
||||
if (guild == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var botUserPerms = guild.CurrentUser.GuildPermissions;
|
||||
|
||||
if (before.VoiceChannel == after.VoiceChannel)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!_voicePlusTextCache.Contains(guild.Id))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles)
|
||||
{
|
||||
try
|
||||
{
|
||||
await guild.Owner.SendErrorAsync(
|
||||
GetTextStatic("vt_exit",
|
||||
NadekoBot.Localization.GetCultureInfo(guild),
|
||||
typeof(Administration).Name.ToLowerInvariant(),
|
||||
Format.Bold(guild.Name))).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
|
||||
_voicePlusTextCache.TryRemove(guild.Id);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var semaphore = _guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1));
|
||||
|
||||
try
|
||||
{
|
||||
await semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var beforeVch = before.VoiceChannel;
|
||||
if (beforeVch != null)
|
||||
{
|
||||
var beforeRoleName = GetRoleName(beforeVch);
|
||||
var beforeRole = guild.Roles.FirstOrDefault(x => x.Name == beforeRoleName);
|
||||
if (beforeRole != null)
|
||||
try
|
||||
{
|
||||
_log.Info("Removing role " + beforeRoleName + " from user " + user.Username);
|
||||
await user.RemoveRoleAsync(beforeRole).ConfigureAwait(false);
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}
|
||||
var afterVch = after.VoiceChannel;
|
||||
if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id)
|
||||
{
|
||||
var roleName = GetRoleName(afterVch);
|
||||
var roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName) ??
|
||||
(IRole) await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false);
|
||||
|
||||
ITextChannel textChannel = guild.TextChannels
|
||||
.FirstOrDefault(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant());
|
||||
if (textChannel == null)
|
||||
{
|
||||
var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false));
|
||||
|
||||
try { await guild.CurrentUser.AddRoleAsync(roleToAdd).ConfigureAwait(false); } catch {/*ignored*/}
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
await created.AddPermissionOverwriteAsync(roleToAdd, new OverwritePermissions(
|
||||
readMessages: PermValue.Allow,
|
||||
sendMessages: PermValue.Allow))
|
||||
.ConfigureAwait(false);
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
await created.AddPermissionOverwriteAsync(guild.EveryoneRole, new OverwritePermissions(
|
||||
readMessages: PermValue.Deny,
|
||||
sendMessages: PermValue.Deny))
|
||||
.ConfigureAwait(false);
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
}
|
||||
_log.Warn("Adding role " + roleToAdd.Name + " to user " + user.Username);
|
||||
await user.AddRoleAsync(roleToAdd).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
semaphore.Release();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static string GetChannelName(string voiceName) =>
|
||||
_channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice";
|
||||
|
||||
private static string GetRoleName(IVoiceChannel ch) =>
|
||||
"nvoice-" + ch.Id;
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
@ -184,7 +60,7 @@ namespace NadekoBot.Modules.Administration
|
||||
try
|
||||
{
|
||||
bool isEnabled;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var conf = uow.GuildConfigs.For(guild.Id, set => set);
|
||||
isEnabled = conf.VoicePlusTextEnabled = !conf.VoicePlusTextEnabled;
|
||||
@ -192,7 +68,7 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
if (!isEnabled)
|
||||
{
|
||||
_voicePlusTextCache.TryRemove(guild.Id);
|
||||
_service.VoicePlusTextCache.TryRemove(guild.Id);
|
||||
foreach (var textChannel in (await guild.GetTextChannelsAsync().ConfigureAwait(false)).Where(c => c.Name.EndsWith("-voice")))
|
||||
{
|
||||
try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
@ -207,7 +83,7 @@ namespace NadekoBot.Modules.Administration
|
||||
await ReplyConfirmLocalized("vt_disabled").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
_voicePlusTextCache.Add(guild.Id);
|
||||
_service.VoicePlusTextCache.Add(guild.Id);
|
||||
await ReplyConfirmLocalized("vt_enabled").ConfigureAwait(false);
|
||||
|
||||
}
|
||||
@ -236,7 +112,7 @@ namespace NadekoBot.Modules.Administration
|
||||
var voiceChannels = await guild.GetVoiceChannelsAsync().ConfigureAwait(false);
|
||||
|
||||
var boundTextChannels = textChannels.Where(c => c.Name.EndsWith("-voice"));
|
||||
var validTxtChannelNames = new HashSet<string>(voiceChannels.Select(c => GetChannelName(c.Name).ToLowerInvariant()));
|
||||
var validTxtChannelNames = new HashSet<string>(voiceChannels.Select(c => _service.GetChannelName(c.Name).ToLowerInvariant()));
|
||||
var invalidTxtChannels = boundTextChannels.Where(c => !validTxtChannelNames.Contains(c.Name));
|
||||
|
||||
foreach (var c in invalidTxtChannels)
|
||||
@ -246,7 +122,7 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
|
||||
var boundRoles = guild.Roles.Where(r => r.Name.StartsWith("nvoice-"));
|
||||
var validRoleNames = new HashSet<string>(voiceChannels.Select(c => GetRoleName(c).ToLowerInvariant()));
|
||||
var validRoleNames = new HashSet<string>(voiceChannels.Select(c => _service.GetRoleName(c).ToLowerInvariant()));
|
||||
var invalidRoles = boundRoles.Where(r => !validRoleNames.Contains(r.Name));
|
||||
|
||||
foreach (var r in invalidRoles)
|
||||
|
@ -21,9 +21,16 @@ namespace NadekoBot.Modules.Games
|
||||
[Group]
|
||||
public class Acropobia : NadekoSubmodule
|
||||
{
|
||||
private readonly DiscordShardedClient _client;
|
||||
|
||||
//channelId, game
|
||||
public static ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>();
|
||||
|
||||
public Acropobia(DiscordShardedClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Acro(int time = 60)
|
||||
@ -32,7 +39,7 @@ namespace NadekoBot.Modules.Games
|
||||
return;
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
var game = new AcrophobiaGame(channel, time);
|
||||
var game = new AcrophobiaGame(_client, _strings, channel, time);
|
||||
if (AcrophobiaGames.TryAdd(channel.Id, game))
|
||||
{
|
||||
try
|
||||
@ -59,6 +66,7 @@ namespace NadekoBot.Modules.Games
|
||||
Voting
|
||||
}
|
||||
|
||||
//todo Isolate, this shouldn't print or anything like that.
|
||||
public class AcrophobiaGame
|
||||
{
|
||||
private readonly ITextChannel _channel;
|
||||
@ -79,10 +87,14 @@ namespace NadekoBot.Modules.Games
|
||||
//text, votes
|
||||
private readonly ConcurrentDictionary<string, int> _votes = new ConcurrentDictionary<string, int>();
|
||||
private readonly Logger _log;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly NadekoStrings _strings;
|
||||
|
||||
public AcrophobiaGame(ITextChannel channel, int time)
|
||||
public AcrophobiaGame(DiscordShardedClient client, NadekoStrings strings, ITextChannel channel, int time)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_client = client;
|
||||
_strings = strings;
|
||||
|
||||
_channel = channel;
|
||||
_time = time;
|
||||
@ -123,7 +135,7 @@ $@"--
|
||||
|
||||
public async Task Run()
|
||||
{
|
||||
NadekoBot.Client.MessageReceived += PotentialAcro;
|
||||
_client.MessageReceived += PotentialAcro;
|
||||
var embed = GetEmbed();
|
||||
|
||||
//SUBMISSIONS PHASE
|
||||
@ -292,14 +304,14 @@ $@"--
|
||||
|
||||
public void EnsureStopped()
|
||||
{
|
||||
NadekoBot.Client.MessageReceived -= PotentialAcro;
|
||||
_client.MessageReceived -= PotentialAcro;
|
||||
if (!_source.IsCancellationRequested)
|
||||
_source.Cancel();
|
||||
}
|
||||
|
||||
private string GetText(string key, params object[] replacements)
|
||||
=> GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(_channel.Guild),
|
||||
=> _strings.GetText(key,
|
||||
_channel.Guild.Id,
|
||||
typeof(Games).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
}
|
||||
|
@ -1,16 +1,10 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using NadekoBot.Services.Games;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
@ -19,72 +13,13 @@ namespace NadekoBot.Modules.Games
|
||||
[Group]
|
||||
public class CleverBotCommands : NadekoSubmodule
|
||||
{
|
||||
private new static Logger _log { get; }
|
||||
private readonly DbHandler _db;
|
||||
private readonly GamesService _games;
|
||||
|
||||
public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; }
|
||||
|
||||
static CleverBotCommands()
|
||||
public CleverBotCommands(DbHandler db, GamesService games)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
|
||||
NadekoBot.AllGuildConfigs
|
||||
.Where(gc => gc.CleverbotEnabled)
|
||||
.ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => new ChatterBotSession(gc.GuildId), true)));
|
||||
|
||||
sw.Stop();
|
||||
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||
}
|
||||
|
||||
public static string PrepareMessage(IUserMessage msg, out ChatterBotSession cleverbot)
|
||||
{
|
||||
var channel = msg.Channel as ITextChannel;
|
||||
cleverbot = null;
|
||||
|
||||
if (channel == null)
|
||||
return null;
|
||||
|
||||
Lazy<ChatterBotSession> lazyCleverbot;
|
||||
if (!CleverbotGuilds.TryGetValue(channel.Guild.Id, out lazyCleverbot))
|
||||
return null;
|
||||
|
||||
cleverbot = lazyCleverbot.Value;
|
||||
|
||||
var nadekoId = NadekoBot.Client.CurrentUser.Id;
|
||||
var normalMention = $"<@{nadekoId}> ";
|
||||
var nickMention = $"<@!{nadekoId}> ";
|
||||
string message;
|
||||
if (msg.Content.StartsWith(normalMention))
|
||||
{
|
||||
message = msg.Content.Substring(normalMention.Length).Trim();
|
||||
}
|
||||
else if (msg.Content.StartsWith(nickMention))
|
||||
{
|
||||
message = msg.Content.Substring(nickMention.Length).Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static async Task<bool> TryAsk(ChatterBotSession cleverbot, ITextChannel channel, string message)
|
||||
{
|
||||
await channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
|
||||
var response = await cleverbot.Think(message).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false); // try twice :\
|
||||
}
|
||||
return true;
|
||||
_db = db;
|
||||
_games = games;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -94,10 +29,9 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
Lazy<ChatterBotSession> throwaway;
|
||||
if (CleverbotGuilds.TryRemove(channel.Guild.Id, out throwaway))
|
||||
if (_games.CleverbotGuilds.TryRemove(channel.Guild.Id, out Lazy<ChatterBotSession> throwaway))
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, false);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
@ -106,9 +40,9 @@ namespace NadekoBot.Modules.Games
|
||||
return;
|
||||
}
|
||||
|
||||
CleverbotGuilds.TryAdd(channel.Guild.Id, new Lazy<ChatterBotSession>(() => new ChatterBotSession(Context.Guild.Id), true));
|
||||
_games.CleverbotGuilds.TryAdd(channel.Guild.Id, new Lazy<ChatterBotSession>(() => new ChatterBotSession(Context.Guild.Id), true));
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, true);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
@ -118,41 +52,6 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
}
|
||||
|
||||
public class ChatterBotSession
|
||||
{
|
||||
private static NadekoRandom rng { get; } = new NadekoRandom();
|
||||
public string ChatterbotId { get; }
|
||||
public string ChannelId { get; }
|
||||
private int _botId = 6;
|
||||
|
||||
public ChatterBotSession(ulong channelId)
|
||||
{
|
||||
ChannelId = channelId.ToString().ToBase64();
|
||||
ChatterbotId = rng.Next(0, 1000000).ToString().ToBase64();
|
||||
}
|
||||
|
||||
private string apiEndpoint => "http://api.program-o.com/v2/chatbot/" +
|
||||
$"?bot_id={_botId}&" +
|
||||
"say={0}&" +
|
||||
$"convo_id=nadekobot_{ChatterbotId}_{ChannelId}&" +
|
||||
"format=json";
|
||||
|
||||
public async Task<string> Think(string message)
|
||||
{
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
var res = await http.GetStringAsync(string.Format(apiEndpoint, message)).ConfigureAwait(false);
|
||||
var cbr = JsonConvert.DeserializeObject<ChatterBotResponse>(res);
|
||||
//Console.WriteLine(cbr.Convo_id);
|
||||
return cbr.BotSay.Replace("<br/>", "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ChatterBotResponse
|
||||
{
|
||||
public string Convo_id { get; set; }
|
||||
public string BotSay { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -56,6 +56,7 @@ namespace NadekoBot.Modules.Games.Hangman
|
||||
public class HangmanGame: IDisposable
|
||||
{
|
||||
private readonly Logger _log;
|
||||
private readonly DiscordShardedClient _client;
|
||||
|
||||
public IMessageChannel GameChannel { get; }
|
||||
public HashSet<char> Guesses { get; } = new HashSet<char>();
|
||||
@ -81,9 +82,11 @@ namespace NadekoBot.Modules.Games.Hangman
|
||||
|
||||
public event Action<HangmanGame> OnEnded;
|
||||
|
||||
public HangmanGame(IMessageChannel channel, string type)
|
||||
public HangmanGame(DiscordShardedClient client, IMessageChannel channel, string type)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_client = client;
|
||||
|
||||
this.GameChannel = channel;
|
||||
this.TermType = type.ToTitleCase();
|
||||
}
|
||||
@ -95,12 +98,12 @@ namespace NadekoBot.Modules.Games.Hangman
|
||||
if (this.Term == null)
|
||||
throw new KeyNotFoundException("Can't find a term with that type. Use hangmanlist command.");
|
||||
// start listening for answers when game starts
|
||||
NadekoBot.Client.MessageReceived += PotentialGuess;
|
||||
_client.MessageReceived += PotentialGuess;
|
||||
}
|
||||
|
||||
public async Task End()
|
||||
{
|
||||
NadekoBot.Client.MessageReceived -= PotentialGuess;
|
||||
_client.MessageReceived -= PotentialGuess;
|
||||
OnEnded(this);
|
||||
var toSend = "Game ended. You **" + (Errors >= MaxErrors ? "LOSE" : "WIN") + "**!\n" + GetHangman();
|
||||
var embed = new EmbedBuilder().WithTitle("Hangman Game")
|
||||
@ -199,7 +202,7 @@ namespace NadekoBot.Modules.Games.Hangman
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
NadekoBot.Client.MessageReceived -= PotentialGuess;
|
||||
_client.MessageReceived -= PotentialGuess;
|
||||
OnEnded = null;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Modules.Games.Hangman;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
@ -14,6 +15,13 @@ namespace NadekoBot.Modules.Games
|
||||
[Group]
|
||||
public class HangmanCommands : NadekoSubmodule
|
||||
{
|
||||
private readonly DiscordShardedClient _client;
|
||||
|
||||
public HangmanCommands(DiscordShardedClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
//channelId, game
|
||||
public static ConcurrentDictionary<ulong, HangmanGame> HangmanGames { get; } = new ConcurrentDictionary<ulong, HangmanGame>();
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -25,7 +33,7 @@ namespace NadekoBot.Modules.Games
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Hangman([Remainder]string type = "All")
|
||||
{
|
||||
var hm = new HangmanGame(Context.Channel, type);
|
||||
var hm = new HangmanGame(_client, Context.Channel, type);
|
||||
|
||||
if (!HangmanGames.TryAdd(Context.Channel.Id, hm))
|
||||
{
|
||||
@ -35,8 +43,7 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
hm.OnEnded += g =>
|
||||
{
|
||||
HangmanGame throwaway;
|
||||
HangmanGames.TryRemove(g.GameChannel.Id, out throwaway);
|
||||
HangmanGames.TryRemove(g.GameChannel.Id, out HangmanGame throwaway);
|
||||
};
|
||||
try
|
||||
{
|
||||
@ -45,8 +52,7 @@ namespace NadekoBot.Modules.Games
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { await Context.Channel.SendErrorAsync(GetText("hangman_start_errored") + " " + ex.Message).ConfigureAwait(false); } catch { }
|
||||
HangmanGame throwaway;
|
||||
HangmanGames.TryRemove(Context.Channel.Id, out throwaway);
|
||||
HangmanGames.TryRemove(Context.Channel.Id, out HangmanGame throwaway);
|
||||
throwaway.Dispose();
|
||||
return;
|
||||
}
|
||||
|
147
src/NadekoBot/Modules/Games/Commands/Models/TypingGame.cs
Normal file
147
src/NadekoBot/Modules/Games/Commands/Models/TypingGame.cs
Normal file
@ -0,0 +1,147 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Games;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Games.Models
|
||||
{
|
||||
public class TypingGame
|
||||
{
|
||||
public const float WORD_VALUE = 4.5f;
|
||||
public ITextChannel Channel { get; }
|
||||
public string CurrentSentence { get; private set; }
|
||||
public bool IsActive { get; private set; }
|
||||
private readonly Stopwatch sw;
|
||||
private readonly List<ulong> finishedUserIds;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly GamesService _games;
|
||||
|
||||
private Logger _log { get; }
|
||||
|
||||
public TypingGame(GamesService games, DiscordShardedClient client, ITextChannel channel)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_games = games;
|
||||
_client = client;
|
||||
|
||||
this.Channel = channel;
|
||||
IsActive = false;
|
||||
sw = new Stopwatch();
|
||||
finishedUserIds = new List<ulong>();
|
||||
}
|
||||
|
||||
public async Task<bool> Stop()
|
||||
{
|
||||
if (!IsActive) return false;
|
||||
_client.MessageReceived -= AnswerReceived;
|
||||
finishedUserIds.Clear();
|
||||
IsActive = false;
|
||||
sw.Stop();
|
||||
sw.Reset();
|
||||
try { await Channel.SendConfirmAsync("Typing contest stopped.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
if (IsActive) return; // can't start running game
|
||||
IsActive = true;
|
||||
CurrentSentence = GetRandomSentence();
|
||||
var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f);
|
||||
try
|
||||
{
|
||||
await Channel.SendConfirmAsync($@":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can.").ConfigureAwait(false);
|
||||
|
||||
|
||||
var msg = await Channel.SendMessageAsync("Starting new typing contest in **3**...").ConfigureAwait(false);
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **2**...").ConfigureAwait(false);
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **1**...").ConfigureAwait(false);
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
|
||||
await msg.ModifyAsync(m => m.Content = Format.Bold(Format.Sanitize(CurrentSentence.Replace(" ", " \x200B")).SanitizeMentions())).ConfigureAwait(false);
|
||||
sw.Start();
|
||||
HandleAnswers();
|
||||
|
||||
while (i > 0)
|
||||
{
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
i--;
|
||||
if (!IsActive)
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
await Stop().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetRandomSentence()
|
||||
{
|
||||
if (_games.TypingArticles.Any())
|
||||
return _games.TypingArticles[new NadekoRandom().Next(0, _games.TypingArticles.Count)].Text;
|
||||
else
|
||||
return $"No typing articles found. Use {NadekoBot.Prefix}typeadd command to add a new article for typing.";
|
||||
|
||||
}
|
||||
|
||||
private void HandleAnswers()
|
||||
{
|
||||
_client.MessageReceived += AnswerReceived;
|
||||
}
|
||||
|
||||
private async Task AnswerReceived(SocketMessage imsg)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (imsg.Author.IsBot)
|
||||
return;
|
||||
var msg = imsg as SocketUserMessage;
|
||||
if (msg == null)
|
||||
return;
|
||||
|
||||
if (this.Channel == null || this.Channel.Id != msg.Channel.Id) return;
|
||||
|
||||
var guess = msg.Content;
|
||||
|
||||
var distance = CurrentSentence.LevenshteinDistance(guess);
|
||||
var decision = Judge(distance, guess.Length);
|
||||
if (decision && !finishedUserIds.Contains(msg.Author.Id))
|
||||
{
|
||||
var elapsed = sw.Elapsed;
|
||||
var wpm = CurrentSentence.Length / WORD_VALUE / elapsed.TotalSeconds * 60;
|
||||
finishedUserIds.Add(msg.Author.Id);
|
||||
await this.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle($"{msg.Author} finished the race!")
|
||||
.AddField(efb => efb.WithName("Place").WithValue($"#{finishedUserIds.Count}").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F1} *[{elapsed.TotalSeconds:F2}sec]*").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Errors").WithValue(distance.ToString()).WithIsInline(true)))
|
||||
.ConfigureAwait(false);
|
||||
if (finishedUserIds.Count % 4 == 0)
|
||||
{
|
||||
await this.Channel.SendConfirmAsync($":exclamation: A lot of people finished, here is the text for those still typing:\n\n**{Format.Sanitize(CurrentSentence.Replace(" ", " \x200B")).SanitizeMentions()}**").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
|
||||
private bool Judge(int errors, int textLength) => errors <= textLength / 25;
|
||||
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Services.Games;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
@ -28,89 +29,20 @@ namespace NadekoBot.Modules.Games
|
||||
[Group]
|
||||
public class PlantPickCommands : NadekoSubmodule
|
||||
{
|
||||
private static ConcurrentHashSet<ulong> generationChannels { get; }
|
||||
//channelid/message
|
||||
private static ConcurrentDictionary<ulong, List<IUserMessage>> plantedFlowers { get; } = new ConcurrentDictionary<ulong, List<IUserMessage>>();
|
||||
//channelId/last generation
|
||||
private static ConcurrentDictionary<ulong, DateTime> lastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||
private readonly CurrencyHandler _ch;
|
||||
private readonly BotConfig _bc;
|
||||
private readonly GamesService _games;
|
||||
private readonly DbHandler _db;
|
||||
|
||||
static PlantPickCommands()
|
||||
public PlantPickCommands(BotConfig bc, CurrencyHandler ch, GamesService games,
|
||||
DbHandler db)
|
||||
{
|
||||
NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
|
||||
generationChannels = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs
|
||||
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
|
||||
_bc = bc;
|
||||
_ch = ch;
|
||||
_games = games;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
private static Task PotentialFlowerGeneration(SocketMessage imsg)
|
||||
{
|
||||
var msg = imsg as SocketUserMessage;
|
||||
if (msg == null || msg.IsAuthor() || msg.Author.IsBot)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var channel = imsg.Channel as ITextChannel;
|
||||
if (channel == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!generationChannels.Contains(channel.Id))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
|
||||
var rng = new NadekoRandom();
|
||||
|
||||
//todo i'm stupid :rofl: wtg kwoth. real async programming :100: :ok_hand: :100: :100: :thumbsup:
|
||||
if (DateTime.Now - TimeSpan.FromSeconds(NadekoBot.BotConfig.CurrencyGenerationCooldown) < lastGeneration) //recently generated in this channel, don't generate again
|
||||
return;
|
||||
|
||||
var num = rng.Next(1, 101) + NadekoBot.BotConfig.CurrencyGenerationChance * 100;
|
||||
|
||||
if (num > 100)
|
||||
{
|
||||
lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now);
|
||||
|
||||
var dropAmount = NadekoBot.BotConfig.CurrencyDropAmount;
|
||||
|
||||
if (dropAmount > 0)
|
||||
{
|
||||
var msgs = new IUserMessage[dropAmount];
|
||||
var prefix = NadekoBot.ModulePrefixes[typeof(Games).Name];
|
||||
var toSend = dropAmount == 1
|
||||
? GetLocalText(channel, "curgen_sn", NadekoBot.BotConfig.CurrencySign)
|
||||
+ " " + GetLocalText(channel, "pick_sn", prefix)
|
||||
: GetLocalText(channel, "curgen_pl", dropAmount, NadekoBot.BotConfig.CurrencySign)
|
||||
+ " " + GetLocalText(channel, "pick_pl", prefix);
|
||||
var file = GetRandomCurrencyImage();
|
||||
using (var fileStream = file.Value.ToStream())
|
||||
{
|
||||
var sent = await channel.SendFileAsync(
|
||||
fileStream,
|
||||
file.Key,
|
||||
toSend).ConfigureAwait(false);
|
||||
|
||||
msgs[0] = sent;
|
||||
}
|
||||
|
||||
plantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.GetCurrentClassLogger().Warn(ex);
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public static string GetLocalText(ITextChannel channel, string key, params object[] replacements) =>
|
||||
NadekoTopLevelModule.GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(channel.GuildId),
|
||||
typeof(Games).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Pick()
|
||||
@ -120,16 +52,15 @@ namespace NadekoBot.Modules.Games
|
||||
if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages)
|
||||
return;
|
||||
|
||||
List<IUserMessage> msgs;
|
||||
|
||||
try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
if (!plantedFlowers.TryRemove(channel.Id, out msgs))
|
||||
if (!_games.PlantedFlowers.TryRemove(channel.Id, out List<IUserMessage> msgs))
|
||||
return;
|
||||
|
||||
await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
|
||||
|
||||
await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false);
|
||||
var msg = await ReplyConfirmLocalized("picked", msgs.Count + NadekoBot.BotConfig.CurrencySign)
|
||||
await _ch.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {_bc.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false);
|
||||
var msg = await ReplyConfirmLocalized("picked", msgs.Count + _bc.CurrencySign)
|
||||
.ConfigureAwait(false);
|
||||
msg.DeleteAfter(10);
|
||||
}
|
||||
@ -141,21 +72,21 @@ namespace NadekoBot.Modules.Games
|
||||
if (amount < 1)
|
||||
return;
|
||||
|
||||
var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {NadekoBot.BotConfig.CurrencyName}", amount, false).ConfigureAwait(false);
|
||||
var removed = await _ch.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {_bc.CurrencyName}", amount, false).ConfigureAwait(false);
|
||||
if (!removed)
|
||||
{
|
||||
await ReplyErrorLocalized("not_enough", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("not_enough", _bc.CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var imgData = GetRandomCurrencyImage();
|
||||
var imgData = _games.GetRandomCurrencyImage();
|
||||
|
||||
//todo upload all currency images to transfer.sh and use that one as cdn
|
||||
//and then
|
||||
|
||||
var msgToSend = GetText("planted",
|
||||
Format.Bold(Context.User.ToString()),
|
||||
amount + NadekoBot.BotConfig.CurrencySign,
|
||||
amount + _bc.CurrencySign,
|
||||
Prefix);
|
||||
|
||||
if (amount > 1)
|
||||
@ -172,7 +103,7 @@ namespace NadekoBot.Modules.Games
|
||||
var msgs = new IUserMessage[amount];
|
||||
msgs[0] = msg;
|
||||
|
||||
plantedFlowers.AddOrUpdate(Context.Channel.Id, msgs.ToList(), (id, old) =>
|
||||
_games.PlantedFlowers.AddOrUpdate(Context.Channel.Id, msgs.ToList(), (id, old) =>
|
||||
{
|
||||
old.AddRange(msgs);
|
||||
return old;
|
||||
@ -190,7 +121,7 @@ namespace NadekoBot.Modules.Games
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
bool enabled;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var guildConfig = uow.GuildConfigs.For(channel.Id, set => set.Include(gc => gc.GenerateCurrencyChannelIds));
|
||||
|
||||
@ -198,13 +129,13 @@ namespace NadekoBot.Modules.Games
|
||||
if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd))
|
||||
{
|
||||
guildConfig.GenerateCurrencyChannelIds.Add(toAdd);
|
||||
generationChannels.Add(channel.Id);
|
||||
_games.GenerationChannels.Add(channel.Id);
|
||||
enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
guildConfig.GenerateCurrencyChannelIds.Remove(toAdd);
|
||||
generationChannels.TryRemove(channel.Id);
|
||||
_games.GenerationChannels.TryRemove(channel.Id);
|
||||
enabled = false;
|
||||
}
|
||||
await uow.CompleteAsync();
|
||||
@ -218,14 +149,6 @@ namespace NadekoBot.Modules.Games
|
||||
await ReplyConfirmLocalized("curgen_disabled").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static KeyValuePair<string, ImmutableArray<byte>> GetRandomCurrencyImage()
|
||||
{
|
||||
var rng = new NadekoRandom();
|
||||
var images = NadekoBot.Images.Currency;
|
||||
|
||||
return images[rng.Next(0, images.Length)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ImageSharp.Processing;
|
||||
using NadekoBot.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
@ -19,6 +20,12 @@ namespace NadekoBot.Modules.Games
|
||||
public class PollCommands : NadekoSubmodule
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
|
||||
private readonly DiscordShardedClient _client;
|
||||
|
||||
public PollCommands(DiscordShardedClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireUserPermission(GuildPermission.ManageMessages)]
|
||||
@ -54,7 +61,7 @@ namespace NadekoBot.Modules.Games
|
||||
if (data.Length < 3)
|
||||
return;
|
||||
|
||||
var poll = new Poll(Context.Message, data[0], data.Skip(1), isPublic: isPublic);
|
||||
var poll = new Poll(_client, _strings, Context.Message, data[0], data.Skip(1), isPublic: isPublic);
|
||||
if (ActivePolls.TryAdd(channel.Guild.Id, poll))
|
||||
{
|
||||
await poll.StartPoll().ConfigureAwait(false);
|
||||
@ -83,10 +90,16 @@ namespace NadekoBot.Modules.Games
|
||||
private string[] answers { get; }
|
||||
private readonly ConcurrentDictionary<ulong, int> _participants = new ConcurrentDictionary<ulong, int>();
|
||||
private readonly string _question;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly NadekoStrings _strings;
|
||||
|
||||
public bool IsPublic { get; }
|
||||
|
||||
public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
|
||||
public Poll(DiscordShardedClient client, NadekoStrings strings, IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
|
||||
{
|
||||
_client = client;
|
||||
_strings = strings;
|
||||
|
||||
_originalMessage = umsg;
|
||||
_guild = ((ITextChannel)umsg.Channel).Guild;
|
||||
_question = question;
|
||||
@ -134,7 +147,7 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
public async Task StartPoll()
|
||||
{
|
||||
NadekoBot.Client.MessageReceived += Vote;
|
||||
_client.MessageReceived += Vote;
|
||||
var msgToSend = GetText("poll_created", Format.Bold(_originalMessage.Author.Username)) + "\n\n" + Format.Bold(_question) + "\n";
|
||||
var num = 1;
|
||||
msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
|
||||
@ -147,7 +160,7 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
public async Task StopPoll()
|
||||
{
|
||||
NadekoBot.Client.MessageReceived -= Vote;
|
||||
_client.MessageReceived -= Vote;
|
||||
await _originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -205,8 +218,8 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
|
||||
private string GetText(string key, params object[] replacements)
|
||||
=> NadekoTopLevelModule.GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(_guild.Id),
|
||||
=> _strings.GetText(key,
|
||||
_guild.Id,
|
||||
typeof(Games).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
}
|
||||
|
@ -3,14 +3,11 @@ using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Games.Commands.Models;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Modules.Games.Models;
|
||||
using NadekoBot.Services.Games;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -19,145 +16,18 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
public partial class Games
|
||||
{
|
||||
public class TypingGame
|
||||
{
|
||||
public const float WORD_VALUE = 4.5f;
|
||||
public ITextChannel Channel { get; }
|
||||
public string CurrentSentence { get; private set; }
|
||||
public bool IsActive { get; private set; }
|
||||
private readonly Stopwatch sw;
|
||||
private readonly List<ulong> finishedUserIds;
|
||||
private Logger _log { get; }
|
||||
|
||||
public TypingGame(ITextChannel channel)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
this.Channel = channel;
|
||||
IsActive = false;
|
||||
sw = new Stopwatch();
|
||||
finishedUserIds = new List<ulong>();
|
||||
}
|
||||
|
||||
public async Task<bool> Stop()
|
||||
{
|
||||
if (!IsActive) return false;
|
||||
NadekoBot.Client.MessageReceived -= AnswerReceived;
|
||||
finishedUserIds.Clear();
|
||||
IsActive = false;
|
||||
sw.Stop();
|
||||
sw.Reset();
|
||||
try { await Channel.SendConfirmAsync("Typing contest stopped.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
if (IsActive) return; // can't start running game
|
||||
IsActive = true;
|
||||
CurrentSentence = GetRandomSentence();
|
||||
var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f);
|
||||
try
|
||||
{
|
||||
await Channel.SendConfirmAsync($@":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can.").ConfigureAwait(false);
|
||||
|
||||
|
||||
var msg = await Channel.SendMessageAsync("Starting new typing contest in **3**...").ConfigureAwait(false);
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **2**...").ConfigureAwait(false);
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **1**...").ConfigureAwait(false);
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
|
||||
await msg.ModifyAsync(m => m.Content = Format.Bold(Format.Sanitize(CurrentSentence.Replace(" ", " \x200B")).SanitizeMentions())).ConfigureAwait(false);
|
||||
sw.Start();
|
||||
HandleAnswers();
|
||||
|
||||
while (i > 0)
|
||||
{
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
i--;
|
||||
if (!IsActive)
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
await Stop().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetRandomSentence()
|
||||
{
|
||||
if (SpeedTypingCommands.TypingArticles.Any())
|
||||
return SpeedTypingCommands.TypingArticles[new NadekoRandom().Next(0, SpeedTypingCommands.TypingArticles.Count)].Text;
|
||||
else
|
||||
return $"No typing articles found. Use {NadekoBot.ModulePrefixes[typeof(Games).Name]}typeadd command to add a new article for typing.";
|
||||
|
||||
}
|
||||
|
||||
private void HandleAnswers()
|
||||
{
|
||||
NadekoBot.Client.MessageReceived += AnswerReceived;
|
||||
}
|
||||
|
||||
private async Task AnswerReceived(SocketMessage imsg)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (imsg.Author.IsBot)
|
||||
return;
|
||||
var msg = imsg as SocketUserMessage;
|
||||
if (msg == null)
|
||||
return;
|
||||
|
||||
if (this.Channel == null || this.Channel.Id != msg.Channel.Id) return;
|
||||
|
||||
var guess = msg.Content;
|
||||
|
||||
var distance = CurrentSentence.LevenshteinDistance(guess);
|
||||
var decision = Judge(distance, guess.Length);
|
||||
if (decision && !finishedUserIds.Contains(msg.Author.Id))
|
||||
{
|
||||
var elapsed = sw.Elapsed;
|
||||
var wpm = CurrentSentence.Length / WORD_VALUE / elapsed.TotalSeconds * 60;
|
||||
finishedUserIds.Add(msg.Author.Id);
|
||||
await this.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle($"{msg.Author} finished the race!")
|
||||
.AddField(efb => efb.WithName("Place").WithValue($"#{finishedUserIds.Count}").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F1} *[{elapsed.TotalSeconds:F2}sec]*").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Errors").WithValue(distance.ToString()).WithIsInline(true)))
|
||||
.ConfigureAwait(false);
|
||||
if (finishedUserIds.Count % 4 == 0)
|
||||
{
|
||||
await this.Channel.SendConfirmAsync($":exclamation: A lot of people finished, here is the text for those still typing:\n\n**{Format.Sanitize(CurrentSentence.Replace(" ", " \x200B")).SanitizeMentions()}**").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
|
||||
private bool Judge(int errors, int textLength) => errors <= textLength / 25;
|
||||
|
||||
}
|
||||
|
||||
[Group]
|
||||
public class SpeedTypingCommands : NadekoSubmodule
|
||||
{
|
||||
public static List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
|
||||
|
||||
private const string _typingArticlesPath = "data/typing_articles2.json";
|
||||
|
||||
static SpeedTypingCommands()
|
||||
{
|
||||
try { TypingArticles = JsonConvert.DeserializeObject<List<TypingArticle>>(File.ReadAllText(_typingArticlesPath)); } catch { }
|
||||
}
|
||||
public static ConcurrentDictionary<ulong, TypingGame> RunningContests = new ConcurrentDictionary<ulong, TypingGame>();
|
||||
private readonly GamesService _games;
|
||||
private readonly DiscordShardedClient _client;
|
||||
|
||||
public SpeedTypingCommands(DiscordShardedClient client, GamesService games)
|
||||
{
|
||||
_games = games;
|
||||
_client = client;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@ -165,7 +35,7 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
var game = RunningContests.GetOrAdd(channel.Guild.Id, id => new TypingGame(channel));
|
||||
var game = RunningContests.GetOrAdd(channel.Guild.Id, id => new TypingGame(_games, _client, channel));
|
||||
|
||||
if (game.IsActive)
|
||||
{
|
||||
@ -202,13 +72,14 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
TypingArticles.Add(new TypingArticle
|
||||
_games.TypingArticles.Add(new TypingArticle
|
||||
{
|
||||
Title = $"Text added on {DateTime.UtcNow} by {Context.User}",
|
||||
Text = text.SanitizeMentions(),
|
||||
});
|
||||
|
||||
File.WriteAllText(_typingArticlesPath, JsonConvert.SerializeObject(TypingArticles));
|
||||
//todo move this to service
|
||||
File.WriteAllText(_games.TypingArticlesPath, JsonConvert.SerializeObject(_games.TypingArticles));
|
||||
|
||||
await channel.SendConfirmAsync("Added new article for typing game.").ConfigureAwait(false);
|
||||
}
|
||||
@ -222,7 +93,7 @@ namespace NadekoBot.Modules.Games
|
||||
if (page < 1)
|
||||
return;
|
||||
|
||||
var articles = TypingArticles.Skip((page - 1) * 15).Take(15).ToArray();
|
||||
var articles = _games.TypingArticles.Skip((page - 1) * 15).Take(15).ToArray();
|
||||
|
||||
if (!articles.Any())
|
||||
{
|
||||
@ -242,13 +113,13 @@ namespace NadekoBot.Modules.Games
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
index -= 1;
|
||||
if (index < 0 || index >= TypingArticles.Count)
|
||||
if (index < 0 || index >= _games.TypingArticles.Count)
|
||||
return;
|
||||
|
||||
var removed = TypingArticles[index];
|
||||
TypingArticles.RemoveAt(index);
|
||||
var removed = _games.TypingArticles[index];
|
||||
_games.TypingArticles.RemoveAt(index);
|
||||
|
||||
File.WriteAllText(_typingArticlesPath, JsonConvert.SerializeObject(TypingArticles));
|
||||
File.WriteAllText(_games.TypingArticlesPath, JsonConvert.SerializeObject(_games.TypingArticles));
|
||||
|
||||
await channel.SendConfirmAsync($"`Removed typing article:` #{index + 1} - {removed.Text.TrimTo(50)}")
|
||||
.ConfigureAwait(false);
|
||||
|
@ -1,8 +1,9 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NLog;
|
||||
using NadekoBot.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
@ -20,6 +21,12 @@ namespace NadekoBot.Modules.Games
|
||||
private static readonly Dictionary<ulong, TicTacToe> _games = new Dictionary<ulong, TicTacToe>();
|
||||
|
||||
private readonly SemaphoreSlim _sem = new SemaphoreSlim(1, 1);
|
||||
private readonly DiscordShardedClient _client;
|
||||
|
||||
public TicTacToeCommands(DiscordShardedClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@ -39,7 +46,7 @@ namespace NadekoBot.Modules.Games
|
||||
});
|
||||
return;
|
||||
}
|
||||
game = new TicTacToe(channel, (IGuildUser)Context.User);
|
||||
game = new TicTacToe(_strings, _client, channel, (IGuildUser)Context.User);
|
||||
_games.Add(channel.Id, game);
|
||||
await ReplyConfirmLocalized("ttt_created").ConfigureAwait(false);
|
||||
|
||||
@ -79,10 +86,15 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
private IUserMessage _previousMessage;
|
||||
private Timer _timeoutTimer;
|
||||
private readonly NadekoStrings _strings;
|
||||
private readonly DiscordShardedClient _client;
|
||||
|
||||
public TicTacToe(ITextChannel channel, IGuildUser firstUser)
|
||||
public TicTacToe(NadekoStrings strings, DiscordShardedClient client, ITextChannel channel, IGuildUser firstUser)
|
||||
{
|
||||
_channel = channel;
|
||||
_strings = strings;
|
||||
_client = client;
|
||||
|
||||
_users = new[] { firstUser, null };
|
||||
_state = new int?[,] {
|
||||
{ null, null, null },
|
||||
@ -95,8 +107,8 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
|
||||
private string GetText(string key, params object[] replacements) =>
|
||||
NadekoTopLevelModule.GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(_channel.GuildId),
|
||||
_strings.GetText(key,
|
||||
_channel.GuildId,
|
||||
typeof(Games).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
|
||||
@ -206,7 +218,7 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
}, null, 15000, Timeout.Infinite);
|
||||
|
||||
NadekoBot.Client.MessageReceived += Client_MessageReceived;
|
||||
_client.MessageReceived += Client_MessageReceived;
|
||||
|
||||
|
||||
_previousMessage = await _channel.EmbedAsync(GetEmbed(GetText("game_started"))).ConfigureAwait(false);
|
||||
@ -283,14 +295,14 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
reason = GetText("ttt_matched_three");
|
||||
_winner = _users[_curUserIndex];
|
||||
NadekoBot.Client.MessageReceived -= Client_MessageReceived;
|
||||
_client.MessageReceived -= Client_MessageReceived;
|
||||
OnEnded?.Invoke(this);
|
||||
}
|
||||
else if (IsDraw())
|
||||
{
|
||||
reason = GetText("ttt_a_draw");
|
||||
_phase = Phase.Ended;
|
||||
NadekoBot.Client.MessageReceived -= Client_MessageReceived;
|
||||
_client.MessageReceived -= Client_MessageReceived;
|
||||
OnEnded?.Invoke(this);
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ using Discord.Net;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
@ -18,6 +19,10 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
{
|
||||
private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1);
|
||||
private readonly Logger _log;
|
||||
private readonly NadekoStrings _strings;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly BotConfig _bc;
|
||||
private readonly CurrencyHandler _ch;
|
||||
|
||||
public IGuild Guild { get; }
|
||||
public ITextChannel Channel { get; }
|
||||
@ -38,9 +43,15 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
|
||||
public int WinRequirement { get; }
|
||||
|
||||
public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq, bool isPokemon)
|
||||
public TriviaGame(NadekoStrings strings, DiscordShardedClient client, BotConfig bc,
|
||||
CurrencyHandler ch, IGuild guild, ITextChannel channel,
|
||||
bool showHints, int winReq, bool isPokemon)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_strings = strings;
|
||||
_client = client;
|
||||
_bc = bc;
|
||||
_ch = ch;
|
||||
|
||||
ShowHints = showHints;
|
||||
Guild = guild;
|
||||
@ -50,8 +61,8 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
}
|
||||
|
||||
private string GetText(string key, params object[] replacements) =>
|
||||
NadekoTopLevelModule.GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(Channel.GuildId),
|
||||
_strings.GetText(key,
|
||||
Channel.GuildId,
|
||||
typeof(Games).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
|
||||
@ -99,7 +110,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
//receive messages
|
||||
try
|
||||
{
|
||||
NadekoBot.Client.MessageReceived += PotentialGuess;
|
||||
_client.MessageReceived += PotentialGuess;
|
||||
|
||||
//allow people to guess
|
||||
GameActive = true;
|
||||
@ -128,7 +139,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
finally
|
||||
{
|
||||
GameActive = false;
|
||||
NadekoBot.Client.MessageReceived -= PotentialGuess;
|
||||
_client.MessageReceived -= PotentialGuess;
|
||||
}
|
||||
if (!triviaCancelSource.IsCancellationRequested)
|
||||
{
|
||||
@ -214,9 +225,9 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
var reward = NadekoBot.BotConfig.TriviaCurrencyReward;
|
||||
var reward = _bc.TriviaCurrencyReward;
|
||||
if (reward > 0)
|
||||
await CurrencyHandler.AddCurrencyAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false);
|
||||
await _ch.AddCurrencyAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Games.Trivia;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -14,8 +17,19 @@ namespace NadekoBot.Modules.Games
|
||||
[Group]
|
||||
public class TriviaCommands : NadekoSubmodule
|
||||
{
|
||||
private readonly CurrencyHandler _ch;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly BotConfig _bc;
|
||||
|
||||
public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>();
|
||||
|
||||
public TriviaCommands(DiscordShardedClient client, BotConfig bc, CurrencyHandler ch)
|
||||
{
|
||||
_ch = ch;
|
||||
_client = client;
|
||||
_bc = bc;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public Task Trivia([Remainder] string additionalArgs = "")
|
||||
@ -35,7 +49,7 @@ namespace NadekoBot.Modules.Games
|
||||
var showHints = !additionalArgs.Contains("nohint");
|
||||
var isPokemon = additionalArgs.Contains("pokemon");
|
||||
|
||||
var trivia = new TriviaGame(channel.Guild, channel, showHints, winReq, isPokemon);
|
||||
var trivia = new TriviaGame(_strings, _client, _bc, _ch, channel.Guild, channel, showHints, winReq, isPokemon);
|
||||
if (RunningTrivias.TryAdd(channel.Guild.Id, trivia))
|
||||
{
|
||||
try
|
||||
|
@ -14,19 +14,20 @@ using System.Net.Http;
|
||||
using ImageSharp;
|
||||
using NadekoBot.DataStructures;
|
||||
using NLog;
|
||||
using NadekoBot.Services.Games;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
[NadekoModule("Games", ">")]
|
||||
public partial class Games : NadekoTopLevelModule
|
||||
{
|
||||
private static readonly ImmutableArray<string> _8BallResponses = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToImmutableArray();
|
||||
private readonly GamesService _games;
|
||||
private readonly IImagesService _images;
|
||||
|
||||
private static readonly Timer _t = new Timer((_) =>
|
||||
public Games(GamesService games, IImagesService images)
|
||||
{
|
||||
_girlRatings.Clear();
|
||||
|
||||
}, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1));
|
||||
_games = games;
|
||||
_images = images;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Choose([Remainder] string list = null)
|
||||
@ -48,7 +49,7 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
||||
.AddField(efb => efb.WithName("❓ " + GetText("question") ).WithValue(question).WithIsInline(false))
|
||||
.AddField(efb => efb.WithName("🎱 " + GetText("8ball")).WithValue(_8BallResponses[new NadekoRandom().Next(0, _8BallResponses.Length)]).WithIsInline(false)));
|
||||
.AddField(efb => efb.WithName("🎱 " + GetText("8ball")).WithValue(_games.EightBallResponses[new NadekoRandom().Next(0, _games.EightBallResponses.Length)]).WithIsInline(false)));
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -94,7 +95,7 @@ namespace NadekoBot.Modules.Games
|
||||
else if ((pick == 0 && nadekoPick == 1) ||
|
||||
(pick == 1 && nadekoPick == 2) ||
|
||||
(pick == 2 && nadekoPick == 0))
|
||||
msg = GetText("rps_win", NadekoBot.Client.CurrentUser.Mention,
|
||||
msg = GetText("rps_win", Context.Client.CurrentUser.Mention,
|
||||
getRpsPick(nadekoPick), getRpsPick(pick));
|
||||
else
|
||||
msg = GetText("rps_win", Context.User.Mention, getRpsPick(pick),
|
||||
@ -103,73 +104,12 @@ namespace NadekoBot.Modules.Games
|
||||
await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<ulong, GirlRating> _girlRatings = new ConcurrentDictionary<ulong, GirlRating>();
|
||||
|
||||
public class GirlRating
|
||||
{
|
||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public double Crazy { get; }
|
||||
public double Hot { get; }
|
||||
public int Roll { get; }
|
||||
public string Advice { get; }
|
||||
public AsyncLazy<string> Url { get; }
|
||||
|
||||
public GirlRating(double crazy, double hot, int roll, string advice)
|
||||
{
|
||||
Crazy = crazy;
|
||||
Hot = hot;
|
||||
Roll = roll;
|
||||
Advice = advice; // convenient to have it here, even though atm there are only few different ones.
|
||||
|
||||
Url = new AsyncLazy<string>(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var ms = new MemoryStream(NadekoBot.Images.WifeMatrix.ToArray(), false))
|
||||
using (var img = new ImageSharp.Image(ms))
|
||||
{
|
||||
const int minx = 35;
|
||||
const int miny = 385;
|
||||
const int length = 345;
|
||||
|
||||
var pointx = (int)(minx + length * (Hot / 10));
|
||||
var pointy = (int)(miny - length * ((Crazy - 4) / 6));
|
||||
|
||||
using (var pointMs = new MemoryStream(NadekoBot.Images.RategirlDot.ToArray(), false))
|
||||
using (var pointImg = new ImageSharp.Image(pointMs))
|
||||
{
|
||||
img.DrawImage(pointImg, 100, default(Size), new Point(pointx - 10, pointy - 10));
|
||||
}
|
||||
|
||||
string url;
|
||||
using (var http = new HttpClient())
|
||||
using (var imgStream = new MemoryStream())
|
||||
{
|
||||
img.Save(imgStream);
|
||||
var byteContent = new ByteArrayContent(imgStream.ToArray());
|
||||
http.AddFakeHeaders();
|
||||
|
||||
var reponse = await http.PutAsync("https://transfer.sh/img.png", byteContent);
|
||||
url = await reponse.Content.ReadAsStringAsync();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task RateGirl(IGuildUser usr)
|
||||
{
|
||||
var gr = _girlRatings.GetOrAdd(usr.Id, GetGirl);
|
||||
var gr = _games.GirlRatings.GetOrAdd(usr.Id, GetGirl);
|
||||
var img = await gr.Url;
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("Girl Rating For " + usr)
|
||||
@ -255,7 +195,7 @@ namespace NadekoBot.Modules.Games
|
||||
"and maybe look at how to replicate that.";
|
||||
}
|
||||
|
||||
return new GirlRating(crazy, hot, roll, advice);
|
||||
return new GirlRating(_images, crazy, hot, roll, advice);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
|
@ -1,184 +0,0 @@
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
public class GoogleTranslator
|
||||
{
|
||||
private static GoogleTranslator _instance;
|
||||
public static GoogleTranslator Instance = _instance ?? (_instance = new GoogleTranslator());
|
||||
|
||||
public IEnumerable<string> Languages => _languageDictionary.Keys.OrderBy(x => x);
|
||||
private readonly Dictionary<string, string> _languageDictionary;
|
||||
|
||||
static GoogleTranslator() { }
|
||||
private GoogleTranslator() {
|
||||
_languageDictionary = new Dictionary<string, string>() {
|
||||
{ "afrikaans", "af"},
|
||||
{ "albanian", "sq"},
|
||||
{ "arabic", "ar"},
|
||||
{ "armenian", "hy"},
|
||||
{ "azerbaijani", "az"},
|
||||
{ "basque", "eu"},
|
||||
{ "belarusian", "be"},
|
||||
{ "bengali", "bn"},
|
||||
{ "bulgarian", "bg"},
|
||||
{ "catalan", "ca"},
|
||||
{ "chinese-traditional", "zh-TW"},
|
||||
{ "chinese-simplified", "zh-CN"},
|
||||
{ "chinese", "zh-CN"},
|
||||
{ "croatian", "hr"},
|
||||
{ "czech", "cs"},
|
||||
{ "danish", "da"},
|
||||
{ "dutch", "nl"},
|
||||
{ "english", "en"},
|
||||
{ "esperanto", "eo"},
|
||||
{ "estonian", "et"},
|
||||
{ "filipino", "tl"},
|
||||
{ "finnish", "fi"},
|
||||
{ "french", "fr"},
|
||||
{ "galician", "gl"},
|
||||
{ "german", "de"},
|
||||
{ "georgian", "ka"},
|
||||
{ "greek", "el"},
|
||||
{ "haitian Creole", "ht"},
|
||||
{ "hebrew", "iw"},
|
||||
{ "hindi", "hi"},
|
||||
{ "hungarian", "hu"},
|
||||
{ "icelandic", "is"},
|
||||
{ "indonesian", "id"},
|
||||
{ "irish", "ga"},
|
||||
{ "italian", "it"},
|
||||
{ "japanese", "ja"},
|
||||
{ "korean", "ko"},
|
||||
{ "lao", "lo"},
|
||||
{ "latin", "la"},
|
||||
{ "latvian", "lv"},
|
||||
{ "lithuanian", "lt"},
|
||||
{ "macedonian", "mk"},
|
||||
{ "malay", "ms"},
|
||||
{ "maltese", "mt"},
|
||||
{ "norwegian", "no"},
|
||||
{ "persian", "fa"},
|
||||
{ "polish", "pl"},
|
||||
{ "portuguese", "pt"},
|
||||
{ "romanian", "ro"},
|
||||
{ "russian", "ru"},
|
||||
{ "serbian", "sr"},
|
||||
{ "slovak", "sk"},
|
||||
{ "slovenian", "sl"},
|
||||
{ "spanish", "es"},
|
||||
{ "swahili", "sw"},
|
||||
{ "swedish", "sv"},
|
||||
{ "tamil", "ta"},
|
||||
{ "telugu", "te"},
|
||||
{ "thai", "th"},
|
||||
{ "turkish", "tr"},
|
||||
{ "ukrainian", "uk"},
|
||||
{ "urdu", "ur"},
|
||||
{ "vietnamese", "vi"},
|
||||
{ "welsh", "cy"},
|
||||
{ "yiddish", "yi"},
|
||||
|
||||
{ "af", "af"},
|
||||
{ "sq", "sq"},
|
||||
{ "ar", "ar"},
|
||||
{ "hy", "hy"},
|
||||
{ "az", "az"},
|
||||
{ "eu", "eu"},
|
||||
{ "be", "be"},
|
||||
{ "bn", "bn"},
|
||||
{ "bg", "bg"},
|
||||
{ "ca", "ca"},
|
||||
{ "zh-tw", "zh-TW"},
|
||||
{ "zh-cn", "zh-CN"},
|
||||
{ "hr", "hr"},
|
||||
{ "cs", "cs"},
|
||||
{ "da", "da"},
|
||||
{ "nl", "nl"},
|
||||
{ "en", "en"},
|
||||
{ "eo", "eo"},
|
||||
{ "et", "et"},
|
||||
{ "tl", "tl"},
|
||||
{ "fi", "fi"},
|
||||
{ "fr", "fr"},
|
||||
{ "gl", "gl"},
|
||||
{ "de", "de"},
|
||||
{ "ka", "ka"},
|
||||
{ "el", "el"},
|
||||
{ "ht", "ht"},
|
||||
{ "iw", "iw"},
|
||||
{ "hi", "hi"},
|
||||
{ "hu", "hu"},
|
||||
{ "is", "is"},
|
||||
{ "id", "id"},
|
||||
{ "ga", "ga"},
|
||||
{ "it", "it"},
|
||||
{ "ja", "ja"},
|
||||
{ "ko", "ko"},
|
||||
{ "lo", "lo"},
|
||||
{ "la", "la"},
|
||||
{ "lv", "lv"},
|
||||
{ "lt", "lt"},
|
||||
{ "mk", "mk"},
|
||||
{ "ms", "ms"},
|
||||
{ "mt", "mt"},
|
||||
{ "no", "no"},
|
||||
{ "fa", "fa"},
|
||||
{ "pl", "pl"},
|
||||
{ "pt", "pt"},
|
||||
{ "ro", "ro"},
|
||||
{ "ru", "ru"},
|
||||
{ "sr", "sr"},
|
||||
{ "sk", "sk"},
|
||||
{ "sl", "sl"},
|
||||
{ "es", "es"},
|
||||
{ "sw", "sw"},
|
||||
{ "sv", "sv"},
|
||||
{ "ta", "ta"},
|
||||
{ "te", "te"},
|
||||
{ "th", "th"},
|
||||
{ "tr", "tr"},
|
||||
{ "uk", "uk"},
|
||||
{ "ur", "ur"},
|
||||
{ "vi", "vi"},
|
||||
{ "cy", "cy"},
|
||||
{ "yi", "yi"},
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<string> Translate(string sourceText, string sourceLanguage, string targetLanguage)
|
||||
{
|
||||
string text;
|
||||
|
||||
if(!_languageDictionary.ContainsKey(sourceLanguage) ||
|
||||
!_languageDictionary.ContainsKey(targetLanguage))
|
||||
throw new ArgumentException();
|
||||
|
||||
|
||||
var url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}",
|
||||
ConvertToLanguageCode(sourceLanguage),
|
||||
ConvertToLanguageCode(targetLanguage),
|
||||
WebUtility.UrlEncode(sourceText));
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
|
||||
text = await http.GetStringAsync(url).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return (string.Concat(JArray.Parse(text)[0].Select(x => x[0])));
|
||||
}
|
||||
|
||||
private string ConvertToLanguageCode(string language)
|
||||
{
|
||||
string mode;
|
||||
_languageDictionary.TryGetValue(language, out mode);
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Searches.Models;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Searches;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
@ -19,27 +20,11 @@ namespace NadekoBot.Modules.Searches
|
||||
[Group]
|
||||
public class JokeCommands : NadekoSubmodule
|
||||
{
|
||||
private static List<WoWJoke> wowJokes { get; } = new List<WoWJoke>();
|
||||
private static List<MagicItem> magicItems { get; } = new List<MagicItem>();
|
||||
private new static readonly Logger _log;
|
||||
private readonly SearchesService _searches;
|
||||
|
||||
static JokeCommands()
|
||||
public JokeCommands(SearchesService searches)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
if (File.Exists("data/wowjokes.json"))
|
||||
{
|
||||
wowJokes = JsonConvert.DeserializeObject<List<WoWJoke>>(File.ReadAllText("data/wowjokes.json"));
|
||||
}
|
||||
else
|
||||
_log.Warn("data/wowjokes.json is missing. WOW Jokes are not loaded.");
|
||||
|
||||
if (File.Exists("data/magicitems.json"))
|
||||
{
|
||||
magicItems = JsonConvert.DeserializeObject<List<MagicItem>>(File.ReadAllText("data/magicitems.json"));
|
||||
}
|
||||
else
|
||||
_log.Warn("data/magicitems.json is missing. Magic items are not loaded.");
|
||||
_searches = searches;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -75,24 +60,24 @@ namespace NadekoBot.Modules.Searches
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task WowJoke()
|
||||
{
|
||||
if (!wowJokes.Any())
|
||||
if (!_searches.WowJokes.Any())
|
||||
{
|
||||
await ReplyErrorLocalized("jokes_not_loaded").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var joke = wowJokes[new NadekoRandom().Next(0, wowJokes.Count)];
|
||||
var joke = _searches.WowJokes[new NadekoRandom().Next(0, _searches.WowJokes.Count)];
|
||||
await Context.Channel.SendConfirmAsync(joke.Question, joke.Answer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task MagicItem()
|
||||
{
|
||||
if (!wowJokes.Any())
|
||||
if (!_searches.WowJokes.Any())
|
||||
{
|
||||
await ReplyErrorLocalized("magicitems_not_loaded").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var item = magicItems[new NadekoRandom().Next(0, magicItems.Count)];
|
||||
var item = _searches.MagicItems[new NadekoRandom().Next(0, _searches.MagicItems.Count)];
|
||||
|
||||
await Context.Channel.SendConfirmAsync("✨" + item.Name, item.Description).ConfigureAwait(false);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Searches
|
||||
obj["name"].GetHashCode();
|
||||
}
|
||||
|
||||
private static string[] trashTalk { get; } = { "Better ban your counters. You are going to carry the game anyway.",
|
||||
private static readonly string[] trashTalk = { "Better ban your counters. You are going to carry the game anyway.",
|
||||
"Go with the flow. Don't think. Just ban one of these.",
|
||||
"DONT READ BELOW! Ban Urgot mid OP 100%. Im smurf Diamond 1.",
|
||||
"Ask your teammates what would they like to play, and ban that.",
|
||||
@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Searches
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
var data = JObject.Parse(await http.GetStringAsync($"http://api.champion.gg/stats/champs/mostBanned?" +
|
||||
$"api_key={NadekoBot.Credentials.LoLApiKey}&page=1&" +
|
||||
$"api_key={_creds.LoLApiKey}&page=1&" +
|
||||
$"limit={showCount}")
|
||||
.ConfigureAwait(false))["data"] as JArray;
|
||||
var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList();
|
||||
@ -127,7 +127,7 @@ namespace NadekoBot.Modules.Searches
|
||||
// await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false);
|
||||
// return;
|
||||
// }
|
||||
// var allData = JArray.Parse(await Classes.http.GetStringAsync($"http://api.champion.gg/champion/{name}?api_key={NadekoBot.Credentials.LOLAPIKey}").ConfigureAwait(false));
|
||||
// var allData = JArray.Parse(await Classes.http.GetStringAsync($"http://api.champion.gg/champion/{name}?api_key={_creds.LOLAPIKey}").ConfigureAwait(false));
|
||||
// JToken data = null;
|
||||
// if (role != null)
|
||||
// {
|
||||
@ -168,7 +168,7 @@ namespace NadekoBot.Modules.Searches
|
||||
// roles[i] = ">" + roles[i] + "<";
|
||||
// }
|
||||
// var general = JArray.Parse(await http.GetStringAsync($"http://api.champion.gg/stats/" +
|
||||
// $"champs/{name}?api_key={NadekoBot.Credentials.LOLAPIKey}")
|
||||
// $"champs/{name}?api_key={_creds.LOLAPIKey}")
|
||||
// .ConfigureAwait(false))
|
||||
// .FirstOrDefault(jt => jt["role"].ToString() == role)?["general"];
|
||||
// if (general == null)
|
||||
|
@ -1,33 +0,0 @@
|
||||
using NadekoBot.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace NadekoBot.Modules.Searches.Models
|
||||
{
|
||||
public class ImdbMovie
|
||||
{
|
||||
public bool Status { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string OriginalTitle { get; set; }
|
||||
public string Year { get; set; }
|
||||
public string Rating { get; set; }
|
||||
public string Plot { get; set; }
|
||||
public string Poster { get; set; }
|
||||
public List<string> Genres { get; set; }
|
||||
public string ImdbURL { get; set; }
|
||||
|
||||
public Dictionary<string, string> Aka { get; set; }
|
||||
|
||||
public override string ToString() =>
|
||||
$@"`Title:` {WebUtility.HtmlDecode(Title)} {(string.IsNullOrEmpty(OriginalTitle) ? "" : $"({OriginalTitle})")}
|
||||
`Year:` {Year}
|
||||
`Rating:` {Rating}
|
||||
`Genre:` {GenresAsString}
|
||||
`Link:` <{ImdbURL}>
|
||||
`Plot:` {System.Net.WebUtility.HtmlDecode(Plot.TrimTo(500))}
|
||||
`Poster:` " + NadekoBot.Google.ShortenUrl(Poster).GetAwaiter().GetResult();
|
||||
public string GenresAsString =>
|
||||
string.Join(", ", Genres);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using Discord;
|
||||
using Discord.API;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
@ -12,7 +13,7 @@ namespace NadekoBot.Modules.Searches.Commands.OMDB
|
||||
{
|
||||
private const string queryUrl = "http://www.omdbapi.com/?t={0}&y=&plot=full&r=json";
|
||||
|
||||
public static async Task<OmdbMovie> FindMovie(string name)
|
||||
public static async Task<OmdbMovie> FindMovie(string name, IGoogleApiService google)
|
||||
{
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
@ -20,7 +21,7 @@ namespace NadekoBot.Modules.Searches.Commands.OMDB
|
||||
var movie = JsonConvert.DeserializeObject<OmdbMovie>(res);
|
||||
if (movie?.Title == null)
|
||||
return null;
|
||||
movie.Poster = await NadekoBot.Google.ShortenUrl(movie.Poster);
|
||||
movie.Poster = await google.ShortenUrl(movie.Poster);
|
||||
return movie;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
@ -17,6 +18,15 @@ namespace NadekoBot.Modules.Searches
|
||||
[Group]
|
||||
public class OsuCommands : NadekoSubmodule
|
||||
{
|
||||
private readonly IGoogleApiService _google;
|
||||
private readonly IBotCredentials _creds;
|
||||
|
||||
public OsuCommands(IGoogleApiService google, IBotCredentials creds)
|
||||
{
|
||||
_google = google;
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Osu(string usr, [Remainder] string mode = null)
|
||||
{
|
||||
@ -51,7 +61,7 @@ namespace NadekoBot.Modules.Searches
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Osub([Remainder] string map)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey))
|
||||
if (string.IsNullOrWhiteSpace(_creds.OsuApiKey))
|
||||
{
|
||||
await ReplyErrorLocalized("osu_api_key").ConfigureAwait(false);
|
||||
return;
|
||||
@ -65,7 +75,7 @@ namespace NadekoBot.Modules.Searches
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
var mapId = ResolveMap(map);
|
||||
var reqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Credentials.OsuApiKey}&{mapId}";
|
||||
var reqString = $"https://osu.ppy.sh/api/get_beatmaps?k={_creds.OsuApiKey}&{mapId}";
|
||||
var obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false))[0];
|
||||
var sb = new System.Text.StringBuilder();
|
||||
var starRating = Math.Round(double.Parse($"{obj["difficultyrating"]}", CultureInfo.InvariantCulture), 2);
|
||||
@ -86,7 +96,7 @@ namespace NadekoBot.Modules.Searches
|
||||
public async Task Osu5(string user, [Remainder] string mode = null)
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey))
|
||||
if (string.IsNullOrWhiteSpace(_creds.OsuApiKey))
|
||||
{
|
||||
await channel.SendErrorAsync("An osu! API key is required.").ConfigureAwait(false);
|
||||
return;
|
||||
@ -107,12 +117,12 @@ namespace NadekoBot.Modules.Searches
|
||||
m = ResolveGameMode(mode);
|
||||
}
|
||||
|
||||
var reqString = $"https://osu.ppy.sh/api/get_user_best?k={NadekoBot.Credentials.OsuApiKey}&u={Uri.EscapeDataString(user)}&type=string&limit=5&m={m}";
|
||||
var reqString = $"https://osu.ppy.sh/api/get_user_best?k={_creds.OsuApiKey}&u={Uri.EscapeDataString(user)}&type=string&limit=5&m={m}";
|
||||
var obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false));
|
||||
var sb = new System.Text.StringBuilder($"`Top 5 plays for {user}:`\n```xl" + Environment.NewLine);
|
||||
foreach (var item in obj)
|
||||
{
|
||||
var mapReqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Credentials.OsuApiKey}&b={item["beatmap_id"]}";
|
||||
var mapReqString = $"https://osu.ppy.sh/api/get_beatmaps?k={_creds.OsuApiKey}&b={item["beatmap_id"]}";
|
||||
var map = JArray.Parse(await http.GetStringAsync(mapReqString).ConfigureAwait(false))[0];
|
||||
var pp = Math.Round(double.Parse($"{item["pp"]}", CultureInfo.InvariantCulture), 2);
|
||||
var acc = CalculateAcc(item, m);
|
||||
|
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Searches
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Placelist()
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync(GetText("list_of_place_tags", NadekoBot.ModulePrefixes[typeof(Searches).Name]),
|
||||
await Context.Channel.SendConfirmAsync(GetText("list_of_place_tags", NadekoBot.Prefix),
|
||||
typesStr)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Searches.Models;
|
||||
using NadekoBot.Services.Searches;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System.Collections.Generic;
|
||||
@ -17,28 +18,14 @@ namespace NadekoBot.Modules.Searches
|
||||
[Group]
|
||||
public class PokemonSearchCommands : NadekoSubmodule
|
||||
{
|
||||
private static Dictionary<string, SearchPokemon> pokemons { get; } = new Dictionary<string, SearchPokemon>();
|
||||
private static Dictionary<string, SearchPokemonAbility> pokemonAbilities { get; } = new Dictionary<string, SearchPokemonAbility>();
|
||||
private readonly SearchesService _searches;
|
||||
|
||||
public const string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json";
|
||||
public Dictionary<string, SearchPokemon> Pokemons => _searches.Pokemons;
|
||||
public Dictionary<string, SearchPokemonAbility> PokemonAbilities => _searches.PokemonAbilities;
|
||||
|
||||
public const string PokemonListFile = "data/pokemon/pokemon_list7.json";
|
||||
private new static readonly Logger _log;
|
||||
|
||||
static PokemonSearchCommands()
|
||||
public PokemonSearchCommands(SearchesService searches)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
if (File.Exists(PokemonListFile))
|
||||
{
|
||||
pokemons = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(PokemonListFile));
|
||||
}
|
||||
else
|
||||
_log.Warn(PokemonListFile + " is missing. Pokemon abilities not loaded.");
|
||||
if (File.Exists(PokemonAbilitiesFile))
|
||||
pokemonAbilities = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemonAbility>>(File.ReadAllText(PokemonAbilitiesFile));
|
||||
else
|
||||
_log.Warn(PokemonAbilitiesFile + " is missing. Pokemon abilities not loaded.");
|
||||
_searches = searches;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -48,7 +35,7 @@ namespace NadekoBot.Modules.Searches
|
||||
if (string.IsNullOrWhiteSpace(pokemon))
|
||||
return;
|
||||
|
||||
foreach (var kvp in pokemons)
|
||||
foreach (var kvp in Pokemons)
|
||||
{
|
||||
if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant())
|
||||
{
|
||||
@ -71,7 +58,7 @@ namespace NadekoBot.Modules.Searches
|
||||
ability = ability?.Trim().ToUpperInvariant().Replace(" ", "");
|
||||
if (string.IsNullOrWhiteSpace(ability))
|
||||
return;
|
||||
foreach (var kvp in pokemonAbilities)
|
||||
foreach (var kvp in PokemonAbilities)
|
||||
{
|
||||
if (kvp.Key.ToUpperInvariant() == ability)
|
||||
{
|
||||
|
@ -1,195 +1,30 @@
|
||||
using Discord.Commands;
|
||||
using Discord;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Services;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Net.Http;
|
||||
using NadekoBot.Attributes;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Searches;
|
||||
|
||||
namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
public partial class Searches
|
||||
{
|
||||
public class StreamStatus
|
||||
{
|
||||
public bool IsLive { get; set; }
|
||||
public string ApiLink { get; set; }
|
||||
public string Views { get; set; }
|
||||
}
|
||||
|
||||
public class HitboxResponse {
|
||||
public bool Success { get; set; } = true;
|
||||
[JsonProperty("media_is_live")]
|
||||
public string MediaIsLive { get; set; }
|
||||
public bool IsLive => MediaIsLive == "1";
|
||||
[JsonProperty("media_views")]
|
||||
public string Views { get; set; }
|
||||
}
|
||||
|
||||
public class TwitchResponse
|
||||
{
|
||||
public string Error { get; set; } = null;
|
||||
public bool IsLive => Stream != null;
|
||||
public StreamInfo Stream { get; set; }
|
||||
|
||||
public class StreamInfo
|
||||
{
|
||||
public int Viewers { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class BeamResponse
|
||||
{
|
||||
public string Error { get; set; } = null;
|
||||
|
||||
[JsonProperty("online")]
|
||||
public bool IsLive { get; set; }
|
||||
public int ViewersCurrent { get; set; }
|
||||
}
|
||||
|
||||
public class StreamNotFoundException : Exception
|
||||
{
|
||||
public StreamNotFoundException(string message) : base("Stream '" + message + "' not found.")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Group]
|
||||
public class StreamNotificationCommands : NadekoSubmodule
|
||||
{
|
||||
private static readonly Timer _checkTimer;
|
||||
private static readonly ConcurrentDictionary<string, StreamStatus> _cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
|
||||
private readonly DbHandler _db;
|
||||
private readonly StreamNotificationService _service;
|
||||
|
||||
private static bool firstPass { get; set; } = true;
|
||||
|
||||
static StreamNotificationCommands()
|
||||
public StreamNotificationCommands(DbHandler db, StreamNotificationService service)
|
||||
{
|
||||
_checkTimer = new Timer(async (state) =>
|
||||
{
|
||||
var oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(_cachedStatuses);
|
||||
_cachedStatuses.Clear();
|
||||
IEnumerable<FollowedStream> streams;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
streams = uow.GuildConfigs.GetAllFollowedStreams();
|
||||
}
|
||||
|
||||
await Task.WhenAll(streams.Select(async fs =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var newStatus = await GetStreamStatus(fs).ConfigureAwait(false);
|
||||
if (firstPass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StreamStatus oldStatus;
|
||||
if (oldCachedStatuses.TryGetValue(newStatus.ApiLink, out oldStatus) &&
|
||||
oldStatus.IsLive != newStatus.IsLive)
|
||||
{
|
||||
var server = NadekoBot.Client.GetGuild(fs.GuildId);
|
||||
var channel = server?.GetTextChannel(fs.ChannelId);
|
||||
if (channel == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
await channel.EmbedAsync(fs.GetEmbed(newStatus, channel.Guild.Id)).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}));
|
||||
|
||||
firstPass = false;
|
||||
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(60));
|
||||
}
|
||||
|
||||
private static async Task<StreamStatus> GetStreamStatus(FollowedStream stream, bool checkCache = true)
|
||||
{
|
||||
string response;
|
||||
StreamStatus result;
|
||||
switch (stream.Type)
|
||||
{
|
||||
case FollowedStream.FollowedStreamType.Hitbox:
|
||||
var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username.ToLowerInvariant()}";
|
||||
if (checkCache && _cachedStatuses.TryGetValue(hitboxUrl, out result))
|
||||
return result;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
response = await http.GetStringAsync(hitboxUrl).ConfigureAwait(false);
|
||||
}
|
||||
var hbData = JsonConvert.DeserializeObject<HitboxResponse>(response);
|
||||
if (!hbData.Success)
|
||||
throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]");
|
||||
result = new StreamStatus()
|
||||
{
|
||||
IsLive = hbData.IsLive,
|
||||
ApiLink = hitboxUrl,
|
||||
Views = hbData.Views
|
||||
};
|
||||
_cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result);
|
||||
return result;
|
||||
case FollowedStream.FollowedStreamType.Twitch:
|
||||
var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username.ToLowerInvariant())}?client_id=67w6z9i09xv2uoojdm9l0wsyph4hxo6";
|
||||
if (checkCache && _cachedStatuses.TryGetValue(twitchUrl, out result))
|
||||
return result;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
response = await http.GetStringAsync(twitchUrl).ConfigureAwait(false);
|
||||
}
|
||||
var twData = JsonConvert.DeserializeObject<TwitchResponse>(response);
|
||||
if (twData.Error != null)
|
||||
{
|
||||
throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]");
|
||||
}
|
||||
result = new StreamStatus()
|
||||
{
|
||||
IsLive = twData.IsLive,
|
||||
ApiLink = twitchUrl,
|
||||
Views = twData.Stream?.Viewers.ToString() ?? "0"
|
||||
};
|
||||
_cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result);
|
||||
return result;
|
||||
case FollowedStream.FollowedStreamType.Beam:
|
||||
var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username.ToLowerInvariant()}";
|
||||
if (checkCache && _cachedStatuses.TryGetValue(beamUrl, out result))
|
||||
return result;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
response = await http.GetStringAsync(beamUrl).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var bmData = JsonConvert.DeserializeObject<BeamResponse>(response);
|
||||
if (bmData.Error != null)
|
||||
throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]");
|
||||
result = new StreamStatus()
|
||||
{
|
||||
IsLive = bmData.IsLive,
|
||||
ApiLink = beamUrl,
|
||||
Views = bmData.ViewersCurrent.ToString()
|
||||
};
|
||||
_cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result);
|
||||
return result;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
_db = db;
|
||||
_service = service;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -218,7 +53,7 @@ namespace NadekoBot.Modules.Searches
|
||||
public async Task ListStreams()
|
||||
{
|
||||
IEnumerable<FollowedStream> streams;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
streams = uow.GuildConfigs
|
||||
.For(Context.Guild.Id,
|
||||
@ -260,7 +95,7 @@ namespace NadekoBot.Modules.Searches
|
||||
};
|
||||
|
||||
bool removed;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(gc => gc.FollowedStreams));
|
||||
removed = config.FollowedStreams.Remove(fs);
|
||||
@ -287,7 +122,7 @@ namespace NadekoBot.Modules.Searches
|
||||
return;
|
||||
try
|
||||
{
|
||||
var streamStatus = (await GetStreamStatus(new FollowedStream
|
||||
var streamStatus = (await _service.GetStreamStatus(new FollowedStream
|
||||
{
|
||||
Username = stream,
|
||||
Type = platform,
|
||||
@ -325,7 +160,7 @@ namespace NadekoBot.Modules.Searches
|
||||
StreamStatus status;
|
||||
try
|
||||
{
|
||||
status = await GetStreamStatus(fs).ConfigureAwait(false);
|
||||
status = await _service.GetStreamStatus(fs).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -333,53 +168,15 @@ namespace NadekoBot.Modules.Searches
|
||||
return;
|
||||
}
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.FollowedStreams))
|
||||
.FollowedStreams
|
||||
.Add(fs);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await channel.EmbedAsync(fs.GetEmbed(status, Context.Guild.Id), GetText("stream_tracked")).ConfigureAwait(false);
|
||||
await channel.EmbedAsync(_service.GetEmbed(fs, status, Context.Guild.Id), GetText("stream_tracked")).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class FollowedStreamExtensions
|
||||
{
|
||||
public static EmbedBuilder GetEmbed(this FollowedStream fs, Searches.StreamStatus status, ulong guildId)
|
||||
{
|
||||
var embed = new EmbedBuilder().WithTitle(fs.Username)
|
||||
.WithUrl(fs.GetLink())
|
||||
.AddField(efb => efb.WithName(fs.GetText("status"))
|
||||
.WithValue(status.IsLive ? "Online" : "Offline")
|
||||
.WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(fs.GetText("viewers"))
|
||||
.WithValue(status.IsLive ? status.Views : "-")
|
||||
.WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(fs.GetText("platform"))
|
||||
.WithValue(fs.Type.ToString())
|
||||
.WithIsInline(true))
|
||||
.WithColor(status.IsLive ? NadekoBot.OkColor : NadekoBot.ErrorColor);
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
public static string GetText(this FollowedStream fs, string key, params object[] replacements) =>
|
||||
NadekoTopLevelModule.GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(fs.GuildId),
|
||||
typeof(Searches).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
|
||||
public static string GetLink(this FollowedStream fs)
|
||||
{
|
||||
if (fs.Type == FollowedStream.FollowedStreamType.Hitbox)
|
||||
return $"http://www.hitbox.tv/{fs.Username}/";
|
||||
if (fs.Type == FollowedStream.FollowedStreamType.Twitch)
|
||||
return $"http://www.twitch.tv/{fs.Username}/";
|
||||
if (fs.Type == FollowedStream.FollowedStreamType.Beam)
|
||||
return $"https://beam.pro/{fs.Username}/";
|
||||
return "??";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,54 +7,23 @@ using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Services.Searches;
|
||||
using NadekoBot.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
public partial class Searches
|
||||
{
|
||||
public struct UserChannelPair
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
}
|
||||
|
||||
[Group]
|
||||
public class TranslateCommands : NadekoSubmodule
|
||||
{
|
||||
private static ConcurrentDictionary<ulong, bool> translatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
|
||||
private static ConcurrentDictionary<UserChannelPair, string> userLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>();
|
||||
private readonly SearchesService _searches;
|
||||
private readonly IGoogleApiService _google;
|
||||
|
||||
static TranslateCommands()
|
||||
public TranslateCommands(SearchesService searches, IGoogleApiService google)
|
||||
{
|
||||
NadekoBot.Client.MessageReceived += async (msg) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var umsg = msg as SocketUserMessage;
|
||||
if (umsg == null)
|
||||
return;
|
||||
|
||||
bool autoDelete;
|
||||
if (!translatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
|
||||
return;
|
||||
var key = new UserChannelPair()
|
||||
{
|
||||
UserId = umsg.Author.Id,
|
||||
ChannelId = umsg.Channel.Id,
|
||||
};
|
||||
|
||||
string langs;
|
||||
if (!userLanguages.TryGetValue(key, out langs))
|
||||
return;
|
||||
|
||||
var text = await TranslateInternal(langs, umsg.Resolve(TagHandling.Ignore))
|
||||
.ConfigureAwait(false);
|
||||
if (autoDelete)
|
||||
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
await umsg.Channel.SendConfirmAsync($"{umsg.Author.Mention} `:` " + text.Replace("<@ ", "<@").Replace("<@! ", "<@!")).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
};
|
||||
_searches = searches;
|
||||
_google = google;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -63,7 +32,7 @@ namespace NadekoBot.Modules.Searches
|
||||
try
|
||||
{
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
var translation = await TranslateInternal(langs, text);
|
||||
var translation = await _searches.Translate(langs, text);
|
||||
await Context.Channel.SendConfirmAsync(GetText("translation") + " " + langs, translation).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
@ -72,19 +41,6 @@ namespace NadekoBot.Modules.Searches
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string> TranslateInternal(string langs, [Remainder] string text = null)
|
||||
{
|
||||
var langarr = langs.ToLowerInvariant().Split('>');
|
||||
if (langarr.Length != 2)
|
||||
throw new ArgumentException();
|
||||
var from = langarr[0];
|
||||
var to = langarr[1];
|
||||
text = text?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
throw new ArgumentException();
|
||||
return (await GoogleTranslator.Instance.Translate(text, from, to).ConfigureAwait(false)).SanitizeMentions();
|
||||
}
|
||||
|
||||
public enum AutoDeleteAutoTranslate
|
||||
{
|
||||
Del,
|
||||
@ -101,18 +57,17 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
if (autoDelete == AutoDeleteAutoTranslate.Del)
|
||||
{
|
||||
translatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true);
|
||||
_searches.TranslatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true);
|
||||
await ReplyConfirmLocalized("atl_ad_started").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool throwaway;
|
||||
if (translatedChannels.TryRemove(channel.Id, out throwaway))
|
||||
if (_searches.TranslatedChannels.TryRemove(channel.Id, out var throwaway))
|
||||
{
|
||||
await ReplyConfirmLocalized("atl_stopped").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (translatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del))
|
||||
if (_searches.TranslatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del))
|
||||
{
|
||||
await ReplyConfirmLocalized("atl_started").ConfigureAwait(false);
|
||||
}
|
||||
@ -130,7 +85,7 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
if (string.IsNullOrWhiteSpace(langs))
|
||||
{
|
||||
if (userLanguages.TryRemove(ucp, out langs))
|
||||
if (_searches.UserLanguages.TryRemove(ucp, out langs))
|
||||
await ReplyConfirmLocalized("atl_removed").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
@ -141,13 +96,13 @@ namespace NadekoBot.Modules.Searches
|
||||
var from = langarr[0];
|
||||
var to = langarr[1];
|
||||
|
||||
if (!GoogleTranslator.Instance.Languages.Contains(from) || !GoogleTranslator.Instance.Languages.Contains(to))
|
||||
if (!_google.Languages.Contains(from) || !_google.Languages.Contains(to))
|
||||
{
|
||||
await ReplyErrorLocalized("invalid_lang").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
userLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
|
||||
_searches.UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
|
||||
|
||||
await ReplyConfirmLocalized("atl_set", from, to).ConfigureAwait(false);
|
||||
}
|
||||
@ -156,7 +111,7 @@ namespace NadekoBot.Modules.Searches
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Translangs()
|
||||
{
|
||||
await Context.Channel.SendTableAsync(GoogleTranslator.Instance.Languages, str => $"{str,-15}", 3);
|
||||
await Context.Channel.SendTableAsync(_google.Languages, str => $"{str,-15}", 3);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Http;
|
||||
using NadekoBot.Services;
|
||||
using System.Threading.Tasks;
|
||||
@ -17,18 +16,27 @@ using NadekoBot.Modules.Searches.Commands.Models;
|
||||
using AngleSharp;
|
||||
using AngleSharp.Dom.Html;
|
||||
using AngleSharp.Dom;
|
||||
using System.Xml;
|
||||
using Configuration = AngleSharp.Configuration;
|
||||
using NadekoBot.Attributes;
|
||||
using Discord.Commands;
|
||||
using ImageSharp.Processing.Processors;
|
||||
using ImageSharp;
|
||||
using NadekoBot.Services.Searches;
|
||||
|
||||
namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
[NadekoModule("Searches", "~")]
|
||||
public partial class Searches : NadekoTopLevelModule
|
||||
{
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly IGoogleApiService _google;
|
||||
private readonly SearchesService _searches;
|
||||
|
||||
public Searches(IBotCredentials creds, IGoogleApiService google, SearchesService searches)
|
||||
{
|
||||
_creds = creds;
|
||||
_google = google;
|
||||
_searches = searches;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Weather([Remainder] string query)
|
||||
{
|
||||
@ -59,16 +67,16 @@ namespace NadekoBot.Modules.Searches
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Time([Remainder] string arg)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(arg) || string.IsNullOrWhiteSpace(NadekoBot.Credentials.GoogleApiKey))
|
||||
if (string.IsNullOrWhiteSpace(arg) || string.IsNullOrWhiteSpace(_creds.GoogleApiKey))
|
||||
return;
|
||||
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
var res = await http.GetStringAsync($"https://maps.googleapis.com/maps/api/geocode/json?address={arg}&key={NadekoBot.Credentials.GoogleApiKey}").ConfigureAwait(false);
|
||||
var res = await http.GetStringAsync($"https://maps.googleapis.com/maps/api/geocode/json?address={arg}&key={_creds.GoogleApiKey}").ConfigureAwait(false);
|
||||
var obj = JsonConvert.DeserializeObject<GeolocationResult>(res);
|
||||
|
||||
var currentSeconds = DateTime.UtcNow.UnixTimestamp();
|
||||
var timeRes = await http.GetStringAsync($"https://maps.googleapis.com/maps/api/timezone/json?location={obj.results[0].Geometry.Location.Lat},{obj.results[0].Geometry.Location.Lng}×tamp={currentSeconds}&key={NadekoBot.Credentials.GoogleApiKey}").ConfigureAwait(false);
|
||||
var timeRes = await http.GetStringAsync($"https://maps.googleapis.com/maps/api/timezone/json?location={obj.results[0].Geometry.Location.Lat},{obj.results[0].Geometry.Location.Lng}×tamp={currentSeconds}&key={_creds.GoogleApiKey}").ConfigureAwait(false);
|
||||
var timeObj = JsonConvert.DeserializeObject<TimeZoneResult>(timeRes);
|
||||
|
||||
var time = DateTime.UtcNow.AddSeconds(timeObj.DstOffset + timeObj.RawOffset);
|
||||
@ -81,7 +89,7 @@ namespace NadekoBot.Modules.Searches
|
||||
public async Task Youtube([Remainder] string query = null)
|
||||
{
|
||||
if (!await ValidateQuery(Context.Channel, query).ConfigureAwait(false)) return;
|
||||
var result = (await NadekoBot.Google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault();
|
||||
var result = (await _google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault();
|
||||
if (string.IsNullOrWhiteSpace(result))
|
||||
{
|
||||
await ReplyErrorLocalized("no_results").ConfigureAwait(false);
|
||||
@ -97,7 +105,7 @@ namespace NadekoBot.Modules.Searches
|
||||
if (!(await ValidateQuery(Context.Channel, query).ConfigureAwait(false))) return;
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
|
||||
var movie = await OmdbProvider.FindMovie(query);
|
||||
var movie = await OmdbProvider.FindMovie(query, _google);
|
||||
if (movie == null)
|
||||
{
|
||||
await ReplyErrorLocalized("imdb_fail").ConfigureAwait(false);
|
||||
@ -137,7 +145,7 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
try
|
||||
{
|
||||
var res = await NadekoBot.Google.GetImageAsync(terms).ConfigureAwait(false);
|
||||
var res = await _google.GetImageAsync(terms).ConfigureAwait(false);
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50))
|
||||
@ -189,7 +197,7 @@ namespace NadekoBot.Modules.Searches
|
||||
terms = WebUtility.UrlEncode(terms).Replace(' ', '+');
|
||||
try
|
||||
{
|
||||
var res = await NadekoBot.Google.GetImageAsync(terms, new NadekoRandom().Next(0, 50)).ConfigureAwait(false);
|
||||
var res = await _google.GetImageAsync(terms, new NadekoRandom().Next(0, 50)).ConfigureAwait(false);
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50))
|
||||
@ -239,7 +247,7 @@ namespace NadekoBot.Modules.Searches
|
||||
if (string.IsNullOrWhiteSpace(ffs))
|
||||
return;
|
||||
|
||||
await Context.Channel.SendConfirmAsync("<" + await NadekoBot.Google.ShortenUrl($"http://lmgtfy.com/?q={ Uri.EscapeUriString(ffs) }") + ">")
|
||||
await Context.Channel.SendConfirmAsync("<" + await _google.ShortenUrl($"http://lmgtfy.com/?q={ Uri.EscapeUriString(ffs) }") + ">")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -249,7 +257,7 @@ namespace NadekoBot.Modules.Searches
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
return;
|
||||
|
||||
var shortened = await NadekoBot.Google.ShortenUrl(arg).ConfigureAwait(false);
|
||||
var shortened = await _google.ShortenUrl(arg).ConfigureAwait(false);
|
||||
|
||||
if (shortened == arg)
|
||||
{
|
||||
@ -315,7 +323,7 @@ namespace NadekoBot.Modules.Searches
|
||||
.WithFooter(efb => efb.WithText(totalResults));
|
||||
|
||||
var desc = await Task.WhenAll(results.Select(async res =>
|
||||
$"[{Format.Bold(res?.Title)}]({(await NadekoBot.Google.ShortenUrl(res?.Link))})\n{res?.Text}\n\n"))
|
||||
$"[{Format.Bold(res?.Title)}]({(await _google.ShortenUrl(res?.Link))})\n{res?.Text}\n\n"))
|
||||
.ConfigureAwait(false);
|
||||
await Context.Channel.EmbedAsync(embed.WithDescription(string.Concat(desc))).ConfigureAwait(false);
|
||||
}
|
||||
@ -339,7 +347,7 @@ namespace NadekoBot.Modules.Searches
|
||||
if (items == null || items.Length == 0)
|
||||
throw new KeyNotFoundException("Cannot find a card by that name");
|
||||
var item = items[new NadekoRandom().Next(0, items.Length)];
|
||||
var storeUrl = await NadekoBot.Google.ShortenUrl(item["store_url"].ToString());
|
||||
var storeUrl = await _google.ShortenUrl(item["store_url"].ToString());
|
||||
var cost = item["cost"].ToString();
|
||||
var desc = item["text"].ToString();
|
||||
var types = string.Join(",\n", item["types"].ToObject<string[]>());
|
||||
@ -351,7 +359,7 @@ namespace NadekoBot.Modules.Searches
|
||||
.AddField(efb => efb.WithName(GetText("store_url")).WithValue(storeUrl).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("cost")).WithValue(cost).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("types")).WithValue(types).WithIsInline(true));
|
||||
//.AddField(efb => efb.WithName("Store Url").WithValue(await NadekoBot.Google.ShortenUrl(items[0]["store_url"].ToString())).WithIsInline(true));
|
||||
//.AddField(efb => efb.WithName("Store Url").WithValue(await _google.ShortenUrl(items[0]["store_url"].ToString())).WithIsInline(true));
|
||||
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
@ -369,7 +377,7 @@ namespace NadekoBot.Modules.Searches
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
|
||||
if (string.IsNullOrWhiteSpace(_creds.MashapeKey))
|
||||
{
|
||||
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
|
||||
return;
|
||||
@ -379,7 +387,7 @@ namespace NadekoBot.Modules.Searches
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey);
|
||||
http.DefaultRequestHeaders.Add("X-Mashape-Key", _creds.MashapeKey);
|
||||
var response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.mashape.com/cards/search/{Uri.EscapeUriString(arg)}")
|
||||
.ConfigureAwait(false);
|
||||
try
|
||||
@ -422,7 +430,7 @@ namespace NadekoBot.Modules.Searches
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Yodify([Remainder] string query = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
|
||||
if (string.IsNullOrWhiteSpace(_creds.MashapeKey))
|
||||
{
|
||||
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
|
||||
return;
|
||||
@ -435,7 +443,7 @@ namespace NadekoBot.Modules.Searches
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey);
|
||||
http.DefaultRequestHeaders.Add("X-Mashape-Key", _creds.MashapeKey);
|
||||
http.DefaultRequestHeaders.Add("Accept", "text/plain");
|
||||
var res = await http.GetStringAsync($"https://yoda.p.mashape.com/yoda?sentence={Uri.EscapeUriString(query)}").ConfigureAwait(false);
|
||||
try
|
||||
@ -457,7 +465,7 @@ namespace NadekoBot.Modules.Searches
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task UrbanDict([Remainder] string query = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
|
||||
if (string.IsNullOrWhiteSpace(_creds.MashapeKey))
|
||||
{
|
||||
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
|
||||
return;
|
||||
@ -531,7 +539,7 @@ namespace NadekoBot.Modules.Searches
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
|
||||
if (string.IsNullOrWhiteSpace(_creds.MashapeKey))
|
||||
{
|
||||
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
|
||||
return;
|
||||
@ -542,7 +550,7 @@ namespace NadekoBot.Modules.Searches
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey);
|
||||
http.DefaultRequestHeaders.Add("X-Mashape-Key", _creds.MashapeKey);
|
||||
res = await http.GetStringAsync($"https://tagdef.p.mashape.com/one.{Uri.EscapeUriString(query)}.json").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -655,7 +663,7 @@ namespace NadekoBot.Modules.Searches
|
||||
usr = (IGuildUser)Context.User;
|
||||
|
||||
var avatarUrl = usr.RealAvatarUrl();
|
||||
var shortenedAvatarUrl = await NadekoBot.Google.ShortenUrl(avatarUrl).ConfigureAwait(false);
|
||||
var shortenedAvatarUrl = await _google.ShortenUrl(avatarUrl).ConfigureAwait(false);
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.AddField(efb => efb.WithName("Username").WithValue(usr.ToString()).WithIsInline(false))
|
||||
.AddField(efb => efb.WithName("Avatar Url").WithValue(shortenedAvatarUrl).WithIsInline(false))
|
||||
@ -682,7 +690,7 @@ namespace NadekoBot.Modules.Searches
|
||||
var found = items["items"][0];
|
||||
var response = $@"`{GetText("title")}` {found["title"]}
|
||||
`{GetText("quality")}` {found["quality"]}
|
||||
`{GetText("url")}:` {await NadekoBot.Google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}";
|
||||
`{GetText("url")}:` {await _google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}";
|
||||
await Context.Channel.SendMessageAsync(response).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
@ -769,7 +777,7 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
tag = tag?.Trim() ?? "";
|
||||
|
||||
var url = await InternalDapiSearch(tag, type).ConfigureAwait(false);
|
||||
var url = await _searches.DapiSearch(tag, type).ConfigureAwait(false);
|
||||
|
||||
if (url == null)
|
||||
await channel.SendErrorAsync(umsg.Author.Mention + " " + GetText("no_results"));
|
||||
|
@ -21,6 +21,8 @@ using NadekoBot.Services.Searches;
|
||||
using NadekoBot.Services.ClashOfClans;
|
||||
using NadekoBot.Services.Music;
|
||||
using NadekoBot.Services.CustomReactions;
|
||||
using NadekoBot.Services.Games;
|
||||
using NadekoBot.Services.Administration;
|
||||
|
||||
namespace NadekoBot
|
||||
{
|
||||
@ -73,11 +75,10 @@ namespace NadekoBot
|
||||
});
|
||||
|
||||
var google = new GoogleApiService(credentials);
|
||||
var strings = new NadekoStrings();
|
||||
|
||||
var greetSettingsService = new GreetSettingsService(AllGuildConfigs, db);
|
||||
|
||||
var localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), db);
|
||||
var strings = new NadekoStrings(localization);
|
||||
|
||||
var greetSettingsService = new GreetSettingsService(Client, AllGuildConfigs, db);
|
||||
|
||||
var commandService = new CommandService(new CommandServiceConfig()
|
||||
{
|
||||
@ -97,10 +98,18 @@ namespace NadekoBot
|
||||
|
||||
//module services
|
||||
var utilityService = new UtilityService(AllGuildConfigs, Client, BotConfig, db);
|
||||
var searchesService = new SearchesService();
|
||||
var searchesService = new SearchesService(Client, google, db);
|
||||
var clashService = new ClashOfClansService(Client, db, localization, strings);
|
||||
var musicService = new MusicService(google, strings, localization, db, soundcloud, credentials);
|
||||
var crService = new CustomReactionsService(db, Client);
|
||||
var gamesService = new GamesService(Client, BotConfig, AllGuildConfigs, strings, images);
|
||||
#region administration
|
||||
var administrationService = new AdministrationService(AllGuildConfigs, commandHandler);
|
||||
var selfService = new SelfService(this, commandHandler, db, BotConfig);
|
||||
var vcRoleService = new VcRoleService(Client, AllGuildConfigs);
|
||||
var vPlusTService = new VplusTService(Client, AllGuildConfigs, strings, db);
|
||||
#endregion
|
||||
|
||||
|
||||
//initialize Services
|
||||
Services = new NServiceProvider.ServiceProviderBuilder() //todo all Adds should be interfaces
|
||||
@ -124,6 +133,10 @@ namespace NadekoBot
|
||||
.Add<MusicService>(musicService)
|
||||
.Add<GreetSettingsService>(greetSettingsService)
|
||||
.Add<CustomReactionsService>(crService)
|
||||
.Add<GamesService>(gamesService)
|
||||
.Add(selfService)
|
||||
.Add(vcRoleService)
|
||||
.Add(vPlusTService)
|
||||
.Build();
|
||||
|
||||
commandHandler.AddServices(Services);
|
||||
|
@ -29,21 +29,22 @@
|
||||
<ItemGroup>
|
||||
<Compile Remove="data\**\*;credentials.json;credentials_example.json" />
|
||||
<Compile Remove="Modules\Administration\**" />
|
||||
<Compile Remove="Modules\Games\**" />
|
||||
<Compile Remove="Modules\NSFW\**" />
|
||||
<Compile Remove="Modules\Permissions\**" />
|
||||
<Compile Remove="Modules\Searches\**" />
|
||||
<EmbeddedResource Remove="Modules\Administration\**" />
|
||||
<EmbeddedResource Remove="Modules\Games\**" />
|
||||
<EmbeddedResource Remove="Modules\NSFW\**" />
|
||||
<EmbeddedResource Remove="Modules\Permissions\**" />
|
||||
<EmbeddedResource Remove="Modules\Searches\**" />
|
||||
<None Remove="Modules\Administration\**" />
|
||||
<None Remove="Modules\Games\**" />
|
||||
<None Remove="Modules\NSFW\**" />
|
||||
<None Remove="Modules\Permissions\**" />
|
||||
<None Remove="Modules\Searches\**" />
|
||||
<Compile Remove="Modules\Gambling\Commands\Lucky7Commands.cs" />
|
||||
<Compile Include="Modules\Administration\Administration.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\MuteCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\SelfCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\ServerGreetCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\UserPunishCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\VcRoleCommands.cs" />
|
||||
<Compile Include="Modules\Administration\Commands\VoicePlusTextCommands.cs" />
|
||||
<None Update="libsodium.dll;opus.dll;libsodium.so;libopus.so">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
@ -0,0 +1,49 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public class AdministrationService
|
||||
{
|
||||
public readonly ConcurrentHashSet<ulong> DeleteMessagesOnCommand;
|
||||
private readonly Logger _log;
|
||||
|
||||
public AdministrationService(IEnumerable<GuildConfig> gcs, CommandHandler cmdHandler)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(gcs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
|
||||
cmdHandler.CommandExecuted += DelMsgOnCmd_Handler;
|
||||
}
|
||||
|
||||
private Task DelMsgOnCmd_Handler(IUserMessage msg, CommandInfo cmd)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var channel = msg.Channel as SocketTextChannel;
|
||||
if (channel == null)
|
||||
return;
|
||||
if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune" && cmd.Name != "pick")
|
||||
await msg.DeleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn("Delmsgoncmd errored...");
|
||||
_log.Warn(ex);
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
275
src/NadekoBot/Services/Administration/MuteService.cs
Normal file
275
src/NadekoBot/Services/Administration/MuteService.cs
Normal file
@ -0,0 +1,275 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public class MuteService
|
||||
{
|
||||
public ConcurrentDictionary<ulong, string> GuildMuteRoles { get; }
|
||||
public ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; }
|
||||
public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, Timer>> UnmuteTimers { get; }
|
||||
= new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, Timer>>();
|
||||
|
||||
public event Action<IGuildUser, MuteType> UserMuted = delegate { };
|
||||
public event Action<IGuildUser, MuteType> UserUnmuted = delegate { };
|
||||
|
||||
private static readonly OverwritePermissions denyOverwrite = new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny);
|
||||
|
||||
private readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly DbHandler _db;
|
||||
|
||||
public MuteService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, DbHandler db)
|
||||
{
|
||||
_client = client;
|
||||
_db = db;
|
||||
|
||||
GuildMuteRoles = new ConcurrentDictionary<ulong, string>(gcs
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
|
||||
.ToDictionary(c => c.GuildId, c => c.MuteRoleName));
|
||||
|
||||
MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(gcs.ToDictionary(
|
||||
k => k.GuildId,
|
||||
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
|
||||
));
|
||||
|
||||
foreach (var conf in gcs)
|
||||
{
|
||||
foreach (var x in conf.UnmuteTimers)
|
||||
{
|
||||
TimeSpan after;
|
||||
if (x.UnmuteAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
|
||||
{
|
||||
after = TimeSpan.FromMinutes(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
after = x.UnmuteAt - DateTime.UtcNow;
|
||||
}
|
||||
StartUnmuteTimer(conf.GuildId, x.UserId, after);
|
||||
}
|
||||
}
|
||||
|
||||
_client.UserJoined += Client_UserJoined;
|
||||
}
|
||||
|
||||
private async Task Client_UserJoined(IGuildUser usr)
|
||||
{
|
||||
try
|
||||
{
|
||||
MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted);
|
||||
|
||||
if (muted == null || !muted.Contains(usr.Id))
|
||||
return;
|
||||
await MuteUser(usr).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.GetCurrentClassLogger().Warn(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task MuteUser(IGuildUser usr, MuteType type = MuteType.All)
|
||||
{
|
||||
if (type == MuteType.All)
|
||||
{
|
||||
await usr.ModifyAsync(x => x.Mute = true).ConfigureAwait(false);
|
||||
var muteRole = await GetMuteRole(usr.Guild);
|
||||
if (!usr.RoleIds.Contains(muteRole.Id))
|
||||
await usr.AddRoleAsync(muteRole).ConfigureAwait(false);
|
||||
StopUnmuteTimer(usr.GuildId, usr.Id);
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.GuildConfigs.For(usr.Guild.Id,
|
||||
set => set.Include(gc => gc.MutedUsers)
|
||||
.Include(gc => gc.UnmuteTimers));
|
||||
config.MutedUsers.Add(new MutedUserId()
|
||||
{
|
||||
UserId = usr.Id
|
||||
});
|
||||
if (MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted))
|
||||
muted.Add(usr.Id);
|
||||
|
||||
config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id);
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
UserMuted(usr, MuteType.All);
|
||||
}
|
||||
else if (type == MuteType.Voice)
|
||||
{
|
||||
await usr.ModifyAsync(x => x.Mute = true).ConfigureAwait(false);
|
||||
UserMuted(usr, MuteType.Voice);
|
||||
}
|
||||
else if (type == MuteType.Chat)
|
||||
{
|
||||
await usr.AddRoleAsync(await GetMuteRole(usr.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
||||
UserMuted(usr, MuteType.Chat);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UnmuteUser(IGuildUser usr, MuteType type = MuteType.All)
|
||||
{
|
||||
if (type == MuteType.All)
|
||||
{
|
||||
StopUnmuteTimer(usr.GuildId, usr.Id);
|
||||
try { await usr.ModifyAsync(x => x.Mute = false).ConfigureAwait(false); } catch { }
|
||||
try { await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild)).ConfigureAwait(false); } catch { /*ignore*/ }
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.GuildConfigs.For(usr.Guild.Id, set => set.Include(gc => gc.MutedUsers)
|
||||
.Include(gc => gc.UnmuteTimers));
|
||||
config.MutedUsers.Remove(new MutedUserId()
|
||||
{
|
||||
UserId = usr.Id
|
||||
});
|
||||
if (MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted))
|
||||
muted.TryRemove(usr.Id);
|
||||
|
||||
config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id);
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
UserUnmuted(usr, MuteType.All);
|
||||
}
|
||||
else if (type == MuteType.Voice)
|
||||
{
|
||||
await usr.ModifyAsync(x => x.Mute = false).ConfigureAwait(false);
|
||||
UserUnmuted(usr, MuteType.Voice);
|
||||
}
|
||||
else if (type == MuteType.Chat)
|
||||
{
|
||||
await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
||||
UserUnmuted(usr, MuteType.Chat);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IRole> GetMuteRole(IGuild guild)
|
||||
{
|
||||
const string defaultMuteRoleName = "nadeko-mute";
|
||||
|
||||
var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName);
|
||||
|
||||
var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
|
||||
if (muteRole == null)
|
||||
{
|
||||
|
||||
//if it doesn't exist, create it
|
||||
try { muteRole = await guild.CreateRoleAsync(muteRoleName, GuildPermissions.None).ConfigureAwait(false); }
|
||||
catch
|
||||
{
|
||||
//if creations fails, maybe the name is not correct, find default one, if doesn't work, create default one
|
||||
muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName) ??
|
||||
await guild.CreateRoleAsync(defaultMuteRoleName, GuildPermissions.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var toOverwrite in (await guild.GetTextChannelsAsync()))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!toOverwrite.PermissionOverwrites.Select(x => x.Permissions).Contains(denyOverwrite))
|
||||
{
|
||||
await toOverwrite.AddPermissionOverwriteAsync(muteRole, denyOverwrite)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
return muteRole;
|
||||
}
|
||||
|
||||
public async Task TimedMute(IGuildUser user, TimeSpan after)
|
||||
{
|
||||
await MuteUser(user).ConfigureAwait(false); // mute the user. This will also remove any previous unmute timers
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.GuildConfigs.For(user.GuildId, set => set.Include(x => x.UnmuteTimers));
|
||||
config.UnmuteTimers.Add(new UnmuteTimer()
|
||||
{
|
||||
UserId = user.Id,
|
||||
UnmuteAt = DateTime.UtcNow + after,
|
||||
}); // add teh unmute timer to the database
|
||||
uow.Complete();
|
||||
}
|
||||
StartUnmuteTimer(user.GuildId, user.Id, after); // start the timer
|
||||
}
|
||||
|
||||
public void StartUnmuteTimer(ulong guildId, ulong userId, TimeSpan after)
|
||||
{
|
||||
//load the unmute timers for this guild
|
||||
var userUnmuteTimers = UnmuteTimers.GetOrAdd(guildId, new ConcurrentDictionary<ulong, Timer>());
|
||||
|
||||
//unmute timer to be added
|
||||
var toAdd = new Timer(async _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var guild = _client.GetGuild(guildId); // load the guild
|
||||
if (guild == null)
|
||||
{
|
||||
RemoveUnmuteTimerFromDb(guildId, userId);
|
||||
return; // if guild can't be found, just remove the timer from db
|
||||
}
|
||||
// unmute the user, this will also remove the timer from the db
|
||||
await UnmuteUser(guild.GetUser(userId)).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RemoveUnmuteTimerFromDb(guildId, userId); // if unmute errored, just remove unmute from db
|
||||
_log.Warn("Couldn't unmute user {0} in guild {1}", userId, guildId);
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}, null, after, Timeout.InfiniteTimeSpan);
|
||||
|
||||
//add it, or stop the old one and add this one
|
||||
userUnmuteTimers.AddOrUpdate(userId, (key) => toAdd, (key, old) =>
|
||||
{
|
||||
old.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
return toAdd;
|
||||
});
|
||||
}
|
||||
|
||||
public void StopUnmuteTimer(ulong guildId, ulong userId)
|
||||
{
|
||||
if (!UnmuteTimers.TryGetValue(guildId, out ConcurrentDictionary<ulong, Timer> userUnmuteTimers)) return;
|
||||
|
||||
if (userUnmuteTimers.TryRemove(userId, out Timer removed))
|
||||
{
|
||||
removed.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveUnmuteTimerFromDb(ulong guildId, ulong userId)
|
||||
{
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.GuildConfigs.For(guildId, set => set.Include(x => x.UnmuteTimers));
|
||||
config.UnmuteTimers.RemoveWhere(x => x.UserId == userId);
|
||||
uow.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum MuteType
|
||||
{
|
||||
Voice,
|
||||
Chat,
|
||||
All
|
||||
}
|
||||
}
|
48
src/NadekoBot/Services/Administration/SelfService.cs
Normal file
48
src/NadekoBot/Services/Administration/SelfService.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public class SelfService
|
||||
{
|
||||
public volatile bool ForwardDMs;
|
||||
public volatile bool ForwardDMsToAllOwners;
|
||||
|
||||
private readonly Logger _log;
|
||||
private readonly NadekoBot _bot;
|
||||
private readonly CommandHandler _cmdHandler;
|
||||
private readonly DbHandler _db;
|
||||
|
||||
public SelfService(NadekoBot bot, CommandHandler cmdHandler, DbHandler db,
|
||||
BotConfig bc)
|
||||
{
|
||||
_bot = bot;
|
||||
_cmdHandler = cmdHandler;
|
||||
_db = db;
|
||||
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
ForwardDMs = config.ForwardMessages;
|
||||
ForwardDMsToAllOwners = config.ForwardToAllOwners;
|
||||
}
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
while (!bot.Ready)
|
||||
await Task.Delay(1000);
|
||||
|
||||
foreach (var cmd in bc.StartupCommands)
|
||||
{
|
||||
await cmdHandler.ExecuteExternal(cmd.GuildId, cmd.ChannelId, cmd.CommandText);
|
||||
await Task.Delay(400).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
102
src/NadekoBot/Services/Administration/VcRoleService.cs
Normal file
102
src/NadekoBot/Services/Administration/VcRoleService.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public class VcRoleService
|
||||
{
|
||||
private readonly Logger _log;
|
||||
|
||||
public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; }
|
||||
|
||||
public VcRoleService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated;
|
||||
VcRoles = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>>();
|
||||
foreach (var gconf in gcs)
|
||||
{
|
||||
var g = client.GetGuild(gconf.GuildId);
|
||||
if (g == null)
|
||||
continue; //todo delete everything from db if guild doesn't exist?
|
||||
|
||||
var infos = new ConcurrentDictionary<ulong, IRole>();
|
||||
VcRoles.TryAdd(gconf.GuildId, infos);
|
||||
foreach (var ri in gconf.VcRoleInfos)
|
||||
{
|
||||
var role = g.GetRole(ri.RoleId);
|
||||
if (role == null)
|
||||
continue; //todo remove this entry from db
|
||||
|
||||
infos.TryAdd(ri.VoiceChannelId, role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState,
|
||||
SocketVoiceState newState)
|
||||
{
|
||||
|
||||
var gusr = usr as SocketGuildUser;
|
||||
if (gusr == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var oldVc = oldState.VoiceChannel;
|
||||
var newVc = newState.VoiceChannel;
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (oldVc != newVc)
|
||||
{
|
||||
ulong guildId;
|
||||
guildId = newVc?.Guild.Id ?? oldVc.Guild.Id;
|
||||
|
||||
if (VcRoles.TryGetValue(guildId, out ConcurrentDictionary<ulong, IRole> guildVcRoles))
|
||||
{
|
||||
//remove old
|
||||
if (oldVc != null && guildVcRoles.TryGetValue(oldVc.Id, out IRole role))
|
||||
{
|
||||
if (gusr.Roles.Contains(role))
|
||||
{
|
||||
try
|
||||
{
|
||||
await gusr.RemoveRoleAsync(role).ConfigureAwait(false);
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
await gusr.RemoveRoleAsync(role).ConfigureAwait(false);
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
//add new
|
||||
if (newVc != null && guildVcRoles.TryGetValue(newVc.Id, out role))
|
||||
{
|
||||
if (!gusr.Roles.Contains(role))
|
||||
await gusr.AddRoleAsync(role).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
157
src/NadekoBot/Services/Administration/VplusTService.cs
Normal file
157
src/NadekoBot/Services/Administration/VplusTService.cs
Normal file
@ -0,0 +1,157 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
public class VplusTService
|
||||
{
|
||||
private readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);
|
||||
|
||||
public readonly ConcurrentHashSet<ulong> VoicePlusTextCache;
|
||||
|
||||
private readonly ConcurrentDictionary<ulong, SemaphoreSlim> _guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>();
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly NadekoStrings _strings;
|
||||
private readonly DbHandler _db;
|
||||
private readonly Logger _log;
|
||||
|
||||
public VplusTService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, NadekoStrings strings,
|
||||
DbHandler db)
|
||||
{
|
||||
_client = client;
|
||||
_strings = strings;
|
||||
_db = db;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
VoicePlusTextCache = new ConcurrentHashSet<ulong>(gcs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
|
||||
_client.UserVoiceStateUpdated += UserUpdatedEventHandler;
|
||||
}
|
||||
|
||||
private Task UserUpdatedEventHandler(SocketUser iuser, SocketVoiceState before, SocketVoiceState after)
|
||||
{
|
||||
var user = (iuser as SocketGuildUser);
|
||||
var guild = user?.Guild;
|
||||
|
||||
if (guild == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var botUserPerms = guild.CurrentUser.GuildPermissions;
|
||||
|
||||
if (before.VoiceChannel == after.VoiceChannel)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!VoicePlusTextCache.Contains(guild.Id))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles)
|
||||
{
|
||||
try
|
||||
{
|
||||
await guild.Owner.SendErrorAsync(
|
||||
_strings.GetText("vt_exit",
|
||||
guild.Id,
|
||||
"Administration".ToLowerInvariant(),
|
||||
Format.Bold(guild.Name))).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
|
||||
VoicePlusTextCache.TryRemove(guild.Id);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var semaphore = _guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1));
|
||||
|
||||
try
|
||||
{
|
||||
await semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var beforeVch = before.VoiceChannel;
|
||||
if (beforeVch != null)
|
||||
{
|
||||
var beforeRoleName = GetRoleName(beforeVch);
|
||||
var beforeRole = guild.Roles.FirstOrDefault(x => x.Name == beforeRoleName);
|
||||
if (beforeRole != null)
|
||||
try
|
||||
{
|
||||
_log.Info("Removing role " + beforeRoleName + " from user " + user.Username);
|
||||
await user.RemoveRoleAsync(beforeRole).ConfigureAwait(false);
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}
|
||||
var afterVch = after.VoiceChannel;
|
||||
if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id)
|
||||
{
|
||||
var roleName = GetRoleName(afterVch);
|
||||
var roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName) ??
|
||||
(IRole)await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false);
|
||||
|
||||
ITextChannel textChannel = guild.TextChannels
|
||||
.FirstOrDefault(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant());
|
||||
if (textChannel == null)
|
||||
{
|
||||
var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false));
|
||||
|
||||
try { await guild.CurrentUser.AddRoleAsync(roleToAdd).ConfigureAwait(false); } catch {/*ignored*/}
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
await created.AddPermissionOverwriteAsync(roleToAdd, new OverwritePermissions(
|
||||
readMessages: PermValue.Allow,
|
||||
sendMessages: PermValue.Allow))
|
||||
.ConfigureAwait(false);
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
await created.AddPermissionOverwriteAsync(guild.EveryoneRole, new OverwritePermissions(
|
||||
readMessages: PermValue.Deny,
|
||||
sendMessages: PermValue.Deny))
|
||||
.ConfigureAwait(false);
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
}
|
||||
_log.Warn("Adding role " + roleToAdd.Name + " to user " + user.Username);
|
||||
await user.AddRoleAsync(roleToAdd).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
semaphore.Release();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public string GetChannelName(string voiceName) =>
|
||||
_channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice";
|
||||
|
||||
public string GetRoleName(IVoiceChannel ch) =>
|
||||
"nvoice-" + ch.Id;
|
||||
}
|
||||
}
|
@ -544,7 +544,7 @@ namespace NadekoBot.Services
|
||||
//////int price;
|
||||
//////if (Permissions.CommandCostCommands.CommandCosts.TryGetValue(cmd.Aliases.First().Trim().ToLowerInvariant(), out price) && price > 0)
|
||||
//////{
|
||||
////// var success = await CurrencyHandler.RemoveCurrencyAsync(context.User.Id, $"Running {cmd.Name} command.", price).ConfigureAwait(false);
|
||||
////// var success = await _ch.RemoveCurrencyAsync(context.User.Id, $"Running {cmd.Name} command.", price).ConfigureAwait(false);
|
||||
////// if (!success)
|
||||
////// {
|
||||
////// return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, $"Insufficient funds. You need {price}{NadekoBot.BotConfig.CurrencySign} to run this command."));
|
||||
|
@ -19,7 +19,7 @@ namespace NadekoBot.Services
|
||||
var optionsBuilder = new DbContextOptionsBuilder();
|
||||
optionsBuilder.UseSqlite(creds.Db.ConnectionString);
|
||||
options = optionsBuilder.Options;
|
||||
//switch (NadekoBot.Credentials.Db.Type.ToUpperInvariant())
|
||||
//switch (_creds.Db.Type.ToUpperInvariant())
|
||||
//{
|
||||
// case "SQLITE":
|
||||
// dbType = typeof(NadekoSqliteContext);
|
||||
|
8
src/NadekoBot/Services/Games/ChatterBotResponse.cs
Normal file
8
src/NadekoBot/Services/Games/ChatterBotResponse.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace NadekoBot.Services.Games
|
||||
{
|
||||
public class ChatterBotResponse
|
||||
{
|
||||
public string Convo_id { get; set; }
|
||||
public string BotSay { get; set; }
|
||||
}
|
||||
}
|
39
src/NadekoBot/Services/Games/ChatterBotSession.cs
Normal file
39
src/NadekoBot/Services/Games/ChatterBotSession.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using NadekoBot.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Immutable;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Games
|
||||
{
|
||||
public class ChatterBotSession
|
||||
{
|
||||
private static NadekoRandom rng { get; } = new NadekoRandom();
|
||||
public string ChatterbotId { get; }
|
||||
public string ChannelId { get; }
|
||||
private int _botId = 6;
|
||||
|
||||
public ChatterBotSession(ulong channelId)
|
||||
{
|
||||
ChannelId = channelId.ToString().ToBase64();
|
||||
ChatterbotId = rng.Next(0, 1000000).ToString().ToBase64();
|
||||
}
|
||||
|
||||
private string apiEndpoint => "http://api.program-o.com/v2/chatbot/" +
|
||||
$"?bot_id={_botId}&" +
|
||||
"say={0}&" +
|
||||
$"convo_id=nadekobot_{ChatterbotId}_{ChannelId}&" +
|
||||
"format=json";
|
||||
|
||||
public async Task<string> Think(string message)
|
||||
{
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
var res = await http.GetStringAsync(string.Format(apiEndpoint, message)).ConfigureAwait(false);
|
||||
var cbr = JsonConvert.DeserializeObject<ChatterBotResponse>(res);
|
||||
//Console.WriteLine(cbr.Convo_id);
|
||||
return cbr.BotSay.Replace("<br/>", "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
206
src/NadekoBot/Services/Games/GamesService.cs
Normal file
206
src/NadekoBot/Services/Games/GamesService.cs
Normal file
@ -0,0 +1,206 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Games
|
||||
{
|
||||
public class GamesService
|
||||
{
|
||||
private readonly BotConfig _bc;
|
||||
|
||||
public readonly ConcurrentDictionary<ulong, GirlRating> GirlRatings = new ConcurrentDictionary<ulong, GirlRating>();
|
||||
public readonly ImmutableArray<string> EightBallResponses;
|
||||
|
||||
private readonly Timer _t;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly NadekoStrings _strings;
|
||||
private readonly IImagesService _images;
|
||||
private readonly Logger _log;
|
||||
|
||||
public ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; }
|
||||
|
||||
public readonly string TypingArticlesPath = "data/typing_articles2.json";
|
||||
public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
|
||||
|
||||
public GamesService(DiscordShardedClient client, BotConfig bc, IEnumerable<GuildConfig> gcs,
|
||||
NadekoStrings strings, IImagesService images)
|
||||
{
|
||||
_bc = bc;
|
||||
_client = client;
|
||||
_strings = strings;
|
||||
_images = images;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
//8ball
|
||||
EightBallResponses = _bc.EightBallResponses.Select(ebr => ebr.Text).ToImmutableArray();
|
||||
|
||||
//girl ratings
|
||||
_t = new Timer((_) =>
|
||||
{
|
||||
GirlRatings.Clear();
|
||||
|
||||
}, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1));
|
||||
|
||||
//cleverbot
|
||||
CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
|
||||
gcs.Where(gc => gc.CleverbotEnabled)
|
||||
.ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => new ChatterBotSession(gc.GuildId), true)));
|
||||
|
||||
//plantpick
|
||||
client.MessageReceived += PotentialFlowerGeneration;
|
||||
GenerationChannels = new ConcurrentHashSet<ulong>(gcs
|
||||
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
|
||||
|
||||
try
|
||||
{
|
||||
TypingArticles = JsonConvert.DeserializeObject<List<TypingArticle>>(File.ReadAllText(TypingArticlesPath));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn("Error while loading typing articles {0}", ex.ToString());
|
||||
TypingArticles = new List<TypingArticle>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string PrepareMessage(IUserMessage msg, out ChatterBotSession cleverbot)
|
||||
{
|
||||
var channel = msg.Channel as ITextChannel;
|
||||
cleverbot = null;
|
||||
|
||||
if (channel == null)
|
||||
return null;
|
||||
|
||||
Lazy<ChatterBotSession> lazyCleverbot;
|
||||
if (!CleverbotGuilds.TryGetValue(channel.Guild.Id, out lazyCleverbot))
|
||||
return null;
|
||||
|
||||
cleverbot = lazyCleverbot.Value;
|
||||
|
||||
var nadekoId = _client.CurrentUser.Id;
|
||||
var normalMention = $"<@{nadekoId}> ";
|
||||
var nickMention = $"<@!{nadekoId}> ";
|
||||
string message;
|
||||
if (msg.Content.StartsWith(normalMention))
|
||||
{
|
||||
message = msg.Content.Substring(normalMention.Length).Trim();
|
||||
}
|
||||
else if (msg.Content.StartsWith(nickMention))
|
||||
{
|
||||
message = msg.Content.Substring(nickMention.Length).Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public async Task<bool> TryAsk(ChatterBotSession cleverbot, ITextChannel channel, string message)
|
||||
{
|
||||
await channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
|
||||
var response = await cleverbot.Think(message).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false); // try twice :\
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ConcurrentHashSet<ulong> GenerationChannels { get; }
|
||||
//channelid/message
|
||||
public ConcurrentDictionary<ulong, List<IUserMessage>> PlantedFlowers { get; } = new ConcurrentDictionary<ulong, List<IUserMessage>>();
|
||||
//channelId/last generation
|
||||
public ConcurrentDictionary<ulong, DateTime> LastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||
|
||||
public KeyValuePair<string, ImmutableArray<byte>> GetRandomCurrencyImage()
|
||||
{
|
||||
var rng = new NadekoRandom();
|
||||
return _images.Currency[rng.Next(0, _images.Currency.Length)];
|
||||
}
|
||||
|
||||
private string GetText(ITextChannel ch, string key, params object[] rep)
|
||||
=> _strings.GetText(key, ch.GuildId, "Games".ToLowerInvariant(), rep);
|
||||
|
||||
private Task PotentialFlowerGeneration(SocketMessage imsg)
|
||||
{
|
||||
var msg = imsg as SocketUserMessage;
|
||||
if (msg == null || msg.Author.IsBot)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var channel = imsg.Channel as ITextChannel;
|
||||
if (channel == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!GenerationChannels.Contains(channel.Id))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var lastGeneration = LastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
|
||||
var rng = new NadekoRandom();
|
||||
|
||||
//todo i'm stupid :rofl: wtg kwoth. real async programming :100: :ok_hand: :100: :100: :thumbsup:
|
||||
if (DateTime.Now - TimeSpan.FromSeconds(_bc.CurrencyGenerationCooldown) < lastGeneration) //recently generated in this channel, don't generate again
|
||||
return;
|
||||
|
||||
var num = rng.Next(1, 101) + _bc.CurrencyGenerationChance * 100;
|
||||
|
||||
if (num > 100)
|
||||
{
|
||||
LastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now);
|
||||
|
||||
var dropAmount = _bc.CurrencyDropAmount;
|
||||
|
||||
if (dropAmount > 0)
|
||||
{
|
||||
var msgs = new IUserMessage[dropAmount];
|
||||
var prefix = NadekoBot.Prefix;
|
||||
var toSend = dropAmount == 1
|
||||
? GetText(channel, "curgen_sn", _bc.CurrencySign)
|
||||
+ " " + GetText(channel, "pick_sn", prefix)
|
||||
: GetText(channel, "curgen_pl", dropAmount, _bc.CurrencySign)
|
||||
+ " " + GetText(channel, "pick_pl", prefix);
|
||||
var file = GetRandomCurrencyImage();
|
||||
using (var fileStream = file.Value.ToStream())
|
||||
{
|
||||
var sent = await channel.SendFileAsync(
|
||||
fileStream,
|
||||
file.Key,
|
||||
toSend).ConfigureAwait(false);
|
||||
|
||||
msgs[0] = sent;
|
||||
}
|
||||
|
||||
PlantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.GetCurrentClassLogger().Warn(ex);
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
71
src/NadekoBot/Services/Games/GirlRating.cs
Normal file
71
src/NadekoBot/Services/Games/GirlRating.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using ImageSharp;
|
||||
using NadekoBot.DataStructures;
|
||||
using NadekoBot.Extensions;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace NadekoBot.Services.Games
|
||||
{
|
||||
public class GirlRating
|
||||
{
|
||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public double Crazy { get; }
|
||||
public double Hot { get; }
|
||||
public int Roll { get; }
|
||||
public string Advice { get; }
|
||||
public AsyncLazy<string> Url { get; }
|
||||
|
||||
public GirlRating(IImagesService _images, double crazy, double hot, int roll, string advice)
|
||||
{
|
||||
Crazy = crazy;
|
||||
Hot = hot;
|
||||
Roll = roll;
|
||||
Advice = advice; // convenient to have it here, even though atm there are only few different ones.
|
||||
|
||||
Url = new AsyncLazy<string>(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var ms = new MemoryStream(_images.WifeMatrix.ToArray(), false))
|
||||
using (var img = new ImageSharp.Image(ms))
|
||||
{
|
||||
const int minx = 35;
|
||||
const int miny = 385;
|
||||
const int length = 345;
|
||||
|
||||
var pointx = (int)(minx + length * (Hot / 10));
|
||||
var pointy = (int)(miny - length * ((Crazy - 4) / 6));
|
||||
|
||||
using (var pointMs = new MemoryStream(_images.RategirlDot.ToArray(), false))
|
||||
using (var pointImg = new ImageSharp.Image(pointMs))
|
||||
{
|
||||
img.DrawImage(pointImg, 100, default(Size), new Point(pointx - 10, pointy - 10));
|
||||
}
|
||||
|
||||
string url;
|
||||
using (var http = new HttpClient())
|
||||
using (var imgStream = new MemoryStream())
|
||||
{
|
||||
img.Save(imgStream);
|
||||
var byteContent = new ByteArrayContent(imgStream.ToArray());
|
||||
http.AddFakeHeaders();
|
||||
|
||||
var reponse = await http.PutAsync("https://transfer.sh/img.png", byteContent);
|
||||
url = await reponse.Content.ReadAsStringAsync();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace NadekoBot.Modules.Games.Commands.Models
|
||||
namespace NadekoBot.Services.Games
|
||||
{
|
||||
public class TypingArticle
|
||||
{
|
@ -1,6 +1,10 @@
|
||||
using NadekoBot.Extensions;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.DataStructures;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@ -15,11 +19,158 @@ namespace NadekoBot.Services
|
||||
private readonly DbHandler _db;
|
||||
|
||||
public readonly ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly Logger _log;
|
||||
|
||||
public GreetSettingsService(IEnumerable<GuildConfig> guildConfigs, DbHandler db)
|
||||
public GreetSettingsService(DiscordShardedClient client, IEnumerable<GuildConfig> guildConfigs, DbHandler db)
|
||||
{
|
||||
_db = db;
|
||||
_client = client;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
GuildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(guildConfigs.ToDictionary(g => g.GuildId, GreetSettings.Create));
|
||||
|
||||
_client.UserJoined += UserJoined;
|
||||
_client.UserLeft += UserLeft;
|
||||
}
|
||||
|
||||
private Task UserLeft(IGuildUser user)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var conf = GetOrAddSettingsForGuild(user.GuildId);
|
||||
|
||||
if (!conf.SendChannelByeMessage) return;
|
||||
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
|
||||
|
||||
if (channel == null) //maybe warn the server owner that the channel is missing
|
||||
return;
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.ChannelByeMessageText, out embedData))
|
||||
{
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
if (conf.AutoDeleteByeMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
return;
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
||||
if (conf.AutoDeleteByeMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task UserJoined(IGuildUser user)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var conf = GetOrAddSettingsForGuild(user.GuildId);
|
||||
|
||||
if (conf.SendChannelGreetMessage)
|
||||
{
|
||||
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId);
|
||||
if (channel != null) //maybe warn the server owner that the channel is missing
|
||||
{
|
||||
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData))
|
||||
{
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
if (conf.AutoDeleteGreetMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
||||
if (conf.AutoDeleteGreetMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.SendDmGreetMessage)
|
||||
{
|
||||
var channel = await user.CreateDMChannelAsync();
|
||||
|
||||
if (channel != null)
|
||||
{
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData))
|
||||
{
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
try
|
||||
{
|
||||
await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.DmGreetMessageText.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
await channel.SendConfirmAsync(msg).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public GreetSettings GetOrAddSettingsForGuild(ulong guildId)
|
||||
@ -210,6 +361,23 @@ namespace NadekoBot.Services
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SetGreetDel(ulong id, int timer)
|
||||
{
|
||||
if (timer < 0 || timer > 600)
|
||||
return;
|
||||
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var conf = uow.GuildConfigs.For(id, set => set);
|
||||
conf.AutoDeleteGreetMessagesTimer = timer;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
GuildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd);
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GreetSettings
|
||||
|
@ -17,7 +17,8 @@ namespace NadekoBot.Services
|
||||
string CarbonKey { get; }
|
||||
|
||||
DBConfig Db { get; }
|
||||
string SoundCloudClientId { get; set; }
|
||||
string SoundCloudClientId { get; }
|
||||
string OsuApiKey { get; }
|
||||
|
||||
bool IsOwner(IUser u);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Google.Apis.Customsearch.v1.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -6,12 +7,28 @@ namespace NadekoBot.Services
|
||||
{
|
||||
public interface IGoogleApiService
|
||||
{
|
||||
IEnumerable<string> Languages { get; }
|
||||
|
||||
Task<IEnumerable<string>> GetVideosByKeywordsAsync(string keywords, int count = 1);
|
||||
Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1);
|
||||
Task<IEnumerable<string>> GetRelatedVideosAsync(string url, int count = 1);
|
||||
Task<IEnumerable<string>> GetPlaylistTracksAsync(string playlistId, int count = 50);
|
||||
Task<IReadOnlyDictionary<string, TimeSpan>> GetVideoDurationsAsync(IEnumerable<string> videoIds);
|
||||
Task<ImageResult> GetImageAsync(string query, int start = 1);
|
||||
Task<string> Translate(string sourceText, string sourceLanguage, string targetLanguage);
|
||||
|
||||
Task<string> ShortenUrl(string url);
|
||||
}
|
||||
|
||||
public struct ImageResult
|
||||
{
|
||||
public Result.ImageData Image { get; }
|
||||
public string Link { get; }
|
||||
|
||||
public ImageResult(Result.ImageData image, string link)
|
||||
{
|
||||
this.Image = image;
|
||||
this.Link = link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ namespace NadekoBot.Services.Impl
|
||||
? "d0bd7768e3a1a2d15430f0dccb871117"
|
||||
: _soundcloudClientId;
|
||||
}
|
||||
set {
|
||||
private set {
|
||||
_soundcloudClientId = value;
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ using Google.Apis.Urlshortener.v1;
|
||||
using Google.Apis.Urlshortener.v1.Data;
|
||||
using NLog;
|
||||
using Google.Apis.Customsearch.v1;
|
||||
using Google.Apis.Customsearch.v1.Data;
|
||||
using System.Net.Http;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
@ -39,6 +41,7 @@ namespace NadekoBot.Services.Impl
|
||||
sh = new UrlshortenerService(bcs);
|
||||
cs = new CustomsearchService(bcs);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(keywords))
|
||||
@ -190,18 +193,6 @@ namespace NadekoBot.Services.Impl
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public struct ImageResult
|
||||
{
|
||||
public Result.ImageData Image { get; }
|
||||
public string Link { get; }
|
||||
|
||||
public ImageResult(Result.ImageData image, string link)
|
||||
{
|
||||
this.Image = image;
|
||||
this.Link = link;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ImageResult> GetImageAsync(string query, int start = 1)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
@ -218,5 +209,168 @@ namespace NadekoBot.Services.Impl
|
||||
|
||||
return new ImageResult(search.Items[0].Image, search.Items[0].Link);
|
||||
}
|
||||
|
||||
public IEnumerable<string> Languages => _languageDictionary.Keys.OrderBy(x => x);
|
||||
private readonly Dictionary<string, string> _languageDictionary = new Dictionary<string, string>() {
|
||||
{ "afrikaans", "af"},
|
||||
{ "albanian", "sq"},
|
||||
{ "arabic", "ar"},
|
||||
{ "armenian", "hy"},
|
||||
{ "azerbaijani", "az"},
|
||||
{ "basque", "eu"},
|
||||
{ "belarusian", "be"},
|
||||
{ "bengali", "bn"},
|
||||
{ "bulgarian", "bg"},
|
||||
{ "catalan", "ca"},
|
||||
{ "chinese-traditional", "zh-TW"},
|
||||
{ "chinese-simplified", "zh-CN"},
|
||||
{ "chinese", "zh-CN"},
|
||||
{ "croatian", "hr"},
|
||||
{ "czech", "cs"},
|
||||
{ "danish", "da"},
|
||||
{ "dutch", "nl"},
|
||||
{ "english", "en"},
|
||||
{ "esperanto", "eo"},
|
||||
{ "estonian", "et"},
|
||||
{ "filipino", "tl"},
|
||||
{ "finnish", "fi"},
|
||||
{ "french", "fr"},
|
||||
{ "galician", "gl"},
|
||||
{ "german", "de"},
|
||||
{ "georgian", "ka"},
|
||||
{ "greek", "el"},
|
||||
{ "haitian Creole", "ht"},
|
||||
{ "hebrew", "iw"},
|
||||
{ "hindi", "hi"},
|
||||
{ "hungarian", "hu"},
|
||||
{ "icelandic", "is"},
|
||||
{ "indonesian", "id"},
|
||||
{ "irish", "ga"},
|
||||
{ "italian", "it"},
|
||||
{ "japanese", "ja"},
|
||||
{ "korean", "ko"},
|
||||
{ "lao", "lo"},
|
||||
{ "latin", "la"},
|
||||
{ "latvian", "lv"},
|
||||
{ "lithuanian", "lt"},
|
||||
{ "macedonian", "mk"},
|
||||
{ "malay", "ms"},
|
||||
{ "maltese", "mt"},
|
||||
{ "norwegian", "no"},
|
||||
{ "persian", "fa"},
|
||||
{ "polish", "pl"},
|
||||
{ "portuguese", "pt"},
|
||||
{ "romanian", "ro"},
|
||||
{ "russian", "ru"},
|
||||
{ "serbian", "sr"},
|
||||
{ "slovak", "sk"},
|
||||
{ "slovenian", "sl"},
|
||||
{ "spanish", "es"},
|
||||
{ "swahili", "sw"},
|
||||
{ "swedish", "sv"},
|
||||
{ "tamil", "ta"},
|
||||
{ "telugu", "te"},
|
||||
{ "thai", "th"},
|
||||
{ "turkish", "tr"},
|
||||
{ "ukrainian", "uk"},
|
||||
{ "urdu", "ur"},
|
||||
{ "vietnamese", "vi"},
|
||||
{ "welsh", "cy"},
|
||||
{ "yiddish", "yi"},
|
||||
|
||||
{ "af", "af"},
|
||||
{ "sq", "sq"},
|
||||
{ "ar", "ar"},
|
||||
{ "hy", "hy"},
|
||||
{ "az", "az"},
|
||||
{ "eu", "eu"},
|
||||
{ "be", "be"},
|
||||
{ "bn", "bn"},
|
||||
{ "bg", "bg"},
|
||||
{ "ca", "ca"},
|
||||
{ "zh-tw", "zh-TW"},
|
||||
{ "zh-cn", "zh-CN"},
|
||||
{ "hr", "hr"},
|
||||
{ "cs", "cs"},
|
||||
{ "da", "da"},
|
||||
{ "nl", "nl"},
|
||||
{ "en", "en"},
|
||||
{ "eo", "eo"},
|
||||
{ "et", "et"},
|
||||
{ "tl", "tl"},
|
||||
{ "fi", "fi"},
|
||||
{ "fr", "fr"},
|
||||
{ "gl", "gl"},
|
||||
{ "de", "de"},
|
||||
{ "ka", "ka"},
|
||||
{ "el", "el"},
|
||||
{ "ht", "ht"},
|
||||
{ "iw", "iw"},
|
||||
{ "hi", "hi"},
|
||||
{ "hu", "hu"},
|
||||
{ "is", "is"},
|
||||
{ "id", "id"},
|
||||
{ "ga", "ga"},
|
||||
{ "it", "it"},
|
||||
{ "ja", "ja"},
|
||||
{ "ko", "ko"},
|
||||
{ "lo", "lo"},
|
||||
{ "la", "la"},
|
||||
{ "lv", "lv"},
|
||||
{ "lt", "lt"},
|
||||
{ "mk", "mk"},
|
||||
{ "ms", "ms"},
|
||||
{ "mt", "mt"},
|
||||
{ "no", "no"},
|
||||
{ "fa", "fa"},
|
||||
{ "pl", "pl"},
|
||||
{ "pt", "pt"},
|
||||
{ "ro", "ro"},
|
||||
{ "ru", "ru"},
|
||||
{ "sr", "sr"},
|
||||
{ "sk", "sk"},
|
||||
{ "sl", "sl"},
|
||||
{ "es", "es"},
|
||||
{ "sw", "sw"},
|
||||
{ "sv", "sv"},
|
||||
{ "ta", "ta"},
|
||||
{ "te", "te"},
|
||||
{ "th", "th"},
|
||||
{ "tr", "tr"},
|
||||
{ "uk", "uk"},
|
||||
{ "ur", "ur"},
|
||||
{ "vi", "vi"},
|
||||
{ "cy", "cy"},
|
||||
{ "yi", "yi"},
|
||||
};
|
||||
|
||||
public async Task<string> Translate(string sourceText, string sourceLanguage, string targetLanguage)
|
||||
{
|
||||
string text;
|
||||
|
||||
if (!_languageDictionary.ContainsKey(sourceLanguage) ||
|
||||
!_languageDictionary.ContainsKey(targetLanguage))
|
||||
throw new ArgumentException();
|
||||
|
||||
|
||||
var url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}",
|
||||
ConvertToLanguageCode(sourceLanguage),
|
||||
ConvertToLanguageCode(targetLanguage),
|
||||
WebUtility.UrlEncode(sourceText));
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
|
||||
text = await http.GetStringAsync(url).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return (string.Concat(JArray.Parse(text)[0].Select(x => x[0])));
|
||||
}
|
||||
|
||||
private string ConvertToLanguageCode(string language)
|
||||
{
|
||||
string mode;
|
||||
_languageDictionary.TryGetValue(language, out mode);
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ using NLog;
|
||||
using System.Diagnostics;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using Discord;
|
||||
|
||||
namespace NadekoBot.Services
|
||||
{
|
||||
@ -20,10 +21,13 @@ namespace NadekoBot.Services
|
||||
/// Used as failsafe in case response key doesn't exist in the selected or default language.
|
||||
/// </summary>
|
||||
private readonly CultureInfo _usCultureInfo = new CultureInfo("en-US");
|
||||
private readonly ILocalization _localization;
|
||||
|
||||
public NadekoStrings()
|
||||
public NadekoStrings(ILocalization loc)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_localization = loc;
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
var allLangsDict = new Dictionary<string, ImmutableDictionary<string, string>>(); // lang:(name:value)
|
||||
foreach (var file in Directory.GetFiles(stringsPath))
|
||||
@ -58,6 +62,9 @@ namespace NadekoBot.Services
|
||||
return val;
|
||||
}
|
||||
|
||||
public string GetText(string key, ulong guildId, string lowerModuleTypeName, params object[] replacements) =>
|
||||
GetText(key, _localization.GetCultureInfo(guildId), lowerModuleTypeName);
|
||||
|
||||
public string GetText(string key, CultureInfo cultureInfo, string lowerModuleTypeName)
|
||||
{
|
||||
var text = GetString(lowerModuleTypeName + "_" + key, cultureInfo);
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace NadekoBot.Modules.Searches.Models
|
||||
namespace NadekoBot.Services.Searches
|
||||
{
|
||||
class MagicItem
|
||||
public class MagicItem
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NadekoBot.Modules.Searches.Models
|
||||
namespace NadekoBot.Services.Searches
|
||||
{
|
||||
public class SearchPokemon
|
||||
{
|
@ -1,4 +1,12 @@
|
||||
using NadekoBot.Extensions;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
@ -7,6 +15,101 @@ namespace NadekoBot.Services.Searches
|
||||
{
|
||||
public class SearchesService
|
||||
{
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly IGoogleApiService _google;
|
||||
private readonly DbHandler _db;
|
||||
private readonly Logger _log;
|
||||
|
||||
public ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
|
||||
public ConcurrentDictionary<UserChannelPair, string> UserLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>();
|
||||
|
||||
public readonly string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json";
|
||||
public readonly string PokemonListFile = "data/pokemon/pokemon_list7.json";
|
||||
public Dictionary<string, SearchPokemon> Pokemons { get; } = new Dictionary<string, SearchPokemon>();
|
||||
public Dictionary<string, SearchPokemonAbility> PokemonAbilities { get; } = new Dictionary<string, SearchPokemonAbility>();
|
||||
|
||||
public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>();
|
||||
public List<MagicItem> MagicItems { get; } = new List<MagicItem>();
|
||||
|
||||
public SearchesService(DiscordShardedClient client, IGoogleApiService google, DbHandler db)
|
||||
{
|
||||
_client = client;
|
||||
_google = google;
|
||||
_db = db;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
//translate commands
|
||||
_client.MessageReceived += async (msg) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var umsg = msg as SocketUserMessage;
|
||||
if (umsg == null)
|
||||
return;
|
||||
|
||||
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out var autoDelete))
|
||||
return;
|
||||
|
||||
var key = new UserChannelPair()
|
||||
{
|
||||
UserId = umsg.Author.Id,
|
||||
ChannelId = umsg.Channel.Id,
|
||||
};
|
||||
|
||||
string langs;
|
||||
if (!UserLanguages.TryGetValue(key, out langs))
|
||||
return;
|
||||
|
||||
var text = await Translate(langs, umsg.Resolve(TagHandling.Ignore))
|
||||
.ConfigureAwait(false);
|
||||
if (autoDelete)
|
||||
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
await umsg.Channel.SendConfirmAsync($"{umsg.Author.Mention} `:` " + text.Replace("<@ ", "<@").Replace("<@! ", "<@!")).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
};
|
||||
|
||||
//pokemon commands
|
||||
if (File.Exists(PokemonListFile))
|
||||
{
|
||||
Pokemons = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(PokemonListFile));
|
||||
}
|
||||
else
|
||||
_log.Warn(PokemonListFile + " is missing. Pokemon abilities not loaded.");
|
||||
if (File.Exists(PokemonAbilitiesFile))
|
||||
PokemonAbilities = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemonAbility>>(File.ReadAllText(PokemonAbilitiesFile));
|
||||
else
|
||||
_log.Warn(PokemonAbilitiesFile + " is missing. Pokemon abilities not loaded.");
|
||||
|
||||
//joke commands
|
||||
if (File.Exists("data/wowjokes.json"))
|
||||
{
|
||||
WowJokes = JsonConvert.DeserializeObject<List<WoWJoke>>(File.ReadAllText("data/wowjokes.json"));
|
||||
}
|
||||
else
|
||||
_log.Warn("data/wowjokes.json is missing. WOW Jokes are not loaded.");
|
||||
|
||||
if (File.Exists("data/magicitems.json"))
|
||||
{
|
||||
MagicItems = JsonConvert.DeserializeObject<List<MagicItem>>(File.ReadAllText("data/magicitems.json"));
|
||||
}
|
||||
else
|
||||
_log.Warn("data/magicitems.json is missing. Magic items are not loaded.");
|
||||
}
|
||||
|
||||
public async Task<string> Translate(string langs, string text = null)
|
||||
{
|
||||
var langarr = langs.ToLowerInvariant().Split('>');
|
||||
if (langarr.Length != 2)
|
||||
throw new ArgumentException();
|
||||
var from = langarr[0];
|
||||
var to = langarr[1];
|
||||
text = text?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
throw new ArgumentException();
|
||||
return (await _google.Translate(text, from, to).ConfigureAwait(false)).SanitizeMentions();
|
||||
}
|
||||
|
||||
public async Task<string> DapiSearch(string tag, DapiSearchType type)
|
||||
{
|
||||
tag = tag?.Replace(" ", "_");
|
||||
@ -65,4 +168,18 @@ namespace NadekoBot.Services.Searches
|
||||
Rule34,
|
||||
Yandere
|
||||
}
|
||||
|
||||
public struct UserChannelPair
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class StreamStatus
|
||||
{
|
||||
public bool IsLive { get; set; }
|
||||
public string ApiLink { get; set; }
|
||||
public string Views { get; set; }
|
||||
}
|
||||
}
|
||||
|
11
src/NadekoBot/Services/Searches/StreamNotFoundException.cs
Normal file
11
src/NadekoBot/Services/Searches/StreamNotFoundException.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace NadekoBot.Services.Searches
|
||||
{
|
||||
public class StreamNotFoundException : Exception
|
||||
{
|
||||
public StreamNotFoundException(string message) : base($"Stream '{message}' not found.")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
190
src/NadekoBot/Services/Searches/StreamNotificationService.cs
Normal file
190
src/NadekoBot/Services/Searches/StreamNotificationService.cs
Normal file
@ -0,0 +1,190 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Searches
|
||||
{
|
||||
public class StreamNotificationService
|
||||
{
|
||||
private readonly Timer _streamCheckTimer;
|
||||
private bool firstStreamNotifPass { get; set; } = true;
|
||||
private readonly ConcurrentDictionary<string, StreamStatus> _cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
|
||||
|
||||
private readonly DbHandler _db;
|
||||
private readonly DiscordShardedClient _client;
|
||||
private readonly NadekoStrings _strings;
|
||||
|
||||
public StreamNotificationService(DbHandler db, DiscordShardedClient client, NadekoStrings strings)
|
||||
{
|
||||
_db = db;
|
||||
_client = client;
|
||||
_strings = strings;
|
||||
}
|
||||
|
||||
public StreamNotificationService()
|
||||
{
|
||||
_streamCheckTimer = new Timer(async (state) =>
|
||||
{
|
||||
var oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(_cachedStatuses);
|
||||
_cachedStatuses.Clear();
|
||||
IEnumerable<FollowedStream> streams;
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
streams = uow.GuildConfigs.GetAllFollowedStreams();
|
||||
}
|
||||
|
||||
await Task.WhenAll(streams.Select(async fs =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var newStatus = await GetStreamStatus(fs).ConfigureAwait(false);
|
||||
if (firstStreamNotifPass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StreamStatus oldStatus;
|
||||
if (oldCachedStatuses.TryGetValue(newStatus.ApiLink, out oldStatus) &&
|
||||
oldStatus.IsLive != newStatus.IsLive)
|
||||
{
|
||||
var server = _client.GetGuild(fs.GuildId);
|
||||
var channel = server?.GetTextChannel(fs.ChannelId);
|
||||
if (channel == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
await channel.EmbedAsync(GetEmbed(fs, newStatus, channel.Guild.Id)).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}));
|
||||
|
||||
firstStreamNotifPass = false;
|
||||
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(60));
|
||||
}
|
||||
|
||||
public async Task<StreamStatus> GetStreamStatus(FollowedStream stream, bool checkCache = true)
|
||||
{
|
||||
string response;
|
||||
StreamStatus result;
|
||||
switch (stream.Type)
|
||||
{
|
||||
case FollowedStream.FollowedStreamType.Hitbox:
|
||||
var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username.ToLowerInvariant()}";
|
||||
if (checkCache && _cachedStatuses.TryGetValue(hitboxUrl, out result))
|
||||
return result;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
response = await http.GetStringAsync(hitboxUrl).ConfigureAwait(false);
|
||||
}
|
||||
var hbData = JsonConvert.DeserializeObject<HitboxResponse>(response);
|
||||
if (!hbData.Success)
|
||||
throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]");
|
||||
result = new StreamStatus()
|
||||
{
|
||||
IsLive = hbData.IsLive,
|
||||
ApiLink = hitboxUrl,
|
||||
Views = hbData.Views
|
||||
};
|
||||
_cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result);
|
||||
return result;
|
||||
case FollowedStream.FollowedStreamType.Twitch:
|
||||
var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username.ToLowerInvariant())}?client_id=67w6z9i09xv2uoojdm9l0wsyph4hxo6";
|
||||
if (checkCache && _cachedStatuses.TryGetValue(twitchUrl, out result))
|
||||
return result;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
response = await http.GetStringAsync(twitchUrl).ConfigureAwait(false);
|
||||
}
|
||||
var twData = JsonConvert.DeserializeObject<TwitchResponse>(response);
|
||||
if (twData.Error != null)
|
||||
{
|
||||
throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]");
|
||||
}
|
||||
result = new StreamStatus()
|
||||
{
|
||||
IsLive = twData.IsLive,
|
||||
ApiLink = twitchUrl,
|
||||
Views = twData.Stream?.Viewers.ToString() ?? "0"
|
||||
};
|
||||
_cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result);
|
||||
return result;
|
||||
case FollowedStream.FollowedStreamType.Beam:
|
||||
var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username.ToLowerInvariant()}";
|
||||
if (checkCache && _cachedStatuses.TryGetValue(beamUrl, out result))
|
||||
return result;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
response = await http.GetStringAsync(beamUrl).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var bmData = JsonConvert.DeserializeObject<BeamResponse>(response);
|
||||
if (bmData.Error != null)
|
||||
throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]");
|
||||
result = new StreamStatus()
|
||||
{
|
||||
IsLive = bmData.IsLive,
|
||||
ApiLink = beamUrl,
|
||||
Views = bmData.ViewersCurrent.ToString()
|
||||
};
|
||||
_cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result);
|
||||
return result;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public EmbedBuilder GetEmbed(FollowedStream fs, StreamStatus status, ulong guildId)
|
||||
{
|
||||
var embed = new EmbedBuilder().WithTitle(fs.Username)
|
||||
.WithUrl(GetLink(fs))
|
||||
.AddField(efb => efb.WithName(GetText(fs, "status"))
|
||||
.WithValue(status.IsLive ? "Online" : "Offline")
|
||||
.WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText(fs, "viewers"))
|
||||
.WithValue(status.IsLive ? status.Views : "-")
|
||||
.WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText(fs, "platform"))
|
||||
.WithValue(fs.Type.ToString())
|
||||
.WithIsInline(true))
|
||||
.WithColor(status.IsLive ? NadekoBot.OkColor : NadekoBot.ErrorColor);
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
public string GetText(FollowedStream fs, string key, params object[] replacements) =>
|
||||
_strings.GetText(key,
|
||||
fs.GuildId,
|
||||
"Searches".ToLowerInvariant(),
|
||||
replacements);
|
||||
|
||||
public string GetLink(FollowedStream fs)
|
||||
{
|
||||
if (fs.Type == FollowedStream.FollowedStreamType.Hitbox)
|
||||
return $"http://www.hitbox.tv/{fs.Username}/";
|
||||
if (fs.Type == FollowedStream.FollowedStreamType.Twitch)
|
||||
return $"http://www.twitch.tv/{fs.Username}/";
|
||||
if (fs.Type == FollowedStream.FollowedStreamType.Beam)
|
||||
return $"https://beam.pro/{fs.Username}/";
|
||||
return "??";
|
||||
}
|
||||
}
|
||||
}
|
35
src/NadekoBot/Services/Searches/StreamResponses.cs
Normal file
35
src/NadekoBot/Services/Searches/StreamResponses.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NadekoBot.Services.Searches
|
||||
{
|
||||
public class HitboxResponse
|
||||
{
|
||||
public bool Success { get; set; } = true;
|
||||
[JsonProperty("media_is_live")]
|
||||
public string MediaIsLive { get; set; }
|
||||
public bool IsLive => MediaIsLive == "1";
|
||||
[JsonProperty("media_views")]
|
||||
public string Views { get; set; }
|
||||
}
|
||||
|
||||
public class TwitchResponse
|
||||
{
|
||||
public string Error { get; set; } = null;
|
||||
public bool IsLive => Stream != null;
|
||||
public StreamInfo Stream { get; set; }
|
||||
|
||||
public class StreamInfo
|
||||
{
|
||||
public int Viewers { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class BeamResponse
|
||||
{
|
||||
public string Error { get; set; } = null;
|
||||
|
||||
[JsonProperty("online")]
|
||||
public bool IsLive { get; set; }
|
||||
public int ViewersCurrent { get; set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace NadekoBot.Modules.Searches.Models
|
||||
namespace NadekoBot.Services.Searches
|
||||
{
|
||||
public class WoWJoke
|
||||
{
|
Loading…
Reference in New Issue
Block a user