Compare commits

...

16 Commits
3.0.2 ... 3.0.3

Author SHA1 Message Date
Kwoth
ed039977c2 Release 3.0.3 2021-09-15 19:07:36 +02:00
Kwoth
786ede3290 Added .config games hangman.currency_reward and a property with the same name in games.yml 2021-09-15 01:50:48 +02:00
Kwoth
3edcca4927 Updated CHANGELOG.md, removed an unused enum 2021-09-15 01:30:26 +02:00
Kwoth
0635a0ddc8 Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-09-15 01:30:06 +02:00
Kwoth
cb514e4219 Update responses.es-ES.json (POEditor.com) 2021-09-14 23:29:46 +00:00
Kwoth
cccb37854c Added .imageonlychannel / .imageonly to prevent users from posting anything but images in the channel 2021-09-15 01:26:41 +02:00
Kwoth
35d549f4e6 Fixed .yun command help 2021-09-14 01:27:37 +02:00
Kwoth
c0d81a5d9c as a shortcut for subscribing to a youtube channel's rss feed**** 2021-09-14 01:11:03 +02:00
Kwoth
711d6c7caa Added .youtubeuploadnotif / .yun as a shortcut to a subscribing channel's rss feed 2021-09-14 01:09:31 +02:00
Kwoth
cea944cdb8 Update responses.uk-UA.json (POEditor.com) 2021-09-13 15:42:50 +00:00
Kwoth
2d70ea487e Update responses.ru-RU.json (POEditor.com) 2021-09-13 15:42:49 +00:00
Kwoth
9f65f979fd Update responses.fr-FR.json (POEditor.com) 2021-09-13 15:42:48 +00:00
Kwoth
c12b41ddd1 Added .massban 2021-09-13 17:41:33 +02:00
Kwoth
a5f9ac1540 .boostmsg will now properly show boost, and not greet message 2021-09-13 12:59:47 +02:00
Kwoth
8c5214def2 Ban .warnp will now prune user's messages 2021-09-13 01:30:11 +02:00
Kwoth
467a8bdaf6 Updated CHANGELOG.md 2021-09-12 22:09:32 +02:00
30 changed files with 6979 additions and 3787 deletions

View File

@@ -4,6 +4,29 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
## Unreleased
## [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
- `.rero` now optionally takes a message id to which to attach the reaction roles
@@ -20,6 +43,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
### Fixed
- `.timely` will now correctly use `Ok` color
- Fixed `.log` commands
### Removed

View File

@@ -2,12 +2,10 @@
using System.Globalization;
using System.Linq;
using System.Reflection;
using AngleSharp.Common;
using Discord.Commands;
using NadekoBot.Common.Attributes;
using NadekoBot.Services;
using NadekoBot.Modules;
using YamlDotNet.Serialization;
namespace NadekoBot.Tests
{

View File

@@ -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,
}
}

View File

@@ -0,0 +1,8 @@
namespace NadekoBot.Services.Database.Models
{
public class ImageOnlyChannel : DbEntity
{
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }
}
}

View File

@@ -2,7 +2,6 @@
namespace NadekoBot.Services.Database.Models
{
public class LogSetting : DbEntity
{
public HashSet<IgnoredLogChannel> IgnoredChannels { get; set; } = new HashSet<IgnoredLogChannel>();

View File

@@ -2,11 +2,9 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using NadekoBot.Services.Database.Models;
using NadekoBot.Services;
using System;
using System.IO;
using Microsoft.Extensions.Logging;
using NadekoBot.Common;
using NadekoBot.Db.Models;
namespace NadekoBot.Services.Database
@@ -60,6 +58,7 @@ namespace NadekoBot.Services.Database
public DbSet<Repeater> Repeaters { get; set; }
public DbSet<Poll> Poll { get; set; }
public DbSet<WaifuInfo> WaifuInfo { get; set; }
public DbSet<ImageOnlyChannel> ImageOnlyChannels { get; set; }
public NadekoContext(DbContextOptions<NadekoContext> options) : base(options)
{
@@ -345,6 +344,10 @@ namespace NadekoBot.Services.Database
.IsUnique());
#endregion
modelBuilder.Entity<ImageOnlyChannel>(ioc => ioc
.HasIndex(x => x.ChannelId)
.IsUnique());
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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");
}
}
}

View File

@@ -187,6 +187,29 @@ namespace NadekoBot.Migrations
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 =>
{
b.Property<int>("Id")

View File

@@ -13,13 +13,31 @@ namespace NadekoBot.Modules.Administration
{
public partial class Administration : NadekoModule<AdministrationService>
{
private readonly ImageOnlyChannelService _imageOnly;
public Administration(ImageOnlyChannelService imageOnly)
{
_imageOnly = imageOnly;
}
public enum List
{
List = 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]
[RequireContext(ContextType.Guild)]

View File

@@ -41,6 +41,15 @@ namespace NadekoBot.Modules.Administration
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]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
@@ -48,7 +57,7 @@ namespace NadekoBot.Modules.Administration
{
if (string.IsNullOrWhiteSpace(text))
{
await GreetMsg().ConfigureAwait(false);
await BoostMsg().ConfigureAwait(false);
return;
}

View File

@@ -1,9 +1,7 @@
using System;
using Discord;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common;
using NadekoBot.Common.Collections;
using NadekoBot.Common.Replacements;
using NadekoBot.Services;
@@ -24,14 +22,11 @@ namespace NadekoBot.Modules.Administration.Services
private readonly DbService _db;
private readonly ILogCommandService _logService;
private readonly IEmbedBuilderService _eb;
public AdministrationService(Bot bot, CommandHandler cmdHandler, DbService db,
ILogCommandService logService, IEmbedBuilderService eb)
public AdministrationService(Bot bot, CommandHandler cmdHandler, DbService db, ILogCommandService logService)
{
_db = db;
_logService = logService;
_eb = eb;
DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(bot.AllGuildConfigs
.Where(g => g.DeleteMessageOnCommand)

View File

@@ -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;
}
}

View File

@@ -123,7 +123,7 @@ namespace NadekoBot.Modules.Administration.Services
break;
case PunishmentAction.Ban:
if (minutes == 0)
await guild.AddBanAsync(user, reason: reason).ConfigureAwait(false);
await guild.AddBanAsync(user, reason: reason, pruneDays: 7).ConfigureAwait(false);
else
await _mute.TimedBan(user.Guild, user, TimeSpan.FromMinutes(minutes), reason)
.ConfigureAwait(false);

View File

@@ -9,9 +9,11 @@ using NadekoBot.Services.Database.Models;
using NadekoBot.Extensions;
using NadekoBot.Modules.Administration.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NadekoBot.Modules.Permissions.Services;
using NadekoBot.Modules.Searches.Common;
using Serilog;
namespace NadekoBot.Modules.Administration
@@ -762,6 +764,78 @@ namespace NadekoBot.Modules.Administration
await ctx.Channel.EmbedAsync(toSend)
.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]
[RequireContext(ContextType.Guild)]
@@ -783,7 +857,7 @@ namespace NadekoBot.Modules.Administration
var banningMessageTask = ctx.Channel.EmbedAsync(_eb.Create()
.WithDescription(GetText(strs.mass_kill_in_progress(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr)
.WithOkColor());
.WithPendingColor());
//do the banning
await Task.WhenAll(bans

View File

@@ -8,6 +8,15 @@ namespace NadekoBot.Modules.Games.Common
[Cloneable]
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)")]
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]
public sealed partial class TriviaConfig
{

View File

@@ -45,7 +45,7 @@ namespace NadekoBot.Modules.Games.Common.Hangman
public uint Errors { get; private set; } = 0;
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> OnGuessFailed = 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)
{
var _ = OnGameEnded(this, null);
var _ = OnGameEnded(this, null, 0);
CurrentPhase = Phase.Ended;
}
}
@@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Games.Common.Hangman
if (input != Term.Word) // failed
return;
var _ = OnGameEnded?.Invoke(this, userName);
var _ = OnGameEnded?.Invoke(this, userName, userId);
CurrentPhase = Phase.Ended;
return;
}
@@ -127,7 +127,7 @@ namespace NadekoBot.Modules.Games.Common.Hangman
else if (Term.Word.All(x => _previousGuesses.IsSupersetOf(Term.Word.ToLowerInvariant()
.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;
}
else // guessed but not last letter

View File

@@ -8,6 +8,7 @@ using NadekoBot.Common.Attributes;
using NadekoBot.Modules.Games.Common.Hangman;
using NadekoBot.Modules.Games.Services;
using NadekoBot.Modules.Games.Common.Hangman.Exceptions;
using NadekoBot.Services;
namespace NadekoBot.Modules.Games
{
@@ -17,10 +18,14 @@ namespace NadekoBot.Modules.Games
public class HangmanCommands : NadekoSubmodule<GamesService>
{
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;
_cs = cs;
_gcs = gcs;
}
[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)
{
@@ -99,6 +104,10 @@ namespace NadekoBot.Modules.Games
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")
.WithDescription(Format.Bold($"{winner} Won."))
.AddField("It was", game.Term.GetWord())

View File

@@ -18,6 +18,25 @@ namespace NadekoBot.Modules.Games.Services
ConfigPrinters.ToString, val => val > 0);
AddParsedProp("trivia.currency_reward", gs => gs.Trivia.CurrencyReward, long.TryParse,
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
};
});
}
}
}
}

View File

@@ -5,6 +5,7 @@ using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Services;
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Serilog;
@@ -15,6 +16,25 @@ namespace NadekoBot.Modules.Searches
[Group]
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]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)]

View File

@@ -172,6 +172,12 @@ namespace NadekoBot.Services
return uow.GuildConfigsForId(gid, set => set).ChannelGreetMessageText;
}
}
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)
=> ByeUsers(conf, channel, new[] {user});

View File

@@ -19,7 +19,7 @@ namespace NadekoBot.Services
private readonly IBotCredentials _creds;
private readonly DateTime _started;
public const string BotVersion = "3.0.2";
public const string BotVersion = "3.0.3";
public string Author => "Kwoth#2452";
public string Library => "Discord.Net";

View File

@@ -1140,6 +1140,9 @@ autobutts:
eightball:
- eightball
- 8ball
ytuploadnotif:
- ytuploadnotif
- yun
feed:
- feed
- feedadd
@@ -1193,6 +1196,8 @@ crypto:
rolelevelreq:
- rolelevelreq
- rlr
massban:
- massban
masskill:
- masskill
pathofexile:
@@ -1249,4 +1254,8 @@ crsimport:
- eximport
crsexport:
- crsexport
- exexport
- exexport
imageonlychannel:
- imageonlychannel
- imageonly
- imagesonly

View File

@@ -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:
# The amount of currency awarded to the winner of the trivia game.

View File

@@ -1912,6 +1912,12 @@ eightball:
desc: "Ask the 8ball a yes/no question."
args:
- "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:
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:
@@ -2010,6 +2016,10 @@ rolelevelreq:
desc: "Set a level requirement on a self-assignable role."
args:
- "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:
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:
@@ -2104,3 +2114,9 @@ nhentai:
args:
- "273426"
- "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:
- ""

View File

@@ -889,6 +889,8 @@
"self_assign_not_level": "That self-assignable role requires at least server level {0}.",
"invalid": "Invalid / Can't be found ({0})",
"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.",
"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}",
@@ -960,5 +962,7 @@
"empty_page": "This page is empty.",
"pages": "Pages",
"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