Compare commits

...

50 Commits

Author SHA1 Message Date
Kwoth
699a5e0c8c Added bot_owner_only attribute to medusa system. Upped version to 4..3.11 2023-01-21 00:33:07 +01:00
Kwoth
76fedc5ff1 .jr will no longer fail if the user isn't in the database yet, fixes #399 2023-01-20 22:36:57 +01:00
Kwoth
992aa781fa .translangs title is now a response string 2023-01-20 06:51:29 +01:00
Kwoth
e52bf798cf Added full list of supported languages for .trans command, improved .translangs output 2023-01-20 05:28:13 +01:00
Kwoth
710f4f2ed8 Updated .translangs, it looks better now and makes it clear which shortcode is for which language 2023-01-20 04:43:25 +01:00
Kwoth
dcc27a4a99 Fixed trivia --nohint, fixes #398 2023-01-19 06:55:33 +01:00
Kwoth
032b6bfd57 Added Bot propety for easy access to the ISelfUser to all medusa *Context types 2023-01-15 04:15:51 +01:00
Kwoth
737bbb8ec1 Added bot_perm support to medusa system 2023-01-15 04:07:04 +01:00
Kwoth
e71708f5e8 .h command show now properly show both channel and server user permission requirements 2023-01-15 00:50:25 +01:00
Kwoth
9841d72622 allow multiple [user_perm] attributes on snek commands 2023-01-15 00:15:27 +01:00
Kwoth
391a3f0513 Medusae names should now be case sensitive and will be saved in the config with the proper capitalization 2023-01-15 00:01:42 +01:00
Kwoth
aa3409a9cf Added [user_perm()] support to medusa system 2023-01-14 22:43:13 +01:00
Kwoth
9a80383327 Added filter support to medusa system 2023-01-14 00:37:38 +01:00
Kwoth
7e61f7fb46 Improved how stickeradd works. Optional parameters will be a hidden feature as it's hard to explain. Tags are now also optional. 2023-01-13 22:55:09 +01:00
Kwoth
d526a2fac6 Merge branch 'stickeradd' into 'v4'
See merge request Kwoth/nadekobot!276
2023-01-11 23:39:14 +00:00
Kwoth
80efb954f1 Added .stickeradd command
Updated docs for medusa
2023-01-11 23:39:14 +00:00
Kwoth
67c156e40f Merge branch 'hokutochen-v4-patch-11221' into 'v4'
Fix for DMHelpText

See merge request Kwoth/nadekobot!283
2023-01-10 14:39:24 +00:00
Hokuto Chen
26f76ef7b9 Fix for DMHelpText 2023-01-10 14:39:23 +00:00
Kwoth
90cee1bfa9 Merge branch 'hokutochen-v4-patch-97342' into 'v4'
Removed Debian 9 support. Added debian 11 support

See merge request Kwoth/nadekobot!282
2023-01-10 14:38:21 +00:00
Hokuto Chen
26171a0ff7 Removed Debian 9 support. Added debian 11 support 2023-01-10 14:38:21 +00:00
Kwoth
59447d7aa8 .deletecurrency will now also reset banked currency, closes #395 2023-01-09 02:59:41 +01:00
Kwoth
a4053d0666 Removed Id property from patronuser 2023-01-09 02:48:11 +01:00
Kwoth
fcd016aed3 Reverted PK fix for patron as it's causing migration to fail 2023-01-09 02:43:18 +01:00
Kwoth
719f62a0ac Merge branch 'v4' of https://gitlab.com/Kwoth/nadekobot into v4 2023-01-09 02:05:54 +01:00
Kwoth
9b9fa2f357 - Fixed some potential causes for ratelimit due to default message retry settings
- Fixed a patron rewards bug caused by monthly donation checking not accounting for year increase
- Fixed a patron rewards bug for users who connected the same discord account with multiple patreon accounts
2023-01-09 02:05:33 +01:00
Kwoth
823f4731c3 Update responses.uk-UA.json (POEditor.com) 2022-12-29 14:38:57 +00:00
Kwoth
5feff8f4b2 Update responses.es-ES.json (POEditor.com) 2022-12-29 14:38:56 +00:00
Kwoth
95d20609a8 Update responses.ru-RU.json (POEditor.com) 2022-12-29 14:38:55 +00:00
Kwoth
b416b9f963 Update responses.pt-BR.json (POEditor.com) 2022-12-29 14:38:53 +00:00
Kwoth
a7c48b13a0 Update responses.pl-PL.json (POEditor.com) 2022-12-29 14:38:52 +00:00
Kwoth
003b71ba00 Update responses.it-IT.json (POEditor.com) 2022-12-29 14:38:51 +00:00
Kwoth
89593dcc2c Update responses.id-ID.json (POEditor.com) 2022-12-29 14:38:50 +00:00
Kwoth
fa9352d1f8 Update responses.de-DE.json (POEditor.com) 2022-12-29 14:38:49 +00:00
Kwoth
4a2f7ffc76 Update responses.fr-FR.json (POEditor.com) 2022-12-29 14:38:48 +00:00
Kwoth
fb1555c075 Update responses.nl-NL.json (POEditor.com) 2022-12-29 14:38:47 +00:00
Kwoth
7a0b409d88 Update responses.zh-TW.json (POEditor.com) 2022-12-29 14:38:46 +00:00
Kwoth
c869f2e335 More nullref fixes in streamrole, ref #392 2022-12-23 17:08:13 +01:00
Kwoth
01da7e813e Fixed a nullref in streamrole service 2022-12-23 17:04:08 +01:00
Kwoth
76b7e09905 Greet/bye messages will now get disabled if they're set to a deleted/unknown channel 2022-12-22 21:37:01 +01:00
Kwoth
8dca948224 Fixed some build warnings. Fixed TimeOut punishment not allowing duration 2022-12-22 21:18:26 +01:00
Kwoth
ffcbfe6467 Fixed a bug for .quotedeleteauthor causing the executing user to delete own messages 2022-12-22 21:11:21 +01:00
Kwoth
d5c70def93 Merge branch 'hokutochen-v4-patch-86494' into 'v4'
added 22.04 warning

See merge request Kwoth/nadekobot!277
2022-12-09 03:06:32 +00:00
Hokuto Chen
34dccab16b added 22.04 warning 2022-12-08 19:43:33 +00:00
Kwoth
2fab61c4f8 Merge branch 'feeds' into 'v4'
Feeds custom message

See merge request Kwoth/nadekobot!273
2022-11-22 21:08:42 +00:00
Kwoth
9f96edbb46 You can now specify an optional custom message in .feed and .yun which will be posted along with an update 2022-11-22 21:08:42 +00:00
Kwoth
3c23b58088 Merge branch 'thread_log' into 'v4'
Thread log

See merge request Kwoth/nadekobot!274
2022-11-22 20:41:57 +00:00
Kwoth
e10530bc0e Thread log 2022-11-22 20:41:57 +00:00
Kwoth
f24692e79b Added .doas command which executes the command as if you were the target user 2022-11-19 22:36:18 +01:00
Kwoth
8a6edc17e4 Optimized .waifuinfo 2022-11-15 22:45:00 +01:00
Kwoth
9ce2837f5a Possible optimization for .waifuinfo 2022-11-13 20:40:37 +01:00
71 changed files with 21943 additions and 544 deletions

View File

@@ -2,6 +2,38 @@
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
## [4.3.11] - 21.01.2023
### Added
- Added `.doas` Bot owner only command
- Added `.stickeradd` command
### Changed
- `.waifuinfo` optimized
- You can now specify an optional custom message in `.feed` and `.yun` which will be posted along with an update
- Greet/bye messages will now get disabled if they're set to a deleted/unknown channel
- Updated response strings
- `.translate` now supports many more languages
- `.translangs` prettier output
### Fixed
- Added logging for thread events
- Fixed a bug for `.quotedeleteauthor` causing the executing user to delete own messages
- Fixed TimeOut punishment not alklowing duration
- Fixed a nullref in streamrole service
- Fixed some potential causes for ratelimit due to default message retry settings
- Fixed a patron rewards bug caused by monthly donation checking not accounting for year increase
- Fixed a patron rewards bug for users who connected the same discord account with multiple patreon accounts
- `.deletecurrency` will now also reset banked currency
- Fixed DMHelpText reply
- `.h` command show now properly show both channel and server user permission requirements
- Many fixes and improvements to medusa system
- Fixed trivia --nohint
- `.joinrace` will no longer fail if the user isn't in the database yet
## [4.3.10] - 10.11.2022
### Added

View File

@@ -15,11 +15,14 @@ w# Setting up NadekoBot on Linux
It is recommended that you use **Ubuntu 20.04**, as there have been nearly no problems with it. Also, **32-bit systems are incompatible**.
### Ubuntu 22.04 is ruled as incompatible so double check which ubuntu version you are using.
##### Compatible operating systems:
- Ubuntu: 16.04, 18.04, 20.04, 21.04, 21.10, 22.04
- Ubuntu: 16.04, 18.04, 20.04, 21.04, 21.10
- Mint: 19, 20
- Debian: 9, 10
- Debian: 10, 11
- CentOS: 7
- openSUSE
- Fedora: 33, 34, 35

View File

@@ -147,6 +147,7 @@ This section will guide you through how to create a simple custom medusa. You ca
<!-- Use latest .net features -->
<LangVersion>preview</LangVersion>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<GenerateRequiresPreviewFeaturesAttribute>true</GenerateRequiresPreviewFeaturesAttribute>
<!-- tell .net that this library will be used as a plugin -->
<EnableDynamicLoading>true</EnableDynamicLoading>

View File

@@ -0,0 +1,10 @@
namespace Nadeko.Snake;
/// <summary>
/// Used as a marker class for bot_perm and user_perm Attributes
/// Has no functionality.
/// </summary>
public abstract class MedusaPermAttribute : Attribute
{
}

View File

@@ -0,0 +1,7 @@
namespace Nadeko.Snake;
[AttributeUsage(AttributeTargets.Method)]
public sealed class bot_owner_onlyAttribute : MedusaPermAttribute
{
}

View File

@@ -0,0 +1,23 @@
using Discord;
namespace Nadeko.Snake;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class bot_permAttribute : MedusaPermAttribute
{
public GuildPermission? GuildPerm { get; }
public ChannelPermission? ChannelPerm { get; }
public bot_permAttribute(GuildPermission perm)
{
GuildPerm = perm;
ChannelPerm = null;
}
public bot_permAttribute(ChannelPermission perm)
{
ChannelPerm = perm;
GuildPerm = null;
}
}

View File

@@ -0,0 +1,22 @@
using Discord;
namespace Nadeko.Snake;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class user_permAttribute : MedusaPermAttribute
{
public GuildPermission? GuildPerm { get; }
public ChannelPermission? ChannelPerm { get; }
public user_permAttribute(GuildPermission perm)
{
GuildPerm = perm;
ChannelPerm = null;
}
public user_permAttribute(ChannelPermission perm)
{
ChannelPerm = perm;
GuildPerm = null;
}
}

View File

@@ -22,6 +22,11 @@ public abstract class AnyContext
/// The user who invoked the command
/// </summary>
public abstract IUser User { get; }
/// <summary>
/// Bot user
/// </summary>
public abstract ISelfUser Bot { get; }
/// <summary>
/// Provides access to strings used by this medusa

View File

@@ -10,7 +10,7 @@ public static class MedusaExtensions
embed: embed.Build(),
options: new()
{
RetryMode = RetryMode.AlwaysRetry
RetryMode = RetryMode.Retry502
});
// unlocalized

View File

@@ -69,7 +69,7 @@ public sealed class Bot
: GatewayIntents.AllUnprivileged,
LogGatewayIntentWarnings = false,
FormatUsersInBidirectionalUnicode = false,
DefaultRetryMode = RetryMode.AlwaysRetry ^ RetryMode.RetryRatelimit
DefaultRetryMode = RetryMode.Retry502
});
_commandService = new(new()

View File

@@ -3,7 +3,7 @@ using NadekoBot.Modules.Administration.Services;
namespace Discord;
[AttributeUsage(AttributeTargets.Method)]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class UserPermAttribute : RequireUserPermissionAttribute
{
public UserPermAttribute(GuildPerm permission)

View File

@@ -29,4 +29,7 @@ public enum LogType
VoicePresenceTts,
UserMuted,
UserWarned,
ThreadDeleted,
ThreadCreated
}

View File

@@ -5,9 +5,11 @@ public sealed class DmContextAdapter : DmContext
public override IMedusaStrings Strings { get; }
public override IDMChannel Channel { get; }
public override IUserMessage Message { get; }
public override ISelfUser Bot { get; }
public override IUser User
=> Message.Author;
private readonly IServiceProvider _services;
private readonly Lazy<IEmbedBuilderService> _ebs;
private readonly Lazy<IBotStrings> _botStrings;
@@ -26,6 +28,7 @@ public sealed class DmContextAdapter : DmContext
Channel = ch;
Message = ctx.Message;
Bot = ctx.Client.CurrentUser;
_ebs = new(_services.GetRequiredService<IEmbedBuilderService>());

View File

@@ -0,0 +1,31 @@
namespace Nadeko.Medusa.Adapters;
public class FilterAdapter : PreconditionAttribute
{
private readonly FilterAttribute _filterAttribute;
private readonly IMedusaStrings _strings;
public FilterAdapter(FilterAttribute filterAttribute,
IMedusaStrings strings)
{
_filterAttribute = filterAttribute;
_strings = strings;
}
public override async Task<PreconditionResult> CheckPermissionsAsync(
ICommandContext context,
CommandInfo command,
IServiceProvider services)
{
var medusaContext = ContextAdapterFactory.CreateNew(context,
_strings,
services);
var result = await _filterAttribute.CheckAsync(medusaContext);
if (!result)
return PreconditionResult.FromError($"Precondition '{_filterAttribute.GetType().Name}' failed.");
return PreconditionResult.FromSuccess();
}
}

View File

@@ -11,6 +11,7 @@ public sealed class GuildContextAdapter : GuildContext
public override IMedusaStrings Strings { get; }
public override IGuild Guild { get; }
public override ITextChannel Channel { get; }
public override ISelfUser Bot { get; }
public override IUserMessage Message
=> _ctx.Message;
@@ -28,6 +29,7 @@ public sealed class GuildContextAdapter : GuildContext
Strings = strings;
User = (IGuildUser)ctx.User;
Bot = ctx.Client.CurrentUser;
_services = services;
_ebs = new(_services.GetRequiredService<IEmbedBuilderService>());

View File

@@ -22,8 +22,6 @@ public sealed class MedusaConfigService : ConfigServiceBase<MedusaConfig>, IMedu
public void AddLoadedMedusa(string name)
{
name = name.Trim().ToLowerInvariant();
ModifyConfig(conf =>
{
if (conf.Loaded is null)
@@ -36,8 +34,6 @@ public sealed class MedusaConfigService : ConfigServiceBase<MedusaConfig>, IMedu
public void RemoveLoadedMedusa(string name)
{
name = name.Trim().ToLowerInvariant();
ModifyConfig(conf =>
{
if (conf.Loaded is null)

View File

@@ -1,5 +1,6 @@
using Discord.Commands.Builders;
using Microsoft.Extensions.DependencyInjection;
using Nadeko.Medusa.Adapters;
using NadekoBot.Common.ModuleBehaviors;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
@@ -382,6 +383,11 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
{
var m = mb.WithName(snekInfo.Name);
foreach (var f in snekInfo.Filters)
{
m.AddPrecondition(new FilterAdapter(f, strings));
}
foreach (var cmd in snekInfo.Commands)
{
m.AddCommand(cmd.Aliases.First(),
@@ -390,7 +396,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
new(cmd),
new(medusaServices),
strings),
CreateCommandFactory(medusaName, cmd));
CreateCommandFactory(medusaName, cmd, strings));
}
foreach (var subInfo in snekInfo.Subsneks)
@@ -399,7 +405,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
private static readonly RequireContextAttribute _reqGuild = new RequireContextAttribute(ContextType.Guild);
private static readonly RequireContextAttribute _reqDm = new RequireContextAttribute(ContextType.DM);
private Action<CommandBuilder> CreateCommandFactory(string medusaName, SnekCommandData cmd)
private Action<CommandBuilder> CreateCommandFactory(string medusaName, SnekCommandData cmd, IMedusaStrings strings)
=> (cb) =>
{
cb.AddAliases(cmd.Aliases.Skip(1).ToArray());
@@ -408,6 +414,31 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
cb.AddPrecondition(_reqGuild);
else if (cmd.ContextType == CommandContextType.Dm)
cb.AddPrecondition(_reqDm);
foreach (var f in cmd.Filters)
cb.AddPrecondition(new FilterAdapter(f, strings));
foreach (var ubp in cmd.UserAndBotPerms)
{
if (ubp is user_permAttribute up)
{
if (up.GuildPerm is { } gp)
cb.AddPrecondition(new UserPermAttribute(gp));
else if (up.ChannelPerm is { } cp)
cb.AddPrecondition(new UserPermAttribute(cp));
}
else if (ubp is bot_permAttribute bp)
{
if (bp.GuildPerm is { } gp)
cb.AddPrecondition(new BotPermAttribute(gp));
else if (bp.ChannelPerm is { } cp)
cb.AddPrecondition(new BotPermAttribute(cp));
}
else if (ubp is bot_owner_onlyAttribute)
{
cb.AddPrecondition(new OwnerOnlyAttribute());
}
}
cb.WithPriority(cmd.Priority);
@@ -750,8 +781,10 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
var cmds = new List<SnekCommandData>();
foreach (var method in methodInfos)
{
var filters = method.GetCustomAttributes<FilterAttribute>().ToArray();
var prio = method.GetCustomAttribute<prioAttribute>()?.Priority ?? 0;
var filters = method.GetCustomAttributes<FilterAttribute>(true).ToArray();
var userAndBotPerms = method.GetCustomAttributes<MedusaPermAttribute>(true)
.ToArray();
var prio = method.GetCustomAttribute<prioAttribute>(true)?.Priority ?? 0;
var paramInfos = method.GetParameters();
var cmdParams = new List<ParamData>();
@@ -828,7 +861,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
}
var cmdAttribute = method.GetCustomAttribute<cmdAttribute>()!;
var cmdAttribute = method.GetCustomAttribute<cmdAttribute>(true)!;
var aliases = cmdAttribute.Aliases;
if (aliases.Length == 0)
aliases = new[] { method.Name.ToLowerInvariant() };
@@ -838,6 +871,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
method,
instance,
filters,
userAndBotPerms,
cmdContext,
diParams,
cmdParams,

View File

@@ -11,6 +11,7 @@ public sealed class SnekCommandData
MethodInfo methodInfo,
Snek module,
FilterAttribute[] filters,
MedusaPermAttribute[] userAndBotPerms,
CommandContextType contextType,
IReadOnlyList<Type> injectedParams,
IReadOnlyList<ParamData> parameters,
@@ -21,6 +22,7 @@ public sealed class SnekCommandData
MethodInfo = methodInfo;
Module = module;
Filters = filters;
UserAndBotPerms = userAndBotPerms;
ContextType = contextType;
InjectedParams = injectedParams;
Parameters = parameters;
@@ -28,6 +30,8 @@ public sealed class SnekCommandData
OptionalStrings = strings;
}
public MedusaPermAttribute[] UserAndBotPerms { get; set; }
public CommandStrings OptionalStrings { get; set; }
public IReadOnlyCollection<string> Aliases { get; }

View File

@@ -10,6 +10,7 @@ namespace NadekoBot.Db;
public class WaifuInfoStats
{
public int WaifuId { get; init; }
public string FullName { get; init; }
public long Price { get; init; }
public string ClaimerName { get; init; }
@@ -17,9 +18,6 @@ public class WaifuInfoStats
public int AffinityCount { get; init; }
public int DivorceCount { get; init; }
public int ClaimCount { get; init; }
public List<WaifuItem> Items { get; init; }
public List<string> Claims { get; init; }
public List<string> Fans { get; init; }
}
public static class WaifuExtensions
@@ -103,6 +101,7 @@ public static class WaifuExtensions
.FirstOrDefault())
.Select(w => new WaifuInfoStats
{
WaifuId = w.WaifuId,
FullName =
ctx.Set<DiscordUser>()
.AsQueryable()
@@ -135,17 +134,6 @@ public static class WaifuExtensions
&& x.NewId == null
&& x.UpdateType == WaifuUpdateType.Claimed),
Price = w.Price,
Claims = ctx.WaifuInfo.AsQueryable()
.Include(x => x.Waifu)
.Where(x => x.ClaimerId == w.WaifuId)
.Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator)
.ToList(),
Fans = ctx.WaifuInfo.AsQueryable()
.Include(x => x.Waifu)
.Where(x => x.AffinityId == w.WaifuId)
.Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator)
.ToList(),
Items = w.Items
})
.FirstOrDefault();

View File

@@ -8,6 +8,8 @@ public class FeedSub : DbEntity
public ulong ChannelId { get; set; }
public string Url { get; set; }
public string Message { get; set; }
public override int GetHashCode()
=> Url.GetHashCode(StringComparison.InvariantCulture) ^ GuildConfigId.GetHashCode();

View File

@@ -19,6 +19,10 @@ public class LogSetting : DbEntity
public ulong? ChannelCreatedId { get; set; }
public ulong? ChannelDestroyedId { get; set; }
public ulong? ChannelUpdatedId { get; set; }
public ulong? ThreadDeletedId { get; set; }
public ulong? ThreadCreatedId { get; set; }
public ulong? UserMutedId { get; set; }

View File

@@ -7,7 +7,7 @@ public class WaifuInfo : DbEntity
{
public int WaifuId { get; set; }
public DiscordUser Waifu { get; set; }
public int? ClaimerId { get; set; }
public DiscordUser Claimer { get; set; }

View File

@@ -7,4 +7,4 @@ public class WaifuItem : DbEntity
public int? WaifuInfoId { get; set; }
public string ItemEmoji { get; set; }
public string Name { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.Mysql
{
public partial class logthread : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<ulong>(
name: "threadcreatedid",
table: "logsettings",
type: "bigint unsigned",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "threaddeletedid",
table: "logsettings",
type: "bigint unsigned",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "threadcreatedid",
table: "logsettings");
migrationBuilder.DropColumn(
name: "threaddeletedid",
table: "logsettings");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.Mysql
{
public partial class feedtext : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "message",
table: "feedsub",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "message",
table: "feedsub");
}
}
}

View File

@@ -975,6 +975,10 @@ namespace NadekoBot.Migrations.Mysql
.HasColumnType("int")
.HasColumnName("guildconfigid");
b.Property<string>("Message")
.HasColumnType("longtext")
.HasColumnName("message");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("varchar(255)")
@@ -1518,6 +1522,14 @@ namespace NadekoBot.Migrations.Mysql
.HasColumnType("bigint unsigned")
.HasColumnName("messageupdatedid");
b.Property<ulong?>("ThreadCreatedId")
.HasColumnType("bigint unsigned")
.HasColumnName("threadcreatedid");
b.Property<ulong?>("ThreadDeletedId")
.HasColumnType("bigint unsigned")
.HasColumnName("threaddeletedid");
b.Property<ulong?>("UserBannedId")
.HasColumnType("bigint unsigned")
.HasColumnName("userbannedid");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.PostgreSql
{
public partial class logthread : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<decimal>(
name: "threadcreatedid",
table: "logsettings",
type: "numeric(20,0)",
nullable: true);
migrationBuilder.AddColumn<decimal>(
name: "threaddeletedid",
table: "logsettings",
type: "numeric(20,0)",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "threadcreatedid",
table: "logsettings");
migrationBuilder.DropColumn(
name: "threaddeletedid",
table: "logsettings");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.PostgreSql
{
public partial class feedtext : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "message",
table: "feedsub",
type: "text",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "message",
table: "feedsub");
}
}
}

View File

@@ -1023,6 +1023,10 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("integer")
.HasColumnName("guildconfigid");
b.Property<string>("Message")
.HasColumnType("text")
.HasColumnName("message");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("text")
@@ -1590,6 +1594,14 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("numeric(20,0)")
.HasColumnName("messageupdatedid");
b.Property<decimal?>("ThreadCreatedId")
.HasColumnType("numeric(20,0)")
.HasColumnName("threadcreatedid");
b.Property<decimal?>("ThreadDeletedId")
.HasColumnType("numeric(20,0)")
.HasColumnName("threaddeletedid");
b.Property<decimal?>("UserBannedId")
.HasColumnType("numeric(20,0)")
.HasColumnName("userbannedid");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations
{
public partial class logthread : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<ulong>(
name: "ThreadCreatedId",
table: "LogSettings",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "ThreadDeletedId",
table: "LogSettings",
type: "INTEGER",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ThreadCreatedId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "ThreadDeletedId",
table: "LogSettings");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations
{
public partial class feedtext : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Message",
table: "FeedSub",
type: "TEXT",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Message",
table: "FeedSub");
}
}
}

View File

@@ -37,7 +37,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId")
.IsUnique();
b.ToTable("AutoPublishChannel");
b.ToTable("AutoPublishChannel", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.BankUser", b =>
@@ -60,7 +60,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId")
.IsUnique();
b.ToTable("BankUsers");
b.ToTable("BankUsers", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
@@ -75,7 +75,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("ClubApplicants");
b.ToTable("ClubApplicants", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.ClubBans", b =>
@@ -90,7 +90,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("ClubBans");
b.ToTable("ClubBans", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.ClubInfo", b =>
@@ -126,7 +126,7 @@ namespace NadekoBot.Migrations
b.HasIndex("OwnerId")
.IsUnique();
b.ToTable("Clubs");
b.ToTable("Clubs", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b =>
@@ -185,7 +185,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("DiscordUser");
b.ToTable("DiscordUser", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
@@ -219,7 +219,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("FollowedStream");
b.ToTable("FollowedStream", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronQuota", b =>
@@ -246,7 +246,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("PatronQuotas");
b.ToTable("PatronQuotas", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
@@ -272,7 +272,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UniquePlatformUserId")
.IsUnique();
b.ToTable("Patrons");
b.ToTable("Patrons", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.StreamOnlineMessage", b =>
@@ -298,7 +298,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("StreamOnlineMessages");
b.ToTable("StreamOnlineMessages", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.XpShopOwnedItem", b =>
@@ -328,7 +328,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId", "ItemType", "ItemKey")
.IsUnique();
b.ToTable("XpShopOwnedItem");
b.ToTable("XpShopOwnedItem", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b =>
@@ -357,7 +357,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("AntiAltSetting");
b.ToTable("AntiAltSetting", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
@@ -389,7 +389,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("AntiRaidSetting");
b.ToTable("AntiRaidSetting", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b =>
@@ -411,7 +411,7 @@ namespace NadekoBot.Migrations
b.HasIndex("AntiSpamSettingId");
b.ToTable("AntiSpamIgnore");
b.ToTable("AntiSpamIgnore", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b =>
@@ -443,7 +443,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("AntiSpamSetting");
b.ToTable("AntiSpamSetting", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoCommand", b =>
@@ -481,7 +481,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("AutoCommands");
b.ToTable("AutoCommands", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
@@ -509,7 +509,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId");
b.ToTable("AutoTranslateChannels");
b.ToTable("AutoTranslateChannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
@@ -537,7 +537,7 @@ namespace NadekoBot.Migrations
b.HasAlternateKey("ChannelId", "UserId");
b.ToTable("AutoTranslateUsers");
b.ToTable("AutoTranslateUsers", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
@@ -563,7 +563,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId")
.IsUnique();
b.ToTable("BanTemplates");
b.ToTable("BanTemplates", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistEntry", b =>
@@ -583,7 +583,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("Blacklist");
b.ToTable("Blacklist", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
@@ -608,7 +608,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("CommandAlias");
b.ToTable("CommandAlias", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
@@ -633,7 +633,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("CommandCooldown");
b.ToTable("CommandCooldown", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b =>
@@ -671,7 +671,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("CurrencyTransactions");
b.ToTable("CurrencyTransactions", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.DelMsgOnCmdChannel", b =>
@@ -696,7 +696,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("DelMsgOnCmdChannel");
b.ToTable("DelMsgOnCmdChannel", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordPermOverride", b =>
@@ -722,7 +722,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId", "Command")
.IsUnique();
b.ToTable("DiscordPermOverrides");
b.ToTable("DiscordPermOverrides", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
@@ -747,7 +747,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId");
b.ToTable("ExcludedItem");
b.ToTable("ExcludedItem", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b =>
@@ -765,6 +765,9 @@ namespace NadekoBot.Migrations
b.Property<int>("GuildConfigId")
.HasColumnType("INTEGER");
b.Property<string>("Message")
.HasColumnType("TEXT");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("TEXT");
@@ -773,7 +776,7 @@ namespace NadekoBot.Migrations
b.HasAlternateKey("GuildConfigId", "Url");
b.ToTable("FeedSub");
b.ToTable("FeedSub", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
@@ -795,7 +798,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("FilterChannelId");
b.ToTable("FilterChannelId", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
@@ -817,7 +820,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("FilteredWord");
b.ToTable("FilteredWord", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterLinksChannelId", b =>
@@ -839,7 +842,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("FilterLinksChannelId");
b.ToTable("FilterLinksChannelId", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterWordsChannelId", b =>
@@ -861,7 +864,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("FilterWordsChannelId");
b.ToTable("FilterWordsChannelId", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GamblingStats", b =>
@@ -887,7 +890,7 @@ namespace NadekoBot.Migrations
b.HasIndex("Feature")
.IsUnique();
b.ToTable("GamblingStats");
b.ToTable("GamblingStats", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
@@ -909,7 +912,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("GCChannelId");
b.ToTable("GCChannelId", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GroupName", b =>
@@ -935,7 +938,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId", "Number")
.IsUnique();
b.ToTable("GroupName");
b.ToTable("GroupName", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
@@ -1067,7 +1070,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WarnExpireHours");
b.ToTable("GuildConfigs");
b.ToTable("GuildConfigs", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b =>
@@ -1093,7 +1096,7 @@ namespace NadekoBot.Migrations
b.HasIndex("LogSettingId", "LogItemId", "ItemType")
.IsUnique();
b.ToTable("IgnoredLogChannels");
b.ToTable("IgnoredLogChannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
@@ -1115,7 +1118,7 @@ namespace NadekoBot.Migrations
b.HasIndex("LogSettingId");
b.ToTable("IgnoredVoicePresenceCHannels");
b.ToTable("IgnoredVoicePresenceCHannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ImageOnlyChannel", b =>
@@ -1141,7 +1144,7 @@ namespace NadekoBot.Migrations
b.HasIndex("ChannelId")
.IsUnique();
b.ToTable("ImageOnlyChannels");
b.ToTable("ImageOnlyChannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
@@ -1186,6 +1189,12 @@ namespace NadekoBot.Migrations
b.Property<ulong?>("MessageUpdatedId")
.HasColumnType("INTEGER");
b.Property<ulong?>("ThreadCreatedId")
.HasColumnType("INTEGER");
b.Property<ulong?>("ThreadDeletedId")
.HasColumnType("INTEGER");
b.Property<ulong?>("UserBannedId")
.HasColumnType("INTEGER");
@@ -1209,7 +1218,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId")
.IsUnique();
b.ToTable("LogSettings");
b.ToTable("LogSettings", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlayerSettings", b =>
@@ -1246,7 +1255,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId")
.IsUnique();
b.ToTable("MusicPlayerSettings");
b.ToTable("MusicPlayerSettings", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>
@@ -1269,7 +1278,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("MusicPlaylists");
b.ToTable("MusicPlaylists", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
@@ -1291,7 +1300,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("MutedUserId");
b.ToTable("MutedUserId", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.NadekoExpression", b =>
@@ -1329,7 +1338,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("Expressions");
b.ToTable("Expressions", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklistedTag", b =>
@@ -1351,7 +1360,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId");
b.ToTable("NsfwBlacklistedTags");
b.ToTable("NsfwBlacklistedTags", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b =>
@@ -1391,7 +1400,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("Permissions");
b.ToTable("Permissions", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlantedCurrency", b =>
@@ -1428,7 +1437,7 @@ namespace NadekoBot.Migrations
b.HasIndex("MessageId")
.IsUnique();
b.ToTable("PlantedCurrency");
b.ToTable("PlantedCurrency", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
@@ -1462,7 +1471,7 @@ namespace NadekoBot.Migrations
b.HasIndex("MusicPlaylistId");
b.ToTable("PlaylistSong");
b.ToTable("PlaylistSong", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Poll", b =>
@@ -1488,7 +1497,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId")
.IsUnique();
b.ToTable("Poll");
b.ToTable("Poll", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PollAnswer", b =>
@@ -1513,7 +1522,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PollId");
b.ToTable("PollAnswer");
b.ToTable("PollAnswer", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PollVote", b =>
@@ -1538,7 +1547,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PollId");
b.ToTable("PollVote");
b.ToTable("PollVote", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b =>
@@ -1574,7 +1583,7 @@ namespace NadekoBot.Migrations
b.HasIndex("Keyword");
b.ToTable("Quotes");
b.ToTable("Quotes", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRoleV2", b =>
@@ -1615,7 +1624,7 @@ namespace NadekoBot.Migrations
b.HasIndex("MessageId", "Emote")
.IsUnique();
b.ToTable("ReactionRoles");
b.ToTable("ReactionRoles", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b =>
@@ -1649,7 +1658,7 @@ namespace NadekoBot.Migrations
b.HasIndex("When");
b.ToTable("Reminders");
b.ToTable("Reminders", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b =>
@@ -1684,7 +1693,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("Repeaters");
b.ToTable("Repeaters", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RewardedUser", b =>
@@ -1713,7 +1722,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PlatformUserId")
.IsUnique();
b.ToTable("RewardedUsers");
b.ToTable("RewardedUsers", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RotatingPlayingStatus", b =>
@@ -1733,7 +1742,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.ToTable("RotatingStatus");
b.ToTable("RotatingStatus", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b =>
@@ -1764,7 +1773,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId", "RoleId")
.IsUnique();
b.ToTable("SelfAssignableRoles");
b.ToTable("SelfAssignableRoles", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntry", b =>
@@ -1807,7 +1816,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("ShopEntry");
b.ToTable("ShopEntry", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntryItem", b =>
@@ -1829,7 +1838,7 @@ namespace NadekoBot.Migrations
b.HasIndex("ShopEntryId");
b.ToTable("ShopEntryItem");
b.ToTable("ShopEntryItem", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredRole", b =>
@@ -1851,7 +1860,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("SlowmodeIgnoredRole");
b.ToTable("SlowmodeIgnoredRole", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredUser", b =>
@@ -1873,7 +1882,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("SlowmodeIgnoredUser");
b.ToTable("SlowmodeIgnoredUser", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b =>
@@ -1898,7 +1907,7 @@ namespace NadekoBot.Migrations
b.HasIndex("StreamRoleSettingsId");
b.ToTable("StreamRoleBlacklistedUser");
b.ToTable("StreamRoleBlacklistedUser", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b =>
@@ -1930,7 +1939,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("StreamRoleSettings");
b.ToTable("StreamRoleSettings", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b =>
@@ -1955,7 +1964,7 @@ namespace NadekoBot.Migrations
b.HasIndex("StreamRoleSettingsId");
b.ToTable("StreamRoleWhitelistedUser");
b.ToTable("StreamRoleWhitelistedUser", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnbanTimer", b =>
@@ -1980,7 +1989,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("UnbanTimer");
b.ToTable("UnbanTimer", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b =>
@@ -2005,7 +2014,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("UnmuteTimer");
b.ToTable("UnmuteTimer", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnroleTimer", b =>
@@ -2033,7 +2042,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("UnroleTimer");
b.ToTable("UnroleTimer", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b =>
@@ -2073,7 +2082,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId", "GuildId")
.IsUnique();
b.ToTable("UserXpStats");
b.ToTable("UserXpStats", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b =>
@@ -2098,7 +2107,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("VcRoleInfo");
b.ToTable("VcRoleInfo", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b =>
@@ -2133,7 +2142,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WaifuId")
.IsUnique();
b.ToTable("WaifuInfo");
b.ToTable("WaifuInfo", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b =>
@@ -2158,7 +2167,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WaifuInfoId");
b.ToTable("WaifuItem");
b.ToTable("WaifuItem", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
@@ -2190,7 +2199,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("WaifuUpdates");
b.ToTable("WaifuUpdates", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Warning", b =>
@@ -2233,7 +2242,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId");
b.ToTable("Warnings");
b.ToTable("Warnings", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WarningPunishment", b =>
@@ -2264,7 +2273,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId");
b.ToTable("WarningPunishment");
b.ToTable("WarningPunishment", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpCurrencyReward", b =>
@@ -2289,7 +2298,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId");
b.ToTable("XpCurrencyReward");
b.ToTable("XpCurrencyReward", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
@@ -2318,7 +2327,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId", "Level")
.IsUnique();
b.ToTable("XpRoleReward");
b.ToTable("XpRoleReward", (string)null);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
@@ -2341,7 +2350,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("XpSettings");
b.ToTable("XpSettings", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>

View File

@@ -78,6 +78,7 @@ public class DangerousCommandsService : INService
await ctx.CurrencyTransactions.DeleteAsync();
await ctx.PlantedCurrency.DeleteAsync();
await ctx.BankUsers.DeleteAsync();
await ctx.SaveChangesAsync();
}

View File

@@ -191,7 +191,7 @@ public class GreetService : INService, IReadyExecutor
if (conf.AutoDeleteByeMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
}
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions)
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions || ex.DiscordCode == DiscordErrorCode.UnknownChannel)
{
Log.Warning(ex, "Missing permissions to send a bye message, the bye message will be disabled on server: {GuildId}", channel.GuildId);
await SetBye(channel.GuildId, channel.Id, false);
@@ -224,7 +224,7 @@ public class GreetService : INService, IReadyExecutor
if (conf.AutoDeleteGreetMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
}
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions)
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions || ex.DiscordCode == DiscordErrorCode.UnknownChannel)
{
Log.Warning(ex, "Missing permissions to send a bye message, the greet message will be disabled on server: {GuildId}", channel.GuildId);
await SetGreet(channel.GuildId, channel.Id, false);

View File

@@ -457,6 +457,7 @@ public class ProtectionService : INService
case PunishmentAction.ChatMute:
case PunishmentAction.VoiceMute:
case PunishmentAction.AddRole:
case PunishmentAction.TimeOut:
return true;
default:
return false;

View File

@@ -0,0 +1,138 @@
using MessageType = Discord.MessageType;
namespace NadekoBot.Modules.Administration;
public sealed class DoAsUserMessage : IUserMessage
{
private readonly string _message;
private IUserMessage _msg;
private readonly IUser _user;
public DoAsUserMessage(SocketUserMessage msg, IUser user, string message)
{
_msg = msg;
_user = user;
_message = message;
}
public ulong Id => _msg.Id;
public DateTimeOffset CreatedAt => _msg.CreatedAt;
public Task DeleteAsync(RequestOptions? options = null)
{
return _msg.DeleteAsync(options);
}
public Task AddReactionAsync(IEmote emote, RequestOptions? options = null)
{
return _msg.AddReactionAsync(emote, options);
}
public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions? options = null)
{
return _msg.RemoveReactionAsync(emote, user, options);
}
public Task RemoveReactionAsync(IEmote emote, ulong userId, RequestOptions? options = null)
{
return _msg.RemoveReactionAsync(emote, userId, options);
}
public Task RemoveAllReactionsAsync(RequestOptions? options = null)
{
return _msg.RemoveAllReactionsAsync(options);
}
public Task RemoveAllReactionsForEmoteAsync(IEmote emote, RequestOptions? options = null)
{
return _msg.RemoveAllReactionsForEmoteAsync(emote, options);
}
public IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emoji, int limit,
RequestOptions? options = null)
{
return _msg.GetReactionUsersAsync(emoji, limit, options);
}
public MessageType Type => _msg.Type;
public MessageSource Source => _msg.Source;
public bool IsTTS => _msg.IsTTS;
public bool IsPinned => _msg.IsPinned;
public bool IsSuppressed => _msg.IsSuppressed;
public bool MentionedEveryone => _msg.MentionedEveryone;
public string Content => _message;
public string CleanContent => _msg.CleanContent;
public DateTimeOffset Timestamp => _msg.Timestamp;
public DateTimeOffset? EditedTimestamp => _msg.EditedTimestamp;
public IMessageChannel Channel => _msg.Channel;
public IUser Author => _user;
public IReadOnlyCollection<IAttachment> Attachments => _msg.Attachments;
public IReadOnlyCollection<IEmbed> Embeds => _msg.Embeds;
public IReadOnlyCollection<ITag> Tags => _msg.Tags;
public IReadOnlyCollection<ulong> MentionedChannelIds => _msg.MentionedChannelIds;
public IReadOnlyCollection<ulong> MentionedRoleIds => _msg.MentionedRoleIds;
public IReadOnlyCollection<ulong> MentionedUserIds => _msg.MentionedUserIds;
public MessageActivity Activity => _msg.Activity;
public MessageApplication Application => _msg.Application;
public MessageReference Reference => _msg.Reference;
public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _msg.Reactions;
public IReadOnlyCollection<IMessageComponent> Components => _msg.Components;
public IReadOnlyCollection<IStickerItem> Stickers => _msg.Stickers;
public MessageFlags? Flags => _msg.Flags;
public IMessageInteraction Interaction => _msg.Interaction;
public Task ModifyAsync(Action<MessageProperties> func, RequestOptions? options = null)
{
return _msg.ModifyAsync(func, options);
}
public Task PinAsync(RequestOptions? options = null)
{
return _msg.PinAsync(options);
}
public Task UnpinAsync(RequestOptions? options = null)
{
return _msg.UnpinAsync(options);
}
public Task CrosspostAsync(RequestOptions? options = null)
{
return _msg.CrosspostAsync(options);
}
public string Resolve(TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name,
TagHandling roleHandling = TagHandling.Name,
TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name)
{
return _msg.Resolve(userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling);
}
public IUserMessage ReferencedMessage => _msg.ReferencedMessage;
}

View File

@@ -35,6 +35,26 @@ public partial class Administration
_medusaLoader = medusaLoader;
}
[Cmd]
[OwnerOnly]
public async Task DoAs(IUser user, [Leftover] string message)
{
if (ctx.User is not IGuildUser { GuildPermissions.Administrator: true })
return;
if (ctx.Guild is SocketGuild sg && ctx.Channel is ISocketMessageChannel ch
&& ctx.Message is SocketUserMessage msg)
{
var fakeMessage = new DoAsUserMessage(msg, user, message);
await _cmdHandler.TryRunCommand(sg, ch, fakeMessage);
}
else
{
await ReplyErrorLocalizedAsync(strs.error_occured);
}
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]

View File

@@ -78,6 +78,9 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
_client.ChannelDestroyed += _client_ChannelDestroyed;
_client.ChannelUpdated += _client_ChannelUpdated;
_client.RoleDeleted += _client_RoleDeleted;
_client.ThreadCreated += _client_ThreadCreated;
_client.ThreadDeleted += _client_ThreadDeleted;
_mute.UserMuted += MuteCommands_UserMuted;
_mute.UserUnmuted += MuteCommands_UserUnmuted;
@@ -87,6 +90,73 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
_punishService.OnUserWarned += PunishServiceOnOnUserWarned;
}
private Task _client_ThreadDeleted(Cacheable<SocketThreadChannel, ulong> sch)
{
_ = Task.Run(async () =>
{
try
{
if (sch.HasValue || sch.Value is not IGuildChannel ch)
return;
if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out var logSetting)
|| logSetting.ThreadDeletedId is null)
return;
ITextChannel? logChannel;
if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ThreadDeleted)) is null)
return;
var title = GetText(logChannel.Guild, strs.thread_deleted);
await logChannel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(CurrentTime(ch.Guild)));
}
catch (Exception)
{
// ignored
}
});
return Task.CompletedTask;
}
private Task _client_ThreadCreated(SocketThreadChannel sch)
{
_ = Task.Run(async () =>
{
try
{
if (sch.Guild is not IGuildChannel ch)
return;
if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out var logSetting)
|| logSetting.ThreadCreatedId is null)
return;
ITextChannel? logChannel;
if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ThreadCreated)) is null)
return;
var title = GetText(logChannel.Guild, strs.thread_created);
await logChannel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(CurrentTime(ch.Guild)));
}
catch (Exception)
{
// ignored
}
});
return Task.CompletedTask;
}
public async Task OnReadyAsync()
=> await Task.WhenAll(PresenceUpdateTask(), IgnoreMessageIdsClearTask());
@@ -725,6 +795,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
ITextChannel? logChannel;
if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) is null)
return;
string title;
if (ch is IVoiceChannel)
title = GetText(logChannel.Guild, strs.voice_chan_destroyed);
@@ -744,7 +815,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
});
return Task.CompletedTask;
}
private Task _client_ChannelCreated(IChannel ich)
{
_ = Task.Run(async () =>

View File

@@ -88,10 +88,6 @@ public class GameStatusEvent : ICurrencyEvent
await msg.ModifyAsync(m =>
{
m.Embed = GetEmbed(PotSize).Build();
},
new()
{
RetryMode = RetryMode.AlwaysRetry
});
}

View File

@@ -79,10 +79,6 @@ public class ReactionEvent : ICurrencyEvent
await msg.ModifyAsync(m =>
{
m.Embed = GetEmbed(PotSize).Build();
},
new()
{
RetryMode = RetryMode.AlwaysRetry
});
}

View File

@@ -244,20 +244,29 @@ public partial class Gambling
var waifuItems = _service.GetWaifuItems().ToDictionary(x => x.ItemEmoji, x => x);
var nobody = GetText(strs.nobody);
var itemsStr = !wi.Items.Any()
var itemList = await _service.GetItems(wi.WaifuId);
var itemsStr = !itemList.Any()
? "-"
: string.Join("\n",
wi.Items.Where(x => waifuItems.TryGetValue(x.ItemEmoji, out _))
.OrderBy(x => waifuItems[x.ItemEmoji].Price)
.GroupBy(x => x.ItemEmoji)
.Select(x => $"{x.Key} x{x.Count(),-3}")
.Chunk(2)
.Select(x => string.Join(" ", x)));
itemList.Where(x => waifuItems.TryGetValue(x.ItemEmoji, out _))
.OrderBy(x => waifuItems[x.ItemEmoji].Price)
.GroupBy(x => x.ItemEmoji)
.Select(x => $"{x.Key} x{x.Count(),-3}")
.Chunk(2)
.Select(x => string.Join(" ", x)));
var fansStr = wi.Fans.Shuffle().Take(30).Select(x => wi.Claims.Contains(x) ? $"{x} 💞" : x).Join('\n');
var claimsNames = (await _service.GetClaimNames(wi.WaifuId));
var claimsStr = claimsNames
.Shuffle()
.Take(30)
.Join('\n');
var fansList = await _service.GetFansNames(wi.WaifuId);
var fansStr = fansList
.Select((x) => claimsNames.Contains(x) ? $"{x} 💞" : x).Join('\n');
if (string.IsNullOrWhiteSpace(fansStr))
fansStr = "-";
@@ -275,9 +284,9 @@ public partial class Gambling
.AddField(GetText(strs.changes_of_heart), $"{wi.AffinityCount} - \"the {affInfo}\"", true)
.AddField(GetText(strs.divorces), wi.DivorceCount.ToString(), true)
.AddField("\u200B", "\u200B", true)
.AddField(GetText(strs.fans(wi.Fans.Count)), fansStr, true)
.AddField(GetText(strs.fans(fansList.Count)), fansStr, true)
.AddField($"Waifus ({wi.ClaimCount})",
wi.ClaimCount == 0 ? nobody : string.Join("\n", wi.Claims.Shuffle().Take(30)),
wi.ClaimCount == 0 ? nobody : claimsStr,
true)
.AddField(GetText(strs.gifts), itemsStr, true);

View File

@@ -414,11 +414,8 @@ public class WaifuService : INService, IReadyExecutor
AffinityName = null,
ClaimCount = 0,
ClaimerName = null,
Claims = new(),
Fans = new(),
DivorceCount = 0,
FullName = null,
Items = new(),
Price = 1
};
}
@@ -426,14 +423,6 @@ public class WaifuService : INService, IReadyExecutor
return wi;
}
public async Task<WaifuInfoStats> GetFullWaifuInfoAsync(IGuildUser target)
{
await using var uow = _db.GetDbContext();
_ = uow.GetOrCreateUser(target);
return await GetFullWaifuInfoAsync(target.Id);
}
public string GetClaimTitle(int count)
{
ClaimTitle title;
@@ -557,4 +546,38 @@ public class WaifuService : INService, IReadyExecutor
}
}
}
public async Task<IReadOnlyCollection<string>> GetClaimNames(int waifuId)
{
await using var ctx = _db.GetDbContext();
return await ctx.GetTable<DiscordUser>()
.Where(x => ctx.GetTable<WaifuInfo>()
.Where(wi => wi.ClaimerId == waifuId)
.Select(wi => wi.WaifuId)
.Contains(x.Id))
.Select(x => $"{x.Username}#{x.Discriminator}")
.ToListAsyncEF();
}
public async Task<IReadOnlyCollection<string>> GetFansNames(int waifuId)
{
await using var ctx = _db.GetDbContext();
return await ctx.GetTable<DiscordUser>()
.Where(x => ctx.GetTable<WaifuInfo>()
.Where(wi => wi.AffinityId == waifuId)
.Select(wi => wi.WaifuId)
.Contains(x.Id))
.Select(x => $"{x.Username}#{x.Discriminator}")
.ToListAsyncEF();
}
public async Task<IReadOnlyCollection<WaifuItem>> GetItems(int waifuId)
{
await using var ctx = _db.GetDbContext();
return await ctx.GetTable<WaifuItem>()
.Where(x => x.WaifuInfoId == ctx.GetTable<WaifuInfo>()
.Where(x => x.WaifuId == waifuId)
.Select(x => x.Id)
.FirstOrDefault())
.ToListAsyncEF();
}
}

View File

@@ -74,11 +74,7 @@ public class TypingGame
var time = _options.StartTime;
var msg = await Channel.SendMessageAsync($"Starting new typing contest in **{time}**...",
options: new()
{
RetryMode = RetryMode.AlwaysRetry
});
var msg = await Channel.SendMessageAsync($"Starting new typing contest in **{time}**...");
do
{

View File

@@ -131,9 +131,12 @@ public sealed class TriviaGame
hintSent = true;
// start a new countdown of the same length
halfGuessTimerTask = TimeOutFactory();
// send a hint out
await OnHint(this, question);
if (!_opts.NoHint)
{
// send a hint out
await OnHint(this, question);
}
continue;
}

View File

@@ -45,7 +45,7 @@ public class HelpService : IExecNoCommand, INService
// only send dm help text if it contains one of the keywords, if they're specified
// if they're not, then reply to every DM
if (settings.DmHelpTextKeywords.Any() && !settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k)))
if (settings.DmHelpTextKeywords is not null && !settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k)))
return Task.CompletedTask;
var rep = new ReplacementBuilder().WithOverride("%prefix%", () => _bss.Data.Prefix)
@@ -152,18 +152,22 @@ public class HelpService : IExecNoCommand, INService
.Any(x => x is OnlyPublicBotAttribute))
toReturn.Add("Only Public Bot");
var userPerm = (UserPermAttribute)cmd.Preconditions.FirstOrDefault(ca => ca is UserPermAttribute);
var userPermString = cmd.Preconditions
.Where(ca => ca is UserPermAttribute)
.Cast<UserPermAttribute>()
.Select(userPerm =>
{
if (userPerm.ChannelPermission is { } cPerm)
return GetPreconditionString(cPerm);
var userPermString = string.Empty;
if (userPerm is not null)
{
if (userPerm.ChannelPermission is { } cPerm)
userPermString = GetPreconditionString(cPerm);
if (userPerm.GuildPermission is { } gPerm)
userPermString = GetPreconditionString(gPerm);
}
if (userPerm.GuildPermission is { } gPerm)
return GetPreconditionString(gPerm);
return string.Empty;
})
.Where(x => !string.IsNullOrWhiteSpace(x))
.Join('\n');
if (overrides is null)
{
if (!string.IsNullOrWhiteSpace(userPermString))
@@ -188,4 +192,4 @@ public class HelpService : IExecNoCommand, INService
private string GetText(LocStr str, IGuild guild)
=> _strings.GetText(str, guild?.Id);
}
}

View File

@@ -16,7 +16,7 @@ public partial class Searches
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)]
public Task YtUploadNotif(string url, [Leftover] ITextChannel channel = null)
public Task YtUploadNotif(string url, ITextChannel channel = null, [Leftover] string message = null)
{
var m = _ytChannelRegex.Match(url);
if (!m.Success)
@@ -24,19 +24,19 @@ public partial class Searches
var channelId = m.Groups["channelid"].Value;
return Feed("https://www.youtube.com/feeds/videos.xml?channel_id=" + channelId, channel);
return Feed("https://www.youtube.com/feeds/videos.xml?channel_id=" + channelId, channel, message);
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)]
public async Task Feed(string url, [Leftover] ITextChannel channel = null)
public async Task Feed(string url, ITextChannel channel = null, [Leftover] string message = null)
{
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)
|| (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps))
{
await ReplyErrorLocalizedAsync(strs.feed_invalid_url);
return;
return;
}
channel ??= (ITextChannel)ctx.Channel;
@@ -51,7 +51,10 @@ public partial class Searches
return;
}
var result = _service.AddFeed(ctx.Guild.Id, channel.Id, url);
if (ctx.User is not IGuildUser gu || !gu.GuildPermissions.Administrator)
message = message?.SanitizeMentions(true);
var result = _service.AddFeed(ctx.Guild.Id, channel.Id, url, message);
if (result == FeedAddResult.Success)
{
await ReplyConfirmLocalizedAsync(strs.feed_added);

View File

@@ -162,7 +162,6 @@ public class FeedsService : INService
}
}
embed.WithTitle(title.TrimTo(256));
var desc = feedItem.Description?.StripHtml();
@@ -171,15 +170,15 @@ public class FeedsService : INService
//send the created embed to all subscribed channels
var feedSendTasks = kvp.Value
.Where(x => x.GuildConfig is not null)
.Select(x => _client.GetGuild(x.GuildConfig.GuildId)
?.GetTextChannel(x.ChannelId))
.Where(x => x is not null)
.Select(x => x.EmbedAsync(embed));
.Where(x => x.GuildConfig is not null)
.Select(x => _client.GetGuild(x.GuildConfig.GuildId)
?.GetTextChannel(x.ChannelId)
?.EmbedAsync(embed, x.Message))
.Where(x => x is not null);
allSendTasks.Add(feedSendTasks.WhenAll());
// as data retrieval was sucessful, reset error counter
// as data retrieval was successful, reset error counter
ClearErrors(rssUrl);
}
}
@@ -207,7 +206,7 @@ public class FeedsService : INService
.ToList();
}
public FeedAddResult AddFeed(ulong guildId, ulong channelId, string rssFeed)
public FeedAddResult AddFeed(ulong guildId, ulong channelId, string rssFeed, string message)
{
ArgumentNullException.ThrowIfNull(rssFeed, nameof(rssFeed));

View File

@@ -220,5 +220,5 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
}
public IEnumerable<string> GetLanguages()
=> _google.Languages.Select(x => x.Key);
=> _google.Languages.GroupBy(x => x.Value).Select(x => $"{x.AsEnumerable().Select(y => y.Key).Join(", ")}");
}

View File

@@ -77,6 +77,19 @@ public partial class Searches
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task Translangs()
=> await ctx.Channel.SendTableAsync(_service.GetLanguages(), str => $"{str,-15}");
{
var langs = _service.GetLanguages().ToList();
var eb = _eb.Create()
.WithTitle(GetText(strs.supported_languages))
.WithOkColor();
foreach (var chunk in langs.Chunk(15))
{
eb.AddField("󠀁", chunk.Join("\n"), isInline: true);
}
await ctx.Channel.EmbedAsync(eb);
}
}
}

View File

@@ -199,7 +199,7 @@ public sealed class PatronageService
}
else
{
if (dbPatron.LastCharge.Month < lastChargeUtc.Month)
if (dbPatron.LastCharge.Month < lastChargeUtc.Month || dbPatron.LastCharge.Year < lastChargeUtc.Year)
{
// user is charged again for this month
// if his sub would end in teh future, extend it by one month.

View File

@@ -270,7 +270,7 @@ public partial class Utility
if (userId == ctx.User.Id || hasManageMessages)
{
var deleted = await _qs.DeleteAllAuthorQuotesAsync(ctx.Guild.Id, ctx.User.Id);
var deleted = await _qs.DeleteAllAuthorQuotesAsync(ctx.Guild.Id, userId);
await ReplyConfirmLocalizedAsync(strs.quotes_deleted_count(deleted));
}
else

View File

@@ -1,12 +1,9 @@
#nullable disable
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db;
using NadekoBot.Modules.Utility.Common;
using NadekoBot.Modules.Utility.Common.Exceptions;
using NadekoBot.Services.Database.Models;
using System.Diagnostics;
using System.Net;
using Nadeko.Common;
namespace NadekoBot.Modules.Utility.Services;
@@ -31,15 +28,16 @@ public class StreamRoleService : IReadyExecutor, INService
_queueRunner = new QueueRunner();
}
private Task OnPresenceUpdate(SocketUser user, SocketPresence oldPresence, SocketPresence newPresence)
private Task OnPresenceUpdate(SocketUser user, SocketPresence? oldPresence, SocketPresence? newPresence)
{
_ = Task.Run(async () =>
{
if (oldPresence.Activities.Count != newPresence.Activities.Count)
if (oldPresence?.Activities?.Count != newPresence?.Activities?.Count)
{
var guildUsers = _client.Guilds
.Select(x => x.GetUser(user.Id));
.Select(x => x.GetUser(user.Id))
.Where(x => x is not null);
foreach (var guildUser in guildUsers)
{
@@ -131,7 +129,7 @@ public class StreamRoleService : IReadyExecutor, INService
/// <param name="guild">Guild Id</param>
/// <param name="keyword">Keyword to set</param>
/// <returns>The keyword set</returns>
public async Task<string> SetKeyword(IGuild guild, string keyword)
public async Task<string?> SetKeyword(IGuild guild, string? keyword)
{
keyword = keyword?.Trim().ToLowerInvariant();
@@ -221,15 +219,15 @@ public class StreamRoleService : IReadyExecutor, INService
await RescanUsers(guild);
}
private async ValueTask RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
private async ValueTask RescanUser(IGuildUser user, StreamRoleSettings setting, IRole? addRole = null)
=> await _queueRunner.EnqueueAsync(() => RescanUserInternal(user, setting, addRole));
private async Task RescanUserInternal(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
private async Task RescanUserInternal(IGuildUser user, StreamRoleSettings setting, IRole? addRole = null)
{
if (user.IsBot)
return;
var g = (StreamingGame)user.Activities.FirstOrDefault(a
var g = (StreamingGame?)user.Activities.FirstOrDefault(a
=> a is StreamingGame
&& (string.IsNullOrWhiteSpace(setting.Keyword)
|| a.Name.ToUpperInvariant().Contains(setting.Keyword.ToUpperInvariant())

View File

@@ -402,6 +402,103 @@ public partial class Utility : NadekoModule
await ctx.OkAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[BotPerm(GuildPerm.ManageEmojisAndStickers)]
[UserPerm(GuildPerm.ManageEmojisAndStickers)]
public async Task StickerAdd(string name = null, string description = null, params string[] tags)
{
string format;
Stream stream;
if (ctx.Message.Stickers.Count is 1 && ctx.Message.Stickers.First() is SocketSticker ss)
{
name ??= ss.Name;
description = ss.Description;
tags = tags is null or { Length: 0 } ? ss.Tags.ToArray() : tags;
format = FormatToExtension(ss.Format);
using var http = _httpFactory.CreateClient();
stream = await http.GetStreamAsync(ss.GetStickerUrl());
}
// else if (ctx.Message.Attachments.FirstOrDefault() is { } attachment)
// {
// var url = attachment?.Url;
//
// if (url is null)
// return;
//
// if (name is null)
// {
// await ReplyErrorLocalizedAsync(strs.sticker_missing_name);
// return;
// }
//
// format = Path.GetExtension(attachment.Filename);
//
// if (attachment is not { Width: 300, Height: 300 })
// {
// await ReplyErrorLocalizedAsync(strs.sticker_invalid_size);
// return;
// }
//
// using var http = _httpFactory.CreateClient();
//
// using var res = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
// if (res.GetContentLength() > 512.Kilobytes().Bytes)
// {
// await ReplyErrorLocalizedAsync(strs.invalid_emoji_link);
// return;
// }
//
// stream = await res.Content.ReadAsStreamAsync();
// }
else
{
await ReplyErrorLocalizedAsync(strs.sticker_error);
return;
}
try
{
if (tags.Length == 0)
tags = new[] { name };
await ctx.Guild.CreateStickerAsync(name,
string.IsNullOrWhiteSpace(description) ? "Missing description" : description,
tags,
stream,
$"{name}.{format}");
await ctx.OkAsync();
}
catch (Exception ex)
{
Log.Warning(ex, "Error occurred while adding a sticker: {Message}", ex.Message);
await ReplyErrorLocalizedAsync(strs.error_occured);
}
finally
{
await stream.DisposeAsync();
}
}
private static string FormatToExtension(StickerFormatType format)
{
switch (format)
{
case StickerFormatType.None:
case StickerFormatType.Png:
case StickerFormatType.Apng:
return "png";
case StickerFormatType.Lottie:
return "lottie";
default:
throw new ArgumentException(nameof (format));
}
}
[Cmd]
[OwnerOnly]
public async Task ListServers(int page = 1)

View File

@@ -302,7 +302,7 @@ public class CommandHandler : INService, IReadyExecutor
}
public Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommandAsync(
CommandContext context,
ICommandContext context,
string input,
int argPos,
IServiceProvider serviceProvider,
@@ -311,7 +311,7 @@ public class CommandHandler : INService, IReadyExecutor
public async Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommand(
CommandContext context,
ICommandContext context,
string input,
IServiceProvider services,
MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)

View File

@@ -88,6 +88,9 @@ public sealed class CurrencyService : ICurrencyService, INService
long amount,
TxData txData)
{
if (amount == 0)
return true;
var wallet = await GetWalletAsync(userId);
var result = await wallet.Take(amount, txData);
if(result)

View File

@@ -10,143 +10,11 @@ using System.Xml;
namespace NadekoBot.Services;
public class GoogleApiService : IGoogleApiService, INService
public sealed partial class GoogleApiService : IGoogleApiService, INService
{
private static readonly Regex
_plRegex = new("(?:youtu\\.be\\/|list=)(?<id>[\\da-zA-Z\\-_]*)", RegexOptions.Compiled);
public IReadOnlyDictionary<string, string> Languages { get; } = 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" }
};
private readonly YouTubeService _yt;
private readonly UrlshortenerService _sh;
@@ -155,7 +23,7 @@ public class GoogleApiService : IGoogleApiService, INService
private readonly IBotCredsProvider _creds;
private readonly IHttpClientFactory _httpFactory;
public GoogleApiService(IBotCredsProvider creds, IHttpClientFactory factory)
public GoogleApiService(IBotCredsProvider creds, IHttpClientFactory factory) : this()
{
_creds = creds;
_httpFactory = factory;

View File

@@ -0,0 +1,158 @@
namespace NadekoBot.Services;
public sealed partial class GoogleApiService
{
private const string SUPPORTED = @"afrikaans af
albanian sq
amharic am
arabic ar
armenian hy
assamese as
aymara ay
azerbaijani az
bambara bm
basque eu
belarusian be
bengali bn
bhojpuri bho
bosnian bs
bulgarian bg
catalan ca
cebuano ceb
chinese zh-CN
chinese-trad zh-TW
corsican co
croatian hr
czech cs
danish da
dhivehi dv
dogri doi
dutch nl
english en
esperanto eo
estonian et
ewe ee
filipino fil
finnish fi
french fr
frisian fy
galician gl
georgian ka
german de
greek el
guarani gn
gujarati gu
haitian ht
hausa ha
hawaiian haw
hebrew he
hindi hi
hmong hmn
hungarian hu
icelandic is
igbo ig
ilocano ilo
indonesian id
irish ga
italian it
japanese ja
javanese jv
kannada kn
kazakh kk
khmer km
kinyarwanda rw
konkani gom
korean ko
krio kri
kurdish ku
kurdish-sor ckb
kyrgyz ky
lao lo
latin la
latvian lv
lingala ln
lithuanian lt
luganda lg
luxembourgish lb
macedonian mk
maithili mai
malagasy mg
malay ms
malayalam ml
maltese mt
maori mi
marathi mr
meiteilon mni-Mtei
mizo lus
mongolian mn
myanmar my
nepali ne
norwegian no
nyanja ny
odia or
oromo om
pashto ps
persian fa
polish pl
portuguese pt
punjabi pa
quechua qu
romanian ro
russian ru
samoan sm
sanskrit sa
scots gd
sepedi nso
serbian sr
sesotho st
shona sn
sindhi sd
sinhala si
slovak sk
slovenian sl
somali so
spanish es
sundanese su
swahili sw
swedish sv
tagalog tl
tajik tg
tamil ta
tatar tt
telugu te
thai th
tigrinya ti
tsonga ts
turkish tr
turkmen tk
twi ak
ukrainian uk
urdu ur
uyghur ug
uzbek uz
vietnamese vi
welsh cy
xhosa xh
yiddish yi
yoruba yo
zulu zu";
public IReadOnlyDictionary<string, string> Languages { get; }
private GoogleApiService()
{
var langs = SUPPORTED.Split("\n")
.Select(x => x.Split(' '))
.ToDictionary(x => x[0].Trim(), x => x[1].Trim());
foreach (var (_, v) in langs.ToArray())
{
langs.Add(v, v);
}
Languages = langs;
}
}

View File

@@ -7,7 +7,7 @@ namespace NadekoBot.Services;
public sealed class StatsService : IStatsService, IReadyExecutor, INService
{
public const string BOT_VERSION = "4.3.10";
public const string BOT_VERSION = "4.3.11";
public string Author
=> "Kwoth#2452";

View File

@@ -23,11 +23,7 @@ public static class MessageChannelExtensions
embeds: embeds is null
? null
: embeds as Embed[] ?? embeds.ToArray(),
components: components,
options: new()
{
RetryMode = RetryMode.AlwaysRetry
});
components: components);
}
public static async Task<IUserMessage> SendAsync(
@@ -155,27 +151,6 @@ public static class MessageChannelExtensions
string? footer = null)
=> ch.SendAsync(eb, MessageType.Error, title, text, url, footer);
// weird stuff
public static Task<IUserMessage> SendTableAsync<T>(
this IMessageChannel ch,
string seed,
IEnumerable<T> items,
Func<T, string> howToPrint,
int columns = 3)
=> ch.SendMessageAsync($@"{seed}```css
{items.Chunk(columns)
.Select(ig => string.Concat(ig.Select(howToPrint)))
.Join("\n")}
```");
public static Task<IUserMessage> SendTableAsync<T>(
this IMessageChannel ch,
IEnumerable<T> items,
Func<T, string> howToPrint,
int columns = 3)
=> ch.SendTableAsync("", items, howToPrint, columns);
public static Task SendPaginatedConfirmAsync(
this ICommandContext ctx,
int currentPage,

View File

@@ -23,11 +23,7 @@ public static class SocketMessageComponentExtensions
? null
: embeds as Embed[] ?? embeds.ToArray(),
components: components,
ephemeral: ephemeral,
options: new()
{
RetryMode = RetryMode.AlwaysRetry
});
ephemeral: ephemeral);
}
public static Task RespondAsync(

View File

@@ -728,6 +728,9 @@ showemojis:
emojiadd:
- emojiadd
- ea
stickeradd:
- stickeradd
- sa
emojiremove:
- emojiremove
- emojirm
@@ -1383,4 +1386,7 @@ patronmessage:
eval:
- eval
autopublish:
- autopublish
- autopublish
doas:
- doas
- execas

View File

@@ -1245,6 +1245,11 @@ emojiremove:
desc: "Removes the specified emoji or emojis from this server."
args:
- ":eagleWarrior: :plumedArcher:"
stickeradd:
desc: "Adds the sticker from your message to this server. Send the sticker along with this command (in the same message)."
args:
- ""
- "name \"description\" tag1 tag2 tagN"
deckshuffle:
desc: "Reshuffles all cards back into the deck."
args:
@@ -1991,13 +1996,22 @@ ytuploadnotif:
desc: |-
Subscribe to a youtube channel's upload rss feed.
Shortcut for `.feed https://www.youtube.com/feeds/videos.xml?channel_id=%3Cyoutube_channel_id`
You can optionally specify a message which will be posted with an update.
args:
- "https://www.youtube.com/channel/UCSJ4gkVC6NrvII8umztf0Ow"
- "https://www.youtube.com/channel/UCSJ4gkVC6NrvII8umztf0Ow"
- "https://www.youtube.com/channel/UCSJ4gkVC6NrvII8umztf0Ow New video is posted"
feed:
desc: "Subscribes to a feed. Bot will post an update up to once every 10 seconds. You can have up to 10 feeds on one server. All feeds must have unique URLs. Set a channel as a second optional parameter to specify where to send the updates."
desc: |-
Subscribes to a feed.
Bot will post an update up to once every 10 seconds.
You can have up to 10 feeds on one server.
All feeds must have unique URLs.
Set a channel as a second optional parameter to specify where to send the updates.
You can optionally specify a message after the channel name which will be posted with an update.
args:
- "https://blog.playstation.com/feed/"
- "https://blog.playstation.com/feed/ #updates"
- "https://blog.playstation.com/feed/ #updates New playstation rss feed post!"
feedremove:
desc: "Stops tracking a feed on the given index. Use `{0}feeds` command to see a list of feeds and their indexes."
args:
@@ -2345,4 +2359,8 @@ threaddelete:
autopublish:
desc: "Make the bot automatically publish all messages posted in the news channel this command was executed in."
args:
- ""
- ""
doas:
desc: "Execute the command as if you were the target user. Requires bot ownership and server administrator permission."
args:
- "@Thief .give all @Admin"

View File

@@ -977,14 +977,14 @@
"created_by": "Created by {0}",
"module_footer": "{0}cmds {1}",
"module_page_empty": "No module on this page.",
"module_description_help": "Get command help, descriptions and usage examples",
"module_description_gambling": "Bet on dice rolls, blackjack, slots, coinflips and others",
"module_description_games": "Play trivia, nunchi, hangman, connect4 and other games",
"module_description_nsfw": "NSFW commands.",
"module_description_music": "Play music from youtube, local files soundcloud and radio streams",
"module_description_utility": "Manage custom quotes, repeating messages and check facts about the server",
"module_description_help": "Get command help, descriptions and usage examples",
"module_description_gambling": "Bet on dice rolls, blackjack, slots, coinflips and others",
"module_description_games": "Play trivia, nunchi, hangman, connect4 and other games",
"module_description_nsfw": "NSFW commands.",
"module_description_music": "Play music from youtube, local files soundcloud and radio streams",
"module_description_utility": "Manage custom quotes, repeating messages and check facts about the server",
"module_description_administration": "Moderation, punish users, setup self assignable roles and greet messages",
"module_description_expressions": "Setup custom bot responses to certain words or phrases",
"module_description_expressions": "Setup custom bot responses to certain words or phrases",
"module_description_permissions": "Setup perms for commands, filter words and set up command cooldowns",
"module_description_searches": "Search for jokes, images of animals, anime and manga",
"module_description_xp": "Gain xp based on chat activity, check users' xp cards",
@@ -1005,7 +1005,7 @@
"linkonly_disable": "This channel is no longer link-only.",
"deleted_x_servers": "Deleted {0} servers.",
"curtr_gift": "Gift from {0} [{1}]",
"curtr_award": "Awarded by bot owner {0} [{1}]",
"curtr_award": "Awarded by bot owner {0} [{1}]",
"curtr_take": "Taken by bot owner {0} [{1}]",
"list_of_medusae": "List of Medusae",
"list_of_unloaded": "List of Available Medusae",
@@ -1051,5 +1051,11 @@
"patron_insuff_tier": "Your Patron Tier insufficient to perform this action.",
"xpshop_already_owned": "You already own this item.",
"xpshop_item_not_found": "An item with that key doesn't exist.",
"xpshop_website": "You can see the list of all Xp Shop items here: <https://xpshop.nadeko.bot>"
"xpshop_website": "You can see the list of all Xp Shop items here: <https://xpshop.nadeko.bot>",
"sticker_invalid_size": "Stickers must be exactly 300x300 pixels.",
"sticker_error": "You must either send a sticker along with this command, or upload a 300x300 .png or .apng image.",
"sticker_missing_name": "Please specify a name for the sticker.",
"thread_deleted": "Thread Deleted",
"thread_created": "Thread Created",
"supported_languages": "Supported Languages"
}

View File

@@ -946,106 +946,106 @@
"reminder_server_list": "Lista przypomnień serwera",
"imageonly_enable": "Ten kanał działa od teraz w trybie tylko obrazki.",
"imageonly_disable": "Ten kanał już nie działa w trybie tylko obrazki.",
"transaction": "",
"finished_track": "",
"playing_track": "",
"queued_track": "",
"removed_track": "",
"autoplaying": "",
"music_autoplay_on": "",
"music_autoplay_off": "",
"track_moved": "",
"atl_not_enabled": "",
"channels": "",
"track_not_found": "",
"removed_track_error": "",
"market_cap_dominance": "",
"circulating_supply": "",
"module_description_expressions": "",
"deleted_x_servers": "",
"curtr_gift": "",
"curtr_award": "",
"curtr_take": "",
"expr_deleted": "",
"expr_insuff_perms": "",
"expressions": "",
"expr_new": "",
"expr_no_found": "",
"expr_no_found_id": "",
"exprs_cleared": "",
"expr_reset": "",
"expr_set": "",
"expr_edited": "",
"stream_online_delete_enabled": "",
"stream_online_delete_disabled": "",
"club_create_error_name": "",
"club_desc_update": "",
"bank_accounts": "",
"module_description_medusa": "",
"list_of_medusae": "",
"list_of_unloaded": "",
"medusa_name_not_found": "",
"medusa_info": "",
"sneks_count": "",
"commands_count": "",
"no_medusa_loaded": "",
"no_medusa_available": "",
"loaded_medusae": "",
"medusa_not_loaded": "",
"medusa_possibly_cant_unload": "",
"medusa_loaded": "",
"medusa_unloaded": "",
"medusa_empty": "",
"medusa_already_loaded": "",
"medusa_invalid_not_found": "",
"bank_balance": "",
"bank_deposited": "",
"bank_withdrew": "",
"bank_withdraw_insuff": "",
"cmd_group_commands": "",
"limit_reached": "",
"feature_limit_reached_you": "",
"feature_limit_reached_owner": "",
"feature_limit_reached_either": "",
"tier": "",
"pledge": "",
"expires": "",
"commands": "",
"groups": "",
"modules": "",
"no_quota_found": "",
"patron_info": "",
"quotas": "",
"patron_not_enabled": "",
"results_in": "",
"patron_msg_sent": "",
"cards": "",
"hand_value": "",
"roll2": "",
"rolls": "",
"available_tests": "",
"test_results_for": "",
"multiplier": "",
"trivia_ended": "",
"card": "",
"guess": "",
"repeater_skip_next": "",
"repeater_dont_skip_next": "",
"remind_timely": "",
"xp_shop_disabled": "",
"timely_time": "",
"buy": "",
"use": "",
"in_use": "",
"xp_shop_item_cant_use": "",
"linkonly_enable": "",
"linkonly_disable": "",
"xp_shop_buy_required_tier": "",
"available_commands": "",
"xpadd_users": "",
"xpshop_buy_success": "",
"patron_insuff_tier": "",
"xpshop_already_owned": "",
"xpshop_item_not_found": "",
"xpshop_website": ""
"transaction": "Transakcja Walutowa",
"finished_track": "Utwór zakończony",
"playing_track": "Odtwarzam utwór #{0}",
"queued_track": "Utwór zakolejkowany",
"removed_track": "Utwór usunięty",
"autoplaying": "Automatycznie dodaje powiązane utwory.",
"music_autoplay_on": "Automatyczne odtwarzanie muzyki włączone. Będę samodzielnie kolejkować powiązane utwory po zakończeniu bieżącego odtwarzania.",
"music_autoplay_off": "Automatyczne odtwarzanie muzyki wyłączone.",
"track_moved": "Utwór przesunięty",
"atl_not_enabled": "Automatyczne tłumaczenie nie jest włączone na tym kanale lub podano niewłaściwy język.",
"channels": "Kanały",
"track_not_found": "Nie znaleziono utworu.",
"removed_track_error": "Utwór o podanym indeksie nie istnieje",
"market_cap_dominance": "Dominacja",
"circulating_supply": "Podaż obiegowa",
"module_description_expressions": "Ustaw własne odpowiedzi bota na wybrane słowa lub wyrażenia",
"deleted_x_servers": "Usunięto {0} serwerów.",
"curtr_gift": "Prezent od {0} [{1}]",
"curtr_award": "Przyznane przez właściciela bota {0} [{1}]",
"curtr_take": "Zabrane przez właściciela bota {0} [{1}]",
"expr_deleted": "Wyrażenie usunięte",
"expr_insuff_perms": "Niewystarczające uprawnienia. Wymaga uprawnień Właściciela Bota dla globalnych wyrażeń, a uprawnień Administratora dla serwerowych wyrażeń.",
"expressions": "Wyrażenia",
"expr_new": "Nowe Wyrażenie",
"expr_no_found": "Nie znaleziono wyrażeń.",
"expr_no_found_id": "Nie znaleziono wyrażenia o podanym id.",
"exprs_cleared": "Wszystkie {0} wyrażenia na tym serwerze zostały usunięte.",
"expr_reset": "Wyrażenia o id {0} nie będą już dodawać reakcji.",
"expr_set": "Wyrażenia o id {0} będą dodawać następujące reakcje do odpowiedzi: {1}",
"expr_edited": "Wyrażenie Zedytowane",
"stream_online_delete_enabled": "Powiadomienia o trwającym streamie będą teraz usuwane kiedy stream się zakończy.",
"stream_online_delete_disabled": "Powiadomienia o trwającym streamie nie będą już usuwane kiedy stream się zakończy.",
"club_create_error_name": "Nie udało się utworzyć klubu. Klub o podanej nazwie już istnieje.",
"club_desc_update": "Zaktualizowano Opis Klubu",
"bank_accounts": "Konta Bankowe",
"module_description_medusa": "**Tylko dla Właściciela Bota.** Załaduj, rozładuj i zarządzaj dynamicznymi modułami. Czytaj więcej [tutaj](https://nadekobot.readthedocs.io/en/latest/medusa/creating-a-medusa/)",
"list_of_medusae": "Lista Meduz",
"list_of_unloaded": "Lista Dostępnych Meduz",
"medusa_name_not_found": "Meduza o podanej nazwie nie istnieje lub nie jest załadowana.",
"medusa_info": "Informacje o Meduzie",
"sneks_count": "Wonsze ({0})",
"commands_count": "Komendy ({0})",
"no_medusa_loaded": "Brak załadowanych meduz.",
"no_medusa_available": "Brak dostępnych meduz.",
"loaded_medusae": "Załadowane Meduzy",
"medusa_not_loaded": "Meduza o podanej nazwie nie jest załadowana.",
"medusa_possibly_cant_unload": "Meduza prawdopodobnie nie została załadowana w pełni. Zrestartuj bota jeśli pojawią się problemy.",
"medusa_loaded": "Meduza {0} została załadowana.",
"medusa_unloaded": "Meduza {0} została rozładowana.",
"medusa_empty": "Meduza nie została załadowana ponieważ nie zawiera żadnych Wonszy.",
"medusa_already_loaded": "Meduza {0} jest już załadowana",
"medusa_invalid_not_found": "Meduza o podanej nazwie nie została znaleziona lub plik był błędny",
"bank_balance": "Masz {0} na swoim koncie bankowym.",
"bank_deposited": "Wpłacono {0} na twoje konto bankowe.",
"bank_withdrew": "Wypłacono {0} z twojego konta bankowego.",
"bank_withdraw_insuff": "Nie masz wystarczającej ilości {0} na swoim koncie bankowym.",
"cmd_group_commands": "grupa komend '{0}'",
"limit_reached": "Osiągnięto limit funkcji wynoszący {0}.",
"feature_limit_reached_you": "Osiągnięto limit {0} funkcji {1}. Być może zwiększenie progu wspierania pozwoli podnieść ten limit.",
"feature_limit_reached_owner": "Właściciel servera osiągnął limit {0} funkcji {1}. Właściciel serwera może być w stanie zwiększyć ten limit przez zwiększenie progu wspierania.",
"feature_limit_reached_either": "Osiągnięto limit {0} funkcji {1}. Albo ty albo właściciel serwera możecie być w stanie zwiększyć ten limit przez zwiększenie progu wspierania.",
"tier": "Próg",
"pledge": "Deklaracja",
"expires": "Wygasa",
"commands": "Komendy",
"groups": "Grupy",
"modules": "Moduły",
"no_quota_found": "Nie znaleziono wartości",
"patron_info": "Informacje o Patronie",
"quotas": "<<< Kwoty >>>",
"patron_not_enabled": "System patronów jest wyłączony.",
"results_in": "wynikiem {0} jest {1}",
"patron_msg_sent": "Zakończono wysyłanie wiadomości do patronów na i powyżej progu {1}. {1} wysłano pomyślnie, a {2} nie udało się.",
"cards": "Karty",
"hand_value": "Siła ręki",
"roll2": "Rzut",
"rolls": "Rzuty",
"available_tests": "Dostępne Testy",
"test_results_for": "Wyniki testu na {0}",
"multiplier": "Mnożnik",
"trivia_ended": "Quiz zakończony",
"card": "Karta",
"guess": "Zgadnięcie",
"repeater_skip_next": "Następne wyzwolenie tego powtórzenia zostanie pominięte.",
"repeater_dont_skip_next": "Następne wyzwolenie tego powtórzenia nie będzie pominięte.",
"remind_timely": "Przypomnę ci o twojej nagrodzie okresowej {0}",
"xp_shop_disabled": "Sklep Xp został wyłączony przez właściciela, lub nie ma towarów na sprzedaż.",
"timely_time": "Czas odebrać twoją nagrodę okresową.",
"buy": "Kup",
"use": "Użyj",
"in_use": "W użyciu",
"xp_shop_item_cant_use": "Nie możesz użyć tego przedmiotu ponieważ nie istnieje lub nie posiadasz go.",
"linkonly_enable": "Ten kanał jest teraz w trybie tylko linki.",
"linkonly_disable": "Ten kanał nie jest już w trybie tylko linki.",
"xp_shop_buy_required_tier": "Kupowanie towarów w tym sklepie wymaga posiadania Rangi Patrona {0} lub wyższej.",
"available_commands": "Dostępne Komendy",
"xpadd_users": "Dodano {0} XP serwerowego dla {1} użytkowników.",
"xpshop_buy_success": "Pomyślnie zakupiono `{0}/{1}`",
"patron_insuff_tier": "Twoja Ranga Patrona jest niewystarczająca do wykonania tej akcji.",
"xpshop_already_owned": "Już posiadasz ten przedmiot.",
"xpshop_item_not_found": "Przedmiot o podanym kluczu nie istnieje.",
"xpshop_website": "Możesz sprawdzić listę wszystkich towarów Sklepu Xp tutaj: <https://xpshop.nadeko.bot>"
}

View File

@@ -195,7 +195,7 @@
"trivia_times_up": "Час вийшов! Правильна відповідь {0}",
"ttt_against_yourself": "Ви не можете грати проти себе.",
"ttt_already_running": "Хрестики-нулики наразі розпочато на цьому сервері.",
"ttt_a_draw": "Нічия",
"ttt_a_draw": "Нічия!",
"ttt_created": "створив гру в Хрестики-нулики.",
"ttt_has_won": "{0} виграв!",
"ttt_matched_three": "Зібрав три",
@@ -976,76 +976,76 @@
"expr_reset": "Вираз з id {0} більше не получає реакцій.",
"expr_set": "Вираз з цим id {0} буде получати вказані реакції до відповіді: {1}",
"expr_edited": "Вираз змінено",
"stream_online_delete_enabled": "",
"stream_online_delete_disabled": "",
"club_create_error_name": "",
"club_desc_update": "",
"bank_accounts": "",
"module_description_medusa": "",
"list_of_medusae": "",
"list_of_unloaded": "",
"medusa_name_not_found": "",
"medusa_info": "",
"sneks_count": "",
"commands_count": "",
"no_medusa_loaded": "",
"no_medusa_available": "",
"loaded_medusae": "",
"medusa_not_loaded": "",
"medusa_possibly_cant_unload": "",
"medusa_loaded": "",
"medusa_unloaded": "",
"medusa_empty": "",
"medusa_already_loaded": "",
"medusa_invalid_not_found": "",
"bank_balance": "",
"bank_deposited": "",
"bank_withdrew": "",
"bank_withdraw_insuff": "",
"cmd_group_commands": "",
"limit_reached": "",
"feature_limit_reached_you": "",
"feature_limit_reached_owner": "",
"feature_limit_reached_either": "",
"tier": "",
"pledge": "",
"expires": "",
"commands": "",
"groups": "",
"modules": "",
"no_quota_found": "",
"patron_info": "",
"quotas": "",
"patron_not_enabled": "",
"results_in": "",
"patron_msg_sent": "",
"cards": "",
"hand_value": "",
"roll2": "",
"rolls": "",
"available_tests": "",
"test_results_for": "",
"multiplier": "",
"trivia_ended": "",
"card": "",
"guess": "",
"repeater_skip_next": "",
"repeater_dont_skip_next": "",
"remind_timely": "",
"xp_shop_disabled": "",
"timely_time": "",
"buy": "",
"use": "",
"in_use": "",
"xp_shop_item_cant_use": "",
"linkonly_enable": "",
"linkonly_disable": "",
"xp_shop_buy_required_tier": "",
"available_commands": "",
"xpadd_users": "",
"xpshop_buy_success": "",
"patron_insuff_tier": "",
"xpshop_already_owned": "",
"xpshop_item_not_found": "",
"xpshop_website": ""
"stream_online_delete_enabled": "Сповіщення про онлайн-стрім тепер видалятимуться, коли стрім переходить у режим офлайн.",
"stream_online_delete_disabled": "Сповіщення про онлайн-стрім більше не видалятимуться, коли стрім переходить у режим офлайн.",
"club_create_error_name": "Не вдалося створити клуб. Клуб з такою назвою вже існує.",
"club_desc_update": "Опис клубу оновлено",
"bank_accounts": "Банківські рахунки",
"module_description_medusa": "**Лише власник бота.** Завантажуйте, вивантажуйте та обробляйте динамічні модулі. Докладніше [тут](https://nadekobot.readthedocs.io/en/latest/medusa/creating-a-medusa/)",
"list_of_medusae": "Список Медуз",
"list_of_unloaded": "Список доступних Медуз",
"medusa_name_not_found": "Медуза з такою назвою не існує або не завантажується.",
"medusa_info": "Інформація про медузу",
"sneks_count": "Снеки ({0})",
"commands_count": "Команди ({0})",
"no_medusa_loaded": "Завантажених медуз немає.",
"no_medusa_available": "Медуза недоступна.",
"loaded_medusae": "Завантажені медузи",
"medusa_not_loaded": "Медуза з такою назвою не завантажується.",
"medusa_possibly_cant_unload": "Медуза, ймовірно, не повністю розвантажена. Перезапустіть бота, якщо виникнуть проблеми.",
"medusa_loaded": "Медуза {0} завантажена.",
"medusa_unloaded": "Медузу {0} вивантажено.",
"medusa_empty": "Медуза не була завантажена, оскільки не містила Снеків.",
"medusa_already_loaded": "Медуза {0} уже завантажено",
"medusa_invalid_not_found": "Медуза з такою назвою не знайдено або файл недійсний",
"bank_balance": "На вашому банківському рахунку є {0}.",
"bank_deposited": "Ви внесли {0} на свій банківський рахунок.",
"bank_withdrew": "Ви зняли {0} зі свого банківського рахунку.",
"bank_withdraw_insuff": "На вашому банківському рахунку недостатньо {0}.",
"cmd_group_commands": "Група команд '{0}'.",
"limit_reached": "Досягнуто ліміту функцій у {0}.",
"feature_limit_reached_you": "Ви досягли обмеження в {0} для функції {1}. Ви можете збільшити цей ліміт, підвищивши рівень свого патрона.",
"feature_limit_reached_owner": "Власник сервера досяг ліміту в {0} для функції {1}. Власник сервера може збільшити цей ліміт, підвищивши рівень патрона.",
"feature_limit_reached_either": "Досягнуто обмеження в {0} для функції {1}. Ви або власник сервера можете підвищити цей ліміт, підвищивши рівень патрона.",
"tier": "Рівень",
"pledge": "Застава",
"expires": "Вичерпується",
"commands": "Команди",
"groups": "Групи",
"modules": "Модулі",
"no_quota_found": "Цитата не знайдена",
"patron_info": "Інформація про патрона",
"quotas": "<<< Цитата >>>",
"patron_not_enabled": "Система патрона вимкнена.",
"results_in": "{0} призводить до {1}с",
"patron_msg_sent": "Надсилання повідомлень патронам рівня {1} і вище закінчено. {1} успішно надіслано та {2} не надіслано.",
"cards": "Карти",
"hand_value": "Вартість руки",
"roll2": "Кидок",
"rolls": "Кидки",
"available_tests": "Доступні тести",
"test_results_for": "Результати тесту для {0}",
"multiplier": "Множник",
"trivia_ended": "Гра вікторини закінчилася",
"card": "Карта",
"guess": "Вгадай",
"repeater_skip_next": "Наступний тригер цього повторювача буде пропущено.",
"repeater_dont_skip_next": "Наступний тригер цього повторювача не буде пропущено.",
"remind_timely": "Я нагадаю вам про вашу своєчасну винагороду {0}",
"xp_shop_disabled": "Магазин Xp відключений власником, або немає товарів у продажу.",
"timely_time": "Настав час вашої своєчасної винагороди.",
"buy": "Купити",
"use": "Використати",
"in_use": "У використані",
"xp_shop_item_cant_use": "Ви не можете використовувати цей предмет, оскільки він не існує або вам не належить.",
"linkonly_enable": "Цей канал тепер доступний лише для посилань.",
"linkonly_disable": "Цей канал більше не доступний лише для посилань.",
"xp_shop_buy_required_tier": "Для купівлі товарів у цьому магазині потрібен рівень патрона {0} або вищий.",
"available_commands": "Доступні команди",
"xpadd_users": "Додано {0} сервер XP до {1} користувачів.",
"xpshop_buy_success": "Успішно придбано `{0}/{1}`",
"patron_insuff_tier": "Рівень вашого патрона недостатній для виконання цієї дії.",
"xpshop_already_owned": "Ви вже володієте цим товаром.",
"xpshop_item_not_found": "Елемент із таким ключем не існує.",
"xpshop_website": "Ви можете переглянути список усіх товарів Xp магазина тут: <https://xpshop.nadeko.bot>"
}