Skip to content

Commit

Permalink
Static pickup queue message handling initial checkin
Browse files Browse the repository at this point in the history
- Changed default run mode of commandservice to Async
- added reactionadded and reactionremoved event handlers to pickupmodule
- added new method for handling the static pickup message
- added default pickups category creation on bot join
- moved all pickup related channels to new pickup category on bot join
- extended repository to enable search for a specific property value
  • Loading branch information
Floydan committed May 23, 2020
1 parent 56d2e56 commit 5e76000
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 94 deletions.
14 changes: 12 additions & 2 deletions PickupBot.Commands/CommandHandlerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

namespace PickupBot.Commands
{
public class CommandHandlerService : InitializedService
public class CommandHandlerService : InitializedService, IDisposable
{
private readonly CommandService _commands;
private readonly DiscordSocketClient _discord;
Expand Down Expand Up @@ -74,7 +74,7 @@ private async Task GetActivityStats()
if (string.IsNullOrWhiteSpace(_rconPassword) ||
string.IsNullOrWhiteSpace(_rconHost) ||
_rconPort <= 0) return;

try
{
var status = await RCON.UDPSendCommand("status", _rconHost, _rconPassword, _rconPort);
Expand Down Expand Up @@ -177,5 +177,15 @@ public override async Task InitializeAsync(CancellationToken cancellationToken)
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
await _commands.AddModulesAsync(GetType().Assembly, _services);
}

public void Dispose()
{
_commands.CommandExecuted -= CommandExecutedAsync;
_discord.MessageReceived -= MessageReceivedAsync;
_discord.ReactionAdded -= ReactionAddedAsync;

((IDisposable) _commands)?.Dispose();
_discord?.Dispose();
}
}
}
197 changes: 185 additions & 12 deletions PickupBot.Commands/Modules/PickupModuleListActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ namespace PickupBot.Commands.Modules
{
[Name("Pickup")]
[Summary("Commands for handling pickup queues")]
public partial class PickupModule : ModuleBase<SocketCommandContext>
public partial class PickupModule : ModuleBase<SocketCommandContext>, IDisposable
{
private readonly IQueueRepository _queueRepository;
private readonly IFlaggedSubscribersRepository _flagRepository;
private readonly ISubscriberActivitiesRepository _activitiesRepository;
private readonly ILogger<PickupModule> _logger;
private readonly DiscordSocketClient _client;
private readonly string _rconPassword;
private readonly string _rconHost;
private readonly int _rconPort;
Expand All @@ -31,16 +32,55 @@ public PickupModule(
IQueueRepository queueRepository,
IFlaggedSubscribersRepository flagRepository,
ISubscriberActivitiesRepository activitiesRepository,
PickupBotSettings pickupBotSettings,
ILogger<PickupModule> logger)
PickupBotSettings pickupBotSettings,
ILogger<PickupModule> logger,
DiscordSocketClient client)
{
_queueRepository = queueRepository;
_flagRepository = flagRepository;
_activitiesRepository = activitiesRepository;
_logger = logger;
_client = client;
_rconPassword = pickupBotSettings.RCONServerPassword ?? "";
_rconHost = pickupBotSettings.RCONHost ?? "";
int.TryParse(pickupBotSettings.RCONPort ?? "0", out _rconPort);

_client.ReactionAdded += SocketClient_ReactionAdded;
_client.ReactionRemoved += SocketClient_ReactionRemoved;
}

private async Task SocketClient_ReactionAdded(Cacheable<IUserMessage, ulong> message, ISocketMessageChannel channel, SocketReaction reaction)
{
if (!(channel is IGuildChannel guildChannel) || guildChannel.Name != "active-pickups") return;

if (reaction.Emote.Name == "\u2705")
{
var queue = await _queueRepository.FindQueueByMessageId(reaction.MessageId, guildChannel.GuildId.ToString());

if (queue != null)
{
var pickupChannel = ((SocketGuild) guildChannel.Guild).Channels.FirstOrDefault(c => c.Name.Equals("pickup")) as SocketTextChannel;
await AddInternal(queue.Name, (SocketGuild)guildChannel.Guild, pickupChannel ?? (SocketTextChannel)guildChannel,
(SocketGuildUser)reaction.User);
}
}
}

private async Task SocketClient_ReactionRemoved(Cacheable<IUserMessage, ulong> message, ISocketMessageChannel channel, SocketReaction reaction)
{
if (!(channel is IGuildChannel guildChannel) || guildChannel.Name != "active-pickups") return;

if (reaction.Emote.Name == "\u2705")
{
var queue = await _queueRepository.FindQueueByMessageId(reaction.MessageId, guildChannel.GuildId.ToString());

if (queue != null)
{
var pickupChannel = ((SocketGuild) guildChannel.Guild).Channels.FirstOrDefault(c => c.Name.Equals("pickup")) as SocketTextChannel;
await LeaveInternal(queue, (SocketGuild)guildChannel.Guild, pickupChannel ?? (SocketTextChannel)guildChannel,
(SocketGuildUser)reaction.User);
}
}
}

[Command("create")]
Expand Down Expand Up @@ -76,13 +116,14 @@ public async Task Create(

var activity = await _activitiesRepository.Find((IGuildUser)Context.User);
activity.PickupCreate += 1;
activity.PickupAdd += 1;
await _activitiesRepository.Update(activity);

var rconEnabled = ops?.ContainsKey("-rcon") ?? true;
if (ops?.ContainsKey("-norcon") == true)
rconEnabled = false;

await _queueRepository.AddQueue(new PickupQueue(Context.Guild.Id.ToString(), queueName)
queue = new PickupQueue(Context.Guild.Id.ToString(), queueName)
{
Name = queueName,
GuildId = Context.Guild.Id.ToString(),
Expand All @@ -93,13 +134,18 @@ await _queueRepository.AddQueue(new PickupQueue(Context.Guild.Id.ToString(), que
TeamSize = teamSize.Value,
IsCoop = ops?.ContainsKey("-coop") ?? false,
Rcon = rconEnabled,
Subscribers = new List<Subscriber> { new Subscriber { Id = Context.User.Id, Name = GetNickname(Context.User) } },
Subscribers = new List<Subscriber>
{new Subscriber {Id = Context.User.Id, Name = GetNickname(Context.User)}},
Host = ops?.ContainsKey("-host") ?? false ? ops["-host"]?.FirstOrDefault() : null,
Port = int.Parse((ops?.ContainsKey("-port") ?? false ? ops["-port"]?.FirstOrDefault() : null) ?? "0"),
Games = ops?.ContainsKey("-game") ?? false ? ops["-game"] : Enumerable.Empty<string>(),
});
};

await _queueRepository.AddQueue(queue);

await Context.Channel.SendMessageAsync($"`Queue '{queueName}' was added by {GetNickname(Context.User)}`");
queue = await SaveStaticQueueMessage(queue, Context.Guild);
await _queueRepository.UpdateQueue(queue);
}

[Command("rename")]
Expand Down Expand Up @@ -174,11 +220,17 @@ public async Task Delete([Name("Queue name"), Summary("Queue name"), Remainder]
var isAdmin = (Context.User as IGuildUser)?.GuildPermissions.Has(GuildPermission.Administrator) ?? false;
if (isAdmin || queue.OwnerId == Context.User.Id.ToString())
{
var queuesChannel = await GetPickupQueuesChannel(Context.Guild);

var result = await _queueRepository.RemoveQueue(queueName, Context.Guild.Id.ToString());
var message = result ?
$"`Queue '{queueName}' has been canceled`" :
$"`Queue with the name '{queueName}' doesn't exists or you are not the owner of the queue!`";
await Context.Channel.SendMessageAsync(message).AutoRemoveMessage(10);

if (!string.IsNullOrEmpty(queue.StaticMessageId))
await queuesChannel.DeleteMessageAsync(Convert.ToUInt64(queue.StaticMessageId));

return;
}

Expand Down Expand Up @@ -454,9 +506,9 @@ private async Task<IVoiceChannel> GetOrCreateVoiceChannel(string name, ulong cat
?? await Context.Guild.CreateVoiceChannelAsync(name, properties => properties.CategoryId = categoryId);
}

private async Task NotifyUsers(PickupQueue queue, string serverName, params SocketGuildUser[] users)
private async Task NotifyUsers(PickupQueue queue, string serverName, IUser guildUser, params SocketGuildUser[] users)
{
var usersList = string.Join(Environment.NewLine, queue.Subscribers.Where(u => u.Id != Context.User.Id).Select(u => $@" - {u.Name}"));
var usersList = string.Join(Environment.NewLine, queue.Subscribers.Where(u => u.Id != guildUser.Id).Select(u => $@" - {u.Name}"));
var header = $"**Contact your teammates on the \"{serverName}\" server and glhf!**";
var remember = "**Remember**" +
$"{Environment.NewLine}" +
Expand All @@ -472,7 +524,15 @@ private async Task NotifyUsers(PickupQueue queue, string serverName, params Sock

foreach (var user in users)
{
await user.SendMessageAsync(embed: embed);
try
{
await user.SendMessageAsync(embed: embed);
await Task.Delay(500);
}
catch (Exception ex)
{
_logger.LogError($"Failed to send DM to {GetNickname(user)}", ex);
}
}
}

Expand All @@ -498,7 +558,12 @@ private async Task<PickupQueue> VerifyQueueByName(string queueName)

private async Task<bool> VerifyUserFlaggedStatus()
{
var flagged = await _flagRepository.IsFlagged((IGuildUser)Context.User);
return await VerifyUserFlaggedStatus((IGuildUser)Context.User);
}

private async Task<bool> VerifyUserFlaggedStatus(IGuildUser user)
{
var flagged = await _flagRepository.IsFlagged(user);
if (flagged == null) return true;

var sb = new StringBuilder()
Expand Down Expand Up @@ -573,6 +638,101 @@ private async Task TriggerRconNotification(PickupQueue queue)
_logger.LogError(e, e.Message);
}
}

private static async Task<PickupQueue> SaveStaticQueueMessage(PickupQueue queue, SocketGuild guild)
{
var queuesChannel = await GetPickupQueuesChannel(guild);

//var denyAll = OverwritePermissions.DenyAll(queuesChannel);
//await queuesChannel.AddPermissionOverwriteAsync(Context.Guild.EveryoneRole, denyAll);

var user = guild.GetUser(Convert.ToUInt64(queue.OwnerId));

var embed = CreateStaticQueueMessageEmbed(queue, user);

AddSubscriberFieldsToStaticQueueMessageFields(queue, embed);
AddWaitingListFieldsToStaticQueueMessageFields(queue, embed);

if (string.IsNullOrEmpty(queue.StaticMessageId))
{
var message = await queuesChannel.SendMessageAsync(embed: embed.Build());
await message.AddReactionsAsync(new IEmote[] { new Emoji("\u2705") }); // timer , new Emoji("\u23F2")

queue.StaticMessageId = message.Id.ToString();
}
else
{
if (await queuesChannel.GetMessageAsync(Convert.ToUInt64(queue.StaticMessageId)) is IUserMessage message)
await message.ModifyAsync(m => { m.Embed = embed.Build(); });
}

return queue;
}

private static EmbedBuilder CreateStaticQueueMessageEmbed(PickupQueue queue, IUser user)
{
var embed = new EmbedBuilder
{
Title = queue.Name,
Author = new EmbedAuthorBuilder
{Name = GetNickname(user), IconUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl()},
Color = Color.Gold
};

embed.WithFields(
new EmbedFieldBuilder {Name = "Created by", Value = GetNickname(user), IsInline = true},
new EmbedFieldBuilder
{
Name = "Game(s)",
Value = string.Join(", ", queue.Games.IsNullOrEmpty() ? new[] {"No game defined"} : queue.Games),
IsInline = true
},
new EmbedFieldBuilder {Name = "Started", Value = queue.Started ? "Yes" : "No", IsInline = true},
new EmbedFieldBuilder {Name = "Host", Value = queue.Host ?? "No host defined", IsInline = true},
new EmbedFieldBuilder
{
Name = "Port",
Value = queue.Port == 0 ? "No port defined" : queue.Port.ToString(),
IsInline = true
},
new EmbedFieldBuilder {Name = "Team size", Value = queue.TeamSize, IsInline = true},
new EmbedFieldBuilder {Name = "Coop", Value = queue.IsCoop ? "Yes" : "No", IsInline = true},
new EmbedFieldBuilder
{Name = "Created", Value = queue.Created.ToString("yyyy-MM-dd\r\nHH:mm:ss 'UTC'"), IsInline = true},
new EmbedFieldBuilder
{Name = "Last updated", Value = queue.Updated.ToString("yyyy-MM-dd\r\nHH:mm:ss 'UTC'"), IsInline = true});
return embed;
}

private static void AddSubscriberFieldsToStaticQueueMessageFields(PickupQueue queue, EmbedBuilder embed)
{
var sb = new StringBuilder();
queue.Subscribers.ForEach(p => sb.AppendLine(p.Name));

embed.WithFields(new EmbedFieldBuilder
{
Name = $"Players in queue [{queue.Subscribers.Count}/{queue.MaxInQueue}]",
Value = queue.Subscribers.IsNullOrEmpty() ? "No players in queue" : sb.ToString(),
IsInline = true
});

sb.Clear();
}

private static void AddWaitingListFieldsToStaticQueueMessageFields(PickupQueue queue, EmbedBuilder embed)
{
var sb = new StringBuilder();
queue.WaitingList.Select((p, i) => $"{i}. {p.Name}").ToList().ForEach(p => sb.AppendLine(p));

embed.WithFields(new EmbedFieldBuilder
{
Name = $"Players in waiting list [{queue.WaitingList.Count}]",
Value = queue.WaitingList.IsNullOrEmpty() ? "No players in waiting list" : sb.ToString(),
IsInline = true
});

sb.Clear();
}

private static string GetNickname(IUser user) =>
user switch
Expand All @@ -592,8 +752,21 @@ private static string GetMention(IMentionable user) =>
_ => user.Mention
};

private static bool IsInPickupChannel(IChannel channel) =>
channel.Name.StartsWith("pickup", StringComparison.OrdinalIgnoreCase);
private static bool IsInPickupChannel(IChannel channel) => channel.Name.StartsWith("pickup", StringComparison.OrdinalIgnoreCase);

private static async Task<ITextChannel> GetPickupQueuesChannel(SocketGuild guild)
{
var queuesChannel = (ITextChannel)guild.TextChannels.FirstOrDefault(c =>
c.Name.Equals("active-pickups", StringComparison.OrdinalIgnoreCase)) ??
await guild.CreateTextChannelAsync("active-pickups",
properties => { properties.Topic = "Active pickups, use reactions to signup"; });
return queuesChannel;
}

public void Dispose()
{
_client.ReactionAdded -= SocketClient_ReactionAdded;
_client?.Dispose();
}
}
}
Loading

0 comments on commit 5e76000

Please sign in to comment.