2019-07-26 10:05:09 +00:00
using System.Collections.Generic ;
2019-07-16 19:59:06 +00:00
using System.Linq ;
2019-04-29 18:21:16 +00:00
using System.Threading.Tasks ;
2019-07-16 19:59:06 +00:00
using App.Metrics ;
2019-04-29 18:21:16 +00:00
using Discord ;
2019-07-26 10:05:09 +00:00
using Humanizer ;
2019-04-29 18:21:16 +00:00
2019-10-05 05:41:00 +00:00
using PluralKit.Bot.CommandSystem ;
2019-05-21 21:40:26 +00:00
namespace PluralKit.Bot.Commands {
2019-10-05 05:41:00 +00:00
public class MiscCommands
{
private BotConfig _botConfig ;
private IMetrics _metrics ;
public MiscCommands ( BotConfig botConfig , IMetrics metrics )
{
_botConfig = botConfig ;
_metrics = metrics ;
}
2019-06-15 10:03:07 +00:00
2019-10-05 05:41:00 +00:00
public async Task Invite ( Context ctx )
2019-06-15 10:03:07 +00:00
{
2019-10-05 05:41:00 +00:00
var clientId = _botConfig . ClientId ? ? ( await ctx . Client . GetApplicationInfoAsync ( ) ) . Id ;
2019-04-29 18:21:16 +00:00
var permissions = new GuildPermissions (
addReactions : true ,
attachFiles : true ,
embedLinks : true ,
manageMessages : true ,
manageWebhooks : true ,
readMessageHistory : true ,
sendMessages : true
) ;
2019-06-15 10:03:07 +00:00
var invite = $"https://discordapp.com/oauth2/authorize?client_id={clientId}&scope=bot&permissions={permissions.RawValue}" ;
2019-10-05 05:41:00 +00:00
await ctx . Reply ( $"{Emojis.Success} Use this link to add PluralKit to your server:\n<{invite}>" ) ;
2019-04-29 18:21:16 +00:00
}
2019-10-05 05:41:00 +00:00
public Task Mn ( Context ctx ) = > ctx . Reply ( "Gotta catch 'em all!" ) ;
public Task Fire ( Context ctx ) = > ctx . Reply ( "*A giant lightning bolt promptly erupts into a pillar of fire as it hits your opponent.*" ) ;
public Task Thunder ( Context ctx ) = > ctx . Reply ( "*A giant ball of lightning is conjured and fired directly at your opponent, vanquishing them.*" ) ;
public Task Freeze ( Context ctx ) = > ctx . Reply ( "*A giant crystal ball of ice is charged and hurled toward your opponent, bursting open and freezing them solid on contact.*" ) ;
public Task Starstorm ( Context ctx ) = > ctx . Reply ( "*Vibrant colours burst forth from the sky as meteors rain down upon your opponent.*" ) ;
2019-07-10 11:57:59 +00:00
2019-10-05 05:41:00 +00:00
public async Task Stats ( Context ctx )
2019-07-16 19:59:06 +00:00
{
2019-10-05 05:41:00 +00:00
var messagesReceived = _metrics . Snapshot . GetForContext ( "Bot" ) . Meters . First ( m = > m . MultidimensionalName = = BotMetrics . MessagesReceived . Name ) . Value ;
var messagesProxied = _metrics . Snapshot . GetForContext ( "Bot" ) . Meters . First ( m = > m . MultidimensionalName = = BotMetrics . MessagesProxied . Name ) . Value ;
2019-07-16 19:59:06 +00:00
2019-10-05 05:41:00 +00:00
var commandsRun = _metrics . Snapshot . GetForContext ( "Bot" ) . Meters . First ( m = > m . MultidimensionalName = = BotMetrics . CommandsRun . Name ) . Value ;
2019-07-16 19:59:06 +00:00
2019-10-05 05:41:00 +00:00
await ctx . Reply ( embed : new EmbedBuilder ( )
2019-07-16 19:59:06 +00:00
. AddField ( "Messages processed" , $"{messagesReceived.OneMinuteRate:F1}/s ({messagesReceived.FifteenMinuteRate:F1}/s over 15m)" )
. AddField ( "Messages proxied" , $"{messagesProxied.OneMinuteRate:F1}/s ({messagesProxied.FifteenMinuteRate:F1}/s over 15m)" )
. AddField ( "Commands executed" , $"{commandsRun.OneMinuteRate:F1}/s ({commandsRun.FifteenMinuteRate:F1}/s over 15m)" )
. Build ( ) ) ;
}
2019-10-05 05:41:00 +00:00
public async Task PermCheckGuild ( Context ctx )
2019-07-26 10:05:09 +00:00
{
2019-10-27 20:58:04 +00:00
IGuild guild ;
if ( ctx . Guild ! = null & & ! ctx . HasNext ( ) )
{
guild = ctx . Guild ;
}
else
{
var guildIdStr = ctx . RemainderOrNull ( ) ? ? throw new PKSyntaxError ( "You must pass a server ID or run this command as ." ) ;
if ( ! ulong . TryParse ( guildIdStr , out var guildId ) )
throw new PKSyntaxError ( $"Could not parse `{guildIdStr.SanitizeMentions()}` as an ID." ) ;
// TODO: will this call break for sharding if you try to request a guild on a different bot instance?
guild = ctx . Client . GetGuild ( guildId ) ;
if ( guild = = null )
throw Errors . GuildNotFound ( guildId ) ;
}
2019-07-26 10:05:09 +00:00
var requiredPermissions = new [ ]
{
2019-10-27 20:58:04 +00:00
ChannelPermission . ViewChannel ,
ChannelPermission . SendMessages ,
ChannelPermission . AddReactions ,
ChannelPermission . AttachFiles ,
ChannelPermission . EmbedLinks ,
2019-07-26 10:05:09 +00:00
ChannelPermission . ManageMessages ,
ChannelPermission . ManageWebhooks
} ;
// Loop through every channel and group them by sets of permissions missing
var permissionsMissing = new Dictionary < ulong , List < ITextChannel > > ( ) ;
foreach ( var channel in await guild . GetTextChannelsAsync ( ) )
{
// TODO: do we need to hide channels here to prevent info-leaking?
var perms = await channel . PermissionsIn ( ) ;
// We use a bitfield so we can set individual permission bits in the loop
ulong missingPermissionField = 0 ;
foreach ( var requiredPermission in requiredPermissions )
if ( ! perms . Has ( requiredPermission ) )
missingPermissionField | = ( ulong ) requiredPermission ;
// If we're not missing any permissions, don't bother adding it to the dict
// This means we can check if the dict is empty to see if all channels are proxyable
if ( missingPermissionField ! = 0 )
{
permissionsMissing . TryAdd ( missingPermissionField , new List < ITextChannel > ( ) ) ;
permissionsMissing [ missingPermissionField ] . Add ( channel ) ;
}
}
// Generate the output embed
var eb = new EmbedBuilder ( )
2019-10-18 11:14:36 +00:00
. WithTitle ( $"Permission check for **{guild.Name.SanitizeMentions()}**" ) ;
2019-07-26 10:05:09 +00:00
if ( permissionsMissing . Count = = 0 )
{
eb . WithDescription ( $"No errors found, all channels proxyable :)" ) . WithColor ( Color . Green ) ;
}
else
{
foreach ( var ( missingPermissionField , channels ) in permissionsMissing )
{
// Each missing permission field can have multiple missing channels
// so we extract them all and generate a comma-separated list
var missingPermissionNames = string . Join ( ", " , new ChannelPermissions ( missingPermissionField )
. ToList ( )
. Select ( perm = > perm . Humanize ( ) . Transform ( To . TitleCase ) ) ) ;
var channelsList = string . Join ( "\n" , channels
. OrderBy ( c = > c . Position )
. Select ( c = > $"#{c.Name}" ) ) ;
2019-10-27 20:58:04 +00:00
eb . AddField ( $"Missing *{missingPermissionNames}*" , channelsList . Truncate ( 1000 ) ) ;
2019-07-26 10:05:09 +00:00
eb . WithColor ( Color . Red ) ;
}
}
// Send! :)
2019-10-05 05:41:00 +00:00
await ctx . Reply ( embed : eb . Build ( ) ) ;
2019-07-26 10:05:09 +00:00
}
2019-04-29 18:21:16 +00:00
}
}