Files
nadekobot/src/NadekoBot/Modules/Administration/SelfCommands.cs

559 lines
21 KiB
C#

using Discord;
using Discord.Commands;
using Discord.Net;
using Discord.WebSocket;
using NadekoBot.Common;
using NadekoBot.Common.Attributes;
using NadekoBot.Common.Replacements;
using NadekoBot.Services.Database.Models;
using NadekoBot.Extensions;
using NadekoBot.Modules.Administration.Services;
using System;
using System.Linq;
using System.Threading.Tasks;
using NadekoBot.Services;
using Serilog;
namespace NadekoBot.Modules.Administration
{
public partial class Administration
{
[Group]
public class SelfCommands : NadekoSubmodule<SelfService>
{
private readonly DiscordSocketClient _client;
private readonly Bot _bot;
private readonly IBotStrings _strings;
private readonly ICoordinator _coord;
public SelfCommands(DiscordSocketClient client, Bot bot, IBotStrings strings, ICoordinator coord)
{
_client = client;
_bot = bot;
_strings = strings;
_coord = coord;
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
public async Task StartupCommandAdd([Leftover] string cmdText)
{
if (cmdText.StartsWith(Prefix + "die", StringComparison.InvariantCulture))
return;
var guser = (IGuildUser)ctx.User;
var cmd = new AutoCommand()
{
CommandText = cmdText,
ChannelId = ctx.Channel.Id,
ChannelName = ctx.Channel.Name,
GuildId = ctx.Guild?.Id,
GuildName = ctx.Guild?.Name,
VoiceChannelId = guser.VoiceChannel?.Id,
VoiceChannelName = guser.VoiceChannel?.Name,
Interval = 0,
};
_service.AddNewAutoCommand(cmd);
await ctx.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle(GetText("scadd"))
.AddField(efb => efb.WithName(GetText("server"))
.WithValue(cmd.GuildId is null ? $"-" : $"{cmd.GuildName}/{cmd.GuildId}").WithIsInline(true))
.AddField(efb => efb.WithName(GetText("channel"))
.WithValue($"{cmd.ChannelName}/{cmd.ChannelId}").WithIsInline(true))
.AddField(efb => efb.WithName(GetText("command_text"))
.WithValue(cmdText).WithIsInline(false))).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
public async Task AutoCommandAdd(int interval, [Leftover] string cmdText)
{
if (cmdText.StartsWith(Prefix + "die", StringComparison.InvariantCulture))
return;
if (interval < 5)
return;
var guser = (IGuildUser)ctx.User;
var cmd = new AutoCommand()
{
CommandText = cmdText,
ChannelId = ctx.Channel.Id,
ChannelName = ctx.Channel.Name,
GuildId = ctx.Guild?.Id,
GuildName = ctx.Guild?.Name,
VoiceChannelId = guser.VoiceChannel?.Id,
VoiceChannelName = guser.VoiceChannel?.Name,
Interval = interval,
};
_service.AddNewAutoCommand(cmd);
await ReplyConfirmLocalizedAsync("autocmd_add", Format.Code(Format.Sanitize(cmdText)), cmd.Interval).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task StartupCommandsList(int page = 1)
{
if (page-- < 1)
return;
var scmds = _service.GetStartupCommands()
.Skip(page * 5)
.Take(5)
.ToList();
if (scmds.Count == 0)
{
await ReplyErrorLocalizedAsync("startcmdlist_none").ConfigureAwait(false);
}
else
{
var i = 0;
await ctx.Channel.SendConfirmAsync(
text: string.Join("\n", scmds
.Select(x => $@"```css
#{++i + page * 5}
[{GetText("server")}]: {(x.GuildId.HasValue ? $"{x.GuildName} #{x.GuildId}" : "-")}
[{GetText("channel")}]: {x.ChannelName} #{x.ChannelId}
[{GetText("command_text")}]: {x.CommandText}```")),
title: string.Empty,
footer: GetText("page", page + 1))
.ConfigureAwait(false);
}
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task AutoCommandsList(int page = 1)
{
if (page-- < 1)
return;
var scmds = _service.GetAutoCommands()
.Skip(page * 5)
.Take(5)
.ToList();
if (!scmds.Any())
{
await ReplyErrorLocalizedAsync("autocmdlist_none").ConfigureAwait(false);
}
else
{
var i = 0;
await ctx.Channel.SendConfirmAsync(
text: string.Join("\n", scmds
.Select(x => $@"```css
#{++i + page * 5}
[{GetText("server")}]: {(x.GuildId.HasValue ? $"{x.GuildName} #{x.GuildId}" : "-")}
[{GetText("channel")}]: {x.ChannelName} #{x.ChannelId}
{GetIntervalText(x.Interval)}
[{GetText("command_text")}]: {x.CommandText}```")),
title: string.Empty,
footer: GetText("page", page + 1))
.ConfigureAwait(false);
}
}
private string GetIntervalText(int interval)
{
return $"[{GetText("interval")}]: {interval}";
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task Wait(int miliseconds)
{
if (miliseconds <= 0)
return;
ctx.Message.DeleteAfter(0);
try
{
var msg = await ctx.Channel.SendConfirmAsync($"⏲ {miliseconds}ms")
.ConfigureAwait(false);
msg.DeleteAfter(miliseconds / 1000);
}
catch { }
await Task.Delay(miliseconds).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
public async Task AutoCommandRemove([Leftover] int index)
{
if (!_service.RemoveAutoCommand(--index, out _))
{
await ReplyErrorLocalizedAsync("acrm_fail").ConfigureAwait(false);
return;
}
await ctx.OkAsync();
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task StartupCommandRemove([Leftover] int index)
{
if (!_service.RemoveStartupCommand(--index, out _))
await ReplyErrorLocalizedAsync("scrm_fail").ConfigureAwait(false);
else
await ReplyConfirmLocalizedAsync("scrm").ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
public async Task StartupCommandsClear()
{
_service.ClearStartupCommands();
await ReplyConfirmLocalizedAsync("startcmds_cleared").ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task ForwardMessages()
{
var enabled = _service.ForwardMessages();
if (enabled)
await ReplyConfirmLocalizedAsync("fwdm_start").ConfigureAwait(false);
else
await ReplyConfirmLocalizedAsync("fwdm_stop").ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task ForwardToAll()
{
var enabled = _service.ForwardToAll();
if (enabled)
await ReplyConfirmLocalizedAsync("fwall_start").ConfigureAwait(false);
else
await ReplyConfirmLocalizedAsync("fwall_stop").ConfigureAwait(false);
}
[NadekoCommand, Aliases]
public async Task ShardStats(int page = 1)
{
if (--page < 0)
return;
var statuses = _coord.GetAllShardStatuses();
var status = string.Join(" : ", statuses
.Select(x => (ConnectionStateToEmoji(x), x))
.GroupBy(x => x.Item1)
.Select(x => $"`{x.Count()} {x.Key}`")
.ToArray());
var allShardStrings = statuses
.Select(st =>
{
var stateStr = ConnectionStateToEmoji(st);
var timeDiff = DateTime.UtcNow - st.LastUpdate;
var maxGuildCountLength = statuses.Max(x => x.GuildCount).ToString().Length;
return $"`{stateStr} " +
$"| #{st.ShardId.ToString().PadBoth(3)} " +
$"| {timeDiff:mm\\:ss} " +
$"| {st.GuildCount.ToString().PadBoth(maxGuildCountLength)} `";
})
.ToArray();
await ctx.SendPaginatedConfirmAsync(page, (curPage) =>
{
var str = string.Join("\n", allShardStrings.Skip(25 * curPage).Take(25));
if (string.IsNullOrWhiteSpace(str))
str = GetText("no_shards_on_page");
return new EmbedBuilder()
.WithOkColor()
.WithDescription($"{status}\n\n{str}");
}, allShardStrings.Length, 25).ConfigureAwait(false);
}
private static string ConnectionStateToEmoji(ShardStatus status)
{
var timeDiff = DateTime.UtcNow - status.LastUpdate;
return status.ConnectionState switch
{
ConnectionState.Connected => "✅",
ConnectionState.Disconnected => "🔻",
_ when timeDiff > TimeSpan.FromSeconds(30) => " ❗ ",
_ => " ⏳"
};
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task RestartShard(int shardId)
{
var success = _coord.RestartShard(shardId);
if (success)
{
await ReplyConfirmLocalizedAsync("shard_reconnecting", Format.Bold("#" + shardId)).ConfigureAwait(false);
}
else
{
await ReplyErrorLocalizedAsync("no_shard_id").ConfigureAwait(false);
}
}
[NadekoCommand, Aliases]
[OwnerOnly]
public Task Leave([Leftover] string guildStr)
{
return _service.LeaveGuild(guildStr);
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task Die()
{
try
{
await ReplyConfirmLocalizedAsync("shutting_down").ConfigureAwait(false);
}
catch
{
// ignored
}
await Task.Delay(2000).ConfigureAwait(false);
_coord.Die();
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task Restart()
{
bool success = _coord.RestartBot();
if (!success)
{
await ReplyErrorLocalizedAsync("restart_fail").ConfigureAwait(false);
return;
}
try { await ReplyConfirmLocalizedAsync("restarting").ConfigureAwait(false); } catch { }
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task SetName([Leftover] string newName)
{
if (string.IsNullOrWhiteSpace(newName))
return;
try
{
await _client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false);
}
catch (RateLimitedException)
{
Log.Warning("You've been ratelimited. Wait 2 hours to change your name");
}
await ReplyConfirmLocalizedAsync("bot_name", Format.Bold(newName)).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[UserPerm(GuildPerm.ManageNicknames)]
[BotPerm(GuildPerm.ChangeNickname)]
[Priority(0)]
public async Task SetNick([Leftover] string newNick = null)
{
if (string.IsNullOrWhiteSpace(newNick))
return;
var curUser = await ctx.Guild.GetCurrentUserAsync().ConfigureAwait(false);
await curUser.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
await ReplyConfirmLocalizedAsync("bot_nick", Format.Bold(newNick) ?? "-").ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[BotPerm(GuildPerm.ManageNicknames)]
[UserPerm(GuildPerm.ManageNicknames)]
[Priority(1)]
public async Task SetNick(IGuildUser gu, [Leftover] string newNick = null)
{
var sg = (SocketGuild) Context.Guild;
if (sg.OwnerId == gu.Id ||
gu.GetRoles().Max(r => r.Position) >= sg.CurrentUser.GetRoles().Max(r => r.Position))
{
await ReplyErrorLocalizedAsync("insuf_perms_i");
return;
}
await gu.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
await ReplyConfirmLocalizedAsync("user_nick", Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-").ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task SetStatus([Leftover] SettableUserStatus status)
{
await _client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false);
await ReplyConfirmLocalizedAsync("bot_status", Format.Bold(status.ToString())).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task SetAvatar([Leftover] string img = null)
{
var success = await _service.SetAvatar(img);
if (success)
{
await ReplyConfirmLocalizedAsync("set_avatar").ConfigureAwait(false);
}
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task SetGame(ActivityType type, [Leftover] string game = null)
{
var rep = new ReplacementBuilder()
.WithDefault(Context)
.Build();
await _service.SetGameAsync(game is null ? game : rep.Replace(game), type).ConfigureAwait(false);
await ReplyConfirmLocalizedAsync("set_game").ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task SetStream(string url, [Leftover] string name = null)
{
name = name ?? "";
await _service.SetStreamAsync(name, url).ConfigureAwait(false);
await ReplyConfirmLocalizedAsync("set_stream").ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task Send(string where, [Leftover] string msg = null)
{
if (string.IsNullOrWhiteSpace(msg))
return;
var ids = where.Split('|');
if (ids.Length != 2)
return;
var sid = ulong.Parse(ids[0]);
var server = _client.Guilds.FirstOrDefault(s => s.Id == sid);
if (server is null)
return;
var rep = new ReplacementBuilder()
.WithDefault(Context)
.Build();
if (ids[1].ToUpperInvariant().StartsWith("C:", StringComparison.InvariantCulture))
{
var cid = ulong.Parse(ids[1].Substring(2));
var ch = server.TextChannels.FirstOrDefault(c => c.Id == cid);
if (ch is null)
{
return;
}
if (CREmbed.TryParse(msg, out var crembed))
{
rep.Replace(crembed);
await ch.EmbedAsync(crembed).ConfigureAwait(false);
await ReplyConfirmLocalizedAsync("message_sent").ConfigureAwait(false);
return;
}
await ch.SendMessageAsync(rep.Replace(msg).SanitizeMentions()).ConfigureAwait(false);
}
else if (ids[1].ToUpperInvariant().StartsWith("U:", StringComparison.InvariantCulture))
{
var uid = ulong.Parse(ids[1].Substring(2));
var user = server.Users.FirstOrDefault(u => u.Id == uid);
if (user is null)
{
return;
}
if (CREmbed.TryParse(msg, out var crembed))
{
rep.Replace(crembed);
await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).EmbedAsync(crembed)
.ConfigureAwait(false);
await ReplyConfirmLocalizedAsync("message_sent").ConfigureAwait(false);
return;
}
await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(rep.Replace(msg).SanitizeMentions()).ConfigureAwait(false);
}
else
{
await ReplyErrorLocalizedAsync("invalid_format").ConfigureAwait(false);
return;
}
await ReplyConfirmLocalizedAsync("message_sent").ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task ImagesReload()
{
await _service.ReloadImagesAsync();
await ReplyConfirmLocalizedAsync("images_loading", 0).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task StringsReload()
{
_strings.Reload();
await ReplyConfirmLocalizedAsync("bot_strings_reloaded").ConfigureAwait(false);
}
private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus)
{
switch (sus)
{
case SettableUserStatus.Online:
return UserStatus.Online;
case SettableUserStatus.Invisible:
return UserStatus.Invisible;
case SettableUserStatus.Idle:
return UserStatus.AFK;
case SettableUserStatus.Dnd:
return UserStatus.DoNotDisturb;
}
return UserStatus.Online;
}
public enum SettableUserStatus
{
Online,
Invisible,
Idle,
Dnd
}
}
}
}