mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-12 18:28:27 -04:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3fc53b0609 | ||
|
62ec2241e4 | ||
|
75f8254a8e | ||
|
f23ffe0c67 | ||
|
d1a818542c | ||
|
15f67e3a51 | ||
|
412f346ac8 | ||
|
8107a80c4c |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -2,6 +2,22 @@
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
## [4.3.7]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `.exprdelserv` (.exds) to completement .exas. Deletes an expression on the current server and is susceptible to .dpo, unlike .exd
|
||||||
|
- Added `.shopreq` which lets you set role requirement for specific shop items
|
||||||
|
- Added `.shopbuy` alias to `.buy`
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed `.convertlist` showing currencies twice (this may not apply to existing users and it may require you to manually remove all currencies from units.json)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Removed `Viewer` field from stream online notification as it is (almost?) always 0.
|
||||||
|
|
||||||
## [4.3.6] - 08.09.2022
|
## [4.3.6] - 08.09.2022
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@@ -24,6 +24,7 @@ public class ShopEntry : DbEntity, IIndexed
|
|||||||
|
|
||||||
//list
|
//list
|
||||||
public HashSet<ShopEntryItem> Items { get; set; } = new();
|
public HashSet<ShopEntryItem> Items { get; set; } = new();
|
||||||
|
public ulong? RoleRequirement { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ShopEntryItem : DbEntity
|
public class ShopEntryItem : DbEntity
|
||||||
|
3550
src/NadekoBot/Migrations/Mysql/20220913192520_shop-role-req.Designer.cs
generated
Normal file
3550
src/NadekoBot/Migrations/Mysql/20220913192520_shop-role-req.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.Mysql
|
||||||
|
{
|
||||||
|
public partial class shoprolereq : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "rolerequirement",
|
||||||
|
table: "shopentry",
|
||||||
|
type: "bigint unsigned",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "rolerequirement",
|
||||||
|
table: "shopentry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3548
src/NadekoBot/Migrations/Mysql/MysqlContextModelSnapshot.cs
Normal file
3548
src/NadekoBot/Migrations/Mysql/MysqlContextModelSnapshot.cs
Normal file
File diff suppressed because it is too large
Load Diff
3694
src/NadekoBot/Migrations/PostgreSql/20220913192529_shop-role-req.Designer.cs
generated
Normal file
3694
src/NadekoBot/Migrations/PostgreSql/20220913192529_shop-role-req.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
public partial class shoprolereq : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<decimal>(
|
||||||
|
name: "rolerequirement",
|
||||||
|
table: "shopentry",
|
||||||
|
type: "numeric(20,0)",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "rolerequirement",
|
||||||
|
table: "shopentry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
2849
src/NadekoBot/Migrations/Sqlite/20220913190532_shop-role-req.Designer.cs
generated
Normal file
2849
src/NadekoBot/Migrations/Sqlite/20220913190532_shop-role-req.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
public partial class shoprolereq : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "RoleRequirement",
|
||||||
|
table: "ShopEntry",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "RoleRequirement",
|
||||||
|
table: "ShopEntry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1745,6 +1745,9 @@ namespace NadekoBot.Migrations
|
|||||||
b.Property<string>("RoleName")
|
b.Property<string>("RoleName")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong?>("RoleRequirement")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int>("Type")
|
b.Property<int>("Type")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
@@ -67,15 +67,23 @@ public partial class Administration
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
public async Task ReactionRolesList()
|
public async Task ReactionRolesList(int page = 1)
|
||||||
{
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
var reros = await _rero.GetReactionRolesAsync(ctx.Guild.Id);
|
var reros = await _rero.GetReactionRolesAsync(ctx.Guild.Id);
|
||||||
|
|
||||||
|
await ctx.SendPaginatedConfirmAsync(page, curPage =>
|
||||||
|
{
|
||||||
var embed = _eb.Create(ctx)
|
var embed = _eb.Create(ctx)
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
var content = string.Empty;
|
var content = string.Empty;
|
||||||
foreach (var g in reros.GroupBy(x => x.MessageId).OrderBy(x => x.Key))
|
foreach (var g in reros.OrderBy(x => x.Group)
|
||||||
|
.Skip(curPage * 10)
|
||||||
|
.GroupBy(x => x.MessageId)
|
||||||
|
.OrderBy(x => x.Key))
|
||||||
{
|
{
|
||||||
var messageId = g.Key;
|
var messageId = g.Key;
|
||||||
content +=
|
content +=
|
||||||
@@ -89,7 +97,8 @@ public partial class Administration
|
|||||||
|
|
||||||
foreach (var rero in ggs)
|
foreach (var rero in ggs)
|
||||||
{
|
{
|
||||||
content += $"\t{rero.Emote} -> {(ctx.Guild.GetRole(rero.RoleId)?.Mention ?? "<missing role>")}";
|
content +=
|
||||||
|
$"\t{rero.Emote} -> {(ctx.Guild.GetRole(rero.RoleId)?.Mention ?? "<missing role>")}";
|
||||||
if (rero.LevelReq > 0)
|
if (rero.LevelReq > 0)
|
||||||
content += $" (lvl {rero.LevelReq}+)";
|
content += $" (lvl {rero.LevelReq}+)";
|
||||||
content += '\n';
|
content += '\n';
|
||||||
@@ -102,7 +111,8 @@ public partial class Administration
|
|||||||
? "There are no reaction roles on this server"
|
? "There are no reaction roles on this server"
|
||||||
: content);
|
: content);
|
||||||
|
|
||||||
await ctx.Channel.EmbedAsync(embed);
|
return embed;
|
||||||
|
}, reros.Count, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
|
@@ -160,15 +160,8 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
found.Response.TrimTo(1000).Replace("](", "]\\(")));
|
found.Response.TrimTo(1000).Replace("](", "]\\(")));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
public async Task ExprDeleteInternalAsync(kwum id)
|
||||||
public async Task ExprDelete(kwum id)
|
|
||||||
{
|
{
|
||||||
if (!AdminInGuildOrOwnerInDm())
|
|
||||||
{
|
|
||||||
await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ex = await _service.DeleteAsync(ctx.Guild?.Id, id);
|
var ex = await _service.DeleteAsync(ctx.Guild?.Id, id);
|
||||||
|
|
||||||
if (ex is not null)
|
if (ex is not null)
|
||||||
@@ -186,6 +179,24 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task ExprDeleteServer(kwum id)
|
||||||
|
=> await ExprDeleteInternalAsync(id);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task ExprDelete(kwum id)
|
||||||
|
{
|
||||||
|
if (!AdminInGuildOrOwnerInDm())
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ExprDeleteInternalAsync(id);
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task ExprReact(kwum id, params string[] emojiStrs)
|
public async Task ExprReact(kwum id, params string[] emojiStrs)
|
||||||
{
|
{
|
||||||
|
@@ -38,4 +38,6 @@ public interface IShopService
|
|||||||
/// <param name="toIndex">Destination index of the entry</param>
|
/// <param name="toIndex">Destination index of the entry</param>
|
||||||
/// <returns>Whether swap was successful</returns>
|
/// <returns>Whether swap was successful</returns>
|
||||||
Task<bool> MoveEntryAsync(ulong guildId, int fromIndex, int toIndex);
|
Task<bool> MoveEntryAsync(ulong guildId, int fromIndex, int toIndex);
|
||||||
|
|
||||||
|
Task<bool> SetItemRoleRequirementAsync(ulong guildId, int index, ulong? roleId);
|
||||||
}
|
}
|
@@ -98,6 +98,23 @@ public partial class Gambling
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entry.RoleRequirement is ulong reqRoleId)
|
||||||
|
{
|
||||||
|
var role = ctx.Guild.GetRole(reqRoleId);
|
||||||
|
if (role is null)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.shop_item_req_role_not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var guser = (IGuildUser)ctx.User;
|
||||||
|
if (!guser.RoleIds.Contains(reqRoleId))
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.shop_item_req_role_unfulfilled(Format.Bold(role.ToString())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (entry.Type == ShopEntryType.Role)
|
if (entry.Type == ShopEntryType.Role)
|
||||||
{
|
{
|
||||||
var guser = (IGuildUser)ctx.User;
|
var guser = (IGuildUser)ctx.User;
|
||||||
@@ -412,6 +429,27 @@ public partial class Gambling
|
|||||||
await ctx.ErrorAsync();
|
await ctx.ErrorAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
public async Task ShopReq(int itemIndex, [Leftover] IRole role = null)
|
||||||
|
{
|
||||||
|
if (--itemIndex < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var succ = await _service.SetItemRoleRequirementAsync(ctx.Guild.Id, itemIndex, role?.Id);
|
||||||
|
if (!succ)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.shop_item_not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role is null)
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.shop_item_role_no_req(itemIndex));
|
||||||
|
else
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.shop_item_role_req(itemIndex + 1, role));
|
||||||
|
}
|
||||||
|
|
||||||
public IEmbedBuilder EntryToEmbed(ShopEntry entry)
|
public IEmbedBuilder EntryToEmbed(ShopEntry entry)
|
||||||
{
|
{
|
||||||
var embed = _eb.Create().WithOkColor();
|
var embed = _eb.Create().WithOkColor();
|
||||||
@@ -443,11 +481,17 @@ public partial class Gambling
|
|||||||
|
|
||||||
public string EntryToString(ShopEntry entry)
|
public string EntryToString(ShopEntry entry)
|
||||||
{
|
{
|
||||||
|
var prepend = string.Empty;
|
||||||
|
if (entry.RoleRequirement is not null)
|
||||||
|
prepend = Format.Italics(GetText(strs.shop_item_requires_role($"<@&{entry.RoleRequirement}>")))
|
||||||
|
+ Environment.NewLine;
|
||||||
|
|
||||||
if (entry.Type == ShopEntryType.Role)
|
if (entry.Type == ShopEntryType.Role)
|
||||||
return GetText(strs.shop_role(Format.Bold(ctx.Guild.GetRole(entry.RoleId)?.Name ?? "MISSING_ROLE")));
|
return prepend
|
||||||
|
+ GetText(strs.shop_role(Format.Bold(ctx.Guild.GetRole(entry.RoleId)?.Name ?? "MISSING_ROLE")));
|
||||||
if (entry.Type == ShopEntryType.List)
|
if (entry.Type == ShopEntryType.List)
|
||||||
return GetText(strs.unique_items_left(entry.Items.Count)) + "\n" + entry.Name;
|
return prepend + GetText(strs.unique_items_left(entry.Items.Count)) + "\n" + entry.Name;
|
||||||
return "";
|
return prepend;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -94,4 +94,20 @@ public class ShopService : IShopService, INService
|
|||||||
await uow.SaveChangesAsync();
|
await uow.SaveChangesAsync();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SetItemRoleRequirementAsync(ulong guildId, int index, ulong? roleId)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
var entries = GetEntriesInternal(uow, guildId);
|
||||||
|
|
||||||
|
if (index >= entries.Count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var entry = entries[index];
|
||||||
|
|
||||||
|
entry.RoleRequirement = roleId;
|
||||||
|
|
||||||
|
await uow.SaveChangesAsync();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,6 +1,4 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
using LinqToDB;
|
|
||||||
using LinqToDB.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Nadeko.Common;
|
using Nadeko.Common;
|
||||||
using NadekoBot.Common.ModuleBehaviors;
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
@@ -12,95 +10,6 @@ using NadekoBot.Services.Database.Models;
|
|||||||
|
|
||||||
namespace NadekoBot.Modules.Searches.Services;
|
namespace NadekoBot.Modules.Searches.Services;
|
||||||
|
|
||||||
public sealed class StreamOnlineMessageDeleterService : INService, IReadyExecutor
|
|
||||||
{
|
|
||||||
private readonly StreamNotificationService _notifService;
|
|
||||||
private readonly DbService _db;
|
|
||||||
private readonly DiscordSocketClient _client;
|
|
||||||
private readonly IPubSub _pubSub;
|
|
||||||
|
|
||||||
public StreamOnlineMessageDeleterService(
|
|
||||||
StreamNotificationService notifService,
|
|
||||||
DbService db,
|
|
||||||
IPubSub pubSub,
|
|
||||||
DiscordSocketClient client)
|
|
||||||
{
|
|
||||||
_notifService = notifService;
|
|
||||||
_db = db;
|
|
||||||
_client = client;
|
|
||||||
_pubSub = pubSub;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task OnReadyAsync()
|
|
||||||
{
|
|
||||||
_notifService.OnlineMessagesSent += OnOnlineMessagesSent;
|
|
||||||
|
|
||||||
if(_client.ShardId == 0)
|
|
||||||
await _pubSub.Sub(_notifService.StreamsOfflineKey, OnStreamsOffline);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnOnlineMessagesSent(FollowedStream.FType type, string name, IReadOnlyCollection<(ulong, ulong)> pairs)
|
|
||||||
{
|
|
||||||
await using var ctx = _db.GetDbContext();
|
|
||||||
foreach (var (channelId, messageId) in pairs)
|
|
||||||
{
|
|
||||||
await ctx.GetTable<StreamOnlineMessage>()
|
|
||||||
.InsertAsync(() => new()
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
Type = type,
|
|
||||||
MessageId = messageId,
|
|
||||||
ChannelId = channelId,
|
|
||||||
DateAdded = DateTime.UtcNow,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async ValueTask OnStreamsOffline(List<StreamData> streamDatas)
|
|
||||||
{
|
|
||||||
if (_client.ShardId != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var pairs = await GetMessagesToDelete(streamDatas);
|
|
||||||
|
|
||||||
foreach (var (channelId, messageId) in pairs)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var textChannel = await _client.GetChannelAsync(channelId) as ITextChannel;
|
|
||||||
if (textChannel is null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
await textChannel.DeleteMessageAsync(messageId);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<IEnumerable<(ulong, ulong)>> GetMessagesToDelete(List<StreamData> streamDatas)
|
|
||||||
{
|
|
||||||
await using var ctx = _db.GetDbContext();
|
|
||||||
|
|
||||||
var toReturn = new List<(ulong, ulong)>();
|
|
||||||
foreach (var sd in streamDatas)
|
|
||||||
{
|
|
||||||
var key = sd.CreateKey();
|
|
||||||
var toDelete = await ctx.GetTable<StreamOnlineMessage>()
|
|
||||||
.Where(x => (x.Type == key.Type && x.Name == key.Name)
|
|
||||||
|| Sql.DateDiff(Sql.DateParts.Day, x.DateAdded, DateTime.UtcNow) > 1)
|
|
||||||
.DeleteWithOutputAsync();
|
|
||||||
|
|
||||||
toReturn.AddRange(toDelete.Select(x => (x.ChannelId, x.MessageId)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return toReturn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public sealed class StreamNotificationService : INService, IReadyExecutor
|
public sealed class StreamNotificationService : INService, IReadyExecutor
|
||||||
{
|
{
|
||||||
private readonly DbService _db;
|
private readonly DbService _db;
|
||||||
@@ -370,7 +279,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
|
|
||||||
var message = string.IsNullOrWhiteSpace(fs.Message) ? "" : rep.Replace(fs.Message);
|
var message = string.IsNullOrWhiteSpace(fs.Message) ? "" : rep.Replace(fs.Message);
|
||||||
|
|
||||||
var msg = await textChannel.EmbedAsync(GetEmbed(fs.GuildId, stream), message);
|
var msg = await textChannel.EmbedAsync(GetEmbed(fs.GuildId, stream, false), message);
|
||||||
|
|
||||||
// only cache the ids of channel/message pairs
|
// only cache the ids of channel/message pairs
|
||||||
if(_deleteOnOfflineServers.Contains(fs.GuildId))
|
if(_deleteOnOfflineServers.Contains(fs.GuildId))
|
||||||
@@ -563,18 +472,22 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEmbedBuilder GetEmbed(ulong guildId, StreamData status)
|
public IEmbedBuilder GetEmbed(ulong guildId, StreamData status, bool showViewers = true)
|
||||||
{
|
{
|
||||||
var embed = _eb.Create()
|
var embed = _eb.Create()
|
||||||
.WithTitle(status.Name)
|
.WithTitle(status.Name)
|
||||||
.WithUrl(status.StreamUrl)
|
.WithUrl(status.StreamUrl)
|
||||||
.WithDescription(status.StreamUrl)
|
.WithDescription(status.StreamUrl)
|
||||||
.AddField(GetText(guildId, strs.status), status.IsLive ? "🟢 Online" : "🔴 Offline", true)
|
.AddField(GetText(guildId, strs.status), status.IsLive ? "🟢 Online" : "🔴 Offline", true);
|
||||||
.AddField(GetText(guildId, strs.viewers),
|
|
||||||
|
if (showViewers)
|
||||||
|
{
|
||||||
|
embed.AddField(GetText(guildId, strs.viewers),
|
||||||
status.Viewers == 0 && !status.IsLive
|
status.Viewers == 0 && !status.IsLive
|
||||||
? "-"
|
? "-"
|
||||||
: status.Viewers,
|
: status.Viewers,
|
||||||
true);
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
if (status.IsLive)
|
if (status.IsLive)
|
||||||
embed = embed.WithOkColor();
|
embed = embed.WithOkColor();
|
||||||
|
@@ -0,0 +1,99 @@
|
|||||||
|
#nullable disable
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Searches.Common;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Searches.Services;
|
||||||
|
|
||||||
|
public sealed class StreamOnlineMessageDeleterService : INService, IReadyExecutor
|
||||||
|
{
|
||||||
|
private readonly StreamNotificationService _notifService;
|
||||||
|
private readonly DbService _db;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly IPubSub _pubSub;
|
||||||
|
|
||||||
|
public StreamOnlineMessageDeleterService(
|
||||||
|
StreamNotificationService notifService,
|
||||||
|
DbService db,
|
||||||
|
IPubSub pubSub,
|
||||||
|
DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
_notifService = notifService;
|
||||||
|
_db = db;
|
||||||
|
_client = client;
|
||||||
|
_pubSub = pubSub;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnReadyAsync()
|
||||||
|
{
|
||||||
|
_notifService.OnlineMessagesSent += OnOnlineMessagesSent;
|
||||||
|
|
||||||
|
if (_client.ShardId == 0)
|
||||||
|
await _pubSub.Sub(_notifService.StreamsOfflineKey, OnStreamsOffline);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnOnlineMessagesSent(
|
||||||
|
FollowedStream.FType type,
|
||||||
|
string name,
|
||||||
|
IReadOnlyCollection<(ulong, ulong)> pairs)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
foreach (var (channelId, messageId) in pairs)
|
||||||
|
{
|
||||||
|
await ctx.GetTable<StreamOnlineMessage>()
|
||||||
|
.InsertAsync(() => new()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Type = type,
|
||||||
|
MessageId = messageId,
|
||||||
|
ChannelId = channelId,
|
||||||
|
DateAdded = DateTime.UtcNow,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ValueTask OnStreamsOffline(List<StreamData> streamDatas)
|
||||||
|
{
|
||||||
|
if (_client.ShardId != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var pairs = await GetMessagesToDelete(streamDatas);
|
||||||
|
|
||||||
|
foreach (var (channelId, messageId) in pairs)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var textChannel = await _client.GetChannelAsync(channelId) as ITextChannel;
|
||||||
|
if (textChannel is null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
await textChannel.DeleteMessageAsync(messageId);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IEnumerable<(ulong, ulong)>> GetMessagesToDelete(List<StreamData> streamDatas)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var toReturn = new List<(ulong, ulong)>();
|
||||||
|
foreach (var sd in streamDatas)
|
||||||
|
{
|
||||||
|
var key = sd.CreateKey();
|
||||||
|
var toDelete = await ctx.GetTable<StreamOnlineMessage>()
|
||||||
|
.Where(x => (x.Type == key.Type && x.Name == key.Name)
|
||||||
|
|| Sql.DateDiff(Sql.DateParts.Day, x.DateAdded, DateTime.UtcNow) > 1)
|
||||||
|
.DeleteWithOutputAsync();
|
||||||
|
|
||||||
|
toReturn.AddRange(toDelete.Select(x => (x.ChannelId, x.MessageId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
}
|
@@ -7,7 +7,7 @@ namespace NadekoBot.Services;
|
|||||||
|
|
||||||
public sealed class StatsService : IStatsService, IReadyExecutor, INService
|
public sealed class StatsService : IStatsService, IReadyExecutor, INService
|
||||||
{
|
{
|
||||||
public const string BOT_VERSION = "4.3.6";
|
public const string BOT_VERSION = "4.3.7";
|
||||||
|
|
||||||
public string Author
|
public string Author
|
||||||
=> "Kwoth#2452";
|
=> "Kwoth#2452";
|
||||||
|
@@ -1003,7 +1003,10 @@ shopswap:
|
|||||||
shopmove:
|
shopmove:
|
||||||
- shopmove
|
- shopmove
|
||||||
buy:
|
buy:
|
||||||
|
- shopbuy
|
||||||
- buy
|
- buy
|
||||||
|
shopreq:
|
||||||
|
- shopreq
|
||||||
gamevoicechannel:
|
gamevoicechannel:
|
||||||
- gamevoicechannel
|
- gamevoicechannel
|
||||||
- gvc
|
- gvc
|
||||||
@@ -1309,6 +1312,10 @@ exprdelete:
|
|||||||
- exd
|
- exd
|
||||||
- exdel
|
- exdel
|
||||||
- dcr
|
- dcr
|
||||||
|
exprdeleteserver:
|
||||||
|
- exprdelserv
|
||||||
|
- exds
|
||||||
|
- exdelserv
|
||||||
exprclear:
|
exprclear:
|
||||||
- exprclear
|
- exprclear
|
||||||
- exc
|
- exc
|
||||||
|
@@ -27,7 +27,7 @@ greetdel:
|
|||||||
- "0"
|
- "0"
|
||||||
- "30"
|
- "30"
|
||||||
greet:
|
greet:
|
||||||
desc: "Toggles anouncements on the current channel when someone joins the server."
|
desc: "Toggles announcements on the current channel when someone joins the server."
|
||||||
args:
|
args:
|
||||||
- ""
|
- ""
|
||||||
greetmsg:
|
greetmsg:
|
||||||
@@ -40,7 +40,7 @@ greetmsg:
|
|||||||
args:
|
args:
|
||||||
- "Welcome, %user.mention%."
|
- "Welcome, %user.mention%."
|
||||||
bye:
|
bye:
|
||||||
desc: "Toggles anouncements on the current channel when someone leaves the server."
|
desc: "Toggles announcements on the current channel when someone leaves the server."
|
||||||
args:
|
args:
|
||||||
- ""
|
- ""
|
||||||
byemsg:
|
byemsg:
|
||||||
@@ -77,7 +77,7 @@ byetest:
|
|||||||
- ""
|
- ""
|
||||||
- "@SomeoneElse"
|
- "@SomeoneElse"
|
||||||
boost:
|
boost:
|
||||||
desc: "Toggles anouncements on the current channel when someone boosts the server."
|
desc: "Toggles announcements on the current channel when someone boosts the server."
|
||||||
args:
|
args:
|
||||||
- ""
|
- ""
|
||||||
boostmsg:
|
boostmsg:
|
||||||
@@ -217,13 +217,17 @@ exprlist:
|
|||||||
- "1"
|
- "1"
|
||||||
- "all"
|
- "all"
|
||||||
exprshow:
|
exprshow:
|
||||||
desc: "Shows a expression's response on a given ID."
|
desc: "Shows an expression's response on a given ID."
|
||||||
args:
|
args:
|
||||||
- "1"
|
- "1"
|
||||||
exprdelete:
|
exprdelete:
|
||||||
desc: "Deletes a expression on a specific index. If ran in DM, it is bot owner only and deletes a global expression. If ran in a server, it requires Administration privileges and removes server expression."
|
desc: "Deletes an expression on a specific index. If ran in DM, it is bot owner only and deletes a global expression. If ran in a server, it requires Administration privileges and removes server expression."
|
||||||
args:
|
args:
|
||||||
- "5"
|
- "5"
|
||||||
|
exprdeleteserver:
|
||||||
|
desc: "Deletes an expression on a specific index on this server."
|
||||||
|
args:
|
||||||
|
- "5c"
|
||||||
exprclear:
|
exprclear:
|
||||||
desc: "Deletes all expression on this server."
|
desc: "Deletes all expression on this server."
|
||||||
args:
|
args:
|
||||||
@@ -906,7 +910,7 @@ playlists:
|
|||||||
args:
|
args:
|
||||||
- "1"
|
- "1"
|
||||||
playlistshow:
|
playlistshow:
|
||||||
desc: "Lists all songs in a playlist spepcified by its id. Paginated, 20 per page."
|
desc: "Lists all songs in a playlist specified by its id. Paginated, 20 per page."
|
||||||
args:
|
args:
|
||||||
- "1"
|
- "1"
|
||||||
deleteplaylist:
|
deleteplaylist:
|
||||||
@@ -962,7 +966,7 @@ convertlist:
|
|||||||
args:
|
args:
|
||||||
- ""
|
- ""
|
||||||
wowjoke:
|
wowjoke:
|
||||||
desc: "Get one of Kwoth's penultimate WoW jokes."
|
desc: "Get one of penultimate WoW jokes."
|
||||||
args:
|
args:
|
||||||
- ""
|
- ""
|
||||||
calculate:
|
calculate:
|
||||||
@@ -992,7 +996,7 @@ pokemonability:
|
|||||||
args:
|
args:
|
||||||
- "overgrow"
|
- "overgrow"
|
||||||
memelist:
|
memelist:
|
||||||
desc: "Shows a list of template keys (and their repspective names) used for `{0}memegen`."
|
desc: "Shows a list of template keys (and their respective names) used for `{0}memegen`."
|
||||||
args:
|
args:
|
||||||
- ""
|
- ""
|
||||||
memegen:
|
memegen:
|
||||||
@@ -1097,7 +1101,7 @@ wiki:
|
|||||||
args:
|
args:
|
||||||
- "query"
|
- "query"
|
||||||
color:
|
color:
|
||||||
desc: "Shows you pictures of colors which correspond to the inputed hex values. Max 10."
|
desc: "Shows you pictures of colors which correspond to the inputted hex values. Max 10."
|
||||||
args:
|
args:
|
||||||
- "00ff00"
|
- "00ff00"
|
||||||
- "f00 0f0 00f"
|
- "f00 0f0 00f"
|
||||||
@@ -1692,7 +1696,7 @@ banprune:
|
|||||||
args:
|
args:
|
||||||
- "3"
|
- "3"
|
||||||
wait:
|
wait:
|
||||||
desc: "Used only as a startup command. Waits a certain number of miliseconds before continuing the execution of the following startup commands."
|
desc: "Used only as a startup command. Waits a certain number of milliseconds before continuing the execution of the following startup commands."
|
||||||
args:
|
args:
|
||||||
- "3000"
|
- "3000"
|
||||||
warnexpire:
|
warnexpire:
|
||||||
@@ -1738,6 +1742,11 @@ shopremove:
|
|||||||
desc: "Removes an item from the shop by its ID."
|
desc: "Removes an item from the shop by its ID."
|
||||||
args:
|
args:
|
||||||
- "1"
|
- "1"
|
||||||
|
shopreq:
|
||||||
|
desc: "Sets a role which will be required to buy the item on the specified index. Specify only index to remove the requirement."
|
||||||
|
args:
|
||||||
|
- "2 Gamers"
|
||||||
|
- "2"
|
||||||
shopchangename:
|
shopchangename:
|
||||||
desc: "Change the name of a shop entry at the specified index. Only works for non-role items"
|
desc: "Change the name of a shop entry at the specified index. Only works for non-role items"
|
||||||
args:
|
args:
|
||||||
|
@@ -725,6 +725,11 @@
|
|||||||
"shop_role_already_bought": "You already bought this role.",
|
"shop_role_already_bought": "You already bought this role.",
|
||||||
"shop_role_purchase": "You've successfully purchased {0} role.",
|
"shop_role_purchase": "You've successfully purchased {0} role.",
|
||||||
"shop_role_purchase_error": "Error assigning role. Your purchase has been refunded.",
|
"shop_role_purchase_error": "Error assigning role. Your purchase has been refunded.",
|
||||||
|
"shop_item_role_req": "Shop item #{0} will now require users to have a {1} role in order to purchase it.",
|
||||||
|
"shop_item_req_role_unfulfilled": "You don't have the required role '{0}' in order to buy this shop item.",
|
||||||
|
"shop_item_req_role_not_found": "The required role for this item doesn't exist anymore. Please contact server administrator.",
|
||||||
|
"shop_item_requires_role": "Requires {0} role to purchase",
|
||||||
|
"shop_item_role_no_req": "Shop item #{0} will no longer require a role.",
|
||||||
"unique_items_left": "{0} unique items left.",
|
"unique_items_left": "{0} unique items left.",
|
||||||
"blocked_commands": "Blocked Commands",
|
"blocked_commands": "Blocked Commands",
|
||||||
"blocked_modules": "Blocked Modules",
|
"blocked_modules": "Blocked Modules",
|
||||||
|
@@ -729,223 +729,6 @@
|
|||||||
"UnitType": "time",
|
"UnitType": "time",
|
||||||
"Modifier": 31536000.0
|
"Modifier": 31536000.0
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"AUD"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 1.4787
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"BGN"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 1.9558
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"BRL"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 3.5991
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"CAD"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 1.4562
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"CHF"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 1.0951
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"CNY"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 7.4565
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"CZK"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 27.025
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"DKK"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 7.4448
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"GBP"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 0.8517
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"HKD"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 8.6631
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"HRK"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 7.4846
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"HUF"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 308.97
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"IDR"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 14814.35
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"ILS"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 4.2241
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"INR"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 74.8703
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"JPY"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 114.27
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"KRW"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 1244.47
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"MXN"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 20.7542
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"MYR"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 4.5205
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"NOK"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 9.2873
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"NZD"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 1.5427
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"PHP"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 51.797
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"PLN"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 4.3436
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"RON"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 4.4505
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"RUB"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 72.4564
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"SEK"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 9.5008
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"SGD"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 1.5196
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"THB"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 38.608
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"TRY"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 3.2977
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"USD"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 1.1168
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"ZAR"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 16.0537
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Triggers": [
|
"Triggers": [
|
||||||
"K",
|
"K",
|
||||||
@@ -970,19 +753,5 @@
|
|||||||
],
|
],
|
||||||
"UnitType": "temperature",
|
"UnitType": "temperature",
|
||||||
"Modifier": 0.00
|
"Modifier": 0.00
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"EUR"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 1.0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Triggers": [
|
|
||||||
"SKK"
|
|
||||||
],
|
|
||||||
"UnitType": "currency",
|
|
||||||
"Modifier": 30.13
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
Reference in New Issue
Block a user