2019-07-26 10:05:09 +00:00
using System.Collections.Generic ;
using System.ComponentModel ;
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 ;
using Discord.Commands ;
2019-07-26 10:05:09 +00:00
using Humanizer ;
2019-04-29 18:21:16 +00:00
2019-05-21 21:40:26 +00:00
namespace PluralKit.Bot.Commands {
2019-04-29 18:21:16 +00:00
public class MiscCommands : ModuleBase < PKCommandContext > {
2019-06-15 10:03:07 +00:00
public BotConfig BotConfig { get ; set ; }
2019-07-16 19:59:06 +00:00
public IMetrics Metrics { get ; set ; }
2019-06-15 10:03:07 +00:00
2019-04-29 18:21:16 +00:00
[Command("invite")]
2019-07-10 11:55:48 +00:00
[Alias("inv")]
2019-04-29 18:21:16 +00:00
[Remarks("invite")]
2019-06-15 10:03:07 +00:00
public async Task Invite ( )
{
var clientId = BotConfig . ClientId ? ? ( await Context . 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-04-29 18:21:16 +00:00
await Context . Channel . SendMessageAsync ( $"{Emojis.Success} Use this link to add PluralKit to your server:\n<{invite}>" ) ;
}
2019-07-10 11:57:59 +00:00
[Command("mn")] public Task Mn ( ) = > Context . Channel . SendMessageAsync ( "Gotta catch 'em all!" ) ;
[Command("fire")] public Task Fire ( ) = > Context . Channel . SendMessageAsync ( "*A giant lightning bolt promptly erupts into a pillar of fire as it hits your opponent.*" ) ;
[Command("thunder")] public Task Thunder ( ) = > Context . Channel . SendMessageAsync ( "*A giant ball of lightning is conjured and fired directly at your opponent, vanquishing them.*" ) ;
[Command("freeze")] public Task Freeze ( ) = > Context . Channel . SendMessageAsync ( "*A giant crystal ball of ice is charged and hurled toward your opponent, bursting open and freezing them solid on contact.*" ) ;
[Command("starstorm")] public Task Starstorm ( ) = > Context . Channel . SendMessageAsync ( "*Vibrant colours burst forth from the sky as meteors rain down upon your opponent.*" ) ;
2019-07-16 19:59:06 +00:00
[Command("stats")]
public async Task Stats ( )
{
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 ;
var commandsRun = Metrics . Snapshot . GetForContext ( "Bot" ) . Meters . First ( m = > m . MultidimensionalName = = BotMetrics . CommandsRun . Name ) . Value ;
await Context . Channel . SendMessageAsync ( embed : new EmbedBuilder ( )
. 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-07-26 10:05:09 +00:00
[Command("permcheck")]
[Summary("permcheck [guild] ")]
public async Task PermCheckGuild ( ulong guildId )
{
// TODO: will this call break for sharding if you try to request a guild on a different bot instance?
var guild = Context . Client . GetGuild ( guildId ) as IGuild ;
if ( guild = = null )
throw Errors . GuildNotFound ( guildId ) ;
var requiredPermissions = new [ ]
{
ChannelPermission . ViewChannel , // Manage Messages automatically grants Send and Add Reactions, but not Read
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 ( )
. WithTitle ( $"Permission check for **{guild.Name}**" ) ;
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}" ) ) ;
eb . AddField ( $"Missing *{missingPermissionNames}*" , channelsList ) ;
eb . WithColor ( Color . Red ) ;
}
}
// Send! :)
await Context . Channel . SendMessageAsync ( embed : eb . Build ( ) ) ;
}
[Command("permcheck")]
[Summary("permcheck [guild] ")]
[ RequireContext ( ContextType . Guild , ErrorMessage =
"When running this command in DMs, you must pass a guild ID." ) ]
public Task PermCheckGuild ( ) = > PermCheckGuild ( Context . Guild . Id ) ;
2019-04-29 18:21:16 +00:00
}
}