diff --git a/locale/Messages.resx b/locale/Messages.resx index 6957663..1a5da43 100644 --- a/locale/Messages.resx +++ b/locale/Messages.resx @@ -1,6 +1,7 @@ - + + - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + I'm ready! - + Deleted message by {0}: - + Cleared message from {0} in channel {1}: {2} - + Edited message by {0}: - + {0}, welcome to {1} - + Bah! - + Bop! - + Beep! - + I do not have permission to execute this command! - + You do not have permission to execute this command! - + You were banned - + Punishment expired - + You specified less than {0} messages! - + You specified more than {0} messages! - + Command help: - + You were kicked - + ms - + Member is already muted! - + Not specified - + Not specified - + Current settings: - + Language - + Prefix - + Remove roles on mute - + Send welcome messages - + Mute role - - Language not supported! - - + + Language not supported! + + Yes - + No - + This user is not banned! - + Member not muted! - + Welcome message - + You need to specify an integer from {0} to {1} instead of {2}! - + {0} was banned - + That setting doesn't exist! - + Receive startup messages - + Invalid setting value specified! - + This role does not exist! - + This channel does not exist! - + I cannot mute someone for more than 28 days using timeouts! Either specify a duration shorter than 28 days, or set a mute role in settings - + I cannot use time-outs on other bots! Try to set a mute role in settings - + {0} has created event {1}! It will take place in {2} and will start <t:{3}:R>! \n {4} - + Role for event creation notifications - + Channel for event notifications - + Event start notifications receivers - + Event "{0}" started - + :( - + Event "{0}" is cancelled! - + Event "{0}" has completed! - + ever - + Cleared {0} messages - + Kicked {0}: {1} - + Muted {0} for{1}: {2} - + Unbanned {0}: {1} - + Unmuted {0}: {1} - + Nothing changed! `{0}` is already set to {1} - + Not specified - + Value of setting `{0}` is now set to {1} - + Bans a user - + Deletes a specified amount of messages in this channel - + Shows this message - + Kicks a member - + Mutes a member - + Shows (inaccurate) latency - + Allows you to change certain preferences for this guild - + Unbans a user - + Unmutes a member - + You need to specify an integer from {0} to {1}! - + You need to specify a user! - + You need to specify a user instead of {0}! - + You need to specify a guild member! - + You need to specify a member of this guild! - + You cannot ban users from this guild! - + You cannot manage messages in this guild! - + You cannot kick members from this guild! - + You cannot moderate members in this guild! - + You cannot manage this guild! - + I cannot ban users from this guild! - + I cannot manage messages in this guild! - + I cannot kick members from this guild! - + I cannot moderate members in this guild! - + I cannot manage this guild! - + You need to specify a reason to ban this user! - + You need to specify a reason to kick this member! - + You need to specify a reason to mute this member! - + You need to specify a reason to unban this user! - + You need to specify a reason for unmute this member! - + You cannot ban the owner of this guild! - + You cannot ban yourself! - + You cannot ban me! - + I cannot ban this user! - + You cannot ban this user! - + You cannot kick the owner of this guild! - + You cannot kick yourself! - + You cannot kick me! - + I cannot kick this member! - + You cannot kick this member! - + You cannot mute the owner of this guild! - + You cannot mute yourself! - + You cannot mute me! - + I cannot mute this member! - + You cannot mute this member! - + You don't need to unmute the owner of this guild! - + You are muted! - + ... - + I cannot unmute this member! - + You cannot unmute this user! - + Event "{0}" will start {1}! - + Early event start notification offset - + I could not find this user in any guild I'm a member of! Check if the ID is correct and that the user was on this server no longer than 30 days ago - + Default role - + Adds a reminder - + Channel for public notifications - + Channel for private notifications - + Return roles on rejoin - + Automatically start scheduled events - + You need to specify reminder text! - + You need to specify when I should send you the reminder! - + Issued by - + {0} has created a new event: - + The event will start at {0} in {1} - + The event will start at {0} until {1} in {2} - + Event details - + The event has lasted for `{0}` - + The event is happening at {0} - + The event is happening at {0} until {1} - + This user is already banned! - + {0} was unbanned - + {0} was muted - + {0} was unmuted - + This member is not muted! - + I could not find this user! - + {0} was kicked - + Reason: {0} - + Expires at: {0} - + This user is already muted! - + From {0}: - + Developers: - - Boyfriend's source code - - + + Boyfriend's source code + + About Boyfriend - - developer & designer, Boyfriend's Wiki creator - - + + developer & designer, Boyfriend's Wiki creator + + main developer - + developer - - Reminder for {0} created - - - Reminder for {0} - - - You asked me to remind you {0} - - - Boyfriend's Settings - - - Setting successfully changed - - - Setting not changed - - - is now - - - Rename members who attempt to hoist themselves - - - Page - - - Page not found! - - - There are {0} total pages - - - Next - - - Previous - + + Reminder for {0} created + + + Reminder for {0} + + + You asked me to remind you {0} + + + Boyfriend's Settings + + + Setting successfully changed + + + Setting not changed + + + is now + + + Rename members who attempt to hoist themselves + + + Page + + + Page not found! + + + There are {0} total pages + + + Next + + + Previous + - {0}'s reminders - + {0}'s reminders + - There's no reminder with that index! - + There's no reminder with that index! + - Reminder deleted - - - You don't have any reminders created! - - - Setting {0} reset - - - All settings have been reset - - - Jump to message: {0} - - - Jump to channel: {0} - - - Index: {0} - - - The reminder will be sent on: {0} - - - Reminder text: {0} - - + Reminder deleted + + + You don't have any reminders created! + + + Setting {0} reset + + + All settings have been reset + + + Jump to message: {0} + + + Jump to channel: {0} + + + Index: {0} + + + The reminder will be sent on: {0} + + + Reminder text: {0} + + + Display name + + + Information about {0} + + + Muted + + + Discord user since + + + Banned + + + Punishments + + + Banned permanently + + + Not in the guild + + + Muted by timeout + + + Muted by mute role + + + Guild member since + + + Nickname + + + Roles + + + Nitro booster since + + \ No newline at end of file diff --git a/locale/Messages.ru.resx b/locale/Messages.ru.resx index 9d9d99d..6f19804 100644 --- a/locale/Messages.ru.resx +++ b/locale/Messages.ru.resx @@ -1,6 +1,7 @@ - + + - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Я запустился! - + Сообщение {0} удалено: - + Очищено сообщение от {0} в канале {1}: {2} - + Сообщение {0} отредактировано: - + {0}, добро пожаловать на сервер {1} - + Бап! - + Боп! - + Бип! - + У меня недостаточно прав для выполнения этой команды! - + У тебя недостаточно прав для выполнения этой команды! - + Время наказания истекло - + Указано менее {0} сообщений! - + Указано более {0} сообщений! - + Справка по командам: - + Вы были выгнаны - + мс - + Участник уже заглушен! - + Не указан - + Не указана - + Текущие настройки: - + Язык - + Префикс - + Удалять роли при муте - + Отправлять приветствия - + Роль мута - - Язык не поддерживается! - - + + Язык не поддерживается! + + Да - + Нет - + Этот пользователь не забанен! - + Участник не заглушен! - + Приветствие - + Надо указать целое число от {0} до {1} вместо {2}! - + {0} был(-а) забанен(-а) - + Такая настройка не существует! - + Получать сообщения о запуске - + Указано недействительное значение для настройки! - + Эта роль не существует! - + Этот канал не существует! - + Я не могу заглушить кого-то на более чем 28 дней, используя тайм-ауты! Или укажи продолжительность менее 28 дней, или установи роль мута в настройках - + Я не могу использовать тайм-ауты на других ботах! Попробуй указать роль мута в настройках - + {0} создал событие {1}! Оно пройдёт в {2} и начнётся <t:{3}:R>!{4} - + Роль для уведомлений о создании событий - + Канал для уведомлений о событиях - + Получатели уведомлений о начале событий - + Событие "{0}" началось - + :( - + Событие "{0}" отменено! - + Событие "{0}" завершено! - + всегда - + Очищено {0} сообщений - + Выгнан {0}: {1} - + Заглушен {0} на{1}: {2} - + Возвращён из бана {0}: {1} - + Разглушен {0}: {1} - + Ничего не изменилось! Значение настройки `{0}` уже {1} - + Не указано - + Значение настройки `{0}` теперь установлено на {1} - + Банит пользователя - + Удаляет указанное количество сообщений в этом канале - + Показывает эту справку - + Выгоняет участника - + Глушит участника - + Показывает (неточную) задержку - + Позволяет менять некоторые настройки под этот сервер - + Возвращает пользователя из бана - + Разглушает участника - + Надо указать целое число от {0} до {1}! - + Надо указать пользователя! - + Надо указать пользователя вместо {0}! - + Надо указать участника сервера! - + Надо указать участника этого сервера! - + Ты не можешь банить пользователей на этом сервере! - + Ты не можешь управлять сообщениями этого сервера! - + Ты не можешь выгонять участников с этого сервера! - + Ты не можешь модерировать участников этого сервера! - + Ты не можешь настраивать этот сервер! - + Я не могу банить пользователей на этом сервере! - + Я не могу управлять сообщениями этого сервера! - + Я не могу выгонять участников с этого сервера! - + Я не могу модерировать участников этого сервера! - + Я не могу настраивать этот сервер! - + Надо указать причину для бана этого участника! - + Надо указать причину для кика этого участника! - + Надо указать причину для мута этого участника! - + Надо указать причину для разбана этого пользователя! - + Надо указать причину для размута этого участника! - + Ты не можешь меня забанить! - + Ты не можешь забанить владельца этого сервера! - + Ты не можешь забанить этого участника! - + Ты не можешь себя забанить! - + Я не могу забанить этого пользователя! - + Ты не можешь выгнать владельца этого сервера! - + Ты не можешь себя выгнать! - + Ты не можешь меня выгнать! - + Я не могу выгнать этого участника - + Ты не можешь выгнать этого участника! - + Ты не можешь заглушить владельца этого сервера! - + Ты не можешь себя заглушить! - + Ты не можешь заглушить меня! - + Я не могу заглушить этого пользователя! - + Ты не можешь заглушить этого участника! - + Тебе не надо возвращать из мута владельца этого сервера! - + Ты заглушен! - + ... - + Ты не можешь вернуть из мута этого пользователя! - + Я не могу вернуть из мута этого пользователя! - + Событие "{0}" начнется {1}! - + Офсет отправки преждевременного уведомления о начале события - + Я не смог найти этого пользователя ни в одном из серверов, в которых я есть. Проверь правильность ID и нахождение пользователя на этом сервере максимум 30 дней назад - - Роль по умолчанию - - + + Роль по умолчанию + + Добавляет напоминание - + Канал для публичных уведомлений - + Канал для приватных уведомлений - + Возвращать роли при перезаходе - + Автоматически начинать события - + Тебе нужно указать текст напоминания! - + Нужно указать время, через которое придёт напоминание! - + Ответственный - + {0} создаёт новое событие: - + Событие пройдёт {0} в канале {1} - + Событие пройдёт с {0} до {1} в {2} - + Подробнее о событии - + Событие длилось `{0}` - + Событие происходит в {0} - + Событие происходит в {0} до {1} - + Этот пользователь уже забанен! - + {0} был(-а) разбанен(-а) - + {0} был(-а) заглушен(-а) - + Этот участник не заглушен! - + {0} был(-а) разглушен(-а) - + Я не смог найти этого пользователя! - + {0} был(-а) выгнан(-а) - + Причина: {0} - + Закончится: {0} - + Этот пользователь уже в муте! - + Вы были забанены - + От {0}: - + Разработчики: - - Исходный код Boyfriend - - + + Исходный код Boyfriend + + О Boyfriend - - разработчик - - + + разработчик + + основной разработчик - - разработчик и дизайнер, создатель Boyfriend's Wiki - - - Напоминание для {0} создано - - - Напоминание для {0} - - - Вы просили напомнить вам {0} - - - Настройки Boyfriend - - - Настройка успешно изменена - - - Настройка не редактирована - - - теперь имеет значение - - - Переименовывать участников, которые пытаются поднять себя - - - Страница - - - Страница не найдена! - - - Всего есть {0} страниц(-ы) - - - Далее - - - Назад - - - Напоминания {0} - - - У тебя нет напоминания с указанным индексом! - - - Напоминание удалено - - - У вас нет созданных напоминаний! - - - Настройка {0} сброшена - - - Все настройки были сброшены - - - Перейти к сообщению: {0} - - - Перейти к каналу: {0} - - - Индекс: {0} - - - Напоминание будет отправлено: {0} - - - Текст напоминалки: {0} - - + + разработчик и дизайнер, создатель Boyfriend's Wiki + + + Напоминание для {0} создано + + + Напоминание для {0} + + + Вы просили напомнить вам {0} + + + Настройки Boyfriend + + + Настройка успешно изменена + + + Настройка не редактирована + + + теперь имеет значение + + + Переименовывать участников, которые пытаются поднять себя + + + Страница + + + Страница не найдена! + + + Всего есть {0} страниц(-ы) + + + Далее + + + Назад + + + Напоминания {0} + + + У тебя нет напоминания с указанным индексом! + + + Напоминание удалено + + + У вас нет созданных напоминаний! + + + Настройка {0} сброшена + + + Все настройки были сброшены + + + Перейти к сообщению: {0} + + + Перейти к каналу: {0} + + + Индекс: {0} + + + Напоминание будет отправлено: {0} + + + Текст напоминалки: {0} + + + Отображаемое имя + + + Информация о {0} + + + Заглушен + + + Вступил в Discord + + + Забанен + + + Наказания + + + Забанен навсегда + + + Не на сервере + + + Заглушен с помощью тайм-аута + + + Заглушен с помощью роли мута + + + Вступил на сервер + + + Никнейм + + + Роли + + + Начал бустить сервер + + \ No newline at end of file diff --git a/locale/Messages.tt-ru.resx b/locale/Messages.tt-ru.resx index a3da5f7..b0335a8 100644 --- a/locale/Messages.tt-ru.resx +++ b/locale/Messages.tt-ru.resx @@ -1,6 +1,6 @@ - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + я родился! - + сообщение {0} вырезано: - + вырезано сообщение (используя `!clear`) от {0} в канале {1}: {2} - + сообщение {0} переделано: - + {0}, добро пожаловать на сервер {1} - + брах! - + брох! - + брух! - + у меня прав нету, сделай что нибудь. - + у тебя прав нету, твои проблемы. - + вы были забанены - + время бана закончиловсь - + ты выбрал менее {0} сообщений - + ты выбрал более {0} сообщений - + туториал по приколам: - + вы были кикнуты - + мс - + шизоид уже замучен! - + *тут ничего нет* - + нъет - + настройки: - + язык - + префикс - + удалять звание при муте - + разглашать о том что пришел новый шизоид - + звание замученного - - такого языка нету... - - + + такого языка нету... + + да - + нъет - + шизик не забанен - + шизоид не замучен! - + здравствуйте (типо настройка) - + выбери число от {0} до {1} вместо {2}! - + {0} забанен - + такой прикол не существует - + получать инфу о старте бота - + криво настроил прикол, давай по новой - + этого звания нету, ты шо - + этого канала нету, ты шо - + ты шо, мутить больше чем на 28 дней таймаут не разрешает, вот настроишь роль мута, тогда поговорим - + я не могу замутить ботов, сделай что нибудь - + {0} приготовил новую движуху {1}! она пройдёт в {2} и начнётся <t:{3}:R>!{4} - + роль для уведомлений о создании движухи - + канал для уведомлений о движухах - + получатели уведомлений о начале движух - + движуха "{0}" начинается - + оъмъомоъемъъео(((( - - движуха "{0}" отменена! - - - движуха "{0}" завершена! - - + + движуха "{0}" отменена! + + + движуха "{0}" завершена! + + всегда - + вырезано {0} забавных сообщений - + выгнан {0}: {1} - + замучен {0} на{1}: {2} - + раззабанен {0}: {1} - + раззамучен {0}: {1} - + ты все сломал! значение прикола `{0}` и так {1} - + нъет - + прикол для `{0}` теперь установлен на {1} - + возводит великий банхаммер над шизоидом - + удаляет сообщения. сколько хош, столько и удалит - + показывает то, что ты сейчас видишь прямо сейчас - + выпинывает шизоида - + мутит шизоида - + показывает пинг (сверхмегаточный (нет)) - + настройки бота под этот сервер - + отводит великий банхаммер от шизоида - + раззамучивает шизоида - + укажи целое число от {0} до {1} - + укажи самого шизика - + надо указать юзверя вместо {0}! - + укажи самого шизика - + укажи шизоида сервера! - + бан - + тебе нельзя иметь власть над сообщениями шизоидов - + кик шизиков нельзя - + тебе нельзя управлять шизоидами - + тебе нельзя редактировать дурку - + я не могу ваще никого банить чел. - + я не могу исправлять орфографический кринж участников, сделай что нибудь. - + я не могу ваще никого кикать чел. - + я не могу контроллировать за всеми ними, сделай что нибудь. - + я не могу этому серверу хоть че либо нибудь изменить, сделай что нибудь. - + укажи зачем банить шизика - + укажи зачем кикать шизика - + укажи зачем мутить шизика - + укажи зачем раззабанивать шизика - + укажи зачам размучивать шизика - + ээбля френдли фаер огонь по своим - + бан админу нельзя - + бан этому шизику нельзя - + самобан нельзя - + я не могу его забанить... - + кик админу нельзя - + самокик нельзя - + ээбля френдли фаер огонь по своим - + я не могу его кикнуть... - + кик этому шизику нельзя - + мут админу нельзя - + самомут нельзя - + ээбля френдли фаер огонь по своим - + я не могу его замутить... - + мут этому шизику нельзя - + сильно - + ты замучен. - + ... - + тебе нельзя раззамучивать - + я не могу его раззамутить... - + движуха "{0}" начнется {1}! - + заранее пнуть в минутах до начала движухи - + у нас такого шизоида нету, проверь, валиден ли ID уважаемого (я забываю о шизоидах если они ливнули минимум месяц назад) - + дефолтное звание - + крафтит напоминалку - + канал для секретных уведомлений - + канал для не секретных уведомлений - + вернуть звания при переподключении в дурку - + автоматом стартить движухи - + для крафта напоминалки нужен текст - + шизоид у меня на часах такого нету - + ответственный - + {0} создает новое событие: - + движуха произойдет {0} в канале {1} - + движуха будет происходить с {0} до {1} в {2} - + побольше о движухе - + все это длилось `{0}` - + движуха происходит в {0} - + движуха происходит в {0} до {1} - + этот шизоид уже лежит в бане - + {0} раззабанен - + {0} в муте - + {0} в размуте - + этого шизоида никто не мутил. - + у нас такого шизоида нету... - + {0} вышел с посторонней помощью - + причина: {0} - + до: {0} - + этот шизоид УЖЕ замучился - + от {0} - + девелоперы: - - репа Boyfriend (тык) - - + + репа Boyfriend (тык) + + немного о Boyfriend - - скучный девелопер + дизайнер создавший Boyfriend's Wiki - - + + скучный девелопер + дизайнер создавший Boyfriend's Wiki + + ВАЖНЫЙ соучастник кодинг-стримов @Octol1ttle - + САМЫЙ ВАЖНЫЙ чел написавший кода больше всех (99.99%) - - напоминалка для {0} скрафченА - - - напоминалка для {0} - - - ты хотел чтоб я напомнил тебе {0} - - - приколы Boyfriend - - - прикол редактирован - - - прикол сдох - - - стало - - - переобувать шизоидов пытающихся поднять себя в табе - - - это страница - - - если я был бы html, я бы сказал 404 - - - ну а если быть точнее, тут всего {0} страниц(-ы) - - - следующее - - - предыдущее - - - напоминалки {0} - - - у тебя нет напоминалки с этим индексом! - - - напоминалка уничтожена - - - ты еще не крафтил напоминалки - - - {0} откачен к заводским - - - откатываемся к заводским... - - - чекнуть сообщение: {0} - - - чекнуть канал: {0} - - - индекс: {0} - - - я пну тебе это: {0} - - - че там в напоминалке: {0} - - + + напоминалка для {0} скрафченА + + + напоминалка для {0} + + + ты хотел чтоб я напомнил тебе {0} + + + приколы Boyfriend + + + прикол редактирован + + + прикол сдох + + + стало + + + переобувать шизоидов пытающихся поднять себя в табе + + + это страница + + + если я был бы html, я бы сказал 404 + + + ну а если быть точнее, тут всего {0} страниц(-ы) + + + следующее + + + предыдущее + + + напоминалки {0} + + + у тебя нет напоминалки с этим индексом! + + + напоминалка уничтожена + + + ты еще не крафтил напоминалки + + + {0} откачен к заводским + + + откатываемся к заводским... + + + чекнуть сообщение: {0} + + + чекнуть канал: {0} + + + индекс: {0} + + + я пну тебе это: {0} + + + че там в напоминалке: {0} + + + дисплейнейм + + + деанон {0} + + + замучен + + + юзер Discord со времен + + + забанен + + + приколы полученные по заслугам + + + забанен + + + вышел из сервера + + + замучен таймаутом + + + замучен ролькой + + + участник сервера со времен + + + сервернейм + + + рольки + + + бустит сервер со времен + + \ No newline at end of file diff --git a/src/Boyfriend.cs b/src/Boyfriend.cs index 246869e..4ba43f8 100644 --- a/src/Boyfriend.cs +++ b/src/Boyfriend.cs @@ -102,7 +102,8 @@ public sealed class Boyfriend .WithCommandGroup() .WithCommandGroup() .WithCommandGroup() - .WithCommandGroup(); + .WithCommandGroup() + .WithCommandGroup(); var responderTypes = typeof(Boyfriend).Assembly .GetExportedTypes() .Where(t => t.IsResponder()); diff --git a/src/Commands/ToolsCommandGroup.cs b/src/Commands/ToolsCommandGroup.cs new file mode 100644 index 0000000..3ac8b70 --- /dev/null +++ b/src/Commands/ToolsCommandGroup.cs @@ -0,0 +1,231 @@ +using System.ComponentModel; +using System.Drawing; +using System.Text; +using Boyfriend.Data; +using Boyfriend.Services; +using JetBrains.Annotations; +using Remora.Commands.Attributes; +using Remora.Commands.Groups; +using Remora.Discord.API.Abstractions.Objects; +using Remora.Discord.API.Abstractions.Rest; +using Remora.Discord.Commands.Attributes; +using Remora.Discord.Commands.Contexts; +using Remora.Discord.Commands.Feedback.Services; +using Remora.Discord.Extensions.Embeds; +using Remora.Discord.Extensions.Formatting; +using Remora.Rest.Core; +using Remora.Results; + +namespace Boyfriend.Commands; + +/// +/// Handles commands related to tools: /showinfo. +/// +[UsedImplicitly] +public class ToolsCommandGroup : CommandGroup +{ + private readonly ICommandContext _context; + private readonly FeedbackService _feedback; + private readonly IDiscordRestGuildAPI _guildApi; + private readonly GuildDataService _guildData; + private readonly IDiscordRestUserAPI _userApi; + + public ToolsCommandGroup( + ICommandContext context, FeedbackService feedback, + GuildDataService guildData, IDiscordRestGuildAPI guildApi, + IDiscordRestUserAPI userApi, IDiscordRestChannelAPI channelApi) + { + _context = context; + _guildData = guildData; + _feedback = feedback; + _guildApi = guildApi; + _userApi = userApi; + } + + /// + /// A slash command that shows information about user. + /// + /// + /// Information in the output: + /// + /// Display name + /// Discord user since + /// Guild nickname + /// Guild member since + /// Nitro booster since + /// Guild roles + /// Active mute information + /// Active ban information + /// Is on guild status + /// + /// + /// The user to show info about. + /// + /// A feedback sending result which may or may not have succeeded. + /// + [Command("showinfo")] + [DiscordDefaultDMPermission(false)] + [Description("Shows info about user")] + [UsedImplicitly] + public async Task ExecuteShowInfoAsync( + [Description("User to show info about")] + IUser? target = null) + { + if (!_context.TryGetContextIDs(out var guildId, out _, out var userId)) + { + return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context"); + } + + var userResult = await _userApi.GetUserAsync(userId, CancellationToken); + if (!userResult.IsDefined(out var user)) + { + return Result.FromError(userResult); + } + + var currentUserResult = await _userApi.GetCurrentUserAsync(CancellationToken); + if (!currentUserResult.IsDefined(out var currentUser)) + { + return Result.FromError(currentUserResult); + } + + var data = await _guildData.GetData(guildId, CancellationToken); + Messages.Culture = GuildSettings.Language.Get(data.Settings); + + return await ShowUserInfoAsync(target ?? user, currentUser, data, guildId, CancellationToken); + } + + private async Task ShowUserInfoAsync( + IUser user, IUser currentUser, GuildData data, Snowflake guildId, CancellationToken ct = default) + { + var builder = new StringBuilder().AppendLine($"### <@{user.ID}>"); + + if (user.GlobalName is not null) + { + builder.Append("- ").AppendLine(Messages.ShowInfoDisplayName) + .AppendLine(Markdown.InlineCode(user.GlobalName)); + } + + builder.Append("- ").AppendLine(Messages.ShowInfoDiscordUserSince) + .AppendLine(Markdown.Timestamp(user.ID.Timestamp)); + + var memberData = data.GetOrCreateMemberData(user.ID); + + var embedColor = ColorsList.Cyan; + + var guildMemberResult = await _guildApi.GetGuildMemberAsync(guildId, user.ID, ct); + DateTimeOffset? communicationDisabledUntil = null; + if (guildMemberResult.IsDefined(out var guildMember)) + { + communicationDisabledUntil = guildMember.CommunicationDisabledUntil.OrDefault(null); + + embedColor = AppendGuildInformation(embedColor, guildMember, builder); + } + + var isMuted = (memberData.MutedUntil is not null && DateTimeOffset.UtcNow <= memberData.MutedUntil) || + communicationDisabledUntil is not null; + + var existingBanResult = await _guildApi.GetGuildBanAsync(guildId, user.ID, ct); + + if (isMuted || existingBanResult.IsDefined()) + { + builder.Append("### ") + .AppendLine(Markdown.Bold(Messages.ShowInfoPunishments)); + } + + if (isMuted) + { + AppendMuteInformation(memberData, communicationDisabledUntil, builder); + + embedColor = ColorsList.Red; + } + + if (existingBanResult.IsDefined()) + { + AppendBanInformation(memberData, builder); + + embedColor = ColorsList.Black; + } + + if (!guildMemberResult.IsSuccess && !existingBanResult.IsDefined()) + { + builder.Append("### ") + .AppendLine(Markdown.Bold(Messages.ShowInfoNotOnGuild)); + + embedColor = ColorsList.Default; + } + + var embed = new EmbedBuilder().WithSmallTitle( + string.Format(Messages.ShowInfoTitle, user.GetTag()), currentUser) + .WithDescription(builder.ToString()) + .WithColour(embedColor) + .WithLargeAvatar(user) + .WithFooter($"ID: {user.ID.ToString()}") + .Build(); + + return await _feedback.SendContextualEmbedResultAsync(embed, ct); + } + + private static Color AppendGuildInformation(Color color, IGuildMember guildMember, StringBuilder builder) + { + if (guildMember.Nickname.IsDefined(out var nickname)) + { + builder.Append("- ").AppendLine(Messages.ShowInfoGuildNickname) + .AppendLine(Markdown.InlineCode(nickname)); + } + + builder.Append("- ").AppendLine(Messages.ShowInfoGuildMemberSince) + .AppendLine(Markdown.Timestamp(guildMember.JoinedAt)); + + if (guildMember.PremiumSince.IsDefined(out var premiumSince)) + { + builder.Append("- ").AppendLine(Messages.ShowInfoGuildMemberPremiumSince) + .AppendLine(Markdown.Timestamp(premiumSince.Value)); + color = ColorsList.Magenta; + } + + if (guildMember.Roles.Count > 0) + { + builder.Append("- ").AppendLine(Messages.ShowInfoGuildRoles); + for (var i = 0; i < guildMember.Roles.Count - 1; i++) + { + builder.Append($"<@&{guildMember.Roles[i]}>, "); + } + + builder.AppendLine($"<@&{guildMember.Roles[^1]}>"); + } + + return color; + } + + private static void AppendBanInformation(MemberData memberData, StringBuilder builder) + { + if (memberData.BannedUntil < DateTimeOffset.MaxValue) + { + builder.Append("- ").AppendLine(Messages.ShowInfoBanned) + .Append(" - ").AppendLine(string.Format( + Messages.DescriptionActionExpiresAt, Markdown.Timestamp(memberData.BannedUntil.Value))); + return; + } + + builder.Append("- ").AppendLine(Messages.ShowInfoBannedPermanently); + } + + private static void AppendMuteInformation( + MemberData memberData, DateTimeOffset? communicationDisabledUntil, StringBuilder builder) + { + builder.Append("- ").AppendLine(Messages.ShowInfoMuted); + if (memberData.MutedUntil is not null && DateTimeOffset.UtcNow <= memberData.MutedUntil) + { + builder.Append(" - ").AppendLine(Messages.ShowInfoMutedByMuteRole) + .Append(" - ").AppendLine(string.Format( + Messages.DescriptionActionExpiresAt, Markdown.Timestamp(memberData.MutedUntil.Value))); + } + + if (communicationDisabledUntil is not null) + { + builder.Append(" - ").AppendLine(Messages.ShowInfoMutedByTimeout) + .Append(" - ").AppendLine(string.Format( + Messages.DescriptionActionExpiresAt, Markdown.Timestamp(communicationDisabledUntil.Value))); + } + } +} diff --git a/src/Extensions.cs b/src/Extensions.cs index 47c7369..c06c4ce 100644 --- a/src/Extensions.cs +++ b/src/Extensions.cs @@ -58,6 +58,23 @@ public static class Extensions return builder; } + /// + /// Adds a user avatar in the thumbnail field. + /// + /// The builder to add the thumbnail to. + /// The user whose avatar to use in the thumbnail field. + /// The builder with the added avatar in the thumbnail field. + public static EmbedBuilder WithLargeAvatar( + this EmbedBuilder builder, IUser avatarSource) + { + var avatarUrlResult = CDN.GetUserAvatarUrl(avatarSource, imageSize: 256); + var avatarUrl = avatarUrlResult.IsSuccess + ? avatarUrlResult.Entity + : CDN.GetDefaultUserAvatarUrl(avatarSource, imageSize: 256).Entity; + + return builder.WithThumbnailUrl(avatarUrl.AbsoluteUri); + } + /// /// Adds a footer representing that the action was performed in the . /// diff --git a/src/Messages.Designer.cs b/src/Messages.Designer.cs index 175e441..974660d 100644 --- a/src/Messages.Designer.cs +++ b/src/Messages.Designer.cs @@ -1035,6 +1035,89 @@ namespace Boyfriend { internal static string ReminderText { get { return ResourceManager.GetString("ReminderText", resourceCulture); + } + + internal static string ShowInfoTitle { + get { + return ResourceManager.GetString("ShowInfoTitle", resourceCulture); + } + } + + internal static string ShowInfoDisplayName { + get { + return ResourceManager.GetString("ShowInfoDisplayName", resourceCulture); + } + } + + internal static string ShowInfoDiscordUserSince { + get { + return ResourceManager.GetString("ShowInfoDiscordUserSince", resourceCulture); + } + } + + internal static string ShowInfoMuted { + get { + return ResourceManager.GetString("ShowInfoMuted", resourceCulture); + } + } + + internal static string ShowInfoBanned { + get { + return ResourceManager.GetString("ShowInfoBanned", resourceCulture); + } + } + + internal static string ShowInfoPunishments { + get { + return ResourceManager.GetString("ShowInfoPunishments", resourceCulture); + } + } + + internal static string ShowInfoBannedPermanently { + get { + return ResourceManager.GetString("ShowInfoBannedPermanently", resourceCulture); + } + } + + internal static string ShowInfoNotOnGuild { + get { + return ResourceManager.GetString("ShowInfoNotOnGuild", resourceCulture); + } + } + + internal static string ShowInfoMutedByTimeout { + get { + return ResourceManager.GetString("ShowInfoMutedByTimeout", resourceCulture); + } + } + + internal static string ShowInfoMutedByMuteRole { + get { + return ResourceManager.GetString("ShowInfoMutedByMuteRole", resourceCulture); + } + } + + internal static string ShowInfoGuildMemberSince { + get { + return ResourceManager.GetString("ShowInfoGuildMemberSince", resourceCulture); + } + } + + internal static string ShowInfoGuildNickname { + get { + return ResourceManager.GetString("ShowInfoGuildNickname", resourceCulture); + } + } + + internal static string ShowInfoGuildRoles { + get { + return ResourceManager.GetString("ShowInfoGuildRoles", resourceCulture); + } + } + + internal static string ShowInfoGuildMemberPremiumSince { + get { + return ResourceManager.GetString("ShowInfoGuildMemberPremiumSince", resourceCulture); } } }