mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-12 10:18:27 -04:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3470762b30 | ||
|
2cbb4ecd3d | ||
|
619bee811d | ||
|
a09be96200 | ||
|
81254ed5f6 | ||
|
d82e3bd444 | ||
|
9011646b02 | ||
|
2a1f45819d | ||
|
d2d0cb9e03 | ||
|
ed039977c2 | ||
|
786ede3290 | ||
|
3edcca4927 | ||
|
0635a0ddc8 | ||
|
cb514e4219 | ||
|
cccb37854c | ||
|
35d549f4e6 | ||
|
c0d81a5d9c | ||
|
711d6c7caa | ||
|
cea944cdb8 | ||
|
2d70ea487e | ||
|
9f65f979fd | ||
|
c12b41ddd1 | ||
|
a5f9ac1540 | ||
|
8c5214def2 | ||
|
467a8bdaf6 |
38
CHANGELOG.md
38
CHANGELOG.md
@@ -2,7 +2,42 @@
|
|||||||
|
|
||||||
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
||||||
|
|
||||||
## Unreleased
|
## [3.0.4] - 16.09.2021
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Fully translated to Brazilian Portuguese 🎉
|
||||||
|
- Added `%server.boosters%` and `%server.boost_level%` placeholders
|
||||||
|
- Added `DmHelpTextKeywords` to `data/bot.yml`
|
||||||
|
- Bot now sends dm help text ONLY if the message contains one of the keywords specified
|
||||||
|
- If no keywords are specified, bot will reply to every DM (like before)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Possible fix for `.repeat` bug
|
||||||
|
- Slight adjustment for repeater logic
|
||||||
|
- Timer should no longer increase on some repeaters
|
||||||
|
- Repeaters should no longer have periods when they're missing from the list
|
||||||
|
- Fixed several commands which used error color for success confirmation messages
|
||||||
|
|
||||||
|
## [3.0.3] - 15.09.2021
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `.massban` to ban multiple people at once. 30 second cooldown
|
||||||
|
- Added `.youtubeuploadnotif` / `.yun` as a shortcut for subscribing to a youtube channel's rss feed
|
||||||
|
- Added `.imageonlychannel` / `.imageonly` to prevent users from posting anything but images in the channel
|
||||||
|
- Added `.config games hangman.currency_reward` and a property with the same name in games.yml
|
||||||
|
- If set, users will gain the specified amount of currency for each hangman win
|
||||||
|
- Fully translated to Spanish, Russian and Ukrainian 🎉
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Ban `.warnp` will now prune user's messages
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `.boostmsg` will now properly show boost, and not greet message
|
||||||
|
|
||||||
|
## [3.0.2] - 12.09.2021
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
@@ -20,6 +55,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
|||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- `.timely` will now correctly use `Ok` color
|
- `.timely` will now correctly use `Ok` color
|
||||||
|
- Fixed `.log` commands
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
@@ -2,12 +2,10 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using AngleSharp.Common;
|
|
||||||
using Discord.Commands;
|
using Discord.Commands;
|
||||||
using NadekoBot.Common.Attributes;
|
using NadekoBot.Common.Attributes;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
using NadekoBot.Modules;
|
using NadekoBot.Modules;
|
||||||
using YamlDotNet.Serialization;
|
|
||||||
|
|
||||||
namespace NadekoBot.Tests
|
namespace NadekoBot.Tests
|
||||||
{
|
{
|
||||||
|
@@ -1,26 +0,0 @@
|
|||||||
namespace NadekoBot.Common
|
|
||||||
{
|
|
||||||
public enum BotConfigEditType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of currency awarded to the winner of the trivia game.
|
|
||||||
/// Default is 0.
|
|
||||||
/// </summary>
|
|
||||||
TriviaCurrencyReward,
|
|
||||||
/// <summary>
|
|
||||||
/// Users can't start trivia games which have smaller win requirement than specified by this setting.
|
|
||||||
/// Default is 0.
|
|
||||||
/// </summary>
|
|
||||||
MinimumTriviaWinReq,
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of XP the user receives when they send a message (which is not too short).
|
|
||||||
/// Default is 3.
|
|
||||||
/// </summary>
|
|
||||||
XpPerMessage,
|
|
||||||
/// <summary>
|
|
||||||
/// This value represents how often the user can receive XP from sending messages.
|
|
||||||
/// Default is 5.
|
|
||||||
/// </summary>
|
|
||||||
XpMinutesTimeout,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -12,7 +12,7 @@ namespace NadekoBot.Common.Configs
|
|||||||
public sealed partial class BotConfig : ICloneable<BotConfig>
|
public sealed partial class BotConfig : ICloneable<BotConfig>
|
||||||
{
|
{
|
||||||
[Comment(@"DO NOT CHANGE")]
|
[Comment(@"DO NOT CHANGE")]
|
||||||
public int Version { get; set; }
|
public int Version { get; set; } = 2;
|
||||||
|
|
||||||
[Comment(@"Most commands, when executed, have a small colored line
|
[Comment(@"Most commands, when executed, have a small colored line
|
||||||
next to the response. The color depends whether the command
|
next to the response. The color depends whether the command
|
||||||
@@ -49,6 +49,11 @@ Supports embeds. How it looks: https://puu.sh/B0BLV.png")]
|
|||||||
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
|
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
|
||||||
public string DmHelpText { get; set; }
|
public string DmHelpText { get; set; }
|
||||||
|
|
||||||
|
[Comment(@"Only users who send a DM to the bot containing one of the specified words will get a DmHelpText response.
|
||||||
|
Case insensitive.
|
||||||
|
Leave empty to reply with DmHelpText to every DM.")]
|
||||||
|
public List<string> DmHelpTextKeywords { get; set; }
|
||||||
|
|
||||||
[Comment(@"This is the response for the .h command")]
|
[Comment(@"This is the response for the .h command")]
|
||||||
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
|
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
|
||||||
public string HelpText { get; set; }
|
public string HelpText { get; set; }
|
||||||
@@ -89,7 +94,6 @@ See RotatingStatuses submodule in Administration.")]
|
|||||||
|
|
||||||
public BotConfig()
|
public BotConfig()
|
||||||
{
|
{
|
||||||
Version = 1;
|
|
||||||
var color = new ColorConfig();
|
var color = new ColorConfig();
|
||||||
Color = color;
|
Color = color;
|
||||||
DefaultLocale = new CultureInfo("en-US");
|
DefaultLocale = new CultureInfo("en-US");
|
||||||
@@ -127,6 +131,14 @@ See RotatingStatuses submodule in Administration.")]
|
|||||||
Prefix = ".";
|
Prefix = ".";
|
||||||
RotateStatuses = false;
|
RotateStatuses = false;
|
||||||
GroupGreets = false;
|
GroupGreets = false;
|
||||||
|
DmHelpTextKeywords = new List<string>()
|
||||||
|
{
|
||||||
|
"help",
|
||||||
|
"commands",
|
||||||
|
"cmds",
|
||||||
|
"module",
|
||||||
|
"can you do"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -89,6 +89,8 @@ namespace NadekoBot.Common.Replacements
|
|||||||
_reps.TryAdd("%server.id%", () => g is null ? "DM" : g.Id.ToString());
|
_reps.TryAdd("%server.id%", () => g is null ? "DM" : g.Id.ToString());
|
||||||
_reps.TryAdd("%server.name%", () => g is null ? "DM" : g.Name);
|
_reps.TryAdd("%server.name%", () => g is null ? "DM" : g.Name);
|
||||||
_reps.TryAdd("%server.members%", () => g != null && g is SocketGuild sg ? sg.MemberCount.ToString() : "?");
|
_reps.TryAdd("%server.members%", () => g != null && g is SocketGuild sg ? sg.MemberCount.ToString() : "?");
|
||||||
|
_reps.TryAdd("%server.boosters%", () => g.PremiumSubscriptionCount.ToString());
|
||||||
|
_reps.TryAdd("%server.boost_level%", () => ((int)g.PremiumTier).ToString());
|
||||||
_reps.TryAdd("%server.time%", () =>
|
_reps.TryAdd("%server.time%", () =>
|
||||||
{
|
{
|
||||||
TimeZoneInfo to = TimeZoneInfo.Local;
|
TimeZoneInfo to = TimeZoneInfo.Local;
|
||||||
|
8
src/NadekoBot/Db/Models/ImageOnlyChannel.cs
Normal file
8
src/NadekoBot/Db/Models/ImageOnlyChannel.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace NadekoBot.Services.Database.Models
|
||||||
|
{
|
||||||
|
public class ImageOnlyChannel : DbEntity
|
||||||
|
{
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public ulong ChannelId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace NadekoBot.Services.Database.Models
|
namespace NadekoBot.Services.Database.Models
|
||||||
{
|
{
|
||||||
|
|
||||||
public class LogSetting : DbEntity
|
public class LogSetting : DbEntity
|
||||||
{
|
{
|
||||||
public HashSet<IgnoredLogChannel> IgnoredChannels { get; set; } = new HashSet<IgnoredLogChannel>();
|
public HashSet<IgnoredLogChannel> IgnoredChannels { get; set; } = new HashSet<IgnoredLogChannel>();
|
||||||
|
@@ -2,11 +2,9 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Design;
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using NadekoBot.Services;
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NadekoBot.Common;
|
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
|
|
||||||
namespace NadekoBot.Services.Database
|
namespace NadekoBot.Services.Database
|
||||||
@@ -60,6 +58,7 @@ namespace NadekoBot.Services.Database
|
|||||||
public DbSet<Repeater> Repeaters { get; set; }
|
public DbSet<Repeater> Repeaters { get; set; }
|
||||||
public DbSet<Poll> Poll { get; set; }
|
public DbSet<Poll> Poll { get; set; }
|
||||||
public DbSet<WaifuInfo> WaifuInfo { get; set; }
|
public DbSet<WaifuInfo> WaifuInfo { get; set; }
|
||||||
|
public DbSet<ImageOnlyChannel> ImageOnlyChannels { get; set; }
|
||||||
|
|
||||||
public NadekoContext(DbContextOptions<NadekoContext> options) : base(options)
|
public NadekoContext(DbContextOptions<NadekoContext> options) : base(options)
|
||||||
{
|
{
|
||||||
@@ -345,6 +344,10 @@ namespace NadekoBot.Services.Database
|
|||||||
.IsUnique());
|
.IsUnique());
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
modelBuilder.Entity<ImageOnlyChannel>(ioc => ioc
|
||||||
|
.HasIndex(x => x.ChannelId)
|
||||||
|
.IsUnique());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2653
src/NadekoBot/Migrations/20210914180026_image-only-channels.Designer.cs
generated
Normal file
2653
src/NadekoBot/Migrations/20210914180026_image-only-channels.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
public partial class imageonlychannels : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ImageOnlyChannels",
|
||||||
|
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),
|
||||||
|
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ImageOnlyChannels", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ImageOnlyChannels_ChannelId",
|
||||||
|
table: "ImageOnlyChannels",
|
||||||
|
column: "ChannelId",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ImageOnlyChannels");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -187,6 +187,29 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("FollowedStream");
|
b.ToTable("FollowedStream");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.ImageOnlyChannel", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.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.ToTable("ImageOnlyChannels");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
|
@@ -13,13 +13,31 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
public partial class Administration : NadekoModule<AdministrationService>
|
public partial class Administration : NadekoModule<AdministrationService>
|
||||||
{
|
{
|
||||||
|
private readonly ImageOnlyChannelService _imageOnly;
|
||||||
|
|
||||||
|
public Administration(ImageOnlyChannelService imageOnly)
|
||||||
|
{
|
||||||
|
_imageOnly = imageOnly;
|
||||||
|
}
|
||||||
|
|
||||||
public enum List
|
public enum List
|
||||||
{
|
{
|
||||||
List = 0,
|
List = 0,
|
||||||
Ls = 0
|
Ls = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
[BotPerm(GuildPerm.Administrator)]
|
||||||
|
public async Task ImageOnlyChannel(StoopidTime time = null)
|
||||||
|
{
|
||||||
|
var newValue = _imageOnly.ToggleImageOnlyChannel(ctx.Guild.Id, ctx.Channel.Id);
|
||||||
|
if (newValue)
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.imageonly_enable);
|
||||||
|
else
|
||||||
|
await ReplyPendingLocalizedAsync(strs.imageonly_disable);
|
||||||
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
|
@@ -90,7 +90,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
await _service.TimedMute(user, ctx.User, time.Time, reason: reason).ConfigureAwait(false);
|
await _service.TimedMute(user, ctx.User, time.Time, reason: reason).ConfigureAwait(false);
|
||||||
await ReplyErrorLocalizedAsync(strs.user_muted_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
await ReplyConfirmLocalizedAsync(strs.user_muted_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -150,7 +150,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Chat, reason: reason).ConfigureAwait(false);
|
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Chat, reason: reason).ConfigureAwait(false);
|
||||||
await ReplyErrorLocalizedAsync(strs.user_chat_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
await ReplyConfirmLocalizedAsync(strs.user_chat_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -209,7 +209,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Voice, reason: reason).ConfigureAwait(false);
|
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Voice, reason: reason).ConfigureAwait(false);
|
||||||
await ReplyErrorLocalizedAsync(strs.user_voice_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
await ReplyConfirmLocalizedAsync(strs.user_voice_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@@ -61,7 +61,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
if (msg is null)
|
if (msg is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await ReplyErrorLocalizedAsync(strs.reprm(msg));
|
await ReplyConfirmLocalizedAsync(strs.reprm(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ReplyErrorLocalizedAsync(strs.protection_not_running("Anti-Alt"));
|
await ReplyConfirmLocalizedAsync(strs.protection_not_running("Anti-Alt"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
@@ -69,11 +69,11 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
if (_service.TryStopAntiRaid(ctx.Guild.Id))
|
if (_service.TryStopAntiRaid(ctx.Guild.Id))
|
||||||
{
|
{
|
||||||
return ReplyErrorLocalizedAsync(strs.prot_disable("Anti-Raid"));
|
return ReplyConfirmLocalizedAsync(strs.prot_disable("Anti-Raid"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return ReplyErrorLocalizedAsync(strs.protection_not_running("Anti-Raid"));
|
return ReplyPendingLocalizedAsync(strs.protection_not_running("Anti-Raid"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,11 +145,11 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
if (_service.TryStopAntiSpam(ctx.Guild.Id))
|
if (_service.TryStopAntiSpam(ctx.Guild.Id))
|
||||||
{
|
{
|
||||||
return ReplyErrorLocalizedAsync(strs.prot_disable("Anti-Spam"));
|
return ReplyConfirmLocalizedAsync(strs.prot_disable("Anti-Spam"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return ReplyErrorLocalizedAsync(strs.protection_not_running("Anti-Spam"));
|
return ReplyPendingLocalizedAsync(strs.protection_not_running("Anti-Spam"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -189,7 +189,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
index--;
|
index--;
|
||||||
var rr = rrs[index];
|
var rr = rrs[index];
|
||||||
_service.Remove(ctx.Guild.Id, index);
|
_service.Remove(ctx.Guild.Id, index);
|
||||||
await ReplyErrorLocalizedAsync(strs.reaction_role_removed(index + 1));
|
await ReplyConfirmLocalizedAsync(strs.reaction_role_removed(index + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
|
@@ -24,11 +24,11 @@ namespace NadekoBot.Modules.Administration
|
|||||||
|
|
||||||
if (newVal)
|
if (newVal)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.adsarm_enable(Prefix));
|
await ReplyConfirmLocalizedAsync(strs.adsarm_enable(Prefix));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.adsarm_disable(Prefix));
|
await ReplyConfirmLocalizedAsync(strs.adsarm_disable(Prefix));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
};
|
};
|
||||||
_service.AddNewAutoCommand(cmd);
|
_service.AddNewAutoCommand(cmd);
|
||||||
|
|
||||||
await ReplyErrorLocalizedAsync(strs.autocmd_add(Format.Code(Format.Sanitize(cmdText)), cmd.Interval));
|
await ReplyConfirmLocalizedAsync(strs.autocmd_add(Format.Code(Format.Sanitize(cmdText)), cmd.Interval));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
@@ -226,7 +226,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
if (enabled)
|
if (enabled)
|
||||||
await ReplyConfirmLocalizedAsync(strs.fwdm_start).ConfigureAwait(false);
|
await ReplyConfirmLocalizedAsync(strs.fwdm_start).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await ReplyConfirmLocalizedAsync(strs.fwdm_stop).ConfigureAwait(false);
|
await ReplyPendingLocalizedAsync(strs.fwdm_stop).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
@@ -238,7 +238,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
if (enabled)
|
if (enabled)
|
||||||
await ReplyConfirmLocalizedAsync(strs.fwall_start).ConfigureAwait(false);
|
await ReplyConfirmLocalizedAsync(strs.fwall_start).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await ReplyConfirmLocalizedAsync(strs.fwall_stop).ConfigureAwait(false);
|
await ReplyPendingLocalizedAsync(strs.fwall_stop).ConfigureAwait(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,7 +376,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
var curUser = await ctx.Guild.GetCurrentUserAsync().ConfigureAwait(false);
|
var curUser = await ctx.Guild.GetCurrentUserAsync().ConfigureAwait(false);
|
||||||
await curUser.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
|
await curUser.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
|
||||||
|
|
||||||
await ReplyErrorLocalizedAsync(strs.bot_nick(Format.Bold(newNick) ?? "-"));
|
await ReplyConfirmLocalizedAsync(strs.bot_nick(Format.Bold(newNick) ?? "-"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
@@ -395,7 +395,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
|
|
||||||
await gu.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
|
await gu.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
|
||||||
|
|
||||||
await ReplyErrorLocalizedAsync(strs.user_nick(Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-"));
|
await ReplyConfirmLocalizedAsync(strs.user_nick(Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
@@ -496,7 +496,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
public async Task ImagesReload()
|
public async Task ImagesReload()
|
||||||
{
|
{
|
||||||
await _service.ReloadImagesAsync();
|
await _service.ReloadImagesAsync();
|
||||||
await ReplyErrorLocalizedAsync(strs.images_loading);
|
await ReplyConfirmLocalizedAsync(strs.images_loading);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
|
@@ -41,6 +41,15 @@ namespace NadekoBot.Modules.Administration
|
|||||||
await ReplyPendingLocalizedAsync(strs.boostdel_off).ConfigureAwait(false);
|
await ReplyPendingLocalizedAsync(strs.boostdel_off).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.ManageGuild)]
|
||||||
|
public Task BoostMsg()
|
||||||
|
{
|
||||||
|
var boostMessage = _service.GetBoostMessage(ctx.Guild.Id);
|
||||||
|
return ReplyConfirmLocalizedAsync(strs.boostmsg_cur(boostMessage?.SanitizeMentions()));
|
||||||
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageGuild)]
|
[UserPerm(GuildPerm.ManageGuild)]
|
||||||
@@ -48,7 +57,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(text))
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
{
|
{
|
||||||
await GreetMsg().ConfigureAwait(false);
|
await BoostMsg().ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
using System;
|
using Discord;
|
||||||
using Discord;
|
|
||||||
using Discord.Commands;
|
using Discord.Commands;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NadekoBot.Common;
|
|
||||||
using NadekoBot.Common.Collections;
|
using NadekoBot.Common.Collections;
|
||||||
using NadekoBot.Common.Replacements;
|
using NadekoBot.Common.Replacements;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
@@ -24,14 +22,11 @@ namespace NadekoBot.Modules.Administration.Services
|
|||||||
|
|
||||||
private readonly DbService _db;
|
private readonly DbService _db;
|
||||||
private readonly ILogCommandService _logService;
|
private readonly ILogCommandService _logService;
|
||||||
private readonly IEmbedBuilderService _eb;
|
|
||||||
|
|
||||||
public AdministrationService(Bot bot, CommandHandler cmdHandler, DbService db,
|
public AdministrationService(Bot bot, CommandHandler cmdHandler, DbService db, ILogCommandService logService)
|
||||||
ILogCommandService logService, IEmbedBuilderService eb)
|
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_logService = logService;
|
_logService = logService;
|
||||||
_eb = eb;
|
|
||||||
|
|
||||||
DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(bot.AllGuildConfigs
|
DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(bot.AllGuildConfigs
|
||||||
.Where(g => g.DeleteMessageOnCommand)
|
.Where(g => g.DeleteMessageOnCommand)
|
||||||
|
@@ -0,0 +1,187 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Discord;
|
||||||
|
using Discord.Net;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using LinqToDB;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using NadekoBot.Common.Collections;
|
||||||
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
using NadekoBot.Services;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration.Services
|
||||||
|
{
|
||||||
|
public sealed class ImageOnlyChannelService : IEarlyBehavior
|
||||||
|
{
|
||||||
|
private readonly IMemoryCache _ticketCache;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly DbService _db;
|
||||||
|
private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _enabledOn;
|
||||||
|
|
||||||
|
private Channel<IUserMessage> _deleteQueue = Channel.CreateBounded<IUserMessage>(new BoundedChannelOptions(100)
|
||||||
|
{
|
||||||
|
FullMode = BoundedChannelFullMode.DropOldest,
|
||||||
|
SingleReader = true,
|
||||||
|
SingleWriter = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
public ImageOnlyChannelService(IMemoryCache ticketCache, DiscordSocketClient client, DbService db)
|
||||||
|
{
|
||||||
|
_ticketCache = ticketCache;
|
||||||
|
_client = client;
|
||||||
|
_db = db;
|
||||||
|
|
||||||
|
var uow = _db.GetDbContext();
|
||||||
|
_enabledOn = uow.ImageOnlyChannels
|
||||||
|
.ToList()
|
||||||
|
.GroupBy(x => x.GuildId)
|
||||||
|
.ToDictionary(x => x.Key, x => new ConcurrentHashSet<ulong>(x.Select(x => x.ChannelId)))
|
||||||
|
.ToConcurrent();
|
||||||
|
|
||||||
|
_ = Task.Run(DeleteQueueRunner);
|
||||||
|
|
||||||
|
_client.ChannelDestroyed += ClientOnChannelDestroyed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task ClientOnChannelDestroyed(SocketChannel ch)
|
||||||
|
{
|
||||||
|
if (ch is not IGuildChannel gch)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
if (_enabledOn.TryGetValue(gch.GuildId, out var channels) && channels.TryRemove(ch.Id))
|
||||||
|
ToggleImageOnlyChannel(gch.GuildId, ch.Id, true);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteQueueRunner()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var toDelete = await _deleteQueue.Reader.ReadAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await toDelete.DeleteAsync();
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
|
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
// disable if bot can't delete messages in the channel
|
||||||
|
ToggleImageOnlyChannel(((ITextChannel)toDelete.Channel).GuildId, toDelete.Channel.Id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ToggleImageOnlyChannel(ulong guildId, ulong channelId, bool forceDisable = false)
|
||||||
|
{
|
||||||
|
var newState = false;
|
||||||
|
using var uow = _db.GetDbContext();
|
||||||
|
if (forceDisable
|
||||||
|
|| (_enabledOn.TryGetValue(guildId, out var channels)
|
||||||
|
&& channels.TryRemove(channelId)))
|
||||||
|
{
|
||||||
|
uow.ImageOnlyChannels.Delete(x => x.ChannelId == channelId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uow.ImageOnlyChannels.Add(new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
ChannelId = channelId
|
||||||
|
});
|
||||||
|
|
||||||
|
channels = _enabledOn.GetOrAdd(guildId, new ConcurrentHashSet<ulong>());
|
||||||
|
channels.Add(channelId);
|
||||||
|
newState = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uow.SaveChanges();
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> RunBehavior(IGuild guild, IUserMessage msg)
|
||||||
|
{
|
||||||
|
if (msg.Channel is not ITextChannel tch)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (msg.Attachments.Any(x => x is { Height: > 0, Width: > 0 }))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_enabledOn.TryGetValue(tch.GuildId, out var chs)
|
||||||
|
|| !chs.Contains(msg.Channel.Id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var user = await tch.Guild.GetUserAsync(msg.Author.Id)
|
||||||
|
?? await _client.Rest.GetGuildUserAsync(tch.GuildId, msg.Author.Id);
|
||||||
|
|
||||||
|
if (user is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// ignore owner and admin
|
||||||
|
if (user.Id == tch.Guild.OwnerId || user.GuildPermissions.Administrator)
|
||||||
|
{
|
||||||
|
Log.Information("Image-Only: Ignoring owner od admin ({ChannelId})", msg.Channel.Id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore users higher in hierarchy
|
||||||
|
var botUser = await tch.Guild.GetCurrentUserAsync();
|
||||||
|
if (user.GetRoles().Max(x => x.Position) >= botUser.GetRoles().Max(x => x.Position))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// can't modify channel perms if not admin apparently
|
||||||
|
if (!botUser.GuildPermissions.ManageGuild)
|
||||||
|
{
|
||||||
|
ToggleImageOnlyChannel( tch.GuildId, tch.Id, true);;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var shouldLock = AddUserTicket(tch.GuildId, msg.Author.Id);
|
||||||
|
if (shouldLock)
|
||||||
|
{
|
||||||
|
await tch.AddPermissionOverwriteAsync(msg.Author, new(sendMessages: PermValue.Deny));
|
||||||
|
Log.Warning("Image-Only: User {User} [{UserId}] has been banned from typing in the channel [{ChannelId}]",
|
||||||
|
msg.Author,
|
||||||
|
msg.Author.Id,
|
||||||
|
msg.Channel.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _deleteQueue.Writer.WriteAsync(msg);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Error deleting message {MessageId} in image-only channel {ChannelId}.",
|
||||||
|
msg.Id,
|
||||||
|
tch.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AddUserTicket(ulong guildId, ulong userId)
|
||||||
|
{
|
||||||
|
var old = _ticketCache.GetOrCreate($"{guildId}_{userId}", entry =>
|
||||||
|
{
|
||||||
|
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
_ticketCache.Set($"{guildId}_{userId}", ++old);
|
||||||
|
|
||||||
|
// if this is the third time that the user posts a
|
||||||
|
// non image in an image-only channel on this server
|
||||||
|
return old > 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Priority { get; } = 0;
|
||||||
|
}
|
||||||
|
}
|
@@ -123,7 +123,7 @@ namespace NadekoBot.Modules.Administration.Services
|
|||||||
break;
|
break;
|
||||||
case PunishmentAction.Ban:
|
case PunishmentAction.Ban:
|
||||||
if (minutes == 0)
|
if (minutes == 0)
|
||||||
await guild.AddBanAsync(user, reason: reason).ConfigureAwait(false);
|
await guild.AddBanAsync(user, reason: reason, pruneDays: 7).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await _mute.TimedBan(user.Guild, user, TimeSpan.FromMinutes(minutes), reason)
|
await _mute.TimedBan(user.Guild, user, TimeSpan.FromMinutes(minutes), reason)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
@@ -9,9 +9,11 @@ using NadekoBot.Services.Database.Models;
|
|||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using NadekoBot.Modules.Administration.Services;
|
using NadekoBot.Modules.Administration.Services;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NadekoBot.Modules.Permissions.Services;
|
using NadekoBot.Modules.Permissions.Services;
|
||||||
|
using NadekoBot.Modules.Searches.Common;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Administration
|
namespace NadekoBot.Modules.Administration
|
||||||
@@ -763,6 +765,78 @@ namespace NadekoBot.Modules.Administration
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
|
[BotPerm(GuildPerm.BanMembers)]
|
||||||
|
[Ratelimit(30)]
|
||||||
|
public async Task MassBan(params string[] userStrings)
|
||||||
|
{
|
||||||
|
if (userStrings.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var missing = new List<string>();
|
||||||
|
var banning = new HashSet<IGuildUser>();
|
||||||
|
|
||||||
|
await ctx.Channel.TriggerTypingAsync();
|
||||||
|
foreach (var userStr in userStrings)
|
||||||
|
{
|
||||||
|
if (ulong.TryParse(userStr, out var userId))
|
||||||
|
{
|
||||||
|
var user = await ctx.Guild.GetUserAsync(userId) ??
|
||||||
|
await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, userId);
|
||||||
|
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
missing.Add(userStr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await CheckRoleHierarchy(user))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
banning.Add(user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
missing.Add(userStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var missStr = string.Join("\n", missing);
|
||||||
|
if (string.IsNullOrWhiteSpace(missStr))
|
||||||
|
missStr = "-";
|
||||||
|
|
||||||
|
var toSend = _eb.Create(ctx)
|
||||||
|
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
|
||||||
|
.AddField(GetText(strs.invalid(missing.Count)), missStr)
|
||||||
|
.WithPendingColor();
|
||||||
|
|
||||||
|
var banningMessage = await ctx.Channel.EmbedAsync(toSend);
|
||||||
|
|
||||||
|
foreach (var toBan in banning)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await toBan.BanAsync(7);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex, "Error banning {User} user in {GuildId} server",
|
||||||
|
toBan.Id,
|
||||||
|
ctx.Guild.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await banningMessage.ModifyAsync(x => x.Embed = _eb.Create()
|
||||||
|
.WithDescription(GetText(strs.mass_ban_completed(banning.Count())))
|
||||||
|
.AddField(GetText(strs.invalid(missing.Count)), missStr)
|
||||||
|
.WithOkColor()
|
||||||
|
.Build()).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.BanMembers)]
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
@@ -783,7 +857,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
var banningMessageTask = ctx.Channel.EmbedAsync(_eb.Create()
|
var banningMessageTask = ctx.Channel.EmbedAsync(_eb.Create()
|
||||||
.WithDescription(GetText(strs.mass_kill_in_progress(bans.Count())))
|
.WithDescription(GetText(strs.mass_kill_in_progress(bans.Count())))
|
||||||
.AddField(GetText(strs.invalid(missing)), missStr)
|
.AddField(GetText(strs.invalid(missing)), missStr)
|
||||||
.WithOkColor());
|
.WithPendingColor());
|
||||||
|
|
||||||
//do the banning
|
//do the banning
|
||||||
await Task.WhenAll(bans
|
await Task.WhenAll(bans
|
||||||
|
@@ -294,7 +294,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
.WithDescription("This will delete all custom reactions on this server.")).ConfigureAwait(false))
|
.WithDescription("This will delete all custom reactions on this server.")).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
var count = _service.DeleteAllCustomReactions(ctx.Guild.Id);
|
var count = _service.DeleteAllCustomReactions(ctx.Guild.Id);
|
||||||
await ReplyErrorLocalizedAsync(strs.cleared(count));
|
await ReplyConfirmLocalizedAsync(strs.cleared(count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -217,7 +217,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task Cash(ulong userId)
|
public async Task Cash(ulong userId)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.has(Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}"));
|
await ReplyConfirmLocalizedAsync(strs.has(Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
@@ -269,7 +269,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
$"Awarded by bot owner. ({ctx.User.Username}/{ctx.User.Id}) {(msg ?? "")}",
|
$"Awarded by bot owner. ({ctx.User.Username}/{ctx.User.Id}) {(msg ?? "")}",
|
||||||
amount,
|
amount,
|
||||||
gamble: (ctx.Client.CurrentUser.Id != usrId)).ConfigureAwait(false);
|
gamble: (ctx.Client.CurrentUser.Id != usrId)).ConfigureAwait(false);
|
||||||
await ReplyErrorLocalizedAsync(strs.awarded(n(amount) + CurrencySign, $"<@{usrId}>"));
|
await ReplyConfirmLocalizedAsync(strs.awarded(n(amount) + CurrencySign, $"<@{usrId}>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
@@ -340,7 +340,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
|
|
||||||
if (await _cs.RemoveAsync(usrId, $"Taken by bot owner.({ctx.User.Username}/{ctx.User.Id})", amount,
|
if (await _cs.RemoveAsync(usrId, $"Taken by bot owner.({ctx.User.Username}/{ctx.User.Id})", amount,
|
||||||
gamble: (ctx.Client.CurrentUser.Id != usrId)).ConfigureAwait(false))
|
gamble: (ctx.Client.CurrentUser.Id != usrId)).ConfigureAwait(false))
|
||||||
await ReplyErrorLocalizedAsync(strs.take(amount + CurrencySign, $"<@{usrId}>"));
|
await ReplyConfirmLocalizedAsync(strs.take(amount + CurrencySign, $"<@{usrId}>"));
|
||||||
else
|
else
|
||||||
await ReplyErrorLocalizedAsync(strs.take_fail(amount + CurrencySign, Format.Code(usrId.ToString()), CurrencySign));
|
await ReplyErrorLocalizedAsync(strs.take_fail(amount + CurrencySign, Format.Code(usrId.ToString()), CurrencySign));
|
||||||
}
|
}
|
||||||
|
@@ -145,11 +145,11 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
|
|
||||||
if (result == DivorceResult.SucessWithPenalty)
|
if (result == DivorceResult.SucessWithPenalty)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.waifu_divorced_like(Format.Bold(w.Waifu.ToString()), amount + CurrencySign));
|
await ReplyConfirmLocalizedAsync(strs.waifu_divorced_like(Format.Bold(w.Waifu.ToString()), amount + CurrencySign));
|
||||||
}
|
}
|
||||||
else if (result == DivorceResult.Success)
|
else if (result == DivorceResult.Success)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.waifu_divorced_notlike(amount + CurrencySign));
|
await ReplyConfirmLocalizedAsync(strs.waifu_divorced_notlike(amount + CurrencySign));
|
||||||
}
|
}
|
||||||
else if (result == DivorceResult.NotYourWife)
|
else if (result == DivorceResult.NotYourWife)
|
||||||
{
|
{
|
||||||
@@ -306,7 +306,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public async Task WaifuGift(int page = 1)
|
public async Task WaifuGift(int page = 1)
|
||||||
{
|
{
|
||||||
if (--page < 0 || page > 3)
|
if (--page < 0 || page > (_config.Waifu.Items.Count - 1) / 9)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var waifuItems = _service.GetWaifuItems();
|
var waifuItems = _service.GetWaifuItems();
|
||||||
|
@@ -8,6 +8,15 @@ namespace NadekoBot.Modules.Games.Common
|
|||||||
[Cloneable]
|
[Cloneable]
|
||||||
public sealed partial class GamesConfig : ICloneable<GamesConfig>
|
public sealed partial class GamesConfig : ICloneable<GamesConfig>
|
||||||
{
|
{
|
||||||
|
[Comment("DO NOT CHANGE")]
|
||||||
|
public int Version { get; set; }
|
||||||
|
|
||||||
|
[Comment("Hangman related settings (.hangman command)")]
|
||||||
|
public HangmanConfig Hangman { get; set; } = new HangmanConfig()
|
||||||
|
{
|
||||||
|
CurrencyReward = 0
|
||||||
|
};
|
||||||
|
|
||||||
[Comment("Trivia related settings (.t command)")]
|
[Comment("Trivia related settings (.t command)")]
|
||||||
public TriviaConfig Trivia { get; set; } = new TriviaConfig()
|
public TriviaConfig Trivia { get; set; } = new TriviaConfig()
|
||||||
{
|
{
|
||||||
@@ -57,6 +66,13 @@ namespace NadekoBot.Modules.Games.Common
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cloneable]
|
||||||
|
public sealed partial class HangmanConfig
|
||||||
|
{
|
||||||
|
[Comment("The amount of currency awarded to the winner of a hangman game")]
|
||||||
|
public long CurrencyReward { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
[Cloneable]
|
[Cloneable]
|
||||||
public sealed partial class TriviaConfig
|
public sealed partial class TriviaConfig
|
||||||
{
|
{
|
||||||
|
@@ -45,7 +45,7 @@ namespace NadekoBot.Modules.Games.Common.Hangman
|
|||||||
public uint Errors { get; private set; } = 0;
|
public uint Errors { get; private set; } = 0;
|
||||||
public uint MaxErrors { get; } = 6;
|
public uint MaxErrors { get; } = 6;
|
||||||
|
|
||||||
public event Func<Hangman, string, Task> OnGameEnded = delegate { return Task.CompletedTask; };
|
public event Func<Hangman, string, ulong, Task> OnGameEnded = delegate { return Task.CompletedTask; };
|
||||||
public event Func<Hangman, string, char, Task> OnLetterAlreadyUsed = delegate { return Task.CompletedTask; };
|
public event Func<Hangman, string, char, Task> OnLetterAlreadyUsed = delegate { return Task.CompletedTask; };
|
||||||
public event Func<Hangman, string, char, Task> OnGuessFailed = delegate { return Task.CompletedTask; };
|
public event Func<Hangman, string, char, Task> OnGuessFailed = delegate { return Task.CompletedTask; };
|
||||||
public event Func<Hangman, string, char, Task> OnGuessSucceeded = delegate { return Task.CompletedTask; };
|
public event Func<Hangman, string, char, Task> OnGuessSucceeded = delegate { return Task.CompletedTask; };
|
||||||
@@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Games.Common.Hangman
|
|||||||
{
|
{
|
||||||
if (++Errors > MaxErrors)
|
if (++Errors > MaxErrors)
|
||||||
{
|
{
|
||||||
var _ = OnGameEnded(this, null);
|
var _ = OnGameEnded(this, null, 0);
|
||||||
CurrentPhase = Phase.Ended;
|
CurrentPhase = Phase.Ended;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Games.Common.Hangman
|
|||||||
if (input != Term.Word) // failed
|
if (input != Term.Word) // failed
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var _ = OnGameEnded?.Invoke(this, userName);
|
var _ = OnGameEnded?.Invoke(this, userName, userId);
|
||||||
CurrentPhase = Phase.Ended;
|
CurrentPhase = Phase.Ended;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -127,7 +127,7 @@ namespace NadekoBot.Modules.Games.Common.Hangman
|
|||||||
else if (Term.Word.All(x => _previousGuesses.IsSupersetOf(Term.Word.ToLowerInvariant()
|
else if (Term.Word.All(x => _previousGuesses.IsSupersetOf(Term.Word.ToLowerInvariant()
|
||||||
.Where(char.IsLetterOrDigit))))
|
.Where(char.IsLetterOrDigit))))
|
||||||
{
|
{
|
||||||
var _ = OnGameEnded.Invoke(this, userName); // if all letters are guessed
|
var _ = OnGameEnded.Invoke(this, userName, userId); // if all letters are guessed
|
||||||
CurrentPhase = Phase.Ended;
|
CurrentPhase = Phase.Ended;
|
||||||
}
|
}
|
||||||
else // guessed but not last letter
|
else // guessed but not last letter
|
||||||
|
@@ -8,6 +8,7 @@ using NadekoBot.Common.Attributes;
|
|||||||
using NadekoBot.Modules.Games.Common.Hangman;
|
using NadekoBot.Modules.Games.Common.Hangman;
|
||||||
using NadekoBot.Modules.Games.Services;
|
using NadekoBot.Modules.Games.Services;
|
||||||
using NadekoBot.Modules.Games.Common.Hangman.Exceptions;
|
using NadekoBot.Modules.Games.Common.Hangman.Exceptions;
|
||||||
|
using NadekoBot.Services;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Games
|
namespace NadekoBot.Modules.Games
|
||||||
{
|
{
|
||||||
@@ -17,10 +18,14 @@ namespace NadekoBot.Modules.Games
|
|||||||
public class HangmanCommands : NadekoSubmodule<GamesService>
|
public class HangmanCommands : NadekoSubmodule<GamesService>
|
||||||
{
|
{
|
||||||
private readonly DiscordSocketClient _client;
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly ICurrencyService _cs;
|
||||||
|
private readonly GamesConfigService _gcs;
|
||||||
|
|
||||||
public HangmanCommands(DiscordSocketClient client)
|
public HangmanCommands(DiscordSocketClient client, ICurrencyService cs, GamesConfigService gcs)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
|
_cs = cs;
|
||||||
|
_gcs = gcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
@@ -83,7 +88,7 @@ namespace NadekoBot.Modules.Games
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Task Hm_OnGameEnded(Hangman game, string winner)
|
Task Hm_OnGameEnded(Hangman game, string winner, ulong userId)
|
||||||
{
|
{
|
||||||
if (winner is null)
|
if (winner is null)
|
||||||
{
|
{
|
||||||
@@ -99,6 +104,10 @@ namespace NadekoBot.Modules.Games
|
|||||||
return ctx.Channel.EmbedAsync(loseEmbed);
|
return ctx.Channel.EmbedAsync(loseEmbed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reward = _gcs.Data.Hangman.CurrencyReward;
|
||||||
|
if (reward > 0)
|
||||||
|
_cs.AddAsync(userId, "hangman win", reward, true);
|
||||||
|
|
||||||
var winEmbed = _eb.Create().WithTitle($"Hangman Game ({game.TermType}) - Ended")
|
var winEmbed = _eb.Create().WithTitle($"Hangman Game ({game.TermType}) - Ended")
|
||||||
.WithDescription(Format.Bold($"{winner} Won."))
|
.WithDescription(Format.Bold($"{winner} Won."))
|
||||||
.AddField("It was", game.Term.GetWord())
|
.AddField("It was", game.Term.GetWord())
|
||||||
|
@@ -18,6 +18,25 @@ namespace NadekoBot.Modules.Games.Services
|
|||||||
ConfigPrinters.ToString, val => val > 0);
|
ConfigPrinters.ToString, val => val > 0);
|
||||||
AddParsedProp("trivia.currency_reward", gs => gs.Trivia.CurrencyReward, long.TryParse,
|
AddParsedProp("trivia.currency_reward", gs => gs.Trivia.CurrencyReward, long.TryParse,
|
||||||
ConfigPrinters.ToString, val => val >= 0);
|
ConfigPrinters.ToString, val => val >= 0);
|
||||||
|
AddParsedProp("hangman.currency_reward", gs => gs.Hangman.CurrencyReward, long.TryParse,
|
||||||
|
ConfigPrinters.ToString, val => val >= 0);
|
||||||
|
|
||||||
|
Migrate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Migrate()
|
||||||
|
{
|
||||||
|
if (_data.Version < 1)
|
||||||
|
{
|
||||||
|
ModifyConfig(c =>
|
||||||
|
{
|
||||||
|
c.Version = 1;
|
||||||
|
c.Hangman = new HangmanConfig()
|
||||||
|
{
|
||||||
|
CurrencyReward = 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -434,7 +434,7 @@ namespace NadekoBot.Modules.Help
|
|||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
public async Task Donate()
|
public async Task Donate()
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.donate(PatreonUrl, PaypalUrl));
|
await ReplyConfirmLocalizedAsync(strs.donate(PatreonUrl, PaypalUrl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -45,6 +45,11 @@ namespace NadekoBot.Modules.Help.Services
|
|||||||
if (string.IsNullOrWhiteSpace(settings.DmHelpText) || settings.DmHelpText == "-")
|
if (string.IsNullOrWhiteSpace(settings.DmHelpText) || settings.DmHelpText == "-")
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
// only send dm help text if it contains one of the keywords, if they're specified
|
||||||
|
// if they're not, then reply to every DM
|
||||||
|
if (settings.DmHelpTextKeywords.Any() && !settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k)))
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
var rep = new ReplacementBuilder()
|
var rep = new ReplacementBuilder()
|
||||||
.WithOverride("%prefix%", () => _bss.Data.Prefix)
|
.WithOverride("%prefix%", () => _bss.Data.Prefix)
|
||||||
.WithOverride("%bot.prefix%", () => _bss.Data.Prefix)
|
.WithOverride("%bot.prefix%", () => _bss.Data.Prefix)
|
||||||
|
@@ -125,7 +125,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
queuedMessage?.DeleteAfter(10, _logService);
|
queuedMessage?.DeleteAfter(10, _logService);
|
||||||
if (mp.IsStopped)
|
if (mp.IsStopped)
|
||||||
{
|
{
|
||||||
var msg = await ReplyErrorLocalizedAsync(strs.queue_stopped(Format.Code(Prefix + "play")));
|
var msg = await ReplyPendingLocalizedAsync(strs.queue_stopped(Format.Code(Prefix + "play")));
|
||||||
msg.DeleteAfter(10, _logService);
|
msg.DeleteAfter(10, _logService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,7 +230,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
await _service.SetVolumeAsync(ctx.Guild.Id, vol);
|
await _service.SetVolumeAsync(ctx.Guild.Id, vol);
|
||||||
await ReplyErrorLocalizedAsync(strs.volume_set(vol));
|
await ReplyConfirmLocalizedAsync(strs.volume_set(vol));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
|
@@ -189,7 +189,7 @@ namespace NadekoBot.Modules.NSFW
|
|||||||
return t;
|
return t;
|
||||||
});
|
});
|
||||||
|
|
||||||
await ReplyErrorLocalizedAsync(strs.started(interval));
|
await ReplyConfirmLocalizedAsync(strs.started(interval));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
@@ -229,7 +229,7 @@ namespace NadekoBot.Modules.NSFW
|
|||||||
return t;
|
return t;
|
||||||
});
|
});
|
||||||
|
|
||||||
await ReplyErrorLocalizedAsync(strs.started(interval));
|
await ReplyConfirmLocalizedAsync(strs.started(interval));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -363,9 +363,9 @@ namespace NadekoBot.Modules.NSFW
|
|||||||
var added = _service.ToggleBlacklistedTag(ctx.Guild.Id, tag);
|
var added = _service.ToggleBlacklistedTag(ctx.Guild.Id, tag);
|
||||||
|
|
||||||
if (added)
|
if (added)
|
||||||
await ReplyErrorLocalizedAsync(strs.blacklisted_tag_add(tag));
|
await ReplyPendingLocalizedAsync(strs.blacklisted_tag_add(tag));
|
||||||
else
|
else
|
||||||
await ReplyErrorLocalizedAsync(strs.blacklisted_tag_remove(tag));
|
await ReplyPendingLocalizedAsync(strs.blacklisted_tag_remove(tag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -217,7 +217,7 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await ReplyErrorLocalizedAsync(strs.perm_out_of_range).ConfigureAwait(false);
|
await ReplyConfirmLocalizedAsync(strs.perm_out_of_range).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
|
@@ -5,6 +5,7 @@ using NadekoBot.Extensions;
|
|||||||
using NadekoBot.Modules.Searches.Services;
|
using NadekoBot.Modules.Searches.Services;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
@@ -15,6 +16,25 @@ namespace NadekoBot.Modules.Searches
|
|||||||
[Group]
|
[Group]
|
||||||
public class FeedCommands : NadekoSubmodule<FeedsService>
|
public class FeedCommands : NadekoSubmodule<FeedsService>
|
||||||
{
|
{
|
||||||
|
private static readonly Regex YtChannelRegex =
|
||||||
|
new Regex(@"youtube\.com\/(?:c\/|channel\/|user\/)?(?<channelid>[a-zA-Z0-9\-]{1,})");
|
||||||
|
|
||||||
|
[NadekoCommand, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.ManageMessages)]
|
||||||
|
public Task YtUploadNotif(string url, [Leftover] ITextChannel channel = null)
|
||||||
|
{
|
||||||
|
var m = YtChannelRegex.Match(url);
|
||||||
|
if (!m.Success)
|
||||||
|
{
|
||||||
|
return ReplyErrorLocalizedAsync(strs.invalid_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
var channelId = m.Groups["channelid"].Value;
|
||||||
|
|
||||||
|
return Feed("https://www.youtube.com/feeds/videos.xml?channel_id=" + channelId, channel);
|
||||||
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageMessages)]
|
[UserPerm(GuildPerm.ManageMessages)]
|
||||||
@@ -46,7 +66,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await ReplyErrorLocalizedAsync(strs.feed_not_valid).ConfigureAwait(false);
|
await ReplyConfirmLocalizedAsync(strs.feed_not_valid).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
|
@@ -191,7 +191,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ReplyErrorLocalizedAsync(strs.stream_message_set_all(count));
|
await ReplyConfirmLocalizedAsync(strs.stream_message_set_all(count));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
|
@@ -118,7 +118,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
|
|
||||||
_searches.UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
|
_searches.UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
|
||||||
|
|
||||||
await ReplyErrorLocalizedAsync(strs.atl_set(from, to));
|
await ReplyConfirmLocalizedAsync(strs.atl_set(from, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
|
@@ -37,7 +37,7 @@ namespace NadekoBot.Modules.Utility
|
|||||||
public async Task AliasesClear()
|
public async Task AliasesClear()
|
||||||
{
|
{
|
||||||
var count = _service.ClearAliases(ctx.Guild.Id);
|
var count = _service.ClearAliases(ctx.Guild.Id);
|
||||||
await ReplyErrorLocalizedAsync(strs.aliases_cleared(count));
|
await ReplyConfirmLocalizedAsync(strs.aliases_cleared(count));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
|
@@ -148,7 +148,7 @@ namespace NadekoBot.Modules.Utility
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.reminder_deleted(index + 1));
|
await ReplyConfirmLocalizedAsync(strs.reminder_deleted(index + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -183,7 +183,9 @@ namespace NadekoBot.Modules.Utility
|
|||||||
private string GetRepeaterInfoString(RunningRepeater runner)
|
private string GetRepeaterInfoString(RunningRepeater runner)
|
||||||
{
|
{
|
||||||
var intervalString = Format.Bold(runner.Repeater.Interval.ToPrettyStringHM());
|
var intervalString = Format.Bold(runner.Repeater.Interval.ToPrettyStringHM());
|
||||||
var executesIn = runner.NextTime - DateTime.UtcNow;
|
var executesIn = runner.NextTime < DateTime.UtcNow
|
||||||
|
? TimeSpan.Zero
|
||||||
|
: runner.NextTime - DateTime.UtcNow;
|
||||||
var executesInString = Format.Bold(executesIn.ToPrettyStringHM());
|
var executesInString = Format.Bold(executesIn.ToPrettyStringHM());
|
||||||
var message = Format.Sanitize(runner.Repeater.Message.TrimTo(50));
|
var message = Format.Sanitize(runner.Repeater.Message.TrimTo(50));
|
||||||
|
|
||||||
|
@@ -76,7 +76,9 @@ where ((guildid >> 22) % {_creds.TotalShards}) == {_client.ShardId};")
|
|||||||
// because repeaters might've been modified meanwhile
|
// because repeaters might've been modified meanwhile
|
||||||
if (timeout > TimeSpan.Zero)
|
if (timeout > TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
await Task.Delay(timeout);
|
await Task.Delay(timeout > TimeSpan.FromMinutes(1)
|
||||||
|
? TimeSpan.FromMinutes(1)
|
||||||
|
: timeout);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,16 +86,17 @@ where ((guildid >> 22) % {_creds.TotalShards}) == {_client.ShardId};")
|
|||||||
var now = DateTime.UtcNow + TimeSpan.FromSeconds(3);
|
var now = DateTime.UtcNow + TimeSpan.FromSeconds(3);
|
||||||
|
|
||||||
var toExecute = new List<RunningRepeater>();
|
var toExecute = new List<RunningRepeater>();
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
lock (_repeaterQueue)
|
lock (_repeaterQueue)
|
||||||
{
|
{
|
||||||
var current = _repeaterQueue.First;
|
var current = _repeaterQueue.First;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
|
||||||
if (current is null || current.Value.NextTime > now)
|
if (current is null || current.Value.NextTime > now)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
toExecute.Add(current.Value);
|
toExecute.Add(current.Value);
|
||||||
_repeaterQueue.RemoveFirst();
|
current = current.Next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,13 +124,23 @@ where ((guildid >> 22) % {_creds.TotalShards}) == {_client.ShardId};")
|
|||||||
{
|
{
|
||||||
if (rep.ErrorCount >= 10)
|
if (rep.ErrorCount >= 10)
|
||||||
{
|
{
|
||||||
|
RemoveFromQueue(rep.Repeater.Id);
|
||||||
await RemoveRepeaterInternal(rep.Repeater);
|
await RemoveRepeaterInternal(rep.Repeater);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdatePosition(rep);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePosition(RunningRepeater rep)
|
||||||
|
{
|
||||||
|
lock (_queueLocker)
|
||||||
|
{
|
||||||
rep.UpdateNextTime();
|
rep.UpdateNextTime();
|
||||||
|
_repeaterQueue.Remove(rep);
|
||||||
AddToQueue(rep);
|
AddToQueue(rep);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> TriggerExternal(ulong guildId, int index)
|
public async Task<bool> TriggerExternal(ulong guildId, int index)
|
||||||
{
|
{
|
||||||
|
@@ -92,5 +92,15 @@ namespace NadekoBot.Modules.Utility.Services
|
|||||||
var initialIntervalMultiplier = 1 - (triggerCount - Math.Truncate(triggerCount));
|
var initialIntervalMultiplier = 1 - (triggerCount - Math.Truncate(triggerCount));
|
||||||
return DateTime.UtcNow + (Repeater.Interval * initialIntervalMultiplier);
|
return DateTime.UtcNow + (Repeater.Interval * initialIntervalMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj is RunningRepeater rr && rr.Repeater.Id == this.Repeater.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return this.Repeater.Id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Xp
|
|||||||
|
|
||||||
_service.XpReset(ctx.Guild.Id, userId);
|
_service.XpReset(ctx.Guild.Id, userId);
|
||||||
|
|
||||||
await ReplyErrorLocalizedAsync(strs.reset_user(userId));
|
await ReplyConfirmLocalizedAsync(strs.reset_user(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
|
@@ -30,7 +30,7 @@ namespace NadekoBot.Modules.Xp.Services
|
|||||||
|
|
||||||
private void Migrate()
|
private void Migrate()
|
||||||
{
|
{
|
||||||
if (_data.Version <= 1)
|
if (_data.Version < 2)
|
||||||
{
|
{
|
||||||
ModifyConfig(c =>
|
ModifyConfig(c =>
|
||||||
{
|
{
|
||||||
|
@@ -128,7 +128,7 @@ namespace NadekoBot.Modules.Xp
|
|||||||
public async Task XpRoleReward(int level)
|
public async Task XpRoleReward(int level)
|
||||||
{
|
{
|
||||||
_service.ResetRoleReward(ctx.Guild.Id, level);
|
_service.ResetRoleReward(ctx.Guild.Id, level);
|
||||||
await ReplyErrorLocalizedAsync(strs.xp_role_reward_cleared(level));
|
await ReplyConfirmLocalizedAsync(strs.xp_role_reward_cleared(level));
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
|
@@ -173,6 +173,12 @@ namespace NadekoBot.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetBoostMessage(ulong gid)
|
||||||
|
{
|
||||||
|
using var uow = _db.GetDbContext();
|
||||||
|
return uow.GuildConfigsForId(gid, set => set).BoostMessage;
|
||||||
|
}
|
||||||
|
|
||||||
private Task ByeUsers(GreetSettings conf, ITextChannel channel, IUser user)
|
private Task ByeUsers(GreetSettings conf, ITextChannel channel, IUser user)
|
||||||
=> ByeUsers(conf, channel, new[] {user});
|
=> ByeUsers(conf, channel, new[] {user});
|
||||||
private async Task ByeUsers(GreetSettings conf, ITextChannel channel, IEnumerable<IUser> users)
|
private async Task ByeUsers(GreetSettings conf, ITextChannel channel, IEnumerable<IUser> users)
|
||||||
|
@@ -19,7 +19,7 @@ namespace NadekoBot.Services
|
|||||||
private readonly IBotCredentials _creds;
|
private readonly IBotCredentials _creds;
|
||||||
private readonly DateTime _started;
|
private readonly DateTime _started;
|
||||||
|
|
||||||
public const string BotVersion = "3.0.2";
|
public const string BotVersion = "3.0.4";
|
||||||
public string Author => "Kwoth#2452";
|
public string Author => "Kwoth#2452";
|
||||||
public string Library => "Discord.Net";
|
public string Library => "Discord.Net";
|
||||||
|
|
||||||
|
@@ -28,19 +28,15 @@ namespace NadekoBot.Services
|
|||||||
AddParsedProp("locale", bs => bs.DefaultLocale, ConfigParsers.Culture, ConfigPrinters.Culture);
|
AddParsedProp("locale", bs => bs.DefaultLocale, ConfigParsers.Culture, ConfigPrinters.Culture);
|
||||||
AddParsedProp("prefix", bs => bs.Prefix, ConfigParsers.String, ConfigPrinters.ToString);
|
AddParsedProp("prefix", bs => bs.Prefix, ConfigParsers.String, ConfigPrinters.ToString);
|
||||||
|
|
||||||
UpdateColors();
|
Migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateColors()
|
private void Migrate()
|
||||||
{
|
{
|
||||||
var ok = _data.Color.Ok;
|
if (_data.Version < 2)
|
||||||
var error = _data.Color.Error;
|
|
||||||
var pend = _data.Color.Pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnStateUpdate()
|
|
||||||
{
|
{
|
||||||
UpdateColors();
|
ModifyConfig(c => c.Version = 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1140,6 +1140,9 @@ autobutts:
|
|||||||
eightball:
|
eightball:
|
||||||
- eightball
|
- eightball
|
||||||
- 8ball
|
- 8ball
|
||||||
|
ytuploadnotif:
|
||||||
|
- ytuploadnotif
|
||||||
|
- yun
|
||||||
feed:
|
feed:
|
||||||
- feed
|
- feed
|
||||||
- feedadd
|
- feedadd
|
||||||
@@ -1193,6 +1196,8 @@ crypto:
|
|||||||
rolelevelreq:
|
rolelevelreq:
|
||||||
- rolelevelreq
|
- rolelevelreq
|
||||||
- rlr
|
- rlr
|
||||||
|
massban:
|
||||||
|
- massban
|
||||||
masskill:
|
masskill:
|
||||||
- masskill
|
- masskill
|
||||||
pathofexile:
|
pathofexile:
|
||||||
@@ -1250,3 +1255,7 @@ crsimport:
|
|||||||
crsexport:
|
crsexport:
|
||||||
- crsexport
|
- crsexport
|
||||||
- exexport
|
- exexport
|
||||||
|
imageonlychannel:
|
||||||
|
- imageonlychannel
|
||||||
|
- imageonly
|
||||||
|
- imagesonly
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# DO NOT CHANGE
|
# DO NOT CHANGE
|
||||||
version: 1
|
version: 2
|
||||||
# Most commands, when executed, have a small colored line
|
# Most commands, when executed, have a small colored line
|
||||||
# next to the response. The color depends whether the command
|
# next to the response. The color depends whether the command
|
||||||
# is completed, errored or in progress (pending)
|
# is completed, errored or in progress (pending)
|
||||||
@@ -28,6 +28,15 @@ forwardToAllOwners: false
|
|||||||
# Supports embeds. How it looks: https://puu.sh/B0BLV.png
|
# Supports embeds. How it looks: https://puu.sh/B0BLV.png
|
||||||
dmHelpText: |-
|
dmHelpText: |-
|
||||||
{"description": "Type `%prefix%h` for help."}
|
{"description": "Type `%prefix%h` for help."}
|
||||||
|
# Only users who send a DM to the bot containing one of the specified words will get a DmHelpText response.
|
||||||
|
# Case insensitive.
|
||||||
|
# Leave empty to reply with DmHelpText to every DM.
|
||||||
|
dmHelpTextKeywords:
|
||||||
|
- help
|
||||||
|
- commands
|
||||||
|
- cmds
|
||||||
|
- module
|
||||||
|
- can you do
|
||||||
# This is the response for the .h command
|
# This is the response for the .h command
|
||||||
helpText: |-
|
helpText: |-
|
||||||
{
|
{
|
||||||
|
@@ -1,3 +1,9 @@
|
|||||||
|
# DO NOT CHANGE
|
||||||
|
version: 1
|
||||||
|
# Hangman related settings (.hangman command)
|
||||||
|
hangman:
|
||||||
|
# The amount of currency awarded to the winner of a hangman game
|
||||||
|
currencyReward: 0
|
||||||
# Trivia related settings (.t command)
|
# Trivia related settings (.t command)
|
||||||
trivia:
|
trivia:
|
||||||
# The amount of currency awarded to the winner of the trivia game.
|
# The amount of currency awarded to the winner of the trivia game.
|
||||||
|
@@ -1912,6 +1912,12 @@ eightball:
|
|||||||
desc: "Ask the 8ball a yes/no question."
|
desc: "Ask the 8ball a yes/no question."
|
||||||
args:
|
args:
|
||||||
- "Is b1nzy a nice guy?"
|
- "Is b1nzy a nice guy?"
|
||||||
|
ytuploadnotif:
|
||||||
|
desc: |-
|
||||||
|
Subscribe to a youtube channel's upload rss feed.
|
||||||
|
Shortcut for `.feed https://www.youtube.com/feeds/videos.xml?channel_id=%3Cyoutube_channel_id`
|
||||||
|
args:
|
||||||
|
- "https://www.youtube.com/channel/UCSJ4gkVC6NrvII8umztf0Ow"
|
||||||
feed:
|
feed:
|
||||||
desc: "Subscribes to a feed. Bot will post an update up to once every 10 seconds. You can have up to 10 feeds on one server. All feeds must have unique URLs. Set a channel as a second optional parameter to specify where to send the updates."
|
desc: "Subscribes to a feed. Bot will post an update up to once every 10 seconds. You can have up to 10 feeds on one server. All feeds must have unique URLs. Set a channel as a second optional parameter to specify where to send the updates."
|
||||||
args:
|
args:
|
||||||
@@ -2010,6 +2016,10 @@ rolelevelreq:
|
|||||||
desc: "Set a level requirement on a self-assignable role."
|
desc: "Set a level requirement on a self-assignable role."
|
||||||
args:
|
args:
|
||||||
- "5 SomeRole"
|
- "5 SomeRole"
|
||||||
|
massban:
|
||||||
|
desc: "Bans multiple users at once. Specify a space separated list of IDs of users who you wish to ban."
|
||||||
|
args:
|
||||||
|
- "123123123 3333333333 444444444"
|
||||||
masskill:
|
masskill:
|
||||||
desc: "Specify a new-line separated list of `userid reason`. You can use Username#discrim instead of UserId. Specified users will be banned from the current server, blacklisted from the bot, and have all of their flowers taken away."
|
desc: "Specify a new-line separated list of `userid reason`. You can use Username#discrim instead of UserId. Specified users will be banned from the current server, blacklisted from the bot, and have all of their flowers taken away."
|
||||||
args:
|
args:
|
||||||
@@ -2104,3 +2114,9 @@ nhentai:
|
|||||||
args:
|
args:
|
||||||
- "273426"
|
- "273426"
|
||||||
- "cute girl"
|
- "cute girl"
|
||||||
|
imageonlychannel:
|
||||||
|
desc: |-
|
||||||
|
Toggles whether the channel only allows images.
|
||||||
|
Users who send more than a few non-image messages will be banned from using the channel.
|
||||||
|
args:
|
||||||
|
- ""
|
@@ -889,6 +889,8 @@
|
|||||||
"self_assign_not_level": "That self-assignable role requires at least server level {0}.",
|
"self_assign_not_level": "That self-assignable role requires at least server level {0}.",
|
||||||
"invalid": "Invalid / Can't be found ({0})",
|
"invalid": "Invalid / Can't be found ({0})",
|
||||||
"mass_kill_in_progress": "Mass Banning and Blacklisting of {0} users is in progress...",
|
"mass_kill_in_progress": "Mass Banning and Blacklisting of {0} users is in progress...",
|
||||||
|
"mass_ban_in_progress": "Banning {0} users...",
|
||||||
|
"mass_ban_completed": "Banned {0} users.",
|
||||||
"mass_kill_completed": "Mass Banning and Blacklisting of {0} users is complete.",
|
"mass_kill_completed": "Mass Banning and Blacklisting of {0} users is complete.",
|
||||||
"failed_finding_novel": "Can't find that novel. Make sure you've typed the exact full name, and that it exists on novelupdates.com",
|
"failed_finding_novel": "Can't find that novel. Make sure you've typed the exact full name, and that it exists on novelupdates.com",
|
||||||
"club_transfered": "Ownership of the club {0} has been transferred to {1}",
|
"club_transfered": "Ownership of the club {0} has been transferred to {1}",
|
||||||
@@ -960,5 +962,7 @@
|
|||||||
"empty_page": "This page is empty.",
|
"empty_page": "This page is empty.",
|
||||||
"pages": "Pages",
|
"pages": "Pages",
|
||||||
"favorites": "Favorites",
|
"favorites": "Favorites",
|
||||||
"tags": "Tags"
|
"tags": "Tags",
|
||||||
|
"imageonly_enable": "This channel is now image-only.",
|
||||||
|
"imageonly_disable": "This channel is no longer image-only."
|
||||||
}
|
}
|
||||||
|
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user