Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Red music bot support (and fix a small null reference bug) #268

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/FMBot.Bot/Builders/GuildSettingBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ public async Task<ResponseModel> GetGuildSettings(ContextModel context, GuildPer
response.Embed.AddField("Missing server-wide permissions", serverPermission.ToString());
}

response.Embed.AddField("Red bot name", guild.RedBotName ?? "N/A");

var guildSettings = new SelectMenuBuilder()
.WithPlaceholder("Select setting you want to change")
.WithCustomId(InteractionConstants.GuildSetting)
Expand Down Expand Up @@ -952,4 +954,41 @@ public async Task<ResponseModel> ToggleChannelCommand(ContextModel context, ulon

return response;
}

public async Task<ResponseModel> SetRedBotName(ContextModel context, IUser lastModifier = null)
{
var response = new ResponseModel
{
ResponseType = ResponseType.Embed,
};

response.Embed.WithTitle("Set Red bot name");
response.Embed.WithColor(DiscordConstants.InformationColorBlue);

var description = new StringBuilder();
var guild = await this._guildService.GetGuildAsync(context.DiscordGuild.Id);

var redBotName = guild.RedBotName;

description.AppendLine();
description.AppendLine($"Current name: `{redBotName ?? "N/A"}`");
description.AppendLine();
description.AppendLine("Your Red bot instance's name must start with, or be the same as, the name you enter.");
description.AppendLine();

var components = new ComponentBuilder();

components.WithButton("Set Red bot name", InteractionConstants.SetRedBotName, style: ButtonStyle.Primary);

response.Embed.WithDescription(description.ToString());

if (lastModifier != null)
{
response.Embed.WithFooter($"Last modified by {lastModifier.Username}");
}

response.Components = components;

return response;
}
}
3 changes: 2 additions & 1 deletion src/FMBot.Bot/Builders/UserBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ public async Task<ResponseModel> BotScrobblingAsync(ContextModel context)
"Currently supported bots:\n" +
"- Cakey Bot\n" +
"- Jockie Music\n" +
"- SoundCloud"
"- SoundCloud\n" +
"- Red"
);

response.Components = new ComponentBuilder()
Expand Down
2 changes: 1 addition & 1 deletion src/FMBot.Bot/Handlers/CommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ private async Task TryScrobbling(SocketUserMessage msg, ICommandContext context)
{
foreach (var musicBot in MusicBot.SupportedBots)
{
if (musicBot.IsAuthor(msg.Author))
if (await musicBot.IsAuthor(msg.Author, context, this._guildService))
{
await this._musicBotService.Scrobble(musicBot, msg, context);
break;
Expand Down
9 changes: 9 additions & 0 deletions src/FMBot.Bot/Models/Modals/SettingModals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,12 @@ public class AddDisabledGuildCommandModal : IModal
[ModalTextInput("command", placeholder: "whoknows", minLength: 1, maxLength: 40)]
public string Command { get; set; }
}

public class SetRedBotNameModal : IModal
{
public string Title => "Set Red bot name";

[InputLabel("Enter new bot name")]
[ModalTextInput("red_bot_name", minLength: 1, maxLength: 15)]
public string NewBotName { get; set; }
}
8 changes: 6 additions & 2 deletions src/FMBot.Bot/Models/MusicBot/MusicBot.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using FMBot.Bot.Services.Guild;

namespace FMBot.Bot.Models.MusicBot;

Expand All @@ -16,7 +19,8 @@ public abstract class MusicBot
{
new JockieMusicBot(),
new CakeyBotMusicBot(),
new SoundCloudMusicBot()
new SoundCloudMusicBot(),
new RedMusicBot()
};

protected MusicBot(string name, bool possiblyIncludesLinks = true, bool skipUploaderName = false)
Expand All @@ -26,7 +30,7 @@ protected MusicBot(string name, bool possiblyIncludesLinks = true, bool skipUplo
this.SkipUploaderName = skipUploaderName;
}

public bool IsAuthor(SocketUser user)
public virtual async Task<bool> IsAuthor(SocketUser user, ICommandContext context, GuildService guildService)
{
return user?.Username?.StartsWith(this.Name, StringComparison.OrdinalIgnoreCase) ?? false;
}
Expand Down
49 changes: 49 additions & 0 deletions src/FMBot.Bot/Models/MusicBot/RedMusicBot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using FMBot.Bot.Services.Guild;

namespace FMBot.Bot.Models.MusicBot;

internal class RedMusicBot : MusicBot
{
public RedMusicBot() : base("Red", true, false)
{
}

public override bool ShouldIgnoreMessage(IUserMessage msg)
{
// Bot sends only single embed per request
if (msg.Embeds.Count != 1)
{
return true;
}

var targetEmbed = msg.Embeds.First();

return targetEmbed.Title == null ||
!targetEmbed.Title.Contains("Now Playing") ||
string.IsNullOrEmpty(targetEmbed.Description);
}

public override string GetTrackQuery(IUserMessage msg)
{
return msg.Embeds.First().Description.Replace(@"\","");
}

public override async Task<bool> IsAuthor(SocketUser user, ICommandContext context, GuildService guildService)
{
var guild = await guildService.GetGuildAsync(context.Guild.Id);
string redBotName = guild.RedBotName;

if (redBotName != null)
{
return user?.Username?.StartsWith(redBotName, StringComparison.OrdinalIgnoreCase) ?? false;
}

return false;
}
}
3 changes: 3 additions & 0 deletions src/FMBot.Bot/Resources/InteractionConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ public static class ToggleCommand
public const string ToggleGuildCommandRemoveModal = "toggle-guild-command-modal-remove";
}

public const string SetRedBotName = "set-red-bot-name";
public const string SetRedBotNameModal = "set-red-bot-name-modal";

public const string WhoKnowsRolePicker = "whoknows-role-picker";
public const string WhoKnowsAlbumRolePicker = "whoknows-album-role-picker";
public const string WhoKnowsTrackRolePicker = "whoknows-track-role-picker";
Expand Down
26 changes: 25 additions & 1 deletion src/FMBot.Bot/Services/Guild/GuildService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ public static (FilterStats Stats, IDictionary<int, FullGuildUser> FilteredGuildU

users = users
.Where(w => w.Value.Roles != null && !guild.BlockedRoles.Any(a => w.Value.Roles.Contains(a)))
.ToDictionary(i => i.Key, i => i.Value);
.ToDictionary(i => i.Key, i => i.Value);

stats.BlockedRolesFiltered = preFilterCount - users.Count;
}
Expand Down Expand Up @@ -1263,4 +1263,28 @@ public async Task UpdateGuildUserLastMessageDate(IGuildUser discordGuildUser, in
Log.Error(e, $"Exception in {nameof(UpdateGuildUserLastMessageDate)}");
}
}

public async Task<bool> SetGuildRedBotNameAsync(IGuild discordGuild, string? botName)
{
await using var db = await this._contextFactory.CreateDbContextAsync();
var existingGuild = await db.Guilds
.AsQueryable()
.FirstOrDefaultAsync(f => f.DiscordGuildId == discordGuild.Id);

if (existingGuild == null)
{
return false;
}

existingGuild.Name = discordGuild.Name;
existingGuild.RedBotName = botName;

db.Entry(existingGuild).State = EntityState.Modified;

await db.SaveChangesAsync();

await RemoveGuildFromCache(discordGuild.Id);

return true;
}
}
6 changes: 4 additions & 2 deletions src/FMBot.Bot/Services/TimerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,11 @@ public void QueueJobs()
Log.Information($"RecurringJob: Adding {nameof(ClearUserCache)}");
RecurringJob.AddOrUpdate(nameof(ClearUserCache), () => ClearUserCache(), "30 */2 * * *");

Log.Information($"Bot Settings: {ConfigData.Data.Shards}");

if (this._botSettings.LastFm.UserIndexFrequencyInDays != null &&
this._botSettings.LastFm.UserIndexFrequencyInDays != 0 &&
ConfigData.Data.Shards.MainInstance == true)
ConfigData.Data.Shards?.MainInstance == true)
{
Log.Information($"RecurringJob: Adding {nameof(AddUsersToIndexQueue)}");
RecurringJob.AddOrUpdate(nameof(AddUsersToIndexQueue), () => AddUsersToIndexQueue(), "0 8 * * *");
Expand All @@ -102,7 +104,7 @@ public void QueueJobs()

if (this._botSettings.LastFm.UserUpdateFrequencyInHours != null &&
this._botSettings.LastFm.UserUpdateFrequencyInHours != 0 &&
ConfigData.Data.Shards.MainInstance == true)
ConfigData.Data.Shards?.MainInstance == true)
{
Log.Information($"RecurringJob: Adding {nameof(AddUsersToUpdateQueue)}");
RecurringJob.AddOrUpdate(nameof(AddUsersToUpdateQueue), () => AddUsersToUpdateQueue(), "0 * * * *");
Expand Down
16 changes: 16 additions & 0 deletions src/FMBot.Bot/Services/TrackService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,23 @@ public async Task<TrackSearchResult> GetTrackFromLink(string description, bool p
}
}

// Sometimes track and artist are reversed (esp. with YouTube bots, e.g. "Title - Artist - Topic")
// Use the order with the most listeners
var trackLfm = await this._dataSourceFactory.GetTrackInfoAsync(trackName, artistName);
var trackLfmReversed = await this._dataSourceFactory.GetTrackInfoAsync(artistName, trackName);

if (trackLfm.Success && trackLfmReversed.Success)
{
trackLfm = trackLfm.Content.TotalListeners >= trackLfmReversed.Content.TotalListeners
? trackLfm
: trackLfmReversed;
}
else
{
// Try to use the reversed if the normal order is not successful
trackLfm = trackLfm.Success ? trackLfm : trackLfmReversed;
}


if (trackLfm.Success)
{
Expand Down
30 changes: 30 additions & 0 deletions src/FMBot.Bot/SlashCommands/GuildSettingSlashCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,12 @@ public async Task GetGuildSetting(string[] inputs)
await this.Context.SendResponse(this.Interactivity, response, ephemeral: false);
}
break;
case GuildSetting.RedBotName:
{
response = await this._guildSettingBuilder.SetRedBotName(new ContextModel(this.Context));
await this.Context.SendResponse(this.Interactivity, response, ephemeral: false);
}
break;
default:
throw new ArgumentOutOfRangeException();
}
Expand Down Expand Up @@ -789,4 +795,28 @@ public async Task ClearDisabledChannelCommand()

await this.Context.UpdateInteractionEmbed(response);
}

[ComponentInteraction(InteractionConstants.SetRedBotName)]
[ServerStaffOnly]
public async Task SetRedBotName()
{
var message = (this.Context.Interaction as SocketMessageComponent)?.Message;

if (message == null)
{
return;
}

await this.Context.Interaction.RespondWithModalAsync<SetRedBotNameModal>($"{InteractionConstants.SetRedBotNameModal}-{message.Id}");
}

[ModalInteraction($"{InteractionConstants.SetRedBotNameModal}-*")]
[ServerStaffOnly]
public async Task SetRedBotName(string messageId, SetRedBotNameModal modal)
{
await this._guildService.SetGuildRedBotNameAsync(this.Context.Guild, modal.NewBotName);

var response = await this._guildSettingBuilder.SetRedBotName(new ContextModel(this.Context), this.Context.User);
await this.Context.UpdateMessageEmbed(response, messageId);
}
}
4 changes: 3 additions & 1 deletion src/FMBot.Domain/Enums/GuildSetting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public enum GuildSetting

[Option("Disabled channel commands", "Toggle commands or the bot per channel")]
DisabledCommands = 30,

[Option("Disabled server commands", "Toggle commands server-wide")]
DisabledGuildCommands = 31,

[Option("Red bot name", "Name of Red bot instance for music bot scrobbling")]
RedBotName = 40,
}
2 changes: 2 additions & 0 deletions src/FMBot.Persistence.Domain/Models/Guild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,6 @@ public class Guild
public ICollection<Webhook> Webhooks { get; set; }

public ulong? WhoKnowsWhitelistRoleId { get; set; }

public string? RedBotName { get; set; }
}
Loading