mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28: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
|
||||
|
||||
-
|
||||
|
||||
## [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
|
||||
|
||||
### Changed
|
||||
- `.ea` will now use an image attachments if you omit imageUrl
|
||||
|
||||
### Added
|
||||
- Added `.emojiadd` with 3 overloads
|
||||
- `.ea :customEmoji:` which copies another server's emoji
|
||||
|
@@ -2,14 +2,33 @@
|
||||
|
||||
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*
|
||||
- Copy and paste this command, then press Enter:
|
||||
- `/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`
|
||||
- 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
|
||||
|
||||
1. Download and run the **new** installer script `cd ~ && wget -N https://gitlab.com/Kwoth/nadeko-bash-installer/-/raw/master/linuxAIO.sh && bash linuxAIO.sh`
|
||||
|
@@ -9,9 +9,9 @@
|
||||
</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.Sinks.Console" Version="4.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||
<PackageReference Include="YamlDotNet" Version="11.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -171,7 +171,7 @@ namespace NadekoBot.Coordinator
|
||||
}
|
||||
}
|
||||
|
||||
status.Process?.Dispose();
|
||||
try { status.Process?.Dispose(); } catch { }
|
||||
|
||||
var proc = StartShardProcess(shardId);
|
||||
_shardStatuses[shardId] = status with
|
||||
|
@@ -8,7 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -19,6 +19,7 @@ using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Common.Configs;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using NadekoBot.Modules.Searches;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot
|
||||
@@ -105,7 +106,7 @@ namespace NadekoBot
|
||||
.AddSingleton<ISeria, JsonSeria>()
|
||||
.AddSingleton<IPubSub, RedisPubSub>()
|
||||
.AddSingleton<IConfigSeria, YamlSeria>()
|
||||
.AddBotStringsServices()
|
||||
.AddBotStringsServices(_creds.TotalShards)
|
||||
.AddConfigServices()
|
||||
.AddConfigMigrators()
|
||||
.AddMemoryCache()
|
||||
|
@@ -14,10 +14,14 @@ namespace NadekoBot.Extensions
|
||||
{
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddBotStringsServices(this IServiceCollection services)
|
||||
=> services
|
||||
public static IServiceCollection AddBotStringsServices(this IServiceCollection services, int totalShards)
|
||||
=> totalShards <= 1
|
||||
? services
|
||||
.AddSingleton<IStringsSource, LocalFileStringsSource>()
|
||||
.AddSingleton<IBotStringsProvider, LocalBotStringsProvider>()
|
||||
.AddSingleton<IBotStrings, BotStrings>()
|
||||
: services.AddSingleton<IStringsSource, LocalFileStringsSource>()
|
||||
.AddSingleton<IBotStringsProvider, RedisBotStringsProvider>()
|
||||
.AddSingleton<IBotStrings, BotStrings>();
|
||||
|
||||
public static IServiceCollection AddConfigServices(this IServiceCollection services)
|
||||
|
@@ -6,7 +6,7 @@ namespace NadekoBot.Common
|
||||
public class ImageUrls
|
||||
{
|
||||
[Comment("DO NOT CHANGE")]
|
||||
public int Version { get; set; } = 2;
|
||||
public int Version { get; set; } = 3;
|
||||
|
||||
public CoinData Coins { get; set; }
|
||||
public Uri[] Currency { get; set; }
|
||||
@@ -27,7 +27,6 @@ namespace NadekoBot.Common
|
||||
public class SlotData
|
||||
{
|
||||
public Uri[] Emojis { get; set; }
|
||||
public Uri[] Numbers { get; set; }
|
||||
public Uri Bg { get; set; }
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Discord;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
@@ -29,6 +30,47 @@ namespace NadekoBot
|
||||
(Footer != null && (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) ||
|
||||
(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()
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using NadekoBot.Db.Models;
|
||||
using System;
|
||||
using NadekoBot.Db.Models;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Discord;
|
||||
@@ -168,7 +169,7 @@ VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
|
||||
public static decimal GetTotalCurrency(this DbSet<DiscordUser> users)
|
||||
{
|
||||
return users
|
||||
.Sum(x => x.CurrencyAmount);
|
||||
.Sum((Func<DiscordUser, decimal>)(x => x.CurrencyAmount));
|
||||
}
|
||||
|
||||
public static decimal GetTopOnePercentCurrency(this DbSet<DiscordUser> users, ulong botId)
|
||||
|
@@ -9,6 +9,11 @@ namespace NadekoBot.Db
|
||||
{
|
||||
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)
|
||||
{
|
||||
var q = quotes.AsQueryable().Where(x => x.GuildId == guildId);
|
||||
|
@@ -18,5 +18,12 @@ namespace NadekoBot.Db
|
||||
.OrderBy(x => x.DateAdded)
|
||||
.Skip(page * 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 string ForgivenBy { 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<ImageOnlyChannel> ImageOnlyChannels { 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)
|
||||
{
|
||||
@@ -196,10 +198,16 @@ namespace NadekoBot.Services.Database
|
||||
#endregion
|
||||
|
||||
#region Warnings
|
||||
var warn = modelBuilder.Entity<Warning>();
|
||||
|
||||
modelBuilder.Entity<Warning>(warn =>
|
||||
{
|
||||
warn.HasIndex(x => x.GuildId);
|
||||
warn.HasIndex(x => x.UserId);
|
||||
warn.HasIndex(x => x.DateAdded);
|
||||
warn.Property(x => x.Weight)
|
||||
.HasDefaultValue(1);
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
#region PatreonRewards
|
||||
@@ -362,6 +370,21 @@ namespace NadekoBot.Services.Database
|
||||
modelBuilder.Entity<NsfwBlacklistedTag>(nbt => nbt
|
||||
.HasIndex(x => x.GuildId)
|
||||
.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");
|
||||
});
|
||||
|
||||
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 =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1967,6 +2023,11 @@ namespace NadekoBot.Migrations
|
||||
b.Property<ulong>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Weight")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(1);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DateAdded");
|
||||
@@ -2189,6 +2250,17 @@ namespace NadekoBot.Migrations
|
||||
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 =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", null)
|
||||
@@ -2536,6 +2608,11 @@ namespace NadekoBot.Migrations
|
||||
b.Navigation("IgnoredChannels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
|
||||
{
|
||||
b.Navigation("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
|
||||
{
|
||||
b.Navigation("AntiAltSetting");
|
||||
|
@@ -318,7 +318,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task Die()
|
||||
public async Task Die(bool graceful = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -329,7 +329,7 @@ namespace NadekoBot.Modules.Administration
|
||||
// ignored
|
||||
}
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
_coord.Die();
|
||||
_coord.Die(graceful);
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
@@ -507,6 +507,14 @@ namespace NadekoBot.Modules.Administration
|
||||
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)
|
||||
{
|
||||
switch (sus)
|
||||
|
@@ -41,8 +41,11 @@ namespace NadekoBot.Modules.Administration.Services
|
||||
}, 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();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(reason))
|
||||
@@ -57,6 +60,7 @@ namespace NadekoBot.Modules.Administration.Services
|
||||
Forgiven = false,
|
||||
Reason = reason,
|
||||
Moderator = modName,
|
||||
Weight = weight,
|
||||
};
|
||||
|
||||
int warnings = 1;
|
||||
@@ -70,7 +74,7 @@ namespace NadekoBot.Modules.Administration.Services
|
||||
.Warnings
|
||||
.ForId(guildId, userId)
|
||||
.Where(w => !w.Forgiven && w.UserId == userId)
|
||||
.Count();
|
||||
.Sum(x => x.Weight);
|
||||
|
||||
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,
|
||||
ulong? roleId, string reason)
|
||||
{
|
||||
|
||||
if (!await CheckPermission(guild, p))
|
||||
return;
|
||||
|
||||
switch (p)
|
||||
{
|
||||
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()
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
|
@@ -54,8 +54,17 @@ namespace NadekoBot.Modules.Administration
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[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))
|
||||
return;
|
||||
|
||||
@@ -76,7 +85,7 @@ namespace NadekoBot.Modules.Administration
|
||||
WarningPunishment punishment;
|
||||
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)
|
||||
{
|
||||
@@ -230,19 +239,29 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
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;
|
||||
foreach (var w in warnings)
|
||||
{
|
||||
i++;
|
||||
var name = GetText(strs.warned_on_by(
|
||||
w.DateAdded.Value.ToString("dd.MM.yyy"),
|
||||
w.DateAdded.Value.ToString("HH:mm"),
|
||||
w.DateAdded?.ToString("dd.MM.yyy"),
|
||||
w.DateAdded?.ToString("HH:mm"),
|
||||
w.Moderator));
|
||||
|
||||
if (w.Forgiven)
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using Cloneable;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common.Yml;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Common
|
||||
@@ -20,6 +21,7 @@ namespace NadekoBot.Modules.Gambling.Common
|
||||
Generation = new GenerationConfig();
|
||||
Timely = new TimelyConfig();
|
||||
Decay = new DecayConfig();
|
||||
Slots = new SlotsConfig();
|
||||
}
|
||||
|
||||
[Comment(@"DO NOT CHANGE")]
|
||||
@@ -64,6 +66,9 @@ Set 0 for unlimited")]
|
||||
[Comment(@"Currency reward per vote.
|
||||
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;
|
||||
|
||||
[Comment(@"Slot config")]
|
||||
public SlotsConfig Slots { get; set; }
|
||||
}
|
||||
|
||||
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 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]
|
||||
public sealed partial class WaifuItemModel
|
||||
{
|
||||
|
@@ -66,11 +66,11 @@ namespace NadekoBot.Modules.Gambling
|
||||
}
|
||||
var embed = _eb.Create()
|
||||
.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_planted), ((BigInteger)ec.Planted) + 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)
|
||||
.WithOkColor();
|
||||
// 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)]
|
||||
[OwnerOnly]
|
||||
[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);
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[OwnerOnly]
|
||||
[Priority(1)]
|
||||
public Task Award(ShmartNumber amount, [Leftover] IGuildUser usr) =>
|
||||
public Task Award(long amount, [Leftover] IGuildUser usr) =>
|
||||
Award(amount, usr.Id);
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[OwnerOnly]
|
||||
[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)
|
||||
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 ?? "")}",
|
||||
amount,
|
||||
gamble: (ctx.Client.CurrentUser.Id != usrId)).ConfigureAwait(false);
|
||||
@@ -275,8 +283,8 @@ namespace NadekoBot.Modules.Gambling
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[OwnerOnly]
|
||||
[Priority(2)]
|
||||
public async Task Award(ShmartNumber amount, [Leftover] IRole role)
|
||||
[Priority(3)]
|
||||
public async Task Award(long amount, [Leftover] IRole role)
|
||||
{
|
||||
var users = (await ctx.Guild.GetUsersAsync().ConfigureAwait(false))
|
||||
.Where(u => u.GetRoles().Contains(role))
|
||||
@@ -284,7 +292,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
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 => amount.Value),
|
||||
users.Select(x => amount),
|
||||
gamble: true)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -298,13 +306,13 @@ namespace NadekoBot.Modules.Gambling
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[OwnerOnly]
|
||||
[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();
|
||||
|
||||
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 => amount.Value),
|
||||
users.Select(x => amount),
|
||||
gamble: true)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -318,7 +326,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[OwnerOnly]
|
||||
[Priority(1)]
|
||||
public async Task Take(ShmartNumber amount, [Leftover] IGuildUser user)
|
||||
public async Task Take(long amount, [Leftover] IGuildUser user)
|
||||
{
|
||||
if (amount <= 0)
|
||||
return;
|
||||
@@ -333,7 +341,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task Take(ShmartNumber amount, [Leftover] ulong usrId)
|
||||
public async Task Take(long amount, [Leftover] ulong usrId)
|
||||
{
|
||||
if (amount <= 0)
|
||||
return;
|
||||
|
@@ -8,10 +8,11 @@ using NadekoBot.Modules.Gambling.Services;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
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]
|
||||
public class PlantPickCommands : GamblingSubmodule<PlantPickService>
|
||||
@@ -53,7 +54,7 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[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)
|
||||
return;
|
||||
@@ -63,18 +64,17 @@ namespace NadekoBot.Modules.Games
|
||||
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)
|
||||
{
|
||||
logService.AddDeleteIgnore(ctx.Message.Id);
|
||||
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]
|
@@ -68,9 +68,18 @@ namespace NadekoBot.Modules.Gambling.Services
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 3;
|
||||
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.Drawing.Processing;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Color = SixLabors.ImageSharp.Color;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
@@ -186,6 +187,8 @@ namespace NadekoBot.Modules.Gambling
|
||||
var numbers = new int[3];
|
||||
result.Rolls.CopyTo(numbers, 0);
|
||||
|
||||
Color fontColor = _config.Slots.CurrencyFontColor;
|
||||
|
||||
bgImage.Mutate(x => x.DrawText(new TextGraphicsOptions
|
||||
{
|
||||
TextOptions = new TextOptions()
|
||||
@@ -194,9 +197,11 @@ namespace NadekoBot.Modules.Gambling
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
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)));
|
||||
|
||||
var bottomFont = _fonts.DottyFont.CreateFont(50);
|
||||
|
||||
bgImage.Mutate(x => x.DrawText(new TextGraphicsOptions
|
||||
{
|
||||
TextOptions = new TextOptions()
|
||||
@@ -205,7 +210,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
WrapTextWidth = 135,
|
||||
}
|
||||
}, amount.ToString(), _fonts.DottyFont.CreateFont(50), SixLabors.ImageSharp.Color.Red,
|
||||
}, amount.ToString(), bottomFont, fontColor,
|
||||
new PointF(129, 472)));
|
||||
|
||||
bgImage.Mutate(x => x.DrawText(new TextGraphicsOptions
|
||||
@@ -216,7 +221,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
WrapTextWidth = 135,
|
||||
}
|
||||
}, ownedAmount.ToString(), _fonts.DottyFont.CreateFont(50), SixLabors.ImageSharp.Color.Red,
|
||||
}, ownedAmount.ToString(), bottomFont, fontColor,
|
||||
new PointF(325, 472)));
|
||||
//sw.PrintLap("drew red text");
|
||||
|
||||
|
@@ -763,7 +763,7 @@ namespace NadekoBot.Modules.Music
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.Administrator)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task MusicQuality()
|
||||
{
|
||||
var quality = await _service.GetMusicQualityAsync(ctx.Guild.Id);
|
||||
@@ -772,7 +772,7 @@ namespace NadekoBot.Modules.Music
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.Administrator)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task MusicQuality(QualityPreset preset)
|
||||
{
|
||||
await _service.SetMusicQualityAsync(ctx.Guild.Id, preset);
|
||||
|
@@ -24,7 +24,7 @@ namespace NadekoBot.Modules.Nsfw.Common
|
||||
return new();
|
||||
|
||||
return images
|
||||
.Where(img => !string.IsNullOrWhiteSpace(img.Directory) && !string.IsNullOrWhiteSpace(img.Image))
|
||||
.Where(img => !string.IsNullOrWhiteSpace(img.Image))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
public class Rule34Object : IImageData
|
||||
{
|
||||
public string Image { get; init; }
|
||||
public string Directory { get; init; }
|
||||
public int Directory { get; init; }
|
||||
public string Tags { 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);
|
||||
|
||||
if (nearest != null)
|
||||
if (nearest is not null)
|
||||
{
|
||||
var embed = _eb.Create()
|
||||
.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();
|
||||
var cryptos = await CryptoData().ConfigureAwait(false);
|
||||
|
||||
if (cryptos is null)
|
||||
return (null, null);
|
||||
|
||||
var crypto = cryptos
|
||||
?.FirstOrDefault(x => x.Id.ToUpperInvariant() == name || x.Name.ToUpperInvariant() == name
|
||||
|| x.Symbol.ToUpperInvariant() == name);
|
||||
@@ -42,7 +45,8 @@ namespace NadekoBot.Modules.Searches.Services
|
||||
(CryptoResponseData Elem, int Distance)? nearest = 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)
|
||||
.Where(x => x.Distance <= 2)
|
||||
.FirstOrDefault();
|
||||
@@ -68,19 +72,18 @@ namespace NadekoBot.Modules.Searches.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var _http = _httpFactory.CreateClient())
|
||||
{
|
||||
var strData = await _http.GetStringAsync(new Uri($"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?" +
|
||||
using var _http = _httpFactory.CreateClient();
|
||||
var strData = await _http.GetStringAsync(
|
||||
$"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?" +
|
||||
$"CMC_PRO_API_KEY={_creds.CoinmarketcapApiKey}" +
|
||||
$"&start=1" +
|
||||
$"&limit=500" +
|
||||
$"&convert=USD"));
|
||||
$"&limit=5000" +
|
||||
$"&convert=USD");
|
||||
|
||||
JsonConvert.DeserializeObject<CryptoResponse>(strData); // just to see if its' valid
|
||||
|
||||
return strData;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error getting crypto data: {Message}", ex.Message);
|
||||
|
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 Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Modules.Searches.Common;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -13,18 +9,14 @@ using SixLabors.ImageSharp.Drawing.Processing;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Modules.Administration;
|
||||
using Serilog;
|
||||
using HorizontalAlignment = SixLabors.Fonts.HorizontalAlignment;
|
||||
using Image = SixLabors.ImageSharp.Image;
|
||||
@@ -34,71 +26,31 @@ namespace NadekoBot.Modules.Searches.Services
|
||||
public class SearchesService : INService
|
||||
{
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IGoogleApiService _google;
|
||||
private readonly DbService _db;
|
||||
private readonly IImageCache _imgs;
|
||||
private readonly IDataCache _cache;
|
||||
private readonly FontProvider _fonts;
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly IEmbedBuilderService _eb;
|
||||
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<MagicItem> MagicItems { get; } = new List<MagicItem>();
|
||||
private readonly List<string> _yomamaJokes;
|
||||
|
||||
public SearchesService(DiscordSocketClient client, IGoogleApiService google,
|
||||
DbService db, Bot bot, IDataCache cache, IHttpClientFactory factory,
|
||||
FontProvider fonts, IBotCredentials creds, IEmbedBuilderService eb)
|
||||
public SearchesService(IGoogleApiService google,
|
||||
IDataCache cache,
|
||||
IHttpClientFactory factory,
|
||||
FontProvider fonts,
|
||||
IBotCredentials creds)
|
||||
{
|
||||
_httpFactory = factory;
|
||||
_client = client;
|
||||
_google = google;
|
||||
_db = db;
|
||||
_imgs = cache.LocalImages;
|
||||
_cache = cache;
|
||||
_fonts = fonts;
|
||||
_creds = creds;
|
||||
_eb = eb;
|
||||
_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
|
||||
if (File.Exists("data/wowjokes.json"))
|
||||
{
|
||||
@@ -340,19 +292,6 @@ namespace NadekoBot.Modules.Searches.Services
|
||||
_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 int yomamaJokeIndex = 0;
|
||||
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 NadekoBot.Extensions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Modules.Searches.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
public partial class Searches
|
||||
{
|
||||
[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]
|
||||
public async Task Translate(string langs, [Leftover] string text = null)
|
||||
public async Task Translate(string from, string to, [Leftover] string text = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ctx.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
var translation = await _searches.Translate(langs, text).ConfigureAwait(false);
|
||||
await SendConfirmAsync(GetText(strs.translation) + " " + langs, translation).ConfigureAwait(false);
|
||||
var translation = await _service.Translate(from, to, text).ConfigureAwait(false);
|
||||
|
||||
var embed = _eb.Create(ctx)
|
||||
.WithOkColor()
|
||||
.AddField(from, text, false)
|
||||
.AddField(to, translation, false);
|
||||
|
||||
await ctx.Channel.EmbedAsync(embed);
|
||||
}
|
||||
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
|
||||
{
|
||||
Del,
|
||||
@@ -68,56 +41,52 @@ namespace NadekoBot.Modules.Searches
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(ChannelPerm.ManageMessages)]
|
||||
[OwnerOnly]
|
||||
public async Task AutoTranslate(AutoDeleteAutoTranslate autoDelete = AutoDeleteAutoTranslate.Nodel)
|
||||
{
|
||||
var channel = (ITextChannel)ctx.Channel;
|
||||
|
||||
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))
|
||||
var toggle = await _service.ToggleAtl(ctx.Guild.Id, ctx.Channel.Id, autoDelete == AutoDeleteAutoTranslate.Del);
|
||||
if (toggle)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.atl_started).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.atl_stopped).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task AutoTransLang([Leftover] string langs = null)
|
||||
public async Task AutoTransLang()
|
||||
{
|
||||
var ucp = (ctx.User.Id, ctx.Channel.Id);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(langs))
|
||||
if (await _service.UnregisterUser(ctx.Channel.Id, ctx.User.Id))
|
||||
{
|
||||
if (_searches.UserLanguages.TryRemove(ucp, out langs))
|
||||
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;
|
||||
}
|
||||
|
||||
var langarr = langs.ToLowerInvariant().Split('>');
|
||||
if (langarr.Length != 2)
|
||||
return;
|
||||
var from = langarr[0];
|
||||
var to = langarr[1];
|
||||
|
||||
if (!_google.Languages.Contains(from) || !_google.Languages.Contains(to))
|
||||
if (succ is false)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.invalid_lang).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
_searches.UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
|
||||
|
||||
await ReplyConfirmLocalizedAsync(strs.atl_set(from, to));
|
||||
}
|
||||
|
||||
@@ -125,7 +94,7 @@ namespace NadekoBot.Modules.Searches
|
||||
[RequireContext(ContextType.Guild)]
|
||||
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;
|
||||
guildName = guildName?.ToUpperInvariant();
|
||||
SocketGuild guild;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(guildName))
|
||||
guild = (SocketGuild)channel.Guild;
|
||||
else
|
||||
guild = _client.Guilds.FirstOrDefault(g => g.Name.ToUpperInvariant() == guildName.ToUpperInvariant());
|
||||
|
||||
if (guild is null)
|
||||
return;
|
||||
var ownername = guild.GetUser(guild.OwnerId);
|
||||
var textchn = guild.TextChannels.Count();
|
||||
var voicechn = guild.VoiceChannels.Count();
|
||||
|
||||
var ownername = guild.GetUser(guild.OwnerId);
|
||||
var textchn = guild.TextChannels.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 features = string.Join("\n", guild.Features);
|
||||
var features = string.Join(", ", guild.Features);
|
||||
if (string.IsNullOrWhiteSpace(features))
|
||||
features = "-";
|
||||
|
||||
var embed = _eb.Create()
|
||||
.WithAuthor(GetText(strs.server_info))
|
||||
.WithTitle(guild.Name)
|
||||
.AddField(GetText(strs.id), guild.Id.ToString(), true)
|
||||
.AddField(GetText(strs.owner), ownername.ToString(), true)
|
||||
.AddField(GetText(strs.members), guild.MemberCount.ToString(), true)
|
||||
.AddField(GetText(strs.text_channels), textchn.ToString(), true)
|
||||
.AddField(GetText(strs.voice_channels), voicechn.ToString(), true)
|
||||
.AddField(GetText(strs.channels), channels, 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.features), features, true)
|
||||
.AddField(GetText(strs.features), features)
|
||||
.WithOkColor();
|
||||
|
||||
if (Uri.IsWellFormedUriString(guild.IconUrl, UriKind.Absolute))
|
||||
embed.WithThumbnailUrl(guild.IconUrl);
|
||||
|
||||
if (guild.Emotes.Any())
|
||||
{
|
||||
embed.AddField(GetText(strs.custom_emojis) + $"({guild.Emotes.Count})",
|
||||
|
@@ -7,10 +7,13 @@ using NadekoBot.Db.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Yml;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Db;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
@@ -20,10 +23,12 @@ namespace NadekoBot.Modules.Utility
|
||||
public class QuoteCommands : NadekoSubmodule
|
||||
{
|
||||
private readonly DbService _db;
|
||||
private readonly IHttpClientFactory _http;
|
||||
|
||||
public QuoteCommands(DbService db)
|
||||
public QuoteCommands(DbService db, IHttpClientFactory http)
|
||||
{
|
||||
_db = db;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
@@ -203,7 +208,7 @@ namespace NadekoBot.Modules.Utility
|
||||
[RequireContext(ContextType.Guild)]
|
||||
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;
|
||||
string response;
|
||||
@@ -211,7 +216,7 @@ namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -248,6 +253,140 @@ namespace NadekoBot.Modules.Utility
|
||||
|
||||
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,22 +80,51 @@ namespace NadekoBot.Modules.Utility
|
||||
}
|
||||
}
|
||||
|
||||
public enum Server
|
||||
{
|
||||
Server = int.MinValue,
|
||||
Srvr = int.MinValue,
|
||||
Serv = int.MinValue,
|
||||
S = int.MinValue,
|
||||
}
|
||||
|
||||
[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)
|
||||
return;
|
||||
|
||||
var embed = _eb.Create()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.reminder_list));
|
||||
.WithTitle(GetText(isServer ? strs.reminder_server_list : strs.reminder_list));
|
||||
|
||||
List<Reminder> rems;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
rems = uow.Reminders.RemindersFor(ctx.User.Id, page)
|
||||
if (isServer)
|
||||
{
|
||||
rems = uow.Reminders
|
||||
.RemindersForServer(ctx.Guild.Id, page)
|
||||
.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
rems = uow.Reminders
|
||||
.RemindersFor(ctx.User.Id, page)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
if (rems.Any())
|
||||
{
|
||||
@@ -121,18 +150,33 @@ namespace NadekoBot.Modules.Utility
|
||||
}
|
||||
|
||||
[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)
|
||||
return;
|
||||
|
||||
var embed = _eb.Create();
|
||||
|
||||
Reminder rem = null;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var rems = uow.Reminders.RemindersFor(ctx.User.Id, index / 10)
|
||||
var rems = isServer
|
||||
? uow.Reminders
|
||||
.RemindersForServer(ctx.Guild.Id, index / 10)
|
||||
.ToList()
|
||||
: uow.Reminders
|
||||
.RemindersFor(ctx.User.Id, index / 10)
|
||||
.ToList();
|
||||
|
||||
var pageIndex = index % 10;
|
||||
if (rems.Count > pageIndex)
|
||||
{
|
||||
|
@@ -227,6 +227,9 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
|
||||
private async Task RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
|
||||
{
|
||||
if (user.IsBot)
|
||||
return;
|
||||
|
||||
var g = (StreamingGame)user.Activities
|
||||
.FirstOrDefault(a => a is StreamingGame &&
|
||||
(string.IsNullOrWhiteSpace(setting.Keyword)
|
||||
@@ -240,7 +243,7 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
addRole = addRole ?? user.Guild.GetRole(setting.AddRoleId);
|
||||
addRole ??= user.Guild.GetRole(setting.AddRoleId);
|
||||
if (addRole is null)
|
||||
{
|
||||
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
|
||||
if (!user.RoleIds.Contains(setting.AddRoleId))
|
||||
if (!user.RoleIds.Contains(addRole.Id))
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@@ -11,11 +11,14 @@ using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Replacements;
|
||||
using NadekoBot.Services;
|
||||
using Serilog;
|
||||
using SystemTextJsonSamples;
|
||||
using JsonSerializer = Newtonsoft.Json.JsonSerializer;
|
||||
|
||||
namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
@@ -121,7 +124,7 @@ namespace NadekoBot.Modules.Utility
|
||||
return _eb.Create().WithOkColor().WithDescription(GetText(strs.no_user_on_this_page));
|
||||
|
||||
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));
|
||||
}, roleUsers.Length, 20).ConfigureAwait(false);
|
||||
}
|
||||
@@ -284,26 +287,34 @@ namespace NadekoBot.Modules.Utility
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireBotPermission(GuildPermission.ManageEmojis)]
|
||||
[RequireUserPermission(GuildPermission.ManageEmojis)]
|
||||
[Priority(0)]
|
||||
[BotPerm(GuildPerm.ManageEmojis)]
|
||||
[UserPerm(GuildPerm.ManageEmojis)]
|
||||
[Priority(2)]
|
||||
public Task EmojiAdd(string name, Emote emote)
|
||||
=> EmojiAdd(name, emote.Url);
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireBotPermission(GuildPermission.ManageEmojis)]
|
||||
[RequireUserPermission(GuildPermission.ManageEmojis)]
|
||||
[BotPerm(GuildPerm.ManageEmojis)]
|
||||
[UserPerm(GuildPerm.ManageEmojis)]
|
||||
[Priority(1)]
|
||||
public Task EmojiAdd(Emote emote)
|
||||
=> EmojiAdd(emote.Name, emote.Url);
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireBotPermission(GuildPermission.ManageEmojis)]
|
||||
[RequireUserPermission(GuildPermission.ManageEmojis)]
|
||||
public async Task EmojiAdd(string name, string url)
|
||||
[BotPerm(GuildPerm.ManageEmojis)]
|
||||
[UserPerm(GuildPerm.ManageEmojis)]
|
||||
[Priority(0)]
|
||||
public async Task EmojiAdd(string name, string url = null)
|
||||
{
|
||||
name = name.Trim(':');
|
||||
|
||||
url ??= ctx.Message.Attachments.FirstOrDefault()?.Url;
|
||||
|
||||
if (url is null)
|
||||
return;
|
||||
|
||||
using var http = _httpFactory.CreateClient();
|
||||
var res = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
|
||||
if (!res.IsImage() || res.GetImageSize() is null or > 262_144)
|
||||
@@ -355,6 +366,47 @@ namespace NadekoBot.Modules.Utility
|
||||
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]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@@ -425,6 +477,8 @@ namespace NadekoBot.Modules.Utility
|
||||
New
|
||||
}
|
||||
|
||||
|
||||
|
||||
// [NadekoCommand, Usage, Description, Aliases]
|
||||
// [RequireContext(ContextType.Guild)]
|
||||
// public async Task CreateMyInvite(CreateInviteType type = CreateInviteType.Any)
|
||||
|
@@ -746,27 +746,6 @@ namespace NadekoBot.Modules.Xp.Services
|
||||
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)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
|
@@ -39,6 +39,8 @@ namespace NadekoBot.Modules.Xp
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task XpRewsReset()
|
||||
{
|
||||
var reply = await PromptUserConfirmAsync(_eb.Create()
|
||||
|
@@ -9,29 +9,29 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="0.16.0" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.1.19" />
|
||||
<PackageReference Include="AngleSharp" Version="0.16.1" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.7.3" />
|
||||
<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="Discord.Net" Version="2.4.0" />
|
||||
<PackageReference Include="CoreCLR-NCalc" Version="2.2.92" />
|
||||
<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.Protobuf" Version="3.17.3" />
|
||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.38.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.38.1">
|
||||
<PackageReference Include="Google.Protobuf" Version="3.19.1" />
|
||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.41.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.42.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Html2Markdown" Version="4.0.0.427" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.8">
|
||||
<PackageReference Include="Html2Markdown" Version="5.0.0.468" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.13" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.13">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</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.EnvironmentVariables" 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="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Scrutor" Version="3.3.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.0.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.3" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0010" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.2.62" />
|
||||
<PackageReference Include="VideoLibrary" Version="3.1.2" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.2.88" />
|
||||
<PackageReference Include="YamlDotNet" Version="11.2.1" />
|
||||
<PackageReference Include="YoutubeExplode" Version="6.0.5" />
|
||||
<PackageReference Include="linq2db.EntityFrameworkCore" Version="5.4.0" />
|
||||
<PackageReference Include="linq2db.EntityFrameworkCore" Version="5.9.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -1,15 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services
|
||||
{
|
||||
public interface ICoordinator
|
||||
{
|
||||
bool RestartBot();
|
||||
void Die();
|
||||
void Die(bool graceful);
|
||||
bool RestartShard(int shardId);
|
||||
IList<ShardStatus> GetAllShardStatuses();
|
||||
int GetGuildCount();
|
||||
Task Reload();
|
||||
}
|
||||
|
||||
public class ShardStatus
|
||||
|
@@ -7,7 +7,7 @@ namespace NadekoBot.Services
|
||||
{
|
||||
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 Name, string Id, string Url)>> GetVideoInfosByKeywordAsync(string keywords, int count = 1);
|
||||
|
@@ -14,7 +14,6 @@ namespace NadekoBot.Services
|
||||
IReadOnlyList<byte[]> Dice { get; }
|
||||
|
||||
IReadOnlyList<byte[]> SlotEmojis { get; }
|
||||
IReadOnlyList<byte[]> SlotNumbers { get; }
|
||||
IReadOnlyList<byte[]> Currency { get; }
|
||||
|
||||
byte[] SlotBackground { get; }
|
||||
|
@@ -226,8 +226,7 @@ namespace NadekoBot.Services
|
||||
return new ImageResult(search.Items[0].Image, search.Items[0].Link);
|
||||
}
|
||||
|
||||
public IEnumerable<string> Languages => _languageDictionary.Keys.OrderBy(x => x);
|
||||
private readonly Dictionary<string, string> _languageDictionary = new Dictionary<string, string>() {
|
||||
public IReadOnlyDictionary<string, string> Languages { get; } = new Dictionary<string, string>() {
|
||||
{ "afrikaans", "af"},
|
||||
{ "albanian", "sq"},
|
||||
{ "arabic", "ar"},
|
||||
@@ -365,8 +364,8 @@ namespace NadekoBot.Services
|
||||
await Task.Yield();
|
||||
string text;
|
||||
|
||||
if (!_languageDictionary.ContainsKey(sourceLanguage) ||
|
||||
!_languageDictionary.ContainsKey(targetLanguage))
|
||||
if (!Languages.ContainsKey(sourceLanguage) ||
|
||||
!Languages.ContainsKey(targetLanguage))
|
||||
throw new ArgumentException(nameof(sourceLanguage) + "/" + nameof(targetLanguage));
|
||||
|
||||
|
||||
@@ -385,7 +384,7 @@ namespace NadekoBot.Services
|
||||
|
||||
private string ConvertToLanguageCode(string language)
|
||||
{
|
||||
_languageDictionary.TryGetValue(language, out var mode);
|
||||
Languages.TryGetValue(language, out var mode);
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
|
@@ -36,7 +36,6 @@ namespace NadekoBot.Services
|
||||
Dice,
|
||||
SlotBg,
|
||||
SlotEmojis,
|
||||
SlotNumbers,
|
||||
Currency,
|
||||
RategirlMatrix,
|
||||
RategirlDot,
|
||||
@@ -57,9 +56,6 @@ namespace NadekoBot.Services
|
||||
public IReadOnlyList<byte[]> SlotEmojis
|
||||
=> GetByteArrayData(ImageKeys.SlotEmojis);
|
||||
|
||||
public IReadOnlyList<byte[]> SlotNumbers
|
||||
=> GetByteArrayData(ImageKeys.SlotNumbers);
|
||||
|
||||
public IReadOnlyList<byte[]> Currency
|
||||
=> GetByteArrayData(ImageKeys.Currency);
|
||||
|
||||
@@ -157,20 +153,7 @@ namespace NadekoBot.Services
|
||||
"https://cdn.nadeko.bot/slots/3.png",
|
||||
"https://cdn.nadeko.bot/slots/4.png",
|
||||
"https://cdn.nadeko.bot/slots/5.png"
|
||||
}.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)),
|
||||
}.Map(x => new Uri(x))
|
||||
},
|
||||
Xp = new ImageUrls.XpData()
|
||||
{
|
||||
@@ -183,6 +166,14 @@ namespace NadekoBot.Services
|
||||
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()
|
||||
@@ -207,9 +198,6 @@ namespace NadekoBot.Services
|
||||
case ImageKeys.SlotEmojis:
|
||||
await Load(key, ImageUrls.Slots.Emojis);
|
||||
break;
|
||||
case ImageKeys.SlotNumbers:
|
||||
await Load(key, ImageUrls.Slots.Numbers);
|
||||
break;
|
||||
case ImageKeys.Currency:
|
||||
await Load(key, ImageUrls.Currency);
|
||||
break;
|
||||
|
@@ -39,11 +39,11 @@ namespace NadekoBot.Services
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Die()
|
||||
public void Die(bool graceful)
|
||||
{
|
||||
_coordClient.Die(new DieRequest()
|
||||
{
|
||||
Graceful = false
|
||||
Graceful = graceful
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,6 +79,11 @@ namespace NadekoBot.Services
|
||||
return res.Statuses.Sum(x => x.GuildCount);
|
||||
}
|
||||
|
||||
public async Task Reload()
|
||||
{
|
||||
await _coordClient.ReloadAsync(new());
|
||||
}
|
||||
|
||||
public Task OnReadyAsync()
|
||||
{
|
||||
Task.Run(async () =>
|
||||
|
@@ -36,7 +36,7 @@ namespace NadekoBot.Services
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Die()
|
||||
public void Die(bool graceful = false)
|
||||
{
|
||||
Environment.Exit(5);
|
||||
}
|
||||
@@ -64,5 +64,10 @@ namespace NadekoBot.Services
|
||||
{
|
||||
return _client.Guilds.Count;
|
||||
}
|
||||
|
||||
public Task Reload()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,7 +20,7 @@ namespace NadekoBot.Services
|
||||
private readonly IBotCredentials _creds;
|
||||
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 Library => "Discord.Net";
|
||||
public double MessagesPerSecond => MessageCounter / GetUptime().TotalSeconds;
|
||||
|
@@ -22,10 +22,12 @@ using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using Color = Discord.Color;
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace NadekoBot.Extensions
|
||||
{
|
||||
@@ -286,8 +288,8 @@ namespace NadekoBot.Extensions
|
||||
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>();
|
||||
|
||||
public static string ToJson<T>(this T any, Formatting formatting = Formatting.Indented) =>
|
||||
JsonConvert.SerializeObject(any, formatting);
|
||||
public static string ToJson<T>(this T any, JsonSerializerOptions options = null) =>
|
||||
JsonSerializer.Serialize(any, options);
|
||||
|
||||
/// <summary>
|
||||
/// Adds fallback fonts to <see cref="TextOptions"/>
|
||||
|
@@ -1263,3 +1263,13 @@ imageonlychannel:
|
||||
- imageonlychannel
|
||||
- imageonly
|
||||
- imagesonly
|
||||
coordreload:
|
||||
- coordreload
|
||||
quotesexport:
|
||||
- quotesexport
|
||||
- qexport
|
||||
quotesimport:
|
||||
- quotesimport
|
||||
- qimport
|
||||
showembed:
|
||||
- showembed
|
@@ -1,5 +1,5 @@
|
||||
# DO NOT CHANGE
|
||||
version: 2
|
||||
version: 4
|
||||
# Currency settings
|
||||
currency:
|
||||
# What is the emoji/character which represents the currency
|
||||
@@ -240,3 +240,7 @@ patreonCurrencyPerCent: 1
|
||||
# Currency reward per vote.
|
||||
# This will work only if you've set up VotesApi and correct credentials for topgg and/or discords voting
|
||||
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
|
||||
version: 2
|
||||
version: 3
|
||||
coins:
|
||||
heads:
|
||||
- https://cdn.nadeko.bot/coins/heads3.png
|
||||
@@ -36,15 +36,4 @@ slots:
|
||||
- https://cdn.nadeko.bot/slots/3.png
|
||||
- https://cdn.nadeko.bot/slots/4.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
|
||||
|
@@ -406,13 +406,15 @@ remind:
|
||||
- "me 1d5h Do something"
|
||||
- "#general 1m Start now!"
|
||||
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:
|
||||
- "3"
|
||||
- "server 2"
|
||||
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:
|
||||
- "1"
|
||||
- "server 2"
|
||||
serverinfo:
|
||||
desc: "Shows info about the server the bot is on. If no server is supplied, it defaults to current one."
|
||||
args:
|
||||
@@ -609,7 +611,7 @@ quoteid:
|
||||
args:
|
||||
- "123456"
|
||||
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:
|
||||
- "123456"
|
||||
draw:
|
||||
@@ -1200,6 +1202,7 @@ emojiadd:
|
||||
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 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.
|
||||
args:
|
||||
- ":someonesCustomEmoji:"
|
||||
@@ -1303,7 +1306,7 @@ poll:
|
||||
autotranslang:
|
||||
desc: "Sets your source and target language to be used with `{0}at`. Specify no parameters to remove previously set value."
|
||||
args:
|
||||
- "en>fr"
|
||||
- "en fr"
|
||||
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."
|
||||
args:
|
||||
@@ -1575,6 +1578,14 @@ crsexport:
|
||||
desc: "Exports custom reactions from the current server (or global custom reactions in DMs) into a .yml file"
|
||||
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:
|
||||
desc: "Shows the list of currently set aliases. Paginated."
|
||||
args:
|
||||
@@ -1595,9 +1606,12 @@ warnlogall:
|
||||
- ""
|
||||
- "2"
|
||||
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:
|
||||
- "@Someone Very rude person"
|
||||
- "3 @Someone Very rude person"
|
||||
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."
|
||||
args:
|
||||
@@ -2062,7 +2076,7 @@ rollduel:
|
||||
- "50 @Someone"
|
||||
- "@Challenger"
|
||||
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:
|
||||
- "Gamer :SomeServerEmoji: Streamer :Other: Watcher :Other2:"
|
||||
- "excl Horde :Horde: Alliance :Alliance:"
|
||||
@@ -2135,3 +2149,12 @@ imageonlychannel:
|
||||
Users who send more than a few non-image messages will be banned from using the channel.
|
||||
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_disabled": "Protokollierung ausgeschaltet.",
|
||||
"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.",
|
||||
"msg_not_found": "Nachricht wurde nicht gefunden.",
|
||||
"time_too_long": "Die spezifizierte Zeit ist zu lang.",
|
||||
@@ -953,5 +951,29 @@
|
||||
"empty_page": "Diese Seite ist leer.",
|
||||
"pages": "Seiten",
|
||||
"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_started": "Started 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.",
|
||||
"card_not_found": "Couldn't find that card.",
|
||||
"catfact": "fact",
|
||||
@@ -555,7 +556,7 @@
|
||||
"error": "Error",
|
||||
"features": "Features",
|
||||
"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_server": "Joined server",
|
||||
"listservers": "ID: {0}\nMembers: {1}\nOwner ID: {2}",
|
||||
@@ -605,12 +606,13 @@
|
||||
"shard": "Shard",
|
||||
"showemojis": "**Name:** {0} **Link:** {1}",
|
||||
"showemojis_none": "No special emojis found.",
|
||||
"text_channels": "Text channels",
|
||||
"channels": "Channels",
|
||||
"text_channels": "Text: {0}",
|
||||
"voice_channels": "Voice: {0}",
|
||||
"uptime": "Uptime",
|
||||
"userid": "{0} of the user {1} is {2}",
|
||||
"roleid": "{0} of the role {1} is {2}",
|
||||
"users": "Users",
|
||||
"voice_channels": "Voice channels",
|
||||
"current_poll_results": "Current poll results",
|
||||
"poll_already_running": "Poll is already running on this server.",
|
||||
"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_cleared": "Warning {0} has been cleared for {1}.",
|
||||
"warnings_none": "No warning on this page.",
|
||||
"warn_weight": "Weight: {0}",
|
||||
"warn_count": "{0} current, {1} total",
|
||||
"warnlog_for": "Warnlog for {0}",
|
||||
"warnpl_none": "No punishments set.",
|
||||
"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_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_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_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",
|
||||
@@ -917,6 +921,7 @@
|
||||
"reaction_role_removed": "Removed ReactionRole message #{0}",
|
||||
"reaction_roles_full": "You've reached the limit on ReactionRole messages. You have to delete some.",
|
||||
"reminder_list": "List of reminders",
|
||||
"reminder_server_list": "List of server reminders",
|
||||
"reminder_deleted": "Reminder #{0} was deleted.",
|
||||
"reminder_not_exist": "Reminder at that index does not exist.",
|
||||
"reminders_none": "No reminder on this page.",
|
||||
|
@@ -177,7 +177,7 @@
|
||||
"cleverbot_enabled": "Cleverbot activado 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_pl": "¡{0} {1} han aparecido!",
|
||||
"curgen_pl": "¡{0} {1} ha aparecido!",
|
||||
"curgen_sn": "¡Ha aparecido {0}!",
|
||||
"game_started": "Juego iniciado",
|
||||
"hangman_game_started": "Juego del ahorcado iniciado",
|
||||
@@ -593,8 +593,6 @@
|
||||
"log_all": "Registrando todos los eventos en este canal.",
|
||||
"log_disabled": "Registros desactivados.",
|
||||
"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}.",
|
||||
"msg_not_found": "Mensaje no encontrado.",
|
||||
"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_reset": "Las advertencias ya no expirarán.",
|
||||
"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",
|
||||
"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.",
|
||||
@@ -934,7 +932,7 @@
|
||||
"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_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_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",
|
||||
@@ -953,5 +951,29 @@
|
||||
"empty_page": "Esta página está vacía.",
|
||||
"pages": "Páginas",
|
||||
"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_show": "La langue de ce serveur est défini sur {0} - {1}",
|
||||
"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_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.",
|
||||
"msg_not_found": "Message introuvable.",
|
||||
"time_too_long": "Le temps spécifié est trop long.",
|
||||
@@ -953,5 +951,29 @@
|
||||
"empty_page": "Cette page est vide.",
|
||||
"pages": "Pages",
|
||||
"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_disabled": "Registros desabilitado.",
|
||||
"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}.",
|
||||
"msg_not_found": "Mensagem não encontrada.",
|
||||
"time_too_long": "O tempo que você especificou é muito longo.",
|
||||
@@ -847,7 +845,7 @@
|
||||
"stopped": "Repost encerrado.",
|
||||
"restart_fail": "Você precisa configurar o RestartCommand no arquivo creds.yml",
|
||||
"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",
|
||||
"rafflecur": "Rifa {0}",
|
||||
"rafflecur_joined": "O usuário {0} entrou no sorteio",
|
||||
@@ -953,5 +951,29 @@
|
||||
"empty_page": "Essa página esta vazia.",
|
||||
"pages": "Páginas",
|
||||
"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_disabled": "Регистрация событий отключена.",
|
||||
"log_events": "Журнал событий, на которые вы можете подписаться:",
|
||||
"log_ignore": "Ведение журнала игнорирует {0}",
|
||||
"log_not_ignore": "Ведение журнала не игнорирует {0}",
|
||||
"log_stop": "Прекращено ведение журнала события {0}.",
|
||||
"msg_not_found": "Сообщение не найдено.",
|
||||
"time_too_long": "Вы указали слишком много времени.",
|
||||
@@ -953,5 +951,29 @@
|
||||
"empty_page": "Эта страница пуста.",
|
||||
"pages": "Страницы",
|
||||
"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_disabled": "Запис подій вимкнено.",
|
||||
"log_events": "Журнал подій, на які можна підписатися:",
|
||||
"log_ignore": "Запис подій ігноруватиметься {0}",
|
||||
"log_not_ignore": "Запис подій не ігноруватиметься {0}",
|
||||
"log_stop": "Зупинено запис подій {0}.",
|
||||
"msg_not_found": "Повідомлення не знайдено.",
|
||||
"time_too_long": "Вказаний Вами час надто довгий.",
|
||||
@@ -953,5 +951,29 @@
|
||||
"empty_page": "Ця сторінка пуста.",
|
||||
"pages": "Сторінки",
|
||||
"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