mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 01:38:27 -04:00
Compare commits
58 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7bff20cc70 | ||
|
29f5dcc359 | ||
|
14f2851072 | ||
|
a2b25f8246 | ||
|
a38951b5ad | ||
|
4c1b911cb7 | ||
|
6c9f231453 | ||
|
83daf3c30f | ||
|
9be8140d4d | ||
|
96c9b699aa | ||
|
3c0768a372 | ||
|
58b22e3d9e | ||
|
0474551e2f | ||
|
c85bdec396 | ||
|
5fa39eaa9f | ||
|
8eaaa35c7a | ||
|
1c24f95efa | ||
|
fcc49dbbdb | ||
|
3a317590b4 | ||
|
36c013fbb5 | ||
|
a9ec8049f6 | ||
|
ced0d97e3a | ||
|
24d0f57dc3 | ||
|
514ecd6be8 | ||
|
02eb6e172b | ||
|
d22c579875 | ||
|
f70e49fc6a | ||
|
8b410561f9 | ||
|
3c79fd1d6f | ||
|
e9f1a9b1dd | ||
|
3c293ae6db | ||
|
a5d9e7de66 | ||
|
4d9e48cd41 | ||
|
b7ead22e09 | ||
|
9f219cddbb | ||
|
cf9792f24a | ||
|
0187dd57ac | ||
|
2d2e54e31e | ||
|
cb7c5e48fd | ||
|
771c2745dc | ||
|
59c0f2f4b3 | ||
|
219ca39cd1 | ||
|
1e6d0806d7 | ||
|
71f1e43272 | ||
|
8499e1da70 | ||
|
a2ea806bed | ||
|
732b5dfeed | ||
|
d4dcdc761a | ||
|
57996ba290 | ||
|
4b29b3a239 | ||
|
54ac955395 | ||
|
f4fa298866 | ||
|
b2fafc964f | ||
|
22b452e449 | ||
|
fda385a5e4 | ||
|
c28f7cfa07 | ||
|
0a029a7847 | ||
|
717543f6c2 |
40
CHANGELOG.md
40
CHANGELOG.md
@@ -4,8 +4,48 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
|
## [3.0.11] - 17.12.2021
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `.remindl` and `.remindrm` commands now supports optional 'server' parameter for Administrators which allows them to delete any reminder created on the server
|
||||||
|
- Added slots.currencyFontColor to gambling.yml
|
||||||
|
- Added `.qexport` and `.qimport` commands which allow you to export and import quotes just like `.crsexport`
|
||||||
|
- Added `.showembed <msgid>` and `.showembed #channel <msgid>` which will show you embed json from the specified message
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `.at` and `.atl` commands reworked
|
||||||
|
- Persist restarts
|
||||||
|
- Will now only translate non-commands
|
||||||
|
- You can switch between `.at del` and `.at` without clearing the user language registrations
|
||||||
|
- Disabling `.at` will clear all user language registrations on that channel
|
||||||
|
- Users can't register languages if the `.at` is not enabled
|
||||||
|
- Looks much nicer
|
||||||
|
- Bot will now reply to user messages with a translation if `del` is disabled
|
||||||
|
- Bot will make an embed with original and translated text with user avatar and name if `del` is enabled
|
||||||
|
- If the bot is unable to delete messages while having `del` enabled, it will reset back to the no-del behavior for the current session
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- `.crypto` now supports top 5000 coins
|
||||||
|
|
||||||
|
## [3.0.10] - 01.12.2021
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `.warn` now supports weighted warnings
|
||||||
|
- `.warnlog` will now show current amount and total amount of warnings
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- `.xprewsreset` now has correct permissions
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Removed slot.numbers from `images.yml` as they're no longer used
|
||||||
|
|
||||||
## [3.0.9] - 21.11.2021
|
## [3.0.9] - 21.11.2021
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `.ea` will now use an image attachments if you omit imageUrl
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Added `.emojiadd` with 3 overloads
|
- Added `.emojiadd` with 3 overloads
|
||||||
- `.ea :customEmoji:` which copies another server's emoji
|
- `.ea :customEmoji:` which copies another server's emoji
|
||||||
|
@@ -2,13 +2,32 @@
|
|||||||
|
|
||||||
Open Terminal (if you don't know how to, click on the magnifying glass on the top right corner of your screen and type **Terminal** on the window that pops up) and navigate to the location where you want to install the bot (for example `cd ~`)
|
Open Terminal (if you don't know how to, click on the magnifying glass on the top right corner of your screen and type **Terminal** on the window that pops up) and navigate to the location where you want to install the bot (for example `cd ~`)
|
||||||
|
|
||||||
##### Installing Homebrew and wget
|
##### Installing Homebrew, wget and dotnet
|
||||||
|
|
||||||
|
###### Homebrew/wget
|
||||||
*Skip this step if you already have homebrew installed*
|
*Skip this step if you already have homebrew installed*
|
||||||
- Copy and paste this command, then press Enter:
|
- Copy and paste this command, then press Enter:
|
||||||
- `/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`
|
- `/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`
|
||||||
- Install wget
|
- Install wget
|
||||||
- `brew install wget`
|
- `brew install wget`
|
||||||
|
|
||||||
|
###### Dotnet
|
||||||
|
- Download [.net5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0)
|
||||||
|
- Open the `.pkg` file you've downloaded and install it.
|
||||||
|
- Run this command in Terminal. There might be output. If there is, disregard it. (copy-paste the entire block)
|
||||||
|
```bash
|
||||||
|
sudo mkdir /usr/local/bin
|
||||||
|
|
||||||
|
sudo mkdir /usr/local/lib
|
||||||
|
```
|
||||||
|
- Run this command in Terminal. There won't be any output. (copy-paste the entire block):
|
||||||
|
```bash
|
||||||
|
sudo ln -s /usr/local/share/dotnet/dotnet /usr/local/bin
|
||||||
|
|
||||||
|
sudo ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/
|
||||||
|
|
||||||
|
sudo ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/
|
||||||
|
```
|
||||||
|
|
||||||
##### Installation Instructions
|
##### Installation Instructions
|
||||||
|
|
||||||
|
@@ -9,9 +9,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Grpc.AspNetCore" Version="2.38.0" />
|
<PackageReference Include="Grpc.AspNetCore" Version="2.41.0" />
|
||||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||||
<PackageReference Include="YamlDotNet" Version="11.2.1" />
|
<PackageReference Include="YamlDotNet" Version="11.2.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@@ -171,7 +171,7 @@ namespace NadekoBot.Coordinator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status.Process?.Dispose();
|
try { status.Process?.Dispose(); } catch { }
|
||||||
|
|
||||||
var proc = StartShardProcess(shardId);
|
var proc = StartShardProcess(shardId);
|
||||||
_shardStatuses[shardId] = status with
|
_shardStatuses[shardId] = status with
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@ using NadekoBot.Common.ModuleBehaviors;
|
|||||||
using NadekoBot.Common.Configs;
|
using NadekoBot.Common.Configs;
|
||||||
using NadekoBot.Db;
|
using NadekoBot.Db;
|
||||||
using NadekoBot.Modules.Administration.Services;
|
using NadekoBot.Modules.Administration.Services;
|
||||||
|
using NadekoBot.Modules.Searches;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace NadekoBot
|
namespace NadekoBot
|
||||||
@@ -105,7 +106,7 @@ namespace NadekoBot
|
|||||||
.AddSingleton<ISeria, JsonSeria>()
|
.AddSingleton<ISeria, JsonSeria>()
|
||||||
.AddSingleton<IPubSub, RedisPubSub>()
|
.AddSingleton<IPubSub, RedisPubSub>()
|
||||||
.AddSingleton<IConfigSeria, YamlSeria>()
|
.AddSingleton<IConfigSeria, YamlSeria>()
|
||||||
.AddBotStringsServices()
|
.AddBotStringsServices(_creds.TotalShards)
|
||||||
.AddConfigServices()
|
.AddConfigServices()
|
||||||
.AddConfigMigrators()
|
.AddConfigMigrators()
|
||||||
.AddMemoryCache()
|
.AddMemoryCache()
|
||||||
|
@@ -14,11 +14,15 @@ namespace NadekoBot.Extensions
|
|||||||
{
|
{
|
||||||
public static class ServiceCollectionExtensions
|
public static class ServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddBotStringsServices(this IServiceCollection services)
|
public static IServiceCollection AddBotStringsServices(this IServiceCollection services, int totalShards)
|
||||||
=> services
|
=> totalShards <= 1
|
||||||
.AddSingleton<IStringsSource, LocalFileStringsSource>()
|
? services
|
||||||
.AddSingleton<IBotStringsProvider, LocalBotStringsProvider>()
|
.AddSingleton<IStringsSource, LocalFileStringsSource>()
|
||||||
.AddSingleton<IBotStrings, BotStrings>();
|
.AddSingleton<IBotStringsProvider, LocalBotStringsProvider>()
|
||||||
|
.AddSingleton<IBotStrings, BotStrings>()
|
||||||
|
: services.AddSingleton<IStringsSource, LocalFileStringsSource>()
|
||||||
|
.AddSingleton<IBotStringsProvider, RedisBotStringsProvider>()
|
||||||
|
.AddSingleton<IBotStrings, BotStrings>();
|
||||||
|
|
||||||
public static IServiceCollection AddConfigServices(this IServiceCollection services)
|
public static IServiceCollection AddConfigServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
@@ -6,7 +6,7 @@ namespace NadekoBot.Common
|
|||||||
public class ImageUrls
|
public class ImageUrls
|
||||||
{
|
{
|
||||||
[Comment("DO NOT CHANGE")]
|
[Comment("DO NOT CHANGE")]
|
||||||
public int Version { get; set; } = 2;
|
public int Version { get; set; } = 3;
|
||||||
|
|
||||||
public CoinData Coins { get; set; }
|
public CoinData Coins { get; set; }
|
||||||
public Uri[] Currency { get; set; }
|
public Uri[] Currency { get; set; }
|
||||||
@@ -27,7 +27,6 @@ namespace NadekoBot.Common
|
|||||||
public class SlotData
|
public class SlotData
|
||||||
{
|
{
|
||||||
public Uri[] Emojis { get; set; }
|
public Uri[] Emojis { get; set; }
|
||||||
public Uri[] Numbers { get; set; }
|
|
||||||
public Uri Bg { get; set; }
|
public Uri Bg { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using Discord;
|
using Discord;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
@@ -29,6 +30,47 @@ namespace NadekoBot
|
|||||||
(Footer != null && (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) ||
|
(Footer != null && (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) ||
|
||||||
(Fields != null && Fields.Length > 0);
|
(Fields != null && Fields.Length > 0);
|
||||||
|
|
||||||
|
public static SmartEmbedText FromEmbed(IEmbed eb, string plainText = null)
|
||||||
|
{
|
||||||
|
var set = new SmartEmbedText();
|
||||||
|
|
||||||
|
set.PlainText = plainText;
|
||||||
|
set.Title = eb.Title;
|
||||||
|
set.Description = eb.Description;
|
||||||
|
set.Url = eb.Url;
|
||||||
|
set.Thumbnail = eb.Thumbnail?.Url;
|
||||||
|
set.Image = eb.Image?.Url;
|
||||||
|
set.Author = eb.Author is EmbedAuthor ea
|
||||||
|
? new()
|
||||||
|
{
|
||||||
|
Name = ea.Name,
|
||||||
|
Url = ea.Url,
|
||||||
|
IconUrl = ea.IconUrl
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
set.Footer = eb.Footer is EmbedFooter ef
|
||||||
|
? new()
|
||||||
|
{
|
||||||
|
Text = ef.Text,
|
||||||
|
IconUrl = ef.IconUrl
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (eb.Fields.Length > 0)
|
||||||
|
set.Fields = eb
|
||||||
|
.Fields
|
||||||
|
.Select(field => new SmartTextEmbedField()
|
||||||
|
{
|
||||||
|
Inline = field.Inline,
|
||||||
|
Name = field.Name,
|
||||||
|
Value = field.Value,
|
||||||
|
})
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
set.Color = eb.Color?.RawValue ?? 0;
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
public EmbedBuilder GetEmbed()
|
public EmbedBuilder GetEmbed()
|
||||||
{
|
{
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using NadekoBot.Db.Models;
|
using System;
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Discord;
|
using Discord;
|
||||||
@@ -168,7 +169,7 @@ VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
|
|||||||
public static decimal GetTotalCurrency(this DbSet<DiscordUser> users)
|
public static decimal GetTotalCurrency(this DbSet<DiscordUser> users)
|
||||||
{
|
{
|
||||||
return users
|
return users
|
||||||
.Sum(x => x.CurrencyAmount);
|
.Sum((Func<DiscordUser, decimal>)(x => x.CurrencyAmount));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static decimal GetTopOnePercentCurrency(this DbSet<DiscordUser> users, ulong botId)
|
public static decimal GetTopOnePercentCurrency(this DbSet<DiscordUser> users, ulong botId)
|
||||||
|
@@ -9,6 +9,11 @@ namespace NadekoBot.Db
|
|||||||
{
|
{
|
||||||
public static class QuoteExtensions
|
public static class QuoteExtensions
|
||||||
{
|
{
|
||||||
|
public static IEnumerable<Quote> GetForGuild(this DbSet<Quote> quotes, ulong guildId)
|
||||||
|
{
|
||||||
|
return quotes.AsQueryable().Where(x => x.GuildId == guildId);
|
||||||
|
}
|
||||||
|
|
||||||
public static IEnumerable<Quote> GetGroup(this DbSet<Quote> quotes, ulong guildId, int page, OrderType order)
|
public static IEnumerable<Quote> GetGroup(this DbSet<Quote> quotes, ulong guildId, int page, OrderType order)
|
||||||
{
|
{
|
||||||
var q = quotes.AsQueryable().Where(x => x.GuildId == guildId);
|
var q = quotes.AsQueryable().Where(x => x.GuildId == guildId);
|
||||||
|
@@ -18,5 +18,12 @@ namespace NadekoBot.Db
|
|||||||
.OrderBy(x => x.DateAdded)
|
.OrderBy(x => x.DateAdded)
|
||||||
.Skip(page * 10)
|
.Skip(page * 10)
|
||||||
.Take(10);
|
.Take(10);
|
||||||
|
|
||||||
|
public static IEnumerable<Reminder> RemindersForServer(this DbSet<Reminder> reminders, ulong serverId, int page)
|
||||||
|
=> reminders.AsQueryable()
|
||||||
|
.Where(x => x.ServerId == serverId)
|
||||||
|
.OrderBy(x => x.DateAdded)
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/NadekoBot/Db/Models/AutoTranslateChannel.cs
Normal file
12
src/NadekoBot/Db/Models/AutoTranslateChannel.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NadekoBot.Services.Database.Models
|
||||||
|
{
|
||||||
|
public class AutoTranslateChannel : DbEntity
|
||||||
|
{
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public ulong ChannelId { get; set; }
|
||||||
|
public bool AutoDelete { get; set; }
|
||||||
|
public IList<AutoTranslateUser> Users { get; set; } = new List<AutoTranslateUser>();
|
||||||
|
}
|
||||||
|
}
|
11
src/NadekoBot/Db/Models/AutoTranslateUser.cs
Normal file
11
src/NadekoBot/Db/Models/AutoTranslateUser.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace NadekoBot.Services.Database.Models
|
||||||
|
{
|
||||||
|
public class AutoTranslateUser : DbEntity
|
||||||
|
{
|
||||||
|
public int ChannelId { get; set; }
|
||||||
|
public AutoTranslateChannel Channel { get; set; }
|
||||||
|
public ulong UserId { get; set; }
|
||||||
|
public string Source { get; set; }
|
||||||
|
public string Target { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -8,5 +8,6 @@
|
|||||||
public bool Forgiven { get; set; }
|
public bool Forgiven { get; set; }
|
||||||
public string ForgivenBy { get; set; }
|
public string ForgivenBy { get; set; }
|
||||||
public string Moderator { get; set; }
|
public string Moderator { get; set; }
|
||||||
|
public int Weight { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -60,6 +60,8 @@ namespace NadekoBot.Services.Database
|
|||||||
public DbSet<WaifuInfo> WaifuInfo { get; set; }
|
public DbSet<WaifuInfo> WaifuInfo { get; set; }
|
||||||
public DbSet<ImageOnlyChannel> ImageOnlyChannels { get; set; }
|
public DbSet<ImageOnlyChannel> ImageOnlyChannels { get; set; }
|
||||||
public DbSet<NsfwBlacklistedTag> NsfwBlacklistedTags { get; set; }
|
public DbSet<NsfwBlacklistedTag> NsfwBlacklistedTags { get; set; }
|
||||||
|
public DbSet<AutoTranslateChannel> AutoTranslateChannels { get; set; }
|
||||||
|
public DbSet<AutoTranslateUser> AutoTranslateUsers { get; set; }
|
||||||
|
|
||||||
public NadekoContext(DbContextOptions<NadekoContext> options) : base(options)
|
public NadekoContext(DbContextOptions<NadekoContext> options) : base(options)
|
||||||
{
|
{
|
||||||
@@ -196,10 +198,16 @@ namespace NadekoBot.Services.Database
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Warnings
|
#region Warnings
|
||||||
var warn = modelBuilder.Entity<Warning>();
|
|
||||||
warn.HasIndex(x => x.GuildId);
|
modelBuilder.Entity<Warning>(warn =>
|
||||||
warn.HasIndex(x => x.UserId);
|
{
|
||||||
warn.HasIndex(x => x.DateAdded);
|
warn.HasIndex(x => x.GuildId);
|
||||||
|
warn.HasIndex(x => x.UserId);
|
||||||
|
warn.HasIndex(x => x.DateAdded);
|
||||||
|
warn.Property(x => x.Weight)
|
||||||
|
.HasDefaultValue(1);
|
||||||
|
});
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region PatreonRewards
|
#region PatreonRewards
|
||||||
@@ -362,6 +370,21 @@ namespace NadekoBot.Services.Database
|
|||||||
modelBuilder.Entity<NsfwBlacklistedTag>(nbt => nbt
|
modelBuilder.Entity<NsfwBlacklistedTag>(nbt => nbt
|
||||||
.HasIndex(x => x.GuildId)
|
.HasIndex(x => x.GuildId)
|
||||||
.IsUnique(false));
|
.IsUnique(false));
|
||||||
|
|
||||||
|
var atch = modelBuilder.Entity<AutoTranslateChannel>();
|
||||||
|
atch.HasIndex(x => x.GuildId)
|
||||||
|
.IsUnique(false);
|
||||||
|
|
||||||
|
atch.HasIndex(x => x.ChannelId)
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
atch
|
||||||
|
.HasMany(x => x.Users)
|
||||||
|
.WithOne(x => x.Channel)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
modelBuilder.Entity<AutoTranslateUser>(atu => atu
|
||||||
|
.HasAlternateKey(x => new { x.ChannelId, x.UserId }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2653
src/NadekoBot/Migrations/20211121002508_weighted-warnings.Designer.cs
generated
Normal file
2653
src/NadekoBot/Migrations/20211121002508_weighted-warnings.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
src/NadekoBot/Migrations/20211121002508_weighted-warnings.cs
Normal file
24
src/NadekoBot/Migrations/20211121002508_weighted-warnings.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
public partial class weightedwarnings : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "Weight",
|
||||||
|
table: "Warnings",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Weight",
|
||||||
|
table: "Warnings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2725
src/NadekoBot/Migrations/20211213145407_atl-rework.Designer.cs
generated
Normal file
2725
src/NadekoBot/Migrations/20211213145407_atl-rework.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
71
src/NadekoBot/Migrations/20211213145407_atl-rework.cs
Normal file
71
src/NadekoBot/Migrations/20211213145407_atl-rework.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
public partial class atlrework : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AutoTranslateChannels",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
AutoDelete = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AutoTranslateChannels", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AutoTranslateUsers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
ChannelId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
UserId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
Source = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Target = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AutoTranslateUsers", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_AutoTranslateUsers_ChannelId_UserId", x => new { x.ChannelId, x.UserId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AutoTranslateUsers_AutoTranslateChannels_ChannelId",
|
||||||
|
column: x => x.ChannelId,
|
||||||
|
principalTable: "AutoTranslateChannels",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AutoTranslateChannels_ChannelId",
|
||||||
|
table: "AutoTranslateChannels",
|
||||||
|
column: "ChannelId",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AutoTranslateChannels_GuildId",
|
||||||
|
table: "AutoTranslateChannels",
|
||||||
|
column: "GuildId");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AutoTranslateUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AutoTranslateChannels");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -340,6 +340,62 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("AutoCommands");
|
b.ToTable("AutoCommands");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("AutoDelete")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateAdded")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ChannelId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.HasIndex("GuildId");
|
||||||
|
|
||||||
|
b.ToTable("AutoTranslateChannels");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ChannelId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateAdded")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Source")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Target")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasAlternateKey("ChannelId", "UserId");
|
||||||
|
|
||||||
|
b.ToTable("AutoTranslateUsers");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -1967,6 +2023,11 @@ namespace NadekoBot.Migrations
|
|||||||
b.Property<ulong>("UserId")
|
b.Property<ulong>("UserId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Weight")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(1);
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("DateAdded");
|
b.HasIndex("DateAdded");
|
||||||
@@ -2189,6 +2250,17 @@ namespace NadekoBot.Migrations
|
|||||||
b.Navigation("GuildConfig");
|
b.Navigation("GuildConfig");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.AutoTranslateChannel", "Channel")
|
||||||
|
.WithMany("Users")
|
||||||
|
.HasForeignKey("ChannelId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Channel");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", null)
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", null)
|
||||||
@@ -2536,6 +2608,11 @@ namespace NadekoBot.Migrations
|
|||||||
b.Navigation("IgnoredChannels");
|
b.Navigation("IgnoredChannels");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Users");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("AntiAltSetting");
|
b.Navigation("AntiAltSetting");
|
||||||
|
@@ -318,7 +318,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
public async Task Die()
|
public async Task Die(bool graceful = false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -329,7 +329,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
await Task.Delay(2000).ConfigureAwait(false);
|
await Task.Delay(2000).ConfigureAwait(false);
|
||||||
_coord.Die();
|
_coord.Die(graceful);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
@@ -506,6 +506,14 @@ namespace NadekoBot.Modules.Administration
|
|||||||
_strings.Reload();
|
_strings.Reload();
|
||||||
await ReplyConfirmLocalizedAsync(strs.bot_strings_reloaded).ConfigureAwait(false);
|
await ReplyConfirmLocalizedAsync(strs.bot_strings_reloaded).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async Task CoordReload()
|
||||||
|
{
|
||||||
|
await _coord.Reload();
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus)
|
private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus)
|
||||||
{
|
{
|
||||||
|
@@ -41,8 +41,11 @@ namespace NadekoBot.Modules.Administration.Services
|
|||||||
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromHours(12));
|
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromHours(12));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<WarningPunishment> Warn(IGuild guild, ulong userId, IUser mod, string reason)
|
public async Task<WarningPunishment> Warn(IGuild guild, ulong userId, IUser mod, int weight, string reason)
|
||||||
{
|
{
|
||||||
|
if (weight <= 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(weight));
|
||||||
|
|
||||||
var modName = mod.ToString();
|
var modName = mod.ToString();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(reason))
|
if (string.IsNullOrWhiteSpace(reason))
|
||||||
@@ -57,6 +60,7 @@ namespace NadekoBot.Modules.Administration.Services
|
|||||||
Forgiven = false,
|
Forgiven = false,
|
||||||
Reason = reason,
|
Reason = reason,
|
||||||
Moderator = modName,
|
Moderator = modName,
|
||||||
|
Weight = weight,
|
||||||
};
|
};
|
||||||
|
|
||||||
int warnings = 1;
|
int warnings = 1;
|
||||||
@@ -70,7 +74,7 @@ namespace NadekoBot.Modules.Administration.Services
|
|||||||
.Warnings
|
.Warnings
|
||||||
.ForId(guildId, userId)
|
.ForId(guildId, userId)
|
||||||
.Where(w => !w.Forgiven && w.UserId == userId)
|
.Where(w => !w.Forgiven && w.UserId == userId)
|
||||||
.Count();
|
.Sum(x => x.Weight);
|
||||||
|
|
||||||
uow.Warnings.Add(warn);
|
uow.Warnings.Add(warn);
|
||||||
|
|
||||||
@@ -95,6 +99,10 @@ namespace NadekoBot.Modules.Administration.Services
|
|||||||
public async Task ApplyPunishment(IGuild guild, IGuildUser user, IUser mod, PunishmentAction p, int minutes,
|
public async Task ApplyPunishment(IGuild guild, IGuildUser user, IUser mod, PunishmentAction p, int minutes,
|
||||||
ulong? roleId, string reason)
|
ulong? roleId, string reason)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (!await CheckPermission(guild, p))
|
||||||
|
return;
|
||||||
|
|
||||||
switch (p)
|
switch (p)
|
||||||
{
|
{
|
||||||
case PunishmentAction.Mute:
|
case PunishmentAction.Mute:
|
||||||
@@ -167,6 +175,40 @@ namespace NadekoBot.Modules.Administration.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to prevent the bot from hitting 403's when it needs to
|
||||||
|
/// apply punishments with insufficient permissions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="guild">Guild the punishment is applied in</param>
|
||||||
|
/// <param name="punish">Punishment to apply</param>
|
||||||
|
/// <returns>Whether the bot has sufficient permissions</returns>
|
||||||
|
private async Task<bool> CheckPermission(IGuild guild, PunishmentAction punish)
|
||||||
|
{
|
||||||
|
|
||||||
|
var botUser = await guild.GetCurrentUserAsync();
|
||||||
|
switch (punish)
|
||||||
|
{
|
||||||
|
case PunishmentAction.Mute:
|
||||||
|
return botUser.GuildPermissions.MuteMembers && botUser.GuildPermissions.ManageRoles;
|
||||||
|
case PunishmentAction.Kick:
|
||||||
|
return botUser.GuildPermissions.KickMembers;
|
||||||
|
case PunishmentAction.Ban:
|
||||||
|
return botUser.GuildPermissions.BanMembers;
|
||||||
|
case PunishmentAction.Softban:
|
||||||
|
return botUser.GuildPermissions.BanMembers; // ban + unban
|
||||||
|
case PunishmentAction.RemoveRoles:
|
||||||
|
return botUser.GuildPermissions.ManageRoles;
|
||||||
|
case PunishmentAction.ChatMute:
|
||||||
|
return botUser.GuildPermissions.ManageRoles; // adds nadeko-mute role
|
||||||
|
case PunishmentAction.VoiceMute:
|
||||||
|
return botUser.GuildPermissions.MuteMembers;
|
||||||
|
case PunishmentAction.AddRole:
|
||||||
|
return botUser.GuildPermissions.ManageRoles;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task CheckAllWarnExpiresAsync()
|
public async Task CheckAllWarnExpiresAsync()
|
||||||
{
|
{
|
||||||
using (var uow = _db.GetDbContext())
|
using (var uow = _db.GetDbContext())
|
||||||
|
@@ -54,8 +54,17 @@ namespace NadekoBot.Modules.Administration
|
|||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.BanMembers)]
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
public async Task Warn(IGuildUser user, [Leftover] string reason = null)
|
public Task Warn(IGuildUser user, [Leftover] string reason = null)
|
||||||
|
=> Warn(1, user, reason);
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
|
public async Task Warn(int weight, IGuildUser user, [Leftover] string reason = null)
|
||||||
{
|
{
|
||||||
|
if (weight <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!await CheckRoleHierarchy(user))
|
if (!await CheckRoleHierarchy(user))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -76,7 +85,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
WarningPunishment punishment;
|
WarningPunishment punishment;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
punishment = await _service.Warn(ctx.Guild, user.Id, ctx.User, reason).ConfigureAwait(false);
|
punishment = await _service.Warn(ctx.Guild, user.Id, ctx.User, weight, reason).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -230,19 +239,29 @@ namespace NadekoBot.Modules.Administration
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var descText = GetText(strs.warn_count(
|
||||||
|
Format.Bold(warnings.Where(x => !x.Forgiven).Sum(x => x.Weight).ToString()),
|
||||||
|
Format.Bold(warnings.Sum(x => x.Weight).ToString())));
|
||||||
|
|
||||||
|
embed.WithDescription(descText);
|
||||||
|
|
||||||
var i = page * 9;
|
var i = page * 9;
|
||||||
foreach (var w in warnings)
|
foreach (var w in warnings)
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
var name = GetText(strs.warned_on_by(
|
var name = GetText(strs.warned_on_by(
|
||||||
w.DateAdded.Value.ToString("dd.MM.yyy"),
|
w.DateAdded?.ToString("dd.MM.yyy"),
|
||||||
w.DateAdded.Value.ToString("HH:mm"),
|
w.DateAdded?.ToString("HH:mm"),
|
||||||
w.Moderator));
|
w.Moderator));
|
||||||
|
|
||||||
if (w.Forgiven)
|
if (w.Forgiven)
|
||||||
name = $"{Format.Strikethrough(name)} {GetText(strs.warn_cleared_by(w.ForgivenBy))}";
|
name = $"{Format.Strikethrough(name)} {GetText(strs.warn_cleared_by(w.ForgivenBy))}";
|
||||||
|
|
||||||
embed.AddField($"#`{i}` " + name, w.Reason.TrimTo(1020));
|
|
||||||
|
embed.AddField($"#`{i}` " + name,
|
||||||
|
Format.Code(GetText(strs.warn_weight(w.Weight))) +
|
||||||
|
'\n' +
|
||||||
|
w.Reason.TrimTo(1000));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -310,7 +310,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
_ = ctx.Channel.TriggerTypingAsync();
|
_ = ctx.Channel.TriggerTypingAsync();
|
||||||
|
|
||||||
var serialized = _service.ExportCrs(ctx.Guild?.Id);
|
var serialized = _service.ExportCrs(ctx.Guild?.Id);
|
||||||
using var stream = await serialized.ToStream();
|
await using var stream = await serialized.ToStream();
|
||||||
await ctx.Channel.SendFileAsync(stream, "crs-export.yml", text: null);
|
await ctx.Channel.SendFileAsync(stream, "crs-export.yml", text: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using Cloneable;
|
using Cloneable;
|
||||||
using NadekoBot.Common;
|
using NadekoBot.Common;
|
||||||
using NadekoBot.Common.Yml;
|
using NadekoBot.Common.Yml;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
using YamlDotNet.Serialization;
|
using YamlDotNet.Serialization;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Gambling.Common
|
namespace NadekoBot.Modules.Gambling.Common
|
||||||
@@ -20,6 +21,7 @@ namespace NadekoBot.Modules.Gambling.Common
|
|||||||
Generation = new GenerationConfig();
|
Generation = new GenerationConfig();
|
||||||
Timely = new TimelyConfig();
|
Timely = new TimelyConfig();
|
||||||
Decay = new DecayConfig();
|
Decay = new DecayConfig();
|
||||||
|
Slots = new SlotsConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Comment(@"DO NOT CHANGE")]
|
[Comment(@"DO NOT CHANGE")]
|
||||||
@@ -64,6 +66,9 @@ Set 0 for unlimited")]
|
|||||||
[Comment(@"Currency reward per vote.
|
[Comment(@"Currency reward per vote.
|
||||||
This will work only if you've set up VotesApi and correct credentials for topgg and/or discords voting")]
|
This will work only if you've set up VotesApi and correct credentials for topgg and/or discords voting")]
|
||||||
public long VoteReward { get; set; } = 100;
|
public long VoteReward { get; set; } = 100;
|
||||||
|
|
||||||
|
[Comment(@"Slot config")]
|
||||||
|
public SlotsConfig Slots { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CurrencyConfig
|
public class CurrencyConfig
|
||||||
@@ -273,6 +278,12 @@ Example: If a waifu is worth 1000, and she receives a negative gift worth 100, h
|
|||||||
public decimal NegativeGiftEffect { get; set; } = 0.50M;
|
public decimal NegativeGiftEffect { get; set; } = 0.50M;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed partial class SlotsConfig
|
||||||
|
{
|
||||||
|
[Comment(@"Hex value of the color which the numbers on the slot image will have.")]
|
||||||
|
public Rgba32 CurrencyFontColor { get; set; } = SixLabors.ImageSharp.Color.Red;
|
||||||
|
}
|
||||||
|
|
||||||
[Cloneable]
|
[Cloneable]
|
||||||
public sealed partial class WaifuItemModel
|
public sealed partial class WaifuItemModel
|
||||||
{
|
{
|
||||||
|
@@ -66,11 +66,11 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
}
|
}
|
||||||
var embed = _eb.Create()
|
var embed = _eb.Create()
|
||||||
.WithTitle(GetText(strs.economy_state))
|
.WithTitle(GetText(strs.economy_state))
|
||||||
.AddField(GetText(strs.currency_owned), ((BigInteger)(ec.Cash - ec.Bot)) + CurrencySign)
|
.AddField(GetText(strs.currency_owned), ((BigInteger)(ec.Cash - ec.Bot)).ToString("N", _enUsCulture) + CurrencySign)
|
||||||
.AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
|
.AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
|
||||||
.AddField(GetText(strs.currency_planted), ((BigInteger)ec.Planted) + CurrencySign)
|
.AddField(GetText(strs.currency_planted), ((BigInteger)ec.Planted) + CurrencySign)
|
||||||
.AddField(GetText(strs.owned_waifus_total), ((BigInteger)ec.Waifus) + CurrencySign)
|
.AddField(GetText(strs.owned_waifus_total), ((BigInteger)ec.Waifus) + CurrencySign)
|
||||||
.AddField(GetText(strs.bot_currency), ec.Bot + CurrencySign)
|
.AddField(GetText(strs.bot_currency), ec.Bot.ToString("N", _enUsCulture) + CurrencySign)
|
||||||
.AddField(GetText(strs.total), ((BigInteger)(ec.Cash + ec.Planted + ec.Waifus)).ToString("N", _enUsCulture) + CurrencySign)
|
.AddField(GetText(strs.total), ((BigInteger)(ec.Cash + ec.Planted + ec.Waifus)).ToString("N", _enUsCulture) + CurrencySign)
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
// ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table
|
// ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table
|
||||||
@@ -247,25 +247,33 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public Task Award(ShmartNumber amount, IGuildUser usr, [Leftover] string msg) =>
|
public Task Award(long amount, IGuildUser usr, [Leftover] string msg) =>
|
||||||
Award(amount, usr.Id, msg);
|
Award(amount, usr.Id, msg);
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public Task Award(ShmartNumber amount, [Leftover] IGuildUser usr) =>
|
public Task Award(long amount, [Leftover] IGuildUser usr) =>
|
||||||
Award(amount, usr.Id);
|
Award(amount, usr.Id);
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
[Priority(2)]
|
[Priority(2)]
|
||||||
public async Task Award(ShmartNumber amount, ulong usrId, [Leftover] string msg = null)
|
public async Task Award(long amount, ulong usrId, [Leftover] string msg = null)
|
||||||
{
|
{
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await _cs.AddAsync(usrId,
|
var usr = await ((DiscordSocketClient)Context.Client).Rest.GetUserAsync(usrId);
|
||||||
|
|
||||||
|
if(usr is null)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.user_not_found).ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _cs.AddAsync(usr,
|
||||||
$"Awarded by bot owner. ({ctx.User.Username}/{ctx.User.Id}) {(msg ?? "")}",
|
$"Awarded by bot owner. ({ctx.User.Username}/{ctx.User.Id}) {(msg ?? "")}",
|
||||||
amount,
|
amount,
|
||||||
gamble: (ctx.Client.CurrentUser.Id != usrId)).ConfigureAwait(false);
|
gamble: (ctx.Client.CurrentUser.Id != usrId)).ConfigureAwait(false);
|
||||||
@@ -275,8 +283,8 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
[Priority(2)]
|
[Priority(3)]
|
||||||
public async Task Award(ShmartNumber amount, [Leftover] IRole role)
|
public async Task Award(long amount, [Leftover] IRole role)
|
||||||
{
|
{
|
||||||
var users = (await ctx.Guild.GetUsersAsync().ConfigureAwait(false))
|
var users = (await ctx.Guild.GetUsersAsync().ConfigureAwait(false))
|
||||||
.Where(u => u.GetRoles().Contains(role))
|
.Where(u => u.GetRoles().Contains(role))
|
||||||
@@ -284,7 +292,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
|
|
||||||
await _cs.AddBulkAsync(users.Select(x => x.Id),
|
await _cs.AddBulkAsync(users.Select(x => x.Id),
|
||||||
users.Select(x => $"Awarded by bot owner to **{role.Name}** role. ({ctx.User.Username}/{ctx.User.Id})"),
|
users.Select(x => $"Awarded by bot owner to **{role.Name}** role. ({ctx.User.Username}/{ctx.User.Id})"),
|
||||||
users.Select(x => amount.Value),
|
users.Select(x => amount),
|
||||||
gamble: true)
|
gamble: true)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -298,13 +306,13 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task Take(ShmartNumber amount, [Leftover] IRole role)
|
public async Task Take(long amount, [Leftover] IRole role)
|
||||||
{
|
{
|
||||||
var users = (await role.GetMembersAsync()).ToList();
|
var users = (await role.GetMembersAsync()).ToList();
|
||||||
|
|
||||||
await _cs.RemoveBulkAsync(users.Select(x => x.Id),
|
await _cs.RemoveBulkAsync(users.Select(x => x.Id),
|
||||||
users.Select(x => $"Taken by bot owner from **{role.Name}** role. ({ctx.User.Username}/{ctx.User.Id})"),
|
users.Select(x => $"Taken by bot owner from **{role.Name}** role. ({ctx.User.Username}/{ctx.User.Id})"),
|
||||||
users.Select(x => amount.Value),
|
users.Select(x => amount),
|
||||||
gamble: true)
|
gamble: true)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -318,7 +326,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public async Task Take(ShmartNumber amount, [Leftover] IGuildUser user)
|
public async Task Take(long amount, [Leftover] IGuildUser user)
|
||||||
{
|
{
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
return;
|
return;
|
||||||
@@ -333,7 +341,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
public async Task Take(ShmartNumber amount, [Leftover] ulong usrId)
|
public async Task Take(long amount, [Leftover] ulong usrId)
|
||||||
{
|
{
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
return;
|
return;
|
||||||
|
@@ -8,10 +8,11 @@ using NadekoBot.Modules.Gambling.Services;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NadekoBot.Modules.Gambling.Common;
|
using NadekoBot.Modules.Gambling.Common;
|
||||||
|
using NadekoBot.Common;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Games
|
namespace NadekoBot.Modules.Gambling
|
||||||
{
|
{
|
||||||
public partial class Games
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public class PlantPickCommands : GamblingSubmodule<PlantPickService>
|
public class PlantPickCommands : GamblingSubmodule<PlantPickService>
|
||||||
@@ -53,7 +54,7 @@ namespace NadekoBot.Modules.Games
|
|||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Plant(int amount = 1, string pass = null)
|
public async Task Plant(ShmartNumber amount, string pass = null)
|
||||||
{
|
{
|
||||||
if (amount < 1)
|
if (amount < 1)
|
||||||
return;
|
return;
|
||||||
@@ -63,18 +64,17 @@ namespace NadekoBot.Modules.Games
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var success = await _service.PlantAsync(ctx.Guild.Id, ctx.Channel, ctx.User.Id, ctx.User.ToString(), amount, pass);
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
await ReplyErrorLocalizedAsync(strs.not_enough( CurrencySign));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (((SocketGuild)ctx.Guild).CurrentUser.GuildPermissions.ManageMessages)
|
if (((SocketGuild)ctx.Guild).CurrentUser.GuildPermissions.ManageMessages)
|
||||||
{
|
{
|
||||||
logService.AddDeleteIgnore(ctx.Message.Id);
|
logService.AddDeleteIgnore(ctx.Message.Id);
|
||||||
await ctx.Message.DeleteAsync().ConfigureAwait(false);
|
await ctx.Message.DeleteAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var success = await _service.PlantAsync(ctx.Guild.Id, ctx.Channel, ctx.User.Id, ctx.User.ToString(), amount, pass);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.not_enough( CurrencySign));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
@@ -68,9 +68,18 @@ namespace NadekoBot.Modules.Gambling.Services
|
|||||||
{
|
{
|
||||||
ModifyConfig(c =>
|
ModifyConfig(c =>
|
||||||
{
|
{
|
||||||
|
c.Version = 3;
|
||||||
c.VoteReward = 100;
|
c.VoteReward = 100;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_data.Version < 4)
|
||||||
|
{
|
||||||
|
ModifyConfig(c =>
|
||||||
|
{
|
||||||
|
c.Version = 4;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -19,6 +19,7 @@ using SixLabors.ImageSharp.Processing;
|
|||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Drawing.Processing;
|
using SixLabors.ImageSharp.Drawing.Processing;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using Color = SixLabors.ImageSharp.Color;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Gambling
|
namespace NadekoBot.Modules.Gambling
|
||||||
{
|
{
|
||||||
@@ -186,6 +187,8 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
var numbers = new int[3];
|
var numbers = new int[3];
|
||||||
result.Rolls.CopyTo(numbers, 0);
|
result.Rolls.CopyTo(numbers, 0);
|
||||||
|
|
||||||
|
Color fontColor = _config.Slots.CurrencyFontColor;
|
||||||
|
|
||||||
bgImage.Mutate(x => x.DrawText(new TextGraphicsOptions
|
bgImage.Mutate(x => x.DrawText(new TextGraphicsOptions
|
||||||
{
|
{
|
||||||
TextOptions = new TextOptions()
|
TextOptions = new TextOptions()
|
||||||
@@ -194,9 +197,11 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
WrapTextWidth = 140,
|
WrapTextWidth = 140,
|
||||||
}
|
}
|
||||||
}, result.Won.ToString(), _fonts.DottyFont.CreateFont(65), SixLabors.ImageSharp.Color.Red,
|
}, result.Won.ToString(), _fonts.DottyFont.CreateFont(65), fontColor,
|
||||||
new PointF(227, 92)));
|
new PointF(227, 92)));
|
||||||
|
|
||||||
|
var bottomFont = _fonts.DottyFont.CreateFont(50);
|
||||||
|
|
||||||
bgImage.Mutate(x => x.DrawText(new TextGraphicsOptions
|
bgImage.Mutate(x => x.DrawText(new TextGraphicsOptions
|
||||||
{
|
{
|
||||||
TextOptions = new TextOptions()
|
TextOptions = new TextOptions()
|
||||||
@@ -205,7 +210,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
WrapTextWidth = 135,
|
WrapTextWidth = 135,
|
||||||
}
|
}
|
||||||
}, amount.ToString(), _fonts.DottyFont.CreateFont(50), SixLabors.ImageSharp.Color.Red,
|
}, amount.ToString(), bottomFont, fontColor,
|
||||||
new PointF(129, 472)));
|
new PointF(129, 472)));
|
||||||
|
|
||||||
bgImage.Mutate(x => x.DrawText(new TextGraphicsOptions
|
bgImage.Mutate(x => x.DrawText(new TextGraphicsOptions
|
||||||
@@ -216,7 +221,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
WrapTextWidth = 135,
|
WrapTextWidth = 135,
|
||||||
}
|
}
|
||||||
}, ownedAmount.ToString(), _fonts.DottyFont.CreateFont(50), SixLabors.ImageSharp.Color.Red,
|
}, ownedAmount.ToString(), bottomFont, fontColor,
|
||||||
new PointF(325, 472)));
|
new PointF(325, 472)));
|
||||||
//sw.PrintLap("drew red text");
|
//sw.PrintLap("drew red text");
|
||||||
|
|
||||||
|
@@ -763,7 +763,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[RequireUserPermission(GuildPermission.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
public async Task MusicQuality()
|
public async Task MusicQuality()
|
||||||
{
|
{
|
||||||
var quality = await _service.GetMusicQualityAsync(ctx.Guild.Id);
|
var quality = await _service.GetMusicQualityAsync(ctx.Guild.Id);
|
||||||
@@ -772,7 +772,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[RequireUserPermission(GuildPermission.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
public async Task MusicQuality(QualityPreset preset)
|
public async Task MusicQuality(QualityPreset preset)
|
||||||
{
|
{
|
||||||
await _service.SetMusicQualityAsync(ctx.Guild.Id, preset);
|
await _service.SetMusicQualityAsync(ctx.Guild.Id, preset);
|
||||||
|
@@ -24,7 +24,7 @@ namespace NadekoBot.Modules.Nsfw.Common
|
|||||||
return new();
|
return new();
|
||||||
|
|
||||||
return images
|
return images
|
||||||
.Where(img => !string.IsNullOrWhiteSpace(img.Directory) && !string.IsNullOrWhiteSpace(img.Image))
|
.Where(img => !string.IsNullOrWhiteSpace(img.Image))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
public class Rule34Object : IImageData
|
public class Rule34Object : IImageData
|
||||||
{
|
{
|
||||||
public string Image { get; init; }
|
public string Image { get; init; }
|
||||||
public string Directory { get; init; }
|
public int Directory { get; init; }
|
||||||
public string Tags { get; init; }
|
public string Tags { get; init; }
|
||||||
public int Score { get; init; }
|
public int Score { get; init; }
|
||||||
|
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace SystemTextJsonSamples
|
||||||
|
{
|
||||||
|
public class LowerCaseNamingPolicy : JsonNamingPolicy
|
||||||
|
{
|
||||||
|
public static LowerCaseNamingPolicy Default = new LowerCaseNamingPolicy();
|
||||||
|
|
||||||
|
public override string ConvertName(string name) =>
|
||||||
|
name.ToLower();
|
||||||
|
}
|
||||||
|
}
|
@@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
|
|
||||||
var (crypto, nearest) = await _service.GetCryptoData(name).ConfigureAwait(false);
|
var (crypto, nearest) = await _service.GetCryptoData(name).ConfigureAwait(false);
|
||||||
|
|
||||||
if (nearest != null)
|
if (nearest is not null)
|
||||||
{
|
{
|
||||||
var embed = _eb.Create()
|
var embed = _eb.Create()
|
||||||
.WithTitle(GetText(strs.crypto_not_found))
|
.WithTitle(GetText(strs.crypto_not_found))
|
||||||
|
16
src/NadekoBot/Modules/Searches/Services/AtlExtensions.cs
Normal file
16
src/NadekoBot/Modules/Searches/Services/AtlExtensions.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Searches
|
||||||
|
{
|
||||||
|
public static class AtlExtensions
|
||||||
|
{
|
||||||
|
public static Task<AutoTranslateChannel> GetByChannelId(this IQueryable<AutoTranslateChannel> set, ulong channelId)
|
||||||
|
=> set
|
||||||
|
.Include(x => x.Users)
|
||||||
|
.FirstOrDefaultAsyncEF(x => x.ChannelId == channelId);
|
||||||
|
}
|
||||||
|
}
|
@@ -35,6 +35,9 @@ namespace NadekoBot.Modules.Searches.Services
|
|||||||
name = name.ToUpperInvariant();
|
name = name.ToUpperInvariant();
|
||||||
var cryptos = await CryptoData().ConfigureAwait(false);
|
var cryptos = await CryptoData().ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (cryptos is null)
|
||||||
|
return (null, null);
|
||||||
|
|
||||||
var crypto = cryptos
|
var crypto = cryptos
|
||||||
?.FirstOrDefault(x => x.Id.ToUpperInvariant() == name || x.Name.ToUpperInvariant() == name
|
?.FirstOrDefault(x => x.Id.ToUpperInvariant() == name || x.Name.ToUpperInvariant() == name
|
||||||
|| x.Symbol.ToUpperInvariant() == name);
|
|| x.Symbol.ToUpperInvariant() == name);
|
||||||
@@ -42,7 +45,8 @@ namespace NadekoBot.Modules.Searches.Services
|
|||||||
(CryptoResponseData Elem, int Distance)? nearest = null;
|
(CryptoResponseData Elem, int Distance)? nearest = null;
|
||||||
if (crypto is null)
|
if (crypto is null)
|
||||||
{
|
{
|
||||||
nearest = cryptos.Select(x => (x, Distance: x.Name.ToUpperInvariant().LevenshteinDistance(name)))
|
nearest = cryptos
|
||||||
|
.Select(x => (x, Distance: x.Name.ToUpperInvariant().LevenshteinDistance(name)))
|
||||||
.OrderBy(x => x.Distance)
|
.OrderBy(x => x.Distance)
|
||||||
.Where(x => x.Distance <= 2)
|
.Where(x => x.Distance <= 2)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
@@ -68,18 +72,17 @@ namespace NadekoBot.Modules.Searches.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var _http = _httpFactory.CreateClient())
|
using var _http = _httpFactory.CreateClient();
|
||||||
{
|
var strData = await _http.GetStringAsync(
|
||||||
var strData = await _http.GetStringAsync(new Uri($"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?" +
|
$"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?" +
|
||||||
$"CMC_PRO_API_KEY={_creds.CoinmarketcapApiKey}" +
|
$"CMC_PRO_API_KEY={_creds.CoinmarketcapApiKey}" +
|
||||||
$"&start=1" +
|
$"&start=1" +
|
||||||
$"&limit=500" +
|
$"&limit=5000" +
|
||||||
$"&convert=USD"));
|
$"&convert=USD");
|
||||||
|
|
||||||
JsonConvert.DeserializeObject<CryptoResponse>(strData); // just to see if its' valid
|
JsonConvert.DeserializeObject<CryptoResponse>(strData); // just to see if its' valid
|
||||||
|
|
||||||
return strData;
|
return strData;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
14
src/NadekoBot/Modules/Searches/Services/ITranslateService.cs
Normal file
14
src/NadekoBot/Modules/Searches/Services/ITranslateService.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Searches
|
||||||
|
{
|
||||||
|
public interface ITranslateService
|
||||||
|
{
|
||||||
|
public Task<string> Translate(string source, string target, string text = null);
|
||||||
|
Task<bool> ToggleAtl(ulong guildId, ulong channelId, bool autoDelete);
|
||||||
|
IEnumerable<string> GetLanguages();
|
||||||
|
Task<bool?> RegisterUserAsync(ulong userId, ulong channelId, string @from, string to);
|
||||||
|
Task<bool> UnregisterUser(ulong channelId, ulong userId);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,10 +1,6 @@
|
|||||||
using Discord;
|
using NadekoBot.Common;
|
||||||
using Discord.WebSocket;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using NadekoBot.Common;
|
|
||||||
using NadekoBot.Modules.Searches.Common;
|
using NadekoBot.Modules.Searches.Common;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
using NadekoBot.Services.Database.Models;
|
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
@@ -13,18 +9,14 @@ using SixLabors.ImageSharp.Drawing.Processing;
|
|||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.ImageSharp.Processing;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AngleSharp.Html.Dom;
|
using AngleSharp.Html.Dom;
|
||||||
using AngleSharp.Html.Parser;
|
using AngleSharp.Html.Parser;
|
||||||
using NadekoBot.Db;
|
|
||||||
using NadekoBot.Modules.Administration;
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using HorizontalAlignment = SixLabors.Fonts.HorizontalAlignment;
|
using HorizontalAlignment = SixLabors.Fonts.HorizontalAlignment;
|
||||||
using Image = SixLabors.ImageSharp.Image;
|
using Image = SixLabors.ImageSharp.Image;
|
||||||
@@ -34,71 +26,31 @@ namespace NadekoBot.Modules.Searches.Services
|
|||||||
public class SearchesService : INService
|
public class SearchesService : INService
|
||||||
{
|
{
|
||||||
private readonly IHttpClientFactory _httpFactory;
|
private readonly IHttpClientFactory _httpFactory;
|
||||||
private readonly DiscordSocketClient _client;
|
|
||||||
private readonly IGoogleApiService _google;
|
private readonly IGoogleApiService _google;
|
||||||
private readonly DbService _db;
|
|
||||||
private readonly IImageCache _imgs;
|
private readonly IImageCache _imgs;
|
||||||
private readonly IDataCache _cache;
|
private readonly IDataCache _cache;
|
||||||
private readonly FontProvider _fonts;
|
private readonly FontProvider _fonts;
|
||||||
private readonly IBotCredentials _creds;
|
private readonly IBotCredentials _creds;
|
||||||
private readonly IEmbedBuilderService _eb;
|
|
||||||
private readonly NadekoRandom _rng;
|
private readonly NadekoRandom _rng;
|
||||||
|
|
||||||
public ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
|
|
||||||
// (userId, channelId)
|
|
||||||
public ConcurrentDictionary<(ulong UserId, ulong ChannelId), string> UserLanguages { get; } = new ConcurrentDictionary<(ulong, ulong), string>();
|
|
||||||
|
|
||||||
public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>();
|
public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>();
|
||||||
public List<MagicItem> MagicItems { get; } = new List<MagicItem>();
|
public List<MagicItem> MagicItems { get; } = new List<MagicItem>();
|
||||||
private readonly List<string> _yomamaJokes;
|
private readonly List<string> _yomamaJokes;
|
||||||
|
|
||||||
public SearchesService(DiscordSocketClient client, IGoogleApiService google,
|
public SearchesService(IGoogleApiService google,
|
||||||
DbService db, Bot bot, IDataCache cache, IHttpClientFactory factory,
|
IDataCache cache,
|
||||||
FontProvider fonts, IBotCredentials creds, IEmbedBuilderService eb)
|
IHttpClientFactory factory,
|
||||||
|
FontProvider fonts,
|
||||||
|
IBotCredentials creds)
|
||||||
{
|
{
|
||||||
_httpFactory = factory;
|
_httpFactory = factory;
|
||||||
_client = client;
|
|
||||||
_google = google;
|
_google = google;
|
||||||
_db = db;
|
|
||||||
_imgs = cache.LocalImages;
|
_imgs = cache.LocalImages;
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
_fonts = fonts;
|
_fonts = fonts;
|
||||||
_creds = creds;
|
_creds = creds;
|
||||||
_eb = eb;
|
|
||||||
_rng = new NadekoRandom();
|
_rng = new NadekoRandom();
|
||||||
|
|
||||||
//translate commands
|
|
||||||
_client.MessageReceived += (msg) =>
|
|
||||||
{
|
|
||||||
var _ = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!(msg is SocketUserMessage umsg))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out var autoDelete))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var key = (umsg.Author.Id, umsg.Channel.Id);
|
|
||||||
|
|
||||||
if (!UserLanguages.TryGetValue(key, out string langs))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var text = await Translate(langs, umsg.Resolve(TagHandling.Ignore))
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
if (autoDelete)
|
|
||||||
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
|
|
||||||
|
|
||||||
await umsg.Channel.SendConfirmAsync(_eb, $"{umsg.Author.Mention} `:` "
|
|
||||||
+ text.Replace("<@ ", "<@", StringComparison.InvariantCulture)
|
|
||||||
.Replace("<@! ", "<@!", StringComparison.InvariantCulture)).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
});
|
|
||||||
return Task.CompletedTask;
|
|
||||||
};
|
|
||||||
|
|
||||||
//joke commands
|
//joke commands
|
||||||
if (File.Exists("data/wowjokes.json"))
|
if (File.Exists("data/wowjokes.json"))
|
||||||
{
|
{
|
||||||
@@ -340,19 +292,6 @@ namespace NadekoBot.Modules.Searches.Services
|
|||||||
_rng.Next(1, max).ToString("000") + ".png";
|
_rng.Next(1, max).ToString("000") + ".png";
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> Translate(string langs, string text = null)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(text))
|
|
||||||
throw new ArgumentException("Text is empty or null", nameof(text));
|
|
||||||
var langarr = langs.ToLowerInvariant().Split('>');
|
|
||||||
if (langarr.Length != 2)
|
|
||||||
throw new ArgumentException("Langs does not have 2 parts separated by a >", nameof(langs));
|
|
||||||
var from = langarr[0];
|
|
||||||
var to = langarr[1];
|
|
||||||
text = text?.Trim();
|
|
||||||
return (await _google.Translate(text, from, to).ConfigureAwait(false)).SanitizeMentions(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly object yomamaLock = new object();
|
private readonly object yomamaLock = new object();
|
||||||
private int yomamaJokeIndex = 0;
|
private int yomamaJokeIndex = 0;
|
||||||
public Task<string> GetYomamaJoke()
|
public Task<string> GetYomamaJoke()
|
||||||
|
237
src/NadekoBot/Modules/Searches/Services/TranslateService.cs
Normal file
237
src/NadekoBot/Modules/Searches/Services/TranslateService.cs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AngleSharp.Common;
|
||||||
|
using Discord;
|
||||||
|
using Discord.Net;
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
using NadekoBot.Services;
|
||||||
|
using NadekoBot.Services.Database;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Searches
|
||||||
|
{
|
||||||
|
public sealed class TranslateService : ITranslateService, ILateExecutor, IReadyExecutor, INService
|
||||||
|
{
|
||||||
|
private readonly IGoogleApiService _google;
|
||||||
|
private readonly DbService _db;
|
||||||
|
private readonly IEmbedBuilderService _eb;
|
||||||
|
private readonly Bot _bot;
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<ulong, bool> _atcs = new();
|
||||||
|
private readonly ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, (string From, string To)>> _users = new();
|
||||||
|
|
||||||
|
public TranslateService(IGoogleApiService google,
|
||||||
|
DbService db,
|
||||||
|
IEmbedBuilderService eb,
|
||||||
|
Bot bot)
|
||||||
|
{
|
||||||
|
_google = google;
|
||||||
|
_db = db;
|
||||||
|
_eb = eb;
|
||||||
|
_bot = bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnReadyAsync()
|
||||||
|
{
|
||||||
|
var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var guilds = _bot.AllGuildConfigs.Select(x => x.GuildId).ToList();
|
||||||
|
var cs = await ctx.AutoTranslateChannels
|
||||||
|
.Include(x => x.Users)
|
||||||
|
.Where(x => guilds.Contains(x.GuildId))
|
||||||
|
.ToListAsyncEF();
|
||||||
|
|
||||||
|
foreach (var c in cs)
|
||||||
|
{
|
||||||
|
_atcs[c.ChannelId] = c.AutoDelete;
|
||||||
|
_users[c.ChannelId] = new(c.Users.ToDictionary(x => x.UserId, x => (x.Source.ToLower(), x.Target.ToLower())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task LateExecute(IGuild guild, IUserMessage msg)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(msg.Content))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (msg is IUserMessage { Channel: ITextChannel tch } um)
|
||||||
|
{
|
||||||
|
if (!_atcs.TryGetValue(tch.Id, out var autoDelete))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_users.TryGetValue(tch.Id, out var users)
|
||||||
|
|| !users.TryGetValue(um.Author.Id, out var langs))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var output = await _google.Translate(msg.Content, langs.From, langs.To);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(output)
|
||||||
|
|| msg.Content.Equals(output, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var embed = _eb.Create()
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
if (autoDelete)
|
||||||
|
{
|
||||||
|
embed
|
||||||
|
.WithAuthor(um.Author.ToString(), um.Author.GetAvatarUrl())
|
||||||
|
.AddField(langs.From, um.Content)
|
||||||
|
.AddField(langs.To, output);
|
||||||
|
|
||||||
|
await tch.EmbedAsync(embed);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await um.DeleteAsync();
|
||||||
|
}
|
||||||
|
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
_atcs.TryUpdate(tch.Id, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await um.ReplyAsync(embed: embed
|
||||||
|
.AddField(langs.To, output)
|
||||||
|
.Build(),
|
||||||
|
allowedMentions: AllowedMentions.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> Translate(string source, string target, string text = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
|
throw new ArgumentException("Text is empty or null", nameof(text));
|
||||||
|
|
||||||
|
var res = await _google.Translate(text, source, target).ConfigureAwait(false);
|
||||||
|
return res.SanitizeMentions(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ToggleAtl(ulong guildId, ulong channelId, bool autoDelete)
|
||||||
|
{
|
||||||
|
var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var old = await ctx.AutoTranslateChannels
|
||||||
|
.ToLinqToDBTable()
|
||||||
|
.FirstOrDefaultAsyncLinqToDB(x => x.ChannelId == channelId);
|
||||||
|
|
||||||
|
if (old is null)
|
||||||
|
{
|
||||||
|
ctx.AutoTranslateChannels
|
||||||
|
.Add(new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
ChannelId = channelId,
|
||||||
|
AutoDelete = autoDelete,
|
||||||
|
});
|
||||||
|
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
|
_atcs[channelId] = autoDelete;
|
||||||
|
_users[channelId] = new();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if autodelete value is different, update the autodelete value
|
||||||
|
// instead of disabling
|
||||||
|
if (old.AutoDelete != autoDelete)
|
||||||
|
{
|
||||||
|
old.AutoDelete = autoDelete;
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
_atcs[channelId] = autoDelete;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.AutoTranslateChannels
|
||||||
|
.ToLinqToDBTable()
|
||||||
|
.DeleteAsync(x => x.ChannelId == channelId);
|
||||||
|
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
_atcs.TryRemove(channelId, out _);
|
||||||
|
_users.TryRemove(channelId, out _);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void UpdateUser(ulong channelId, ulong userId, string from, string to)
|
||||||
|
{
|
||||||
|
var dict = _users.GetOrAdd(channelId, new ConcurrentDictionary<ulong, (string, string)>());
|
||||||
|
dict[userId] = (from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool?> RegisterUserAsync(ulong userId, ulong channelId, string from, string to)
|
||||||
|
{
|
||||||
|
if (!_google.Languages.ContainsKey(from) || !_google.Languages.ContainsKey(to))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var ctx = _db.GetDbContext();
|
||||||
|
var ch = await ctx.AutoTranslateChannels
|
||||||
|
.GetByChannelId(channelId);
|
||||||
|
|
||||||
|
if (ch is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var user = ch.Users
|
||||||
|
.FirstOrDefault(x => x.UserId == userId);
|
||||||
|
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
ch.Users.Add(user = new()
|
||||||
|
{
|
||||||
|
Source = from,
|
||||||
|
Target = to,
|
||||||
|
UserId = userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
|
UpdateUser(channelId, userId, from, to);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it's different from old settings, update
|
||||||
|
if (user.Source != from || user.Target != to)
|
||||||
|
{
|
||||||
|
user.Source = from;
|
||||||
|
user.Target = to;
|
||||||
|
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
|
UpdateUser(channelId, userId, from, to);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await UnregisterUser(channelId, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> UnregisterUser(ulong channelId, ulong userId)
|
||||||
|
{
|
||||||
|
var ctx = _db.GetDbContext();
|
||||||
|
var rows = await ctx.AutoTranslateUsers
|
||||||
|
.ToLinqToDBTable()
|
||||||
|
.DeleteAsync(x => x.UserId == userId &&
|
||||||
|
x.Channel.ChannelId == channelId);
|
||||||
|
|
||||||
|
if (_users.TryGetValue(channelId, out var inner))
|
||||||
|
inner.TryRemove(userId, out _);
|
||||||
|
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
return rows > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> GetLanguages() => _google.Languages.Select(x => x.Key);
|
||||||
|
}
|
||||||
|
}
|
@@ -2,35 +2,29 @@
|
|||||||
using Discord.Commands;
|
using Discord.Commands;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Linq;
|
|
||||||
using NadekoBot.Common.Attributes;
|
using NadekoBot.Common.Attributes;
|
||||||
using NadekoBot.Services;
|
|
||||||
using NadekoBot.Modules.Searches.Services;
|
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Searches
|
namespace NadekoBot.Modules.Searches
|
||||||
{
|
{
|
||||||
public partial class Searches
|
public partial class Searches
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public class TranslateCommands : NadekoSubmodule
|
public class TranslateCommands : NadekoSubmodule<ITranslateService>
|
||||||
{
|
{
|
||||||
private readonly SearchesService _searches;
|
|
||||||
private readonly IGoogleApiService _google;
|
|
||||||
|
|
||||||
public TranslateCommands(SearchesService searches, IGoogleApiService google)
|
|
||||||
{
|
|
||||||
_searches = searches;
|
|
||||||
_google = google;
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
public async Task Translate(string langs, [Leftover] string text = null)
|
public async Task Translate(string from, string to, [Leftover] string text = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ctx.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
await ctx.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||||
var translation = await _searches.Translate(langs, text).ConfigureAwait(false);
|
var translation = await _service.Translate(from, to, text).ConfigureAwait(false);
|
||||||
await SendConfirmAsync(GetText(strs.translation) + " " + langs, translation).ConfigureAwait(false);
|
|
||||||
|
var embed = _eb.Create(ctx)
|
||||||
|
.WithOkColor()
|
||||||
|
.AddField(from, text, false)
|
||||||
|
.AddField(to, translation, false);
|
||||||
|
|
||||||
|
await ctx.Channel.EmbedAsync(embed);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -38,27 +32,6 @@ namespace NadekoBot.Modules.Searches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//[NadekoCommand, Usage, Description, Aliases]
|
|
||||||
//[OwnerOnly]
|
|
||||||
//public async Task Obfuscate([Leftover] string txt)
|
|
||||||
//{
|
|
||||||
// var lastItem = "en";
|
|
||||||
// foreach (var item in _google.Languages.Except(new[] { "en" }).Where(x => x.Length < 4))
|
|
||||||
// {
|
|
||||||
// var txt2 = await _searches.Translate(lastItem + ">" + item, txt);
|
|
||||||
// await ctx.Channel.EmbedAsync(_eb.Create()
|
|
||||||
// .WithOkColor()
|
|
||||||
// .WithTitle(lastItem + ">" + item)
|
|
||||||
// .AddField("Input", txt)
|
|
||||||
// .AddField("Output", txt2));
|
|
||||||
// txt = txt2;
|
|
||||||
// await Task.Delay(500);
|
|
||||||
// lastItem = item;
|
|
||||||
// }
|
|
||||||
// txt = await _searches.Translate(lastItem + ">en", txt);
|
|
||||||
// await SendConfirmAsync("Final output:\n\n" + txt);
|
|
||||||
//}
|
|
||||||
|
|
||||||
public enum AutoDeleteAutoTranslate
|
public enum AutoDeleteAutoTranslate
|
||||||
{
|
{
|
||||||
Del,
|
Del,
|
||||||
@@ -68,56 +41,52 @@ namespace NadekoBot.Modules.Searches
|
|||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
[BotPerm(ChannelPerm.ManageMessages)]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
public async Task AutoTranslate(AutoDeleteAutoTranslate autoDelete = AutoDeleteAutoTranslate.Nodel)
|
public async Task AutoTranslate(AutoDeleteAutoTranslate autoDelete = AutoDeleteAutoTranslate.Nodel)
|
||||||
{
|
{
|
||||||
var channel = (ITextChannel)ctx.Channel;
|
var toggle = await _service.ToggleAtl(ctx.Guild.Id, ctx.Channel.Id, autoDelete == AutoDeleteAutoTranslate.Del);
|
||||||
|
if (toggle)
|
||||||
if (autoDelete == AutoDeleteAutoTranslate.Del)
|
|
||||||
{
|
|
||||||
_searches.TranslatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true);
|
|
||||||
await ReplyConfirmLocalizedAsync(strs.atl_ad_started).ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_searches.TranslatedChannels.TryRemove(channel.Id, out _))
|
|
||||||
{
|
|
||||||
await ReplyConfirmLocalizedAsync(strs.atl_stopped).ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_searches.TranslatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del))
|
|
||||||
{
|
{
|
||||||
await ReplyConfirmLocalizedAsync(strs.atl_started).ConfigureAwait(false);
|
await ReplyConfirmLocalizedAsync(strs.atl_started).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.atl_stopped).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task AutoTransLang([Leftover] string langs = null)
|
public async Task AutoTransLang()
|
||||||
{
|
{
|
||||||
var ucp = (ctx.User.Id, ctx.Channel.Id);
|
if (await _service.UnregisterUser(ctx.Channel.Id, ctx.User.Id))
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(langs))
|
|
||||||
{
|
{
|
||||||
if (_searches.UserLanguages.TryRemove(ucp, out langs))
|
await ReplyConfirmLocalizedAsync(strs.atl_removed).ConfigureAwait(false);
|
||||||
await ReplyConfirmLocalizedAsync(strs.atl_removed).ConfigureAwait(false);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task AutoTransLang(string from, string to)
|
||||||
|
{
|
||||||
|
var succ = await _service.RegisterUserAsync(ctx.User.Id,
|
||||||
|
ctx.Channel.Id,
|
||||||
|
from.ToLower(),
|
||||||
|
to.ToLower());
|
||||||
|
|
||||||
|
if (succ is null)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.atl_not_enabled);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var langarr = langs.ToLowerInvariant().Split('>');
|
if (succ is false)
|
||||||
if (langarr.Length != 2)
|
|
||||||
return;
|
|
||||||
var from = langarr[0];
|
|
||||||
var to = langarr[1];
|
|
||||||
|
|
||||||
if (!_google.Languages.Contains(from) || !_google.Languages.Contains(to))
|
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.invalid_lang).ConfigureAwait(false);
|
await ReplyErrorLocalizedAsync(strs.invalid_lang).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_searches.UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
|
|
||||||
|
|
||||||
await ReplyConfirmLocalizedAsync(strs.atl_set(from, to));
|
await ReplyConfirmLocalizedAsync(strs.atl_set(from, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +94,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Translangs()
|
public async Task Translangs()
|
||||||
{
|
{
|
||||||
await ctx.Channel.SendTableAsync(_google.Languages, str => $"{str,-15}", 3).ConfigureAwait(false);
|
await ctx.Channel.SendTableAsync(_service.GetLanguages(), str => $"{str,-15}", 3).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -32,35 +32,40 @@ namespace NadekoBot.Modules.Utility
|
|||||||
var channel = (ITextChannel)ctx.Channel;
|
var channel = (ITextChannel)ctx.Channel;
|
||||||
guildName = guildName?.ToUpperInvariant();
|
guildName = guildName?.ToUpperInvariant();
|
||||||
SocketGuild guild;
|
SocketGuild guild;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(guildName))
|
if (string.IsNullOrWhiteSpace(guildName))
|
||||||
guild = (SocketGuild)channel.Guild;
|
guild = (SocketGuild)channel.Guild;
|
||||||
else
|
else
|
||||||
guild = _client.Guilds.FirstOrDefault(g => g.Name.ToUpperInvariant() == guildName.ToUpperInvariant());
|
guild = _client.Guilds.FirstOrDefault(g => g.Name.ToUpperInvariant() == guildName.ToUpperInvariant());
|
||||||
|
|
||||||
if (guild is null)
|
if (guild is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ownername = guild.GetUser(guild.OwnerId);
|
var ownername = guild.GetUser(guild.OwnerId);
|
||||||
var textchn = guild.TextChannels.Count();
|
var textchn = guild.TextChannels.Count;
|
||||||
var voicechn = guild.VoiceChannels.Count();
|
var voicechn = guild.VoiceChannels.Count;
|
||||||
|
var channels = $@"{GetText(strs.text_channels(textchn))}
|
||||||
|
{GetText(strs.voice_channels(voicechn))}";
|
||||||
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(guild.Id >> 22);
|
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(guild.Id >> 22);
|
||||||
var features = string.Join("\n", guild.Features);
|
var features = string.Join(", ", guild.Features);
|
||||||
if (string.IsNullOrWhiteSpace(features))
|
if (string.IsNullOrWhiteSpace(features))
|
||||||
features = "-";
|
features = "-";
|
||||||
|
|
||||||
var embed = _eb.Create()
|
var embed = _eb.Create()
|
||||||
.WithAuthor(GetText(strs.server_info))
|
.WithAuthor(GetText(strs.server_info))
|
||||||
.WithTitle(guild.Name)
|
.WithTitle(guild.Name)
|
||||||
.AddField(GetText(strs.id), guild.Id.ToString(), true)
|
.AddField(GetText(strs.id), guild.Id.ToString(), true)
|
||||||
.AddField(GetText(strs.owner), ownername.ToString(), true)
|
.AddField(GetText(strs.owner), ownername.ToString(), true)
|
||||||
.AddField(GetText(strs.members), guild.MemberCount.ToString(), true)
|
.AddField(GetText(strs.members), guild.MemberCount.ToString(), true)
|
||||||
.AddField(GetText(strs.text_channels), textchn.ToString(), true)
|
.AddField(GetText(strs.channels), channels, true)
|
||||||
.AddField(GetText(strs.voice_channels), voicechn.ToString(), true)
|
|
||||||
.AddField(GetText(strs.created_at), $"{createdAt:dd.MM.yyyy HH:mm}", true)
|
.AddField(GetText(strs.created_at), $"{createdAt:dd.MM.yyyy HH:mm}", true)
|
||||||
.AddField(GetText(strs.region), guild.VoiceRegionId.ToString(), true)
|
|
||||||
.AddField(GetText(strs.roles), (guild.Roles.Count - 1).ToString(), true)
|
.AddField(GetText(strs.roles), (guild.Roles.Count - 1).ToString(), true)
|
||||||
.AddField(GetText(strs.features), features, true)
|
.AddField(GetText(strs.features), features)
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
if (Uri.IsWellFormedUriString(guild.IconUrl, UriKind.Absolute))
|
if (Uri.IsWellFormedUriString(guild.IconUrl, UriKind.Absolute))
|
||||||
embed.WithThumbnailUrl(guild.IconUrl);
|
embed.WithThumbnailUrl(guild.IconUrl);
|
||||||
|
|
||||||
if (guild.Emotes.Any())
|
if (guild.Emotes.Any())
|
||||||
{
|
{
|
||||||
embed.AddField(GetText(strs.custom_emojis) + $"({guild.Emotes.Count})",
|
embed.AddField(GetText(strs.custom_emojis) + $"({guild.Emotes.Count})",
|
||||||
|
@@ -7,10 +7,13 @@ using NadekoBot.Db.Models;
|
|||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using NadekoBot.Common.Yml;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using NadekoBot.Db;
|
using NadekoBot.Db;
|
||||||
|
using YamlDotNet.Serialization;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Utility
|
namespace NadekoBot.Modules.Utility
|
||||||
{
|
{
|
||||||
@@ -20,10 +23,12 @@ namespace NadekoBot.Modules.Utility
|
|||||||
public class QuoteCommands : NadekoSubmodule
|
public class QuoteCommands : NadekoSubmodule
|
||||||
{
|
{
|
||||||
private readonly DbService _db;
|
private readonly DbService _db;
|
||||||
|
private readonly IHttpClientFactory _http;
|
||||||
|
|
||||||
public QuoteCommands(DbService db)
|
public QuoteCommands(DbService db, IHttpClientFactory http)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
|
_http = http;
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
@@ -203,7 +208,7 @@ namespace NadekoBot.Modules.Utility
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task QuoteDelete(int id)
|
public async Task QuoteDelete(int id)
|
||||||
{
|
{
|
||||||
var isAdmin = ((IGuildUser)ctx.Message.Author).GuildPermissions.ManageMessages;
|
var hasManageMessages = ((IGuildUser)ctx.Message.Author).GuildPermissions.ManageMessages;
|
||||||
|
|
||||||
var success = false;
|
var success = false;
|
||||||
string response;
|
string response;
|
||||||
@@ -211,7 +216,7 @@ namespace NadekoBot.Modules.Utility
|
|||||||
{
|
{
|
||||||
var q = uow.Quotes.GetById(id);
|
var q = uow.Quotes.GetById(id);
|
||||||
|
|
||||||
if ((q?.GuildId != ctx.Guild.Id) || (!isAdmin && q.AuthorId != ctx.Message.Author.Id))
|
if ((q?.GuildId != ctx.Guild.Id) || (!hasManageMessages && q.AuthorId != ctx.Message.Author.Id))
|
||||||
{
|
{
|
||||||
response = GetText(strs.quotes_remove_none);
|
response = GetText(strs.quotes_remove_none);
|
||||||
}
|
}
|
||||||
@@ -248,6 +253,140 @@ namespace NadekoBot.Modules.Utility
|
|||||||
|
|
||||||
await ReplyConfirmLocalizedAsync(strs.quotes_deleted(Format.Bold(keyword.SanitizeAllMentions()))).ConfigureAwait(false);
|
await ReplyConfirmLocalizedAsync(strs.quotes_deleted(Format.Bold(keyword.SanitizeAllMentions()))).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ExportedQuote
|
||||||
|
{
|
||||||
|
public static ExportedQuote FromModel(Quote quote)
|
||||||
|
=> new ExportedQuote()
|
||||||
|
{
|
||||||
|
Id = ((kwum)quote.Id).ToString(),
|
||||||
|
An = quote.AuthorName,
|
||||||
|
Aid = quote.AuthorId,
|
||||||
|
Txt = quote.Text
|
||||||
|
};
|
||||||
|
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string An { get; set; }
|
||||||
|
public ulong Aid { get; set; }
|
||||||
|
public string Txt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string _prependExport =
|
||||||
|
@"# Keys are keywords, Each key has a LIST of quotes in the following format:
|
||||||
|
# - id: Alphanumeric id used for commands related to the quote. (Note, when using .quotesimport, a new id will be generated.)
|
||||||
|
# an: Author name
|
||||||
|
# aid: Author id
|
||||||
|
# txt: Quote text
|
||||||
|
";
|
||||||
|
private static readonly ISerializer _exportSerializer = new SerializerBuilder()
|
||||||
|
.WithEventEmitter(args => new MultilineScalarFlowStyleEmitter(args))
|
||||||
|
.WithNamingConvention(YamlDotNet.Serialization.NamingConventions.CamelCaseNamingConvention.Instance)
|
||||||
|
.WithIndentedSequences()
|
||||||
|
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults)
|
||||||
|
.DisableAliases()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
public async Task QuotesExport()
|
||||||
|
{
|
||||||
|
IEnumerable<Quote> quotes;
|
||||||
|
using (var uow = _db.GetDbContext())
|
||||||
|
{
|
||||||
|
quotes = uow.Quotes
|
||||||
|
.GetForGuild(ctx.Guild.Id)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
var crsDict = quotes
|
||||||
|
.GroupBy(x => x.Keyword)
|
||||||
|
.ToDictionary(x => x.Key, x => x.Select(ExportedQuote.FromModel));
|
||||||
|
|
||||||
|
var text = _prependExport + _exportSerializer
|
||||||
|
.Serialize(crsDict)
|
||||||
|
.UnescapeUnicodeCodePoints();
|
||||||
|
|
||||||
|
await using var stream = await text.ToStream();
|
||||||
|
await ctx.Channel.SendFileAsync(stream, "quote-export.yml", text: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
[Ratelimit(300)]
|
||||||
|
#if GLOBAL_NADEKO
|
||||||
|
[OwnerOnly]
|
||||||
|
#endif
|
||||||
|
public async Task QuotesImport([Leftover]string input = null)
|
||||||
|
{
|
||||||
|
input = input?.Trim();
|
||||||
|
|
||||||
|
_ = ctx.Channel.TriggerTypingAsync();
|
||||||
|
|
||||||
|
if (input is null)
|
||||||
|
{
|
||||||
|
var attachment = ctx.Message.Attachments.FirstOrDefault();
|
||||||
|
if (attachment is null)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.expr_import_no_input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var client = _http.CreateClient();
|
||||||
|
input = await client.GetStringAsync(attachment.Url);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.expr_import_no_input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var succ = await ImportCrsAsync(ctx.Guild.Id, input);
|
||||||
|
if (!succ)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.expr_import_invalid_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ImportCrsAsync(ulong guildId, string input)
|
||||||
|
{
|
||||||
|
Dictionary<string, List<ExportedQuote>> data;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data = Yaml.Deserializer.Deserialize<Dictionary<string, List<ExportedQuote>>>(input);
|
||||||
|
if (data.Sum(x => x.Value.Count) == 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
foreach (var entry in data)
|
||||||
|
{
|
||||||
|
var keyword = entry.Key;
|
||||||
|
await uow.Quotes
|
||||||
|
.AddRangeAsync(entry.Value
|
||||||
|
.Where(quote => !string.IsNullOrWhiteSpace(quote.Txt))
|
||||||
|
.Select(quote => new Quote()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
Keyword = keyword,
|
||||||
|
Text = quote.Txt,
|
||||||
|
AuthorId = quote.Aid,
|
||||||
|
AuthorName = quote.An,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
await uow.SaveChangesAsync();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -80,21 +80,50 @@ namespace NadekoBot.Modules.Utility
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Server
|
||||||
|
{
|
||||||
|
Server = int.MinValue,
|
||||||
|
Srvr = int.MinValue,
|
||||||
|
Serv = int.MinValue,
|
||||||
|
S = int.MinValue,
|
||||||
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
public async Task RemindList(int page = 1)
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
[Priority(0)]
|
||||||
|
public Task RemindList(Server _, int page = 1)
|
||||||
|
=> RemindList(page, true);
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[Priority(1)]
|
||||||
|
public Task RemindList(int page = 1)
|
||||||
|
=> RemindList(page, false);
|
||||||
|
|
||||||
|
private async Task RemindList(int page, bool isServer)
|
||||||
{
|
{
|
||||||
if (--page < 0)
|
if (--page < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var embed = _eb.Create()
|
var embed = _eb.Create()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.reminder_list));
|
.WithTitle(GetText(isServer ? strs.reminder_server_list : strs.reminder_list));
|
||||||
|
|
||||||
List<Reminder> rems;
|
List<Reminder> rems;
|
||||||
using (var uow = _db.GetDbContext())
|
using (var uow = _db.GetDbContext())
|
||||||
{
|
{
|
||||||
rems = uow.Reminders.RemindersFor(ctx.User.Id, page)
|
if (isServer)
|
||||||
.ToList();
|
{
|
||||||
|
rems = uow.Reminders
|
||||||
|
.RemindersForServer(ctx.Guild.Id, page)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rems = uow.Reminders
|
||||||
|
.RemindersFor(ctx.User.Id, page)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rems.Any())
|
if (rems.Any())
|
||||||
@@ -121,18 +150,33 @@ namespace NadekoBot.Modules.Utility
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
public async Task RemindDelete(int index)
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
[Priority(0)]
|
||||||
|
public Task RemindDelete(Server _, int index)
|
||||||
|
=> RemindDelete(index, true);
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[Priority(1)]
|
||||||
|
public Task RemindDelete(int index)
|
||||||
|
=> RemindDelete(index, false);
|
||||||
|
|
||||||
|
private async Task RemindDelete(int index, bool isServer)
|
||||||
{
|
{
|
||||||
if (--index < 0)
|
if (--index < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var embed = _eb.Create();
|
|
||||||
|
|
||||||
Reminder rem = null;
|
Reminder rem = null;
|
||||||
using (var uow = _db.GetDbContext())
|
using (var uow = _db.GetDbContext())
|
||||||
{
|
{
|
||||||
var rems = uow.Reminders.RemindersFor(ctx.User.Id, index / 10)
|
var rems = isServer
|
||||||
.ToList();
|
? uow.Reminders
|
||||||
|
.RemindersForServer(ctx.Guild.Id, index / 10)
|
||||||
|
.ToList()
|
||||||
|
: uow.Reminders
|
||||||
|
.RemindersFor(ctx.User.Id, index / 10)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
var pageIndex = index % 10;
|
var pageIndex = index % 10;
|
||||||
if (rems.Count > pageIndex)
|
if (rems.Count > pageIndex)
|
||||||
{
|
{
|
||||||
|
@@ -227,6 +227,9 @@ namespace NadekoBot.Modules.Utility.Services
|
|||||||
|
|
||||||
private async Task RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
|
private async Task RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
|
||||||
{
|
{
|
||||||
|
if (user.IsBot)
|
||||||
|
return;
|
||||||
|
|
||||||
var g = (StreamingGame)user.Activities
|
var g = (StreamingGame)user.Activities
|
||||||
.FirstOrDefault(a => a is StreamingGame &&
|
.FirstOrDefault(a => a is StreamingGame &&
|
||||||
(string.IsNullOrWhiteSpace(setting.Keyword)
|
(string.IsNullOrWhiteSpace(setting.Keyword)
|
||||||
@@ -240,7 +243,7 @@ namespace NadekoBot.Modules.Utility.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
addRole = addRole ?? user.Guild.GetRole(setting.AddRoleId);
|
addRole ??= user.Guild.GetRole(setting.AddRoleId);
|
||||||
if (addRole is null)
|
if (addRole is null)
|
||||||
{
|
{
|
||||||
await StopStreamRole(user.Guild).ConfigureAwait(false);
|
await StopStreamRole(user.Guild).ConfigureAwait(false);
|
||||||
@@ -249,9 +252,12 @@ namespace NadekoBot.Modules.Utility.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
//check if he doesn't have addrole already, to avoid errors
|
//check if he doesn't have addrole already, to avoid errors
|
||||||
if (!user.RoleIds.Contains(setting.AddRoleId))
|
if (!user.RoleIds.Contains(addRole.Id))
|
||||||
|
{
|
||||||
await user.AddRoleAsync(addRole).ConfigureAwait(false);
|
await user.AddRoleAsync(addRole).ConfigureAwait(false);
|
||||||
Log.Information("Added stream role to user {0} in {1} server", user.ToString(), user.Guild.ToString());
|
Log.Information("Added stream role to user {0} in {1} server", user.ToString(),
|
||||||
|
user.Guild.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
|
catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
|
||||||
{
|
{
|
||||||
|
@@ -11,11 +11,14 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NadekoBot.Common.Replacements;
|
using NadekoBot.Common.Replacements;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
using SystemTextJsonSamples;
|
||||||
|
using JsonSerializer = Newtonsoft.Json.JsonSerializer;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Utility
|
namespace NadekoBot.Modules.Utility
|
||||||
{
|
{
|
||||||
@@ -119,9 +122,9 @@ namespace NadekoBot.Modules.Utility
|
|||||||
|
|
||||||
if (pageUsers.Count == 0)
|
if (pageUsers.Count == 0)
|
||||||
return _eb.Create().WithOkColor().WithDescription(GetText(strs.no_user_on_this_page));
|
return _eb.Create().WithOkColor().WithDescription(GetText(strs.no_user_on_this_page));
|
||||||
|
|
||||||
return _eb.Create().WithOkColor()
|
return _eb.Create().WithOkColor()
|
||||||
.WithTitle(GetText(strs.inrole_list(Format.Bold(role?.Name ?? "No Role") + $" - {roleUsers.Length}")))
|
.WithTitle(GetText(strs.inrole_list(Format.Bold(role?.Name ?? "No Role"), roleUsers.Length)))
|
||||||
.WithDescription(string.Join("\n", pageUsers));
|
.WithDescription(string.Join("\n", pageUsers));
|
||||||
}, roleUsers.Length, 20).ConfigureAwait(false);
|
}, roleUsers.Length, 20).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -284,26 +287,34 @@ namespace NadekoBot.Modules.Utility
|
|||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[RequireBotPermission(GuildPermission.ManageEmojis)]
|
[BotPerm(GuildPerm.ManageEmojis)]
|
||||||
[RequireUserPermission(GuildPermission.ManageEmojis)]
|
[UserPerm(GuildPerm.ManageEmojis)]
|
||||||
[Priority(0)]
|
[Priority(2)]
|
||||||
public Task EmojiAdd(string name, Emote emote)
|
public Task EmojiAdd(string name, Emote emote)
|
||||||
=> EmojiAdd(name, emote.Url);
|
=> EmojiAdd(name, emote.Url);
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[RequireBotPermission(GuildPermission.ManageEmojis)]
|
[BotPerm(GuildPerm.ManageEmojis)]
|
||||||
[RequireUserPermission(GuildPermission.ManageEmojis)]
|
[UserPerm(GuildPerm.ManageEmojis)]
|
||||||
|
[Priority(1)]
|
||||||
public Task EmojiAdd(Emote emote)
|
public Task EmojiAdd(Emote emote)
|
||||||
=> EmojiAdd(emote.Name, emote.Url);
|
=> EmojiAdd(emote.Name, emote.Url);
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[RequireBotPermission(GuildPermission.ManageEmojis)]
|
[BotPerm(GuildPerm.ManageEmojis)]
|
||||||
[RequireUserPermission(GuildPermission.ManageEmojis)]
|
[UserPerm(GuildPerm.ManageEmojis)]
|
||||||
public async Task EmojiAdd(string name, string url)
|
[Priority(0)]
|
||||||
|
public async Task EmojiAdd(string name, string url = null)
|
||||||
{
|
{
|
||||||
name = name.Trim(':');
|
name = name.Trim(':');
|
||||||
|
|
||||||
|
url ??= ctx.Message.Attachments.FirstOrDefault()?.Url;
|
||||||
|
|
||||||
|
if (url is null)
|
||||||
|
return;
|
||||||
|
|
||||||
using var http = _httpFactory.CreateClient();
|
using var http = _httpFactory.CreateClient();
|
||||||
var res = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
|
var res = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
|
||||||
if (!res.IsImage() || res.GetImageSize() is null or > 262_144)
|
if (!res.IsImage() || res.GetImageSize() is null or > 262_144)
|
||||||
@@ -354,7 +365,48 @@ namespace NadekoBot.Modules.Utility
|
|||||||
|
|
||||||
await ctx.Channel.EmbedAsync(embed);
|
await ctx.Channel.EmbedAsync(embed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public Task ShowEmbed(ulong messageId)
|
||||||
|
=> ShowEmbed((ITextChannel)ctx.Channel, messageId);
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions _showEmbedSerializerOptions = new JsonSerializerOptions()
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
IgnoreNullValues = true,
|
||||||
|
PropertyNamingPolicy = LowerCaseNamingPolicy.Default
|
||||||
|
};
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task ShowEmbed(ITextChannel ch, ulong messageId)
|
||||||
|
{
|
||||||
|
var user = (IGuildUser)ctx.User;
|
||||||
|
var perms = user.GetPermissions(ch);
|
||||||
|
if (!perms.ReadMessageHistory || !perms.ViewChannel)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.insuf_perms_u);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = await ch.GetMessageAsync(messageId);
|
||||||
|
if (msg is null)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.msg_not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var embed = msg.Embeds.FirstOrDefault();
|
||||||
|
if (embed is null)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = SmartEmbedText.FromEmbed(embed, msg.Content).ToJson(_showEmbedSerializerOptions);
|
||||||
|
await SendConfirmAsync(Format.Sanitize(json).Replace("](", "]\\("));
|
||||||
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
@@ -425,6 +477,8 @@ namespace NadekoBot.Modules.Utility
|
|||||||
New
|
New
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// [NadekoCommand, Usage, Description, Aliases]
|
// [NadekoCommand, Usage, Description, Aliases]
|
||||||
// [RequireContext(ContextType.Guild)]
|
// [RequireContext(ContextType.Guild)]
|
||||||
// public async Task CreateMyInvite(CreateInviteType type = CreateInviteType.Any)
|
// public async Task CreateMyInvite(CreateInviteType type = CreateInviteType.Any)
|
||||||
|
@@ -746,27 +746,6 @@ namespace NadekoBot.Modules.Xp.Services
|
|||||||
guildRank);
|
guildRank);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (int Level, int LevelXp, int LevelRequiredXp) GetLevelData(UserXpStats stats)
|
|
||||||
{
|
|
||||||
var baseXp = XpService.XP_REQUIRED_LVL_1;
|
|
||||||
|
|
||||||
var required = baseXp;
|
|
||||||
var totalXp = 0;
|
|
||||||
var lvl = 1;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
required = (int) (baseXp + baseXp / 4.0 * (lvl - 1));
|
|
||||||
|
|
||||||
if (required + totalXp > stats.Xp)
|
|
||||||
break;
|
|
||||||
|
|
||||||
totalXp += required;
|
|
||||||
lvl++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (lvl - 1, stats.Xp - totalXp, required);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ToggleExcludeServer(ulong id)
|
public bool ToggleExcludeServer(ulong id)
|
||||||
{
|
{
|
||||||
using (var uow = _db.GetDbContext())
|
using (var uow = _db.GetDbContext())
|
||||||
|
@@ -39,6 +39,8 @@ namespace NadekoBot.Modules.Xp
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
public async Task XpRewsReset()
|
public async Task XpRewsReset()
|
||||||
{
|
{
|
||||||
var reply = await PromptUserConfirmAsync(_eb.Create()
|
var reply = await PromptUserConfirmAsync(_eb.Create()
|
||||||
|
@@ -9,29 +9,29 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AngleSharp" Version="0.16.0" />
|
<PackageReference Include="AngleSharp" Version="0.16.1" />
|
||||||
<PackageReference Include="AWSSDK.S3" Version="3.7.1.19" />
|
<PackageReference Include="AWSSDK.S3" Version="3.7.7.3" />
|
||||||
<PackageReference Include="Cloneable" Version="1.3.0" />
|
<PackageReference Include="Cloneable" Version="1.3.0" />
|
||||||
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.1" />
|
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.2" />
|
||||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||||
<PackageReference Include="Discord.Net" Version="2.4.0" />
|
<PackageReference Include="Discord.Net" Version="2.4.0" />
|
||||||
<PackageReference Include="CoreCLR-NCalc" Version="2.2.92" />
|
<PackageReference Include="CoreCLR-NCalc" Version="2.2.92" />
|
||||||
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
|
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
|
||||||
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.53.0.2378" />
|
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.55.0.2449" />
|
||||||
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084" />
|
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084" />
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.17.3" />
|
<PackageReference Include="Google.Protobuf" Version="3.19.1" />
|
||||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.38.0" />
|
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.41.0" />
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.38.1">
|
<PackageReference Include="Grpc.Tools" Version="2.42.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Html2Markdown" Version="4.0.0.427" />
|
<PackageReference Include="Html2Markdown" Version="5.0.0.468" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.13" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.13">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.13" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
|
||||||
@@ -44,15 +44,13 @@
|
|||||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="Scrutor" Version="3.3.0" />
|
<PackageReference Include="Scrutor" Version="3.3.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.0.1" />
|
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.0" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.3" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0010" />
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0010" />
|
||||||
<PackageReference Include="StackExchange.Redis" Version="2.2.62" />
|
<PackageReference Include="StackExchange.Redis" Version="2.2.88" />
|
||||||
<PackageReference Include="VideoLibrary" Version="3.1.2" />
|
|
||||||
<PackageReference Include="YamlDotNet" Version="11.2.1" />
|
<PackageReference Include="YamlDotNet" Version="11.2.1" />
|
||||||
<PackageReference Include="YoutubeExplode" Version="6.0.5" />
|
<PackageReference Include="linq2db.EntityFrameworkCore" Version="5.9.0" />
|
||||||
<PackageReference Include="linq2db.EntityFrameworkCore" Version="5.4.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,15 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace NadekoBot.Services
|
namespace NadekoBot.Services
|
||||||
{
|
{
|
||||||
public interface ICoordinator
|
public interface ICoordinator
|
||||||
{
|
{
|
||||||
bool RestartBot();
|
bool RestartBot();
|
||||||
void Die();
|
void Die(bool graceful);
|
||||||
bool RestartShard(int shardId);
|
bool RestartShard(int shardId);
|
||||||
IList<ShardStatus> GetAllShardStatuses();
|
IList<ShardStatus> GetAllShardStatuses();
|
||||||
int GetGuildCount();
|
int GetGuildCount();
|
||||||
|
Task Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ShardStatus
|
public class ShardStatus
|
||||||
|
@@ -7,7 +7,7 @@ namespace NadekoBot.Services
|
|||||||
{
|
{
|
||||||
public interface IGoogleApiService : INService
|
public interface IGoogleApiService : INService
|
||||||
{
|
{
|
||||||
IEnumerable<string> Languages { get; }
|
IReadOnlyDictionary<string, string> Languages { get; }
|
||||||
|
|
||||||
Task<IEnumerable<string>> GetVideoLinksByKeywordAsync(string keywords, int count = 1);
|
Task<IEnumerable<string>> GetVideoLinksByKeywordAsync(string keywords, int count = 1);
|
||||||
Task<IEnumerable<(string Name, string Id, string Url)>> GetVideoInfosByKeywordAsync(string keywords, int count = 1);
|
Task<IEnumerable<(string Name, string Id, string Url)>> GetVideoInfosByKeywordAsync(string keywords, int count = 1);
|
||||||
|
@@ -14,7 +14,6 @@ namespace NadekoBot.Services
|
|||||||
IReadOnlyList<byte[]> Dice { get; }
|
IReadOnlyList<byte[]> Dice { get; }
|
||||||
|
|
||||||
IReadOnlyList<byte[]> SlotEmojis { get; }
|
IReadOnlyList<byte[]> SlotEmojis { get; }
|
||||||
IReadOnlyList<byte[]> SlotNumbers { get; }
|
|
||||||
IReadOnlyList<byte[]> Currency { get; }
|
IReadOnlyList<byte[]> Currency { get; }
|
||||||
|
|
||||||
byte[] SlotBackground { get; }
|
byte[] SlotBackground { get; }
|
||||||
|
@@ -226,8 +226,7 @@ namespace NadekoBot.Services
|
|||||||
return new ImageResult(search.Items[0].Image, search.Items[0].Link);
|
return new ImageResult(search.Items[0].Image, search.Items[0].Link);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> Languages => _languageDictionary.Keys.OrderBy(x => x);
|
public IReadOnlyDictionary<string, string> Languages { get; } = new Dictionary<string, string>() {
|
||||||
private readonly Dictionary<string, string> _languageDictionary = new Dictionary<string, string>() {
|
|
||||||
{ "afrikaans", "af"},
|
{ "afrikaans", "af"},
|
||||||
{ "albanian", "sq"},
|
{ "albanian", "sq"},
|
||||||
{ "arabic", "ar"},
|
{ "arabic", "ar"},
|
||||||
@@ -365,8 +364,8 @@ namespace NadekoBot.Services
|
|||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
string text;
|
string text;
|
||||||
|
|
||||||
if (!_languageDictionary.ContainsKey(sourceLanguage) ||
|
if (!Languages.ContainsKey(sourceLanguage) ||
|
||||||
!_languageDictionary.ContainsKey(targetLanguage))
|
!Languages.ContainsKey(targetLanguage))
|
||||||
throw new ArgumentException(nameof(sourceLanguage) + "/" + nameof(targetLanguage));
|
throw new ArgumentException(nameof(sourceLanguage) + "/" + nameof(targetLanguage));
|
||||||
|
|
||||||
|
|
||||||
@@ -385,7 +384,7 @@ namespace NadekoBot.Services
|
|||||||
|
|
||||||
private string ConvertToLanguageCode(string language)
|
private string ConvertToLanguageCode(string language)
|
||||||
{
|
{
|
||||||
_languageDictionary.TryGetValue(language, out var mode);
|
Languages.TryGetValue(language, out var mode);
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -36,7 +36,6 @@ namespace NadekoBot.Services
|
|||||||
Dice,
|
Dice,
|
||||||
SlotBg,
|
SlotBg,
|
||||||
SlotEmojis,
|
SlotEmojis,
|
||||||
SlotNumbers,
|
|
||||||
Currency,
|
Currency,
|
||||||
RategirlMatrix,
|
RategirlMatrix,
|
||||||
RategirlDot,
|
RategirlDot,
|
||||||
@@ -57,9 +56,6 @@ namespace NadekoBot.Services
|
|||||||
public IReadOnlyList<byte[]> SlotEmojis
|
public IReadOnlyList<byte[]> SlotEmojis
|
||||||
=> GetByteArrayData(ImageKeys.SlotEmojis);
|
=> GetByteArrayData(ImageKeys.SlotEmojis);
|
||||||
|
|
||||||
public IReadOnlyList<byte[]> SlotNumbers
|
|
||||||
=> GetByteArrayData(ImageKeys.SlotNumbers);
|
|
||||||
|
|
||||||
public IReadOnlyList<byte[]> Currency
|
public IReadOnlyList<byte[]> Currency
|
||||||
=> GetByteArrayData(ImageKeys.Currency);
|
=> GetByteArrayData(ImageKeys.Currency);
|
||||||
|
|
||||||
@@ -157,20 +153,7 @@ namespace NadekoBot.Services
|
|||||||
"https://cdn.nadeko.bot/slots/3.png",
|
"https://cdn.nadeko.bot/slots/3.png",
|
||||||
"https://cdn.nadeko.bot/slots/4.png",
|
"https://cdn.nadeko.bot/slots/4.png",
|
||||||
"https://cdn.nadeko.bot/slots/5.png"
|
"https://cdn.nadeko.bot/slots/5.png"
|
||||||
}.Map(x => new Uri(x)),
|
}.Map(x => new Uri(x))
|
||||||
Numbers = new[]
|
|
||||||
{
|
|
||||||
"https://cdn.nadeko.bot/other/slots/numbers/0.png",
|
|
||||||
"https://cdn.nadeko.bot/other/slots/numbers/1.png",
|
|
||||||
"https://cdn.nadeko.bot/other/slots/numbers/2.png",
|
|
||||||
"https://cdn.nadeko.bot/other/slots/numbers/3.png",
|
|
||||||
"https://cdn.nadeko.bot/other/slots/numbers/4.png",
|
|
||||||
"https://cdn.nadeko.bot/other/slots/numbers/5.png",
|
|
||||||
"https://cdn.nadeko.bot/other/slots/numbers/6.png",
|
|
||||||
"https://cdn.nadeko.bot/other/slots/numbers/7.png",
|
|
||||||
"https://cdn.nadeko.bot/other/slots/numbers/8.png",
|
|
||||||
"https://cdn.nadeko.bot/other/slots/numbers/9.png"
|
|
||||||
}.Map(x => new Uri(x)),
|
|
||||||
},
|
},
|
||||||
Xp = new ImageUrls.XpData()
|
Xp = new ImageUrls.XpData()
|
||||||
{
|
{
|
||||||
@@ -183,6 +166,14 @@ namespace NadekoBot.Services
|
|||||||
File.WriteAllText(_imagesPath, Yaml.Serializer.Serialize(newData));
|
File.WriteAllText(_imagesPath, Yaml.Serializer.Serialize(newData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// removed numbers from slots
|
||||||
|
var localImageUrls = Yaml.Deserializer.Deserialize<ImageUrls>(File.ReadAllText(_imagesPath));
|
||||||
|
if (localImageUrls.Version == 2)
|
||||||
|
{
|
||||||
|
localImageUrls.Version = 3;
|
||||||
|
File.WriteAllText(_imagesPath, Yaml.Serializer.Serialize(localImageUrls));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Reload()
|
public async Task Reload()
|
||||||
@@ -207,9 +198,6 @@ namespace NadekoBot.Services
|
|||||||
case ImageKeys.SlotEmojis:
|
case ImageKeys.SlotEmojis:
|
||||||
await Load(key, ImageUrls.Slots.Emojis);
|
await Load(key, ImageUrls.Slots.Emojis);
|
||||||
break;
|
break;
|
||||||
case ImageKeys.SlotNumbers:
|
|
||||||
await Load(key, ImageUrls.Slots.Numbers);
|
|
||||||
break;
|
|
||||||
case ImageKeys.Currency:
|
case ImageKeys.Currency:
|
||||||
await Load(key, ImageUrls.Currency);
|
await Load(key, ImageUrls.Currency);
|
||||||
break;
|
break;
|
||||||
|
@@ -39,11 +39,11 @@ namespace NadekoBot.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Die()
|
public void Die(bool graceful)
|
||||||
{
|
{
|
||||||
_coordClient.Die(new DieRequest()
|
_coordClient.Die(new DieRequest()
|
||||||
{
|
{
|
||||||
Graceful = false
|
Graceful = graceful
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +79,11 @@ namespace NadekoBot.Services
|
|||||||
return res.Statuses.Sum(x => x.GuildCount);
|
return res.Statuses.Sum(x => x.GuildCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Reload()
|
||||||
|
{
|
||||||
|
await _coordClient.ReloadAsync(new());
|
||||||
|
}
|
||||||
|
|
||||||
public Task OnReadyAsync()
|
public Task OnReadyAsync()
|
||||||
{
|
{
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
|
@@ -36,7 +36,7 @@ namespace NadekoBot.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Die()
|
public void Die(bool graceful = false)
|
||||||
{
|
{
|
||||||
Environment.Exit(5);
|
Environment.Exit(5);
|
||||||
}
|
}
|
||||||
@@ -64,5 +64,10 @@ namespace NadekoBot.Services
|
|||||||
{
|
{
|
||||||
return _client.Guilds.Count;
|
return _client.Guilds.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task Reload()
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -20,7 +20,7 @@ namespace NadekoBot.Services
|
|||||||
private readonly IBotCredentials _creds;
|
private readonly IBotCredentials _creds;
|
||||||
private readonly DateTime _started;
|
private readonly DateTime _started;
|
||||||
|
|
||||||
public const string BotVersion = "3.0.8";
|
public const string BotVersion = "3.0.11";
|
||||||
public string Author => "Kwoth#2452";
|
public string Author => "Kwoth#2452";
|
||||||
public string Library => "Discord.Net";
|
public string Library => "Discord.Net";
|
||||||
public double MessagesPerSecond => MessageCounter / GetUptime().TotalSeconds;
|
public double MessagesPerSecond => MessageCounter / GetUptime().TotalSeconds;
|
||||||
|
@@ -22,10 +22,12 @@ using System.Linq;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NadekoBot.Common.Attributes;
|
using NadekoBot.Common.Attributes;
|
||||||
using Color = Discord.Color;
|
using Color = Discord.Color;
|
||||||
|
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||||
|
|
||||||
namespace NadekoBot.Extensions
|
namespace NadekoBot.Extensions
|
||||||
{
|
{
|
||||||
@@ -286,8 +288,8 @@ namespace NadekoBot.Extensions
|
|||||||
public static async Task<IEnumerable<IGuildUser>> GetMembersAsync(this IRole role) =>
|
public static async Task<IEnumerable<IGuildUser>> GetMembersAsync(this IRole role) =>
|
||||||
(await role.Guild.GetUsersAsync(CacheMode.CacheOnly).ConfigureAwait(false)).Where(u => u.RoleIds.Contains(role.Id)) ?? Enumerable.Empty<IGuildUser>();
|
(await role.Guild.GetUsersAsync(CacheMode.CacheOnly).ConfigureAwait(false)).Where(u => u.RoleIds.Contains(role.Id)) ?? Enumerable.Empty<IGuildUser>();
|
||||||
|
|
||||||
public static string ToJson<T>(this T any, Formatting formatting = Formatting.Indented) =>
|
public static string ToJson<T>(this T any, JsonSerializerOptions options = null) =>
|
||||||
JsonConvert.SerializeObject(any, formatting);
|
JsonSerializer.Serialize(any, options);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds fallback fonts to <see cref="TextOptions"/>
|
/// Adds fallback fonts to <see cref="TextOptions"/>
|
||||||
|
@@ -1263,3 +1263,13 @@ imageonlychannel:
|
|||||||
- imageonlychannel
|
- imageonlychannel
|
||||||
- imageonly
|
- imageonly
|
||||||
- imagesonly
|
- imagesonly
|
||||||
|
coordreload:
|
||||||
|
- coordreload
|
||||||
|
quotesexport:
|
||||||
|
- quotesexport
|
||||||
|
- qexport
|
||||||
|
quotesimport:
|
||||||
|
- quotesimport
|
||||||
|
- qimport
|
||||||
|
showembed:
|
||||||
|
- showembed
|
@@ -1,5 +1,5 @@
|
|||||||
# DO NOT CHANGE
|
# DO NOT CHANGE
|
||||||
version: 2
|
version: 4
|
||||||
# Currency settings
|
# Currency settings
|
||||||
currency:
|
currency:
|
||||||
# What is the emoji/character which represents the currency
|
# What is the emoji/character which represents the currency
|
||||||
@@ -240,3 +240,7 @@ patreonCurrencyPerCent: 1
|
|||||||
# Currency reward per vote.
|
# Currency reward per vote.
|
||||||
# This will work only if you've set up VotesApi and correct credentials for topgg and/or discords voting
|
# This will work only if you've set up VotesApi and correct credentials for topgg and/or discords voting
|
||||||
voteReward: 100
|
voteReward: 100
|
||||||
|
# Slot config
|
||||||
|
slots:
|
||||||
|
# Hex value of the color which the numbers on the slot image will have.
|
||||||
|
currencyFontColor: ff0000
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# DO NOT CHANGE
|
# DO NOT CHANGE
|
||||||
version: 2
|
version: 3
|
||||||
coins:
|
coins:
|
||||||
heads:
|
heads:
|
||||||
- https://cdn.nadeko.bot/coins/heads3.png
|
- https://cdn.nadeko.bot/coins/heads3.png
|
||||||
@@ -36,15 +36,4 @@ slots:
|
|||||||
- https://cdn.nadeko.bot/slots/3.png
|
- https://cdn.nadeko.bot/slots/3.png
|
||||||
- https://cdn.nadeko.bot/slots/4.png
|
- https://cdn.nadeko.bot/slots/4.png
|
||||||
- https://cdn.nadeko.bot/slots/5.png
|
- https://cdn.nadeko.bot/slots/5.png
|
||||||
numbers:
|
|
||||||
- https://cdn.nadeko.bot/other/slots/numbers/0.png
|
|
||||||
- https://cdn.nadeko.bot/other/slots/numbers/1.png
|
|
||||||
- https://cdn.nadeko.bot/other/slots/numbers/2.png
|
|
||||||
- https://cdn.nadeko.bot/other/slots/numbers/3.png
|
|
||||||
- https://cdn.nadeko.bot/other/slots/numbers/4.png
|
|
||||||
- https://cdn.nadeko.bot/other/slots/numbers/5.png
|
|
||||||
- https://cdn.nadeko.bot/other/slots/numbers/6.png
|
|
||||||
- https://cdn.nadeko.bot/other/slots/numbers/7.png
|
|
||||||
- https://cdn.nadeko.bot/other/slots/numbers/8.png
|
|
||||||
- https://cdn.nadeko.bot/other/slots/numbers/9.png
|
|
||||||
bg: https://cdn.nadeko.bot/slots/slots_bg.png
|
bg: https://cdn.nadeko.bot/slots/slots_bg.png
|
||||||
|
@@ -406,13 +406,15 @@ remind:
|
|||||||
- "me 1d5h Do something"
|
- "me 1d5h Do something"
|
||||||
- "#general 1m Start now!"
|
- "#general 1m Start now!"
|
||||||
reminddelete:
|
reminddelete:
|
||||||
desc: "Deletes a reminder on the specified index."
|
desc: "Deletes a reminder on the specified index. You can specify 'server' option if you're an Administrator, and you want to delete a reminder on this server created by someone else. "
|
||||||
args:
|
args:
|
||||||
- "3"
|
- "3"
|
||||||
|
- "server 2"
|
||||||
remindlist:
|
remindlist:
|
||||||
desc: "Lists all reminders you created. Paginated."
|
desc: "Lists all reminders you created. You can specify 'server' option if you're an Administrator to list all reminders created on this server. Paginated."
|
||||||
args:
|
args:
|
||||||
- "1"
|
- "1"
|
||||||
|
- "server 2"
|
||||||
serverinfo:
|
serverinfo:
|
||||||
desc: "Shows info about the server the bot is on. If no server is supplied, it defaults to current one."
|
desc: "Shows info about the server the bot is on. If no server is supplied, it defaults to current one."
|
||||||
args:
|
args:
|
||||||
@@ -609,7 +611,7 @@ quoteid:
|
|||||||
args:
|
args:
|
||||||
- "123456"
|
- "123456"
|
||||||
quotedelete:
|
quotedelete:
|
||||||
desc: "Deletes a quote with the specified ID. You have to be either server Administrator or the creator of the quote to delete it."
|
desc: "Deletes a quote with the specified ID. You have to either have the Manage Messages permission or be the creator of the quote to delete it."
|
||||||
args:
|
args:
|
||||||
- "123456"
|
- "123456"
|
||||||
draw:
|
draw:
|
||||||
@@ -1200,6 +1202,7 @@ emojiadd:
|
|||||||
Adds the specified emoji to this server.
|
Adds the specified emoji to this server.
|
||||||
You can specify a name before the emoji to add it under a different name.
|
You can specify a name before the emoji to add it under a different name.
|
||||||
You can specify a name followed by an image link to add a new emoji from an image.
|
You can specify a name followed by an image link to add a new emoji from an image.
|
||||||
|
You can omit imageUrl and instead upload the image as an attachment.
|
||||||
Image size has to be below 256KB.
|
Image size has to be below 256KB.
|
||||||
args:
|
args:
|
||||||
- ":someonesCustomEmoji:"
|
- ":someonesCustomEmoji:"
|
||||||
@@ -1303,7 +1306,7 @@ poll:
|
|||||||
autotranslang:
|
autotranslang:
|
||||||
desc: "Sets your source and target language to be used with `{0}at`. Specify no parameters to remove previously set value."
|
desc: "Sets your source and target language to be used with `{0}at`. Specify no parameters to remove previously set value."
|
||||||
args:
|
args:
|
||||||
- "en>fr"
|
- "en fr"
|
||||||
autotranslate:
|
autotranslate:
|
||||||
desc: "Starts automatic translation of all messages by users who set their `{0}atl` in this channel. You can set \"del\" parameter to automatically delete all translated user messages."
|
desc: "Starts automatic translation of all messages by users who set their `{0}atl` in this channel. You can set \"del\" parameter to automatically delete all translated user messages."
|
||||||
args:
|
args:
|
||||||
@@ -1575,6 +1578,14 @@ crsexport:
|
|||||||
desc: "Exports custom reactions from the current server (or global custom reactions in DMs) into a .yml file"
|
desc: "Exports custom reactions from the current server (or global custom reactions in DMs) into a .yml file"
|
||||||
args:
|
args:
|
||||||
- ""
|
- ""
|
||||||
|
quotesimport:
|
||||||
|
desc: "Upload the file or send the raw .yml data with this command to import all quotes from the specified string or file into the current server."
|
||||||
|
args:
|
||||||
|
- "<upload .yml file>"
|
||||||
|
quotesexport:
|
||||||
|
desc: "Exports quotes from the current server into a .yml file"
|
||||||
|
args:
|
||||||
|
- ""
|
||||||
aliaslist:
|
aliaslist:
|
||||||
desc: "Shows the list of currently set aliases. Paginated."
|
desc: "Shows the list of currently set aliases. Paginated."
|
||||||
args:
|
args:
|
||||||
@@ -1595,9 +1606,12 @@ warnlogall:
|
|||||||
- ""
|
- ""
|
||||||
- "2"
|
- "2"
|
||||||
warn:
|
warn:
|
||||||
desc: "Warns a user."
|
desc: |-
|
||||||
|
Warns a user with an optional reason.
|
||||||
|
You can specify a warning weight integer before the user. For example, 3 would mean that this warning counts as 3 warnings.
|
||||||
args:
|
args:
|
||||||
- "@Someone Very rude person"
|
- "@Someone Very rude person"
|
||||||
|
- "3 @Someone Very rude person"
|
||||||
scadd:
|
scadd:
|
||||||
desc: "Adds a command to the list of commands which will be executed automatically in the current channel, in the order they were added in, by the bot when it startups up."
|
desc: "Adds a command to the list of commands which will be executed automatically in the current channel, in the order they were added in, by the bot when it startups up."
|
||||||
args:
|
args:
|
||||||
@@ -2062,7 +2076,7 @@ rollduel:
|
|||||||
- "50 @Someone"
|
- "50 @Someone"
|
||||||
- "@Challenger"
|
- "@Challenger"
|
||||||
reactionroles:
|
reactionroles:
|
||||||
desc: "Specify role names and server emojis with which they're represented, the bot will then add those emojis to the previous message in the channel, and users will be able to get the roles by clicking on the emoji. You can set 'excl' as the parameter before the reactions and roles to make them exclusive. You can have up to 5 of these enabled on one server at a time. Optionally you can specify target message if you don't want it to be the previous one."
|
desc: "Specify role names and server emojis with which they're represented, the bot will then add those emojis to the previous message in the channel, and users will be able to get the roles by clicking on the emoji. You can set 'excl' as the parameter before the reactions and roles to make them exclusive. You can have up to 10 of these enabled on one server at a time. Optionally you can specify target message if you don't want it to be the previous one."
|
||||||
args:
|
args:
|
||||||
- "Gamer :SomeServerEmoji: Streamer :Other: Watcher :Other2:"
|
- "Gamer :SomeServerEmoji: Streamer :Other: Watcher :Other2:"
|
||||||
- "excl Horde :Horde: Alliance :Alliance:"
|
- "excl Horde :Horde: Alliance :Alliance:"
|
||||||
@@ -2134,4 +2148,13 @@ imageonlychannel:
|
|||||||
Toggles whether the channel only allows images.
|
Toggles whether the channel only allows images.
|
||||||
Users who send more than a few non-image messages will be banned from using the channel.
|
Users who send more than a few non-image messages will be banned from using the channel.
|
||||||
args:
|
args:
|
||||||
- ""
|
- ""
|
||||||
|
coordreload:
|
||||||
|
desc: "Reloads coordinator config"
|
||||||
|
args:
|
||||||
|
- ""
|
||||||
|
showembed:
|
||||||
|
desc: "Prints the json equivalent of the embed of the message specified by its Id."
|
||||||
|
args:
|
||||||
|
- "820022733172121600"
|
||||||
|
- "#some-channel 820022733172121600"
|
@@ -593,8 +593,6 @@
|
|||||||
"log_all": "Alle Events werden nun in diesem Channel geloggt.",
|
"log_all": "Alle Events werden nun in diesem Channel geloggt.",
|
||||||
"log_disabled": "Protokollierung ausgeschaltet.",
|
"log_disabled": "Protokollierung ausgeschaltet.",
|
||||||
"log_events": "Protokoll Ereignisse die du Abonnieren kannst:",
|
"log_events": "Protokoll Ereignisse die du Abonnieren kannst:",
|
||||||
"log_ignore": "Protokollierung wird {0} ignorieren",
|
|
||||||
"log_not_ignore": "Protokollierung wird {0} nicht ignorieren",
|
|
||||||
"log_stop": "Protokollierung des {0} Ereignisses wurde gestoppt.",
|
"log_stop": "Protokollierung des {0} Ereignisses wurde gestoppt.",
|
||||||
"msg_not_found": "Nachricht wurde nicht gefunden.",
|
"msg_not_found": "Nachricht wurde nicht gefunden.",
|
||||||
"time_too_long": "Die spezifizierte Zeit ist zu lang.",
|
"time_too_long": "Die spezifizierte Zeit ist zu lang.",
|
||||||
@@ -953,5 +951,29 @@
|
|||||||
"empty_page": "Diese Seite ist leer.",
|
"empty_page": "Diese Seite ist leer.",
|
||||||
"pages": "Seiten",
|
"pages": "Seiten",
|
||||||
"favorites": "Favoriten",
|
"favorites": "Favoriten",
|
||||||
"tags": "Stichworte"
|
"tags": "Stichworte",
|
||||||
|
"invalid_emoji_link": "",
|
||||||
|
"emoji_add_error": "",
|
||||||
|
"emoji_added": "",
|
||||||
|
"boost_on": "",
|
||||||
|
"boost_off": "",
|
||||||
|
"boostmsg_cur": "",
|
||||||
|
"boostmsg_enable": "",
|
||||||
|
"boostmsg_new": "",
|
||||||
|
"boostdel_off": "",
|
||||||
|
"boostdel_on": "",
|
||||||
|
"log_ignored_channels": "",
|
||||||
|
"log_ignored_users": "",
|
||||||
|
"log_ignore_user": "",
|
||||||
|
"log_not_ignore_user": "",
|
||||||
|
"log_ignore_chan": "",
|
||||||
|
"log_not_ignore_chan": "",
|
||||||
|
"streams_cleared": "",
|
||||||
|
"warn_weight": "",
|
||||||
|
"warn_count": "",
|
||||||
|
"mass_ban_in_progress": "",
|
||||||
|
"mass_ban_completed": "",
|
||||||
|
"reminder_server_list": "",
|
||||||
|
"imageonly_enable": "",
|
||||||
|
"imageonly_disable": ""
|
||||||
}
|
}
|
@@ -450,6 +450,7 @@
|
|||||||
"atl_set": "Your auto-translate language has been set to {0}>{1}",
|
"atl_set": "Your auto-translate language has been set to {0}>{1}",
|
||||||
"atl_started": "Started automatic translation of messages on this channel.",
|
"atl_started": "Started automatic translation of messages on this channel.",
|
||||||
"atl_stopped": "Stopped automatic translation of messages on this channel.",
|
"atl_stopped": "Stopped automatic translation of messages on this channel.",
|
||||||
|
"atl_not_enabled": "Automatic translation is not enabled on this channel or you've provided an invalid language.",
|
||||||
"bad_input_format": "Bad input format, or something went wrong.",
|
"bad_input_format": "Bad input format, or something went wrong.",
|
||||||
"card_not_found": "Couldn't find that card.",
|
"card_not_found": "Couldn't find that card.",
|
||||||
"catfact": "fact",
|
"catfact": "fact",
|
||||||
@@ -555,7 +556,7 @@
|
|||||||
"error": "Error",
|
"error": "Error",
|
||||||
"features": "Features",
|
"features": "Features",
|
||||||
"index_out_of_range": "Index out of range.",
|
"index_out_of_range": "Index out of range.",
|
||||||
"inrole_list": "List of users in {0} role",
|
"inrole_list": "List of users in {0} role ({1})",
|
||||||
"joined_discord": "Joined Discord",
|
"joined_discord": "Joined Discord",
|
||||||
"joined_server": "Joined server",
|
"joined_server": "Joined server",
|
||||||
"listservers": "ID: {0}\nMembers: {1}\nOwner ID: {2}",
|
"listservers": "ID: {0}\nMembers: {1}\nOwner ID: {2}",
|
||||||
@@ -605,12 +606,13 @@
|
|||||||
"shard": "Shard",
|
"shard": "Shard",
|
||||||
"showemojis": "**Name:** {0} **Link:** {1}",
|
"showemojis": "**Name:** {0} **Link:** {1}",
|
||||||
"showemojis_none": "No special emojis found.",
|
"showemojis_none": "No special emojis found.",
|
||||||
"text_channels": "Text channels",
|
"channels": "Channels",
|
||||||
|
"text_channels": "Text: {0}",
|
||||||
|
"voice_channels": "Voice: {0}",
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
"userid": "{0} of the user {1} is {2}",
|
"userid": "{0} of the user {1} is {2}",
|
||||||
"roleid": "{0} of the role {1} is {2}",
|
"roleid": "{0} of the role {1} is {2}",
|
||||||
"users": "Users",
|
"users": "Users",
|
||||||
"voice_channels": "Voice channels",
|
|
||||||
"current_poll_results": "Current poll results",
|
"current_poll_results": "Current poll results",
|
||||||
"poll_already_running": "Poll is already running on this server.",
|
"poll_already_running": "Poll is already running on this server.",
|
||||||
"poll_created": "📃 {0} has created a poll",
|
"poll_created": "📃 {0} has created a poll",
|
||||||
@@ -665,6 +667,8 @@
|
|||||||
"warning_clear_fail": "Warning not cleared. Either the warning at that index doesn't exist, or it has already been cleared.",
|
"warning_clear_fail": "Warning not cleared. Either the warning at that index doesn't exist, or it has already been cleared.",
|
||||||
"warning_cleared": "Warning {0} has been cleared for {1}.",
|
"warning_cleared": "Warning {0} has been cleared for {1}.",
|
||||||
"warnings_none": "No warning on this page.",
|
"warnings_none": "No warning on this page.",
|
||||||
|
"warn_weight": "Weight: {0}",
|
||||||
|
"warn_count": "{0} current, {1} total",
|
||||||
"warnlog_for": "Warnlog for {0}",
|
"warnlog_for": "Warnlog for {0}",
|
||||||
"warnpl_none": "No punishments set.",
|
"warnpl_none": "No punishments set.",
|
||||||
"warn_expire_set_delete": "Warnings will be deleted after {0} days.",
|
"warn_expire_set_delete": "Warnings will be deleted after {0} days.",
|
||||||
@@ -680,7 +684,7 @@
|
|||||||
"clpa_obsolete": ":tada: **Patreon currency rewards are now automatic!** :tada:\nThis command is now obsolete.\nIf you did not receive your reward for this month's pledge, below are some of the reasons as to why that might be.",
|
"clpa_obsolete": ":tada: **Patreon currency rewards are now automatic!** :tada:\nThis command is now obsolete.\nIf you did not receive your reward for this month's pledge, below are some of the reasons as to why that might be.",
|
||||||
"clpa_fail_already": "Maybe you've already received your reward for this month. You can receive rewards only once a month unless you increase your pledge.\nYou can check it by using `.curtrs` command.",
|
"clpa_fail_already": "Maybe you've already received your reward for this month. You can receive rewards only once a month unless you increase your pledge.\nYou can check it by using `.curtrs` command.",
|
||||||
"clpa_fail_already_title": "Already rewarded",
|
"clpa_fail_already_title": "Already rewarded",
|
||||||
"clpa_fail_conn": "Your discord account might not be connected to Patreon. If you are unsure what that means, or don't know how to connect it - you have to go to [Patreon account settings page](https://patreon.com/settings/account) and click 'Connect to discord' button.",
|
"clpa_fail_conn": "Your discord account might not be connected to Patreon. If you are unsure what that means, or don't know how to connect it - you have to go to [Patreon account settings page](https://www.patreon.com/settings/apps) and click 'Connect to discord' button.",
|
||||||
"clpa_fail_conn_title": "Discord account not connected",
|
"clpa_fail_conn_title": "Discord account not connected",
|
||||||
"clpa_fail_sup": "In order to be eligible for the reward, you must support the project on patreon. You can use {0} command to get the link.",
|
"clpa_fail_sup": "In order to be eligible for the reward, you must support the project on patreon. You can use {0} command to get the link.",
|
||||||
"clpa_fail_sup_title": "Not supporting",
|
"clpa_fail_sup_title": "Not supporting",
|
||||||
@@ -917,6 +921,7 @@
|
|||||||
"reaction_role_removed": "Removed ReactionRole message #{0}",
|
"reaction_role_removed": "Removed ReactionRole message #{0}",
|
||||||
"reaction_roles_full": "You've reached the limit on ReactionRole messages. You have to delete some.",
|
"reaction_roles_full": "You've reached the limit on ReactionRole messages. You have to delete some.",
|
||||||
"reminder_list": "List of reminders",
|
"reminder_list": "List of reminders",
|
||||||
|
"reminder_server_list": "List of server reminders",
|
||||||
"reminder_deleted": "Reminder #{0} was deleted.",
|
"reminder_deleted": "Reminder #{0} was deleted.",
|
||||||
"reminder_not_exist": "Reminder at that index does not exist.",
|
"reminder_not_exist": "Reminder at that index does not exist.",
|
||||||
"reminders_none": "No reminder on this page.",
|
"reminders_none": "No reminder on this page.",
|
||||||
|
@@ -177,7 +177,7 @@
|
|||||||
"cleverbot_enabled": "Cleverbot activado en este canal.",
|
"cleverbot_enabled": "Cleverbot activado en este canal.",
|
||||||
"curgen_disabled": "La generación de moneda ha sido desactivada en este canal.",
|
"curgen_disabled": "La generación de moneda ha sido desactivada en este canal.",
|
||||||
"curgen_enabled": "La generación de moneda ha sido habilitada en este canal.",
|
"curgen_enabled": "La generación de moneda ha sido habilitada en este canal.",
|
||||||
"curgen_pl": "¡{0} {1} han aparecido!",
|
"curgen_pl": "¡{0} {1} ha aparecido!",
|
||||||
"curgen_sn": "¡Ha aparecido {0}!",
|
"curgen_sn": "¡Ha aparecido {0}!",
|
||||||
"game_started": "Juego iniciado",
|
"game_started": "Juego iniciado",
|
||||||
"hangman_game_started": "Juego del ahorcado iniciado",
|
"hangman_game_started": "Juego del ahorcado iniciado",
|
||||||
@@ -593,8 +593,6 @@
|
|||||||
"log_all": "Registrando todos los eventos en este canal.",
|
"log_all": "Registrando todos los eventos en este canal.",
|
||||||
"log_disabled": "Registros desactivados.",
|
"log_disabled": "Registros desactivados.",
|
||||||
"log_events": "Registrar los eventos que puedes seguir en:",
|
"log_events": "Registrar los eventos que puedes seguir en:",
|
||||||
"log_ignore": "Los registros ignorarán {0}",
|
|
||||||
"log_not_ignore": "Los registros no ignorarán {0}",
|
|
||||||
"log_stop": "Se detuvo el registro del evento {0}.",
|
"log_stop": "Se detuvo el registro del evento {0}.",
|
||||||
"msg_not_found": "Mensaje no encontrado.",
|
"msg_not_found": "Mensaje no encontrado.",
|
||||||
"time_too_long": "El tiempo especificado es mucho.",
|
"time_too_long": "El tiempo especificado es mucho.",
|
||||||
@@ -805,7 +803,7 @@
|
|||||||
"warn_expire_set_clear": "Las advertencias se reiniciarán cada {0} días.",
|
"warn_expire_set_clear": "Las advertencias se reiniciarán cada {0} días.",
|
||||||
"warn_expire_reset": "Las advertencias ya no expirarán.",
|
"warn_expire_reset": "Las advertencias ya no expirarán.",
|
||||||
"warn_punish_set_timed": "Aplicaré el castigo {0} por {2} a los usuarios con {1} advertencias.",
|
"warn_punish_set_timed": "Aplicaré el castigo {0} por {2} a los usuarios con {1} advertencias.",
|
||||||
"clpa_obsolete": ":tada: **¡Las recompensas de Patreon ahora son automáticas!** :tada:\nEste comando está obsoleto.\nSi no recibiste tu recompensa de este mes, abajo tienes alguna de las posibles razones.",
|
"clpa_obsolete": ":tada: **¡Las recompensas de Patreon ahora son automáticas!** :tada:\nEste comando está obsoleto.\nSi no recibiste tu recompensa de este mes, abajo tienes algunas de las posibles razones.",
|
||||||
"time_new": "Tiempo",
|
"time_new": "Tiempo",
|
||||||
"timezone_db_api_key": "Necesitas activar tu clave de API de TimezoneDB. Puedes hacerlo haciendo click en el link que recibiste en tu e-mail con tu clave de API.",
|
"timezone_db_api_key": "Necesitas activar tu clave de API de TimezoneDB. Puedes hacerlo haciendo click en el link que recibiste en tu e-mail con tu clave de API.",
|
||||||
"rolehoist_enabled": "El rol {0} ahora se muestra separado de los miembros en línea.",
|
"rolehoist_enabled": "El rol {0} ahora se muestra separado de los miembros en línea.",
|
||||||
@@ -934,7 +932,7 @@
|
|||||||
"module_page_empty": "No hay módulos en esta pagina",
|
"module_page_empty": "No hay módulos en esta pagina",
|
||||||
"module_description_help": "Obtén ayuda con los comandos, descripciones y ejemplos de uso.",
|
"module_description_help": "Obtén ayuda con los comandos, descripciones y ejemplos de uso.",
|
||||||
"module_description_gambling": "Apuesta a las tiradas de dados, al blackjack, a las tragamonedas, al lanzamiento de monedas y a otros\n",
|
"module_description_gambling": "Apuesta a las tiradas de dados, al blackjack, a las tragamonedas, al lanzamiento de monedas y a otros\n",
|
||||||
"module_description_games": "Juega a la trivia, al nunchi, al colgado, conecta4 y otros juegos.",
|
"module_description_games": "Juega al trivial, al nunchi, al colgado, conecta4 y a otros juegos.",
|
||||||
"module_description_nsfw": "Comandos NSFW",
|
"module_description_nsfw": "Comandos NSFW",
|
||||||
"module_description_music": "Reproduce música de YouTube, archivos locales, SoundCloud y radios.",
|
"module_description_music": "Reproduce música de YouTube, archivos locales, SoundCloud y radios.",
|
||||||
"module_description_utility": "Administra citas personalizadas, repeticiones de mensajes y revisa datos sobre el servidor",
|
"module_description_utility": "Administra citas personalizadas, repeticiones de mensajes y revisa datos sobre el servidor",
|
||||||
@@ -953,5 +951,29 @@
|
|||||||
"empty_page": "Esta página está vacía.",
|
"empty_page": "Esta página está vacía.",
|
||||||
"pages": "Páginas",
|
"pages": "Páginas",
|
||||||
"favorites": "Favoritos",
|
"favorites": "Favoritos",
|
||||||
"tags": "Etiquetas"
|
"tags": "Etiquetas",
|
||||||
|
"invalid_emoji_link": "",
|
||||||
|
"emoji_add_error": "",
|
||||||
|
"emoji_added": "",
|
||||||
|
"boost_on": "",
|
||||||
|
"boost_off": "",
|
||||||
|
"boostmsg_cur": "",
|
||||||
|
"boostmsg_enable": "",
|
||||||
|
"boostmsg_new": "",
|
||||||
|
"boostdel_off": "",
|
||||||
|
"boostdel_on": "",
|
||||||
|
"log_ignored_channels": "",
|
||||||
|
"log_ignored_users": "",
|
||||||
|
"log_ignore_user": "",
|
||||||
|
"log_not_ignore_user": "",
|
||||||
|
"log_ignore_chan": "",
|
||||||
|
"log_not_ignore_chan": "",
|
||||||
|
"streams_cleared": "",
|
||||||
|
"warn_weight": "",
|
||||||
|
"warn_count": "",
|
||||||
|
"mass_ban_in_progress": "",
|
||||||
|
"mass_ban_completed": "",
|
||||||
|
"reminder_server_list": "",
|
||||||
|
"imageonly_enable": "",
|
||||||
|
"imageonly_disable": ""
|
||||||
}
|
}
|
@@ -590,11 +590,9 @@
|
|||||||
"lang_set_fail": "Échec de la définition des paramètres régionaux. Revoyez l'aide de cette commande.",
|
"lang_set_fail": "Échec de la définition des paramètres régionaux. Revoyez l'aide de cette commande.",
|
||||||
"lang_set_show": "La langue de ce serveur est défini sur {0} - {1}",
|
"lang_set_show": "La langue de ce serveur est défini sur {0} - {1}",
|
||||||
"log": "Journalisation des évènements de {0} dans ce salon.",
|
"log": "Journalisation des évènements de {0} dans ce salon.",
|
||||||
"log_all": "Journalisation de tout les évènements dans ce salon.",
|
"log_all": "Journalisation de tous les évènements dans ce salon.",
|
||||||
"log_disabled": "Journalisation désactivée.",
|
"log_disabled": "Journalisation désactivée.",
|
||||||
"log_events": "Evènements de journalisation auxquels vous pouvez vous abonner :",
|
"log_events": "Evènements de journalisation auxquels vous pouvez vous abonner :",
|
||||||
"log_ignore": "La journalisation ignorera {0}",
|
|
||||||
"log_not_ignore": "La journalisation n'ignorera pas {0}",
|
|
||||||
"log_stop": "La journalisation de l'évènement {0} arrêtée.",
|
"log_stop": "La journalisation de l'évènement {0} arrêtée.",
|
||||||
"msg_not_found": "Message introuvable.",
|
"msg_not_found": "Message introuvable.",
|
||||||
"time_too_long": "Le temps spécifié est trop long.",
|
"time_too_long": "Le temps spécifié est trop long.",
|
||||||
@@ -953,5 +951,29 @@
|
|||||||
"empty_page": "Cette page est vide.",
|
"empty_page": "Cette page est vide.",
|
||||||
"pages": "Pages",
|
"pages": "Pages",
|
||||||
"favorites": "Favoris",
|
"favorites": "Favoris",
|
||||||
"tags": "Tags"
|
"tags": "Tags",
|
||||||
|
"invalid_emoji_link": "Le lien spécifié n'est pas une image ou excède 256KB.",
|
||||||
|
"emoji_add_error": "Erreur lors de l'ajout d'emoji. Soit vous n'avez plus d'emplacements pour emoji, soit la taille de l'image est inadéquate.",
|
||||||
|
"emoji_added": "Nouveau emoji ajouté: {0}",
|
||||||
|
"boost_on": "Annonces Boost activées dans ce salon.",
|
||||||
|
"boost_off": "Annonces Boost désactivées.",
|
||||||
|
"boostmsg_cur": "Message Boost actuel: {0}",
|
||||||
|
"boostmsg_enable": "Activez les messages Boost en tapant {0}",
|
||||||
|
"boostmsg_new": "Nouveau message Boost défini.",
|
||||||
|
"boostdel_off": "La suppression automatique des messages Boost a été désactivée.",
|
||||||
|
"boostdel_on": "Les messages Boost seront supprimés après {0} secondes.",
|
||||||
|
"log_ignored_channels": "Salons ignorés",
|
||||||
|
"log_ignored_users": "Utilisateurs ignorés",
|
||||||
|
"log_ignore_user": "La journalisation ignorera le membre {0}",
|
||||||
|
"log_not_ignore_user": "La journalisation n'ignorera plus le membre {0}",
|
||||||
|
"log_ignore_chan": "La journalisation ignorera le salon {0}",
|
||||||
|
"log_not_ignore_chan": "La journalisation n'ignorera plus le salon {0}",
|
||||||
|
"streams_cleared": "Tous les streams suivis sur ce server ont été supprimés.",
|
||||||
|
"warn_weight": "Poids: {0}",
|
||||||
|
"warn_count": "{0} actuel(s), {1} total",
|
||||||
|
"mass_ban_in_progress": "En train de bannir {0} membres...",
|
||||||
|
"mass_ban_completed": "{0} membres bannis.",
|
||||||
|
"reminder_server_list": "Liste des rappels du server.",
|
||||||
|
"imageonly_enable": "Ce salon est maintenant exclusivement pour les images.",
|
||||||
|
"imageonly_disable": "Ce salon n'est plus exclusivement pour les images."
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -593,8 +593,6 @@
|
|||||||
"log_all": "Registrando todos os eventos nesse canal.",
|
"log_all": "Registrando todos os eventos nesse canal.",
|
||||||
"log_disabled": "Registros desabilitado.",
|
"log_disabled": "Registros desabilitado.",
|
||||||
"log_events": "Eventos de registro que você pode assinar:",
|
"log_events": "Eventos de registro que você pode assinar:",
|
||||||
"log_ignore": "Registros vai ignorar {0}",
|
|
||||||
"log_not_ignore": "Registros não irá ignorar {0}",
|
|
||||||
"log_stop": "Parando de registrar evento {0}.",
|
"log_stop": "Parando de registrar evento {0}.",
|
||||||
"msg_not_found": "Mensagem não encontrada.",
|
"msg_not_found": "Mensagem não encontrada.",
|
||||||
"time_too_long": "O tempo que você especificou é muito longo.",
|
"time_too_long": "O tempo que você especificou é muito longo.",
|
||||||
@@ -847,7 +845,7 @@
|
|||||||
"stopped": "Repost encerrado.",
|
"stopped": "Repost encerrado.",
|
||||||
"restart_fail": "Você precisa configurar o RestartCommand no arquivo creds.yml",
|
"restart_fail": "Você precisa configurar o RestartCommand no arquivo creds.yml",
|
||||||
"restarting": "Reiniciando.",
|
"restarting": "Reiniciando.",
|
||||||
"edit_fail": "Não existe reação personalizada com essa ID.",
|
"edit_fail": "Não existe reação personalizada com esse ID.",
|
||||||
"streaming": "Transmitindo",
|
"streaming": "Transmitindo",
|
||||||
"rafflecur": "Rifa {0}",
|
"rafflecur": "Rifa {0}",
|
||||||
"rafflecur_joined": "O usuário {0} entrou no sorteio",
|
"rafflecur_joined": "O usuário {0} entrou no sorteio",
|
||||||
@@ -953,5 +951,29 @@
|
|||||||
"empty_page": "Essa página esta vazia.",
|
"empty_page": "Essa página esta vazia.",
|
||||||
"pages": "Páginas",
|
"pages": "Páginas",
|
||||||
"favorites": "Favoritos",
|
"favorites": "Favoritos",
|
||||||
"tags": "Etiquetas"
|
"tags": "Etiquetas",
|
||||||
|
"invalid_emoji_link": "O link especificado não retorna para uma imagem ou excede 256KB.",
|
||||||
|
"emoji_add_error": "Erro ao adicionar emoji. O servidor não possui mais slots de emoji disponíveis ou o tamanho da imagem é incompatível.",
|
||||||
|
"emoji_added": "Novo emoji adicionado: {0}",
|
||||||
|
"boost_on": "Os anúncios de impulsionamento foram ativados neste canal.",
|
||||||
|
"boost_off": "Os anúncios de impulsionamento foram desativados.",
|
||||||
|
"boostmsg_cur": "Mensagem atual de impulsionamento: {0}",
|
||||||
|
"boostmsg_enable": "Ative os anúncios de impulsionamento digitando {0}",
|
||||||
|
"boostmsg_new": "Nova mensagem de impulsionamento definida.",
|
||||||
|
"boostdel_off": "A deleção automática dos anúncios de impulsionamento foi desativada.",
|
||||||
|
"boostdel_on": "Anúncios de impulsionamento serão deletados após {0} segundos.",
|
||||||
|
"log_ignored_channels": "Canais ignorados",
|
||||||
|
"log_ignored_users": "Usuários ignorados",
|
||||||
|
"log_ignore_user": "O registro de logs passará a ignorar o usuário {0}",
|
||||||
|
"log_not_ignore_user": "O registro de logs não vai mais ignorar o usuário {0}",
|
||||||
|
"log_ignore_chan": "O registro de logs passará a ignorar o canal {0}",
|
||||||
|
"log_not_ignore_chan": "O registro de logs não vai mais ignorar o canal {0}",
|
||||||
|
"streams_cleared": "Todas as transmissões seguidas neste servidor foram removidas.",
|
||||||
|
"warn_weight": "Peso: {0}",
|
||||||
|
"warn_count": "{0} atual, {1} total",
|
||||||
|
"mass_ban_in_progress": "Banindo {0} usuários...",
|
||||||
|
"mass_ban_completed": "{0} usuários banidos.",
|
||||||
|
"reminder_server_list": "Lista de lembretes do servidor",
|
||||||
|
"imageonly_enable": "Agora este canal é exclusivo para imagens.",
|
||||||
|
"imageonly_disable": "Este canal não é mais exclusivo para imagens."
|
||||||
}
|
}
|
@@ -593,8 +593,6 @@
|
|||||||
"log_all": "Регистрация всех событий в этом канале.",
|
"log_all": "Регистрация всех событий в этом канале.",
|
||||||
"log_disabled": "Регистрация событий отключена.",
|
"log_disabled": "Регистрация событий отключена.",
|
||||||
"log_events": "Журнал событий, на которые вы можете подписаться:",
|
"log_events": "Журнал событий, на которые вы можете подписаться:",
|
||||||
"log_ignore": "Ведение журнала игнорирует {0}",
|
|
||||||
"log_not_ignore": "Ведение журнала не игнорирует {0}",
|
|
||||||
"log_stop": "Прекращено ведение журнала события {0}.",
|
"log_stop": "Прекращено ведение журнала события {0}.",
|
||||||
"msg_not_found": "Сообщение не найдено.",
|
"msg_not_found": "Сообщение не найдено.",
|
||||||
"time_too_long": "Вы указали слишком много времени.",
|
"time_too_long": "Вы указали слишком много времени.",
|
||||||
@@ -953,5 +951,29 @@
|
|||||||
"empty_page": "Эта страница пуста.",
|
"empty_page": "Эта страница пуста.",
|
||||||
"pages": "Страницы",
|
"pages": "Страницы",
|
||||||
"favorites": "Любимое",
|
"favorites": "Любимое",
|
||||||
"tags": "Теги"
|
"tags": "Теги",
|
||||||
|
"invalid_emoji_link": "",
|
||||||
|
"emoji_add_error": "",
|
||||||
|
"emoji_added": "",
|
||||||
|
"boost_on": "",
|
||||||
|
"boost_off": "",
|
||||||
|
"boostmsg_cur": "",
|
||||||
|
"boostmsg_enable": "",
|
||||||
|
"boostmsg_new": "",
|
||||||
|
"boostdel_off": "",
|
||||||
|
"boostdel_on": "",
|
||||||
|
"log_ignored_channels": "",
|
||||||
|
"log_ignored_users": "",
|
||||||
|
"log_ignore_user": "",
|
||||||
|
"log_not_ignore_user": "",
|
||||||
|
"log_ignore_chan": "",
|
||||||
|
"log_not_ignore_chan": "",
|
||||||
|
"streams_cleared": "",
|
||||||
|
"warn_weight": "",
|
||||||
|
"warn_count": "",
|
||||||
|
"mass_ban_in_progress": "",
|
||||||
|
"mass_ban_completed": "",
|
||||||
|
"reminder_server_list": "",
|
||||||
|
"imageonly_enable": "",
|
||||||
|
"imageonly_disable": ""
|
||||||
}
|
}
|
@@ -593,8 +593,6 @@
|
|||||||
"log_all": "Запис усіх подій на цьому каналі.",
|
"log_all": "Запис усіх подій на цьому каналі.",
|
||||||
"log_disabled": "Запис подій вимкнено.",
|
"log_disabled": "Запис подій вимкнено.",
|
||||||
"log_events": "Журнал подій, на які можна підписатися:",
|
"log_events": "Журнал подій, на які можна підписатися:",
|
||||||
"log_ignore": "Запис подій ігноруватиметься {0}",
|
|
||||||
"log_not_ignore": "Запис подій не ігноруватиметься {0}",
|
|
||||||
"log_stop": "Зупинено запис подій {0}.",
|
"log_stop": "Зупинено запис подій {0}.",
|
||||||
"msg_not_found": "Повідомлення не знайдено.",
|
"msg_not_found": "Повідомлення не знайдено.",
|
||||||
"time_too_long": "Вказаний Вами час надто довгий.",
|
"time_too_long": "Вказаний Вами час надто довгий.",
|
||||||
@@ -953,5 +951,29 @@
|
|||||||
"empty_page": "Ця сторінка пуста.",
|
"empty_page": "Ця сторінка пуста.",
|
||||||
"pages": "Сторінки",
|
"pages": "Сторінки",
|
||||||
"favorites": "Улюблене",
|
"favorites": "Улюблене",
|
||||||
"tags": "Теги"
|
"tags": "Теги",
|
||||||
|
"invalid_emoji_link": "",
|
||||||
|
"emoji_add_error": "",
|
||||||
|
"emoji_added": "",
|
||||||
|
"boost_on": "",
|
||||||
|
"boost_off": "",
|
||||||
|
"boostmsg_cur": "",
|
||||||
|
"boostmsg_enable": "",
|
||||||
|
"boostmsg_new": "",
|
||||||
|
"boostdel_off": "",
|
||||||
|
"boostdel_on": "",
|
||||||
|
"log_ignored_channels": "",
|
||||||
|
"log_ignored_users": "",
|
||||||
|
"log_ignore_user": "",
|
||||||
|
"log_not_ignore_user": "",
|
||||||
|
"log_ignore_chan": "",
|
||||||
|
"log_not_ignore_chan": "",
|
||||||
|
"streams_cleared": "",
|
||||||
|
"warn_weight": "",
|
||||||
|
"warn_count": "",
|
||||||
|
"mass_ban_in_progress": "",
|
||||||
|
"mass_ban_completed": "",
|
||||||
|
"reminder_server_list": "",
|
||||||
|
"imageonly_enable": "",
|
||||||
|
"imageonly_disable": ""
|
||||||
}
|
}
|
Reference in New Issue
Block a user