-
Notifications
You must be signed in to change notification settings - Fork 2
ZYancey/CSBot/18/Smite #415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: zyancey/CSBot/413/TSNativeRoomfinder
Are you sure you want to change the base?
Changes from 7 commits
882fa2d
2af9aa6
a4a4e3a
f664ee4
307c459
e209a19
c4f5e67
1759e3f
5dca574
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| /* eslint-disable @typescript-eslint/no-unsafe-assignment */ | ||
| import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
| import type { Client, User } from 'discord.js'; | ||
| import { smite } from './smite.js'; | ||
| import * as smiteUtils from '../helpers/smiteUtils.js'; | ||
| import { UserMessageError } from '../helpers/UserMessageError.js'; | ||
|
|
||
| vi.mock('../helpers/smiteUtils.js', async () => { | ||
| const actual = await vi.importActual('../helpers/smiteUtils.js'); | ||
| return { | ||
| ...actual, | ||
| isAdmin: vi.fn(), | ||
| setUserSmitten: vi.fn(), | ||
| }; | ||
| }); | ||
|
|
||
| describe('smite', () => { | ||
| const mockReply = vi.fn<GuildedCommandContext['reply']>(); | ||
| const mockGetUser = vi.fn<GuildedCommandContext['options']['getUser']>(); | ||
| const mockFetch = vi.fn(); | ||
|
|
||
| let context: GuildedCommandContext; | ||
| let mockTargetUser: User; | ||
| let mockExecutingUser: User; | ||
| let mockClient: Client; | ||
|
|
||
| beforeEach(() => { | ||
| vi.clearAllMocks(); | ||
|
|
||
| mockTargetUser = { | ||
| id: 'target-user-id', | ||
| username: 'TargetUser', | ||
| } as User; | ||
|
|
||
| mockExecutingUser = { | ||
| id: 'executing-user-id', | ||
| username: 'ExecutingUser', | ||
| } as User; | ||
|
|
||
| mockClient = { | ||
| user: { | ||
| id: 'bot-user-id', | ||
| username: 'TestBot', | ||
| }, | ||
| } as Client; | ||
|
|
||
| context = { | ||
| reply: mockReply, | ||
| options: { | ||
| getUser: mockGetUser, | ||
| }, | ||
| member: { | ||
| id: 'executing-user-id', | ||
| }, | ||
| user: mockExecutingUser, | ||
| guild: { | ||
| id: 'test-guild-id', | ||
| members: { | ||
| fetch: mockFetch, | ||
| }, | ||
| }, | ||
| client: mockClient, | ||
| } as unknown as GuildedCommandContext; | ||
|
|
||
| mockGetUser.mockReturnValue(mockTargetUser); | ||
| mockFetch.mockResolvedValue({ | ||
| id: 'target-user-id', | ||
| }); | ||
| }); | ||
|
|
||
| it('should smite non-admin executor for 60 seconds when they try to use the command', async () => { | ||
| vi.mocked(smiteUtils.isAdmin).mockReturnValue(false); | ||
|
|
||
| await smite.execute(context); | ||
|
|
||
| expect(smiteUtils.setUserSmitten).toHaveBeenCalledWith( | ||
| mockExecutingUser.id, | ||
| 'test-guild-id', | ||
| true | ||
| ); | ||
| expect(mockReply).toHaveBeenCalledWith( | ||
| expect.objectContaining({ | ||
| embeds: expect.arrayContaining([ | ||
| expect.objectContaining({ | ||
| data: expect.objectContaining({ | ||
| title: '⚡ Hubris! ⚡', | ||
| }), | ||
| }), | ||
| ]), | ||
| }) | ||
| ); | ||
| }); | ||
|
|
||
| it('should show wack image if user tries to smite themselves', async () => { | ||
| vi.mocked(smiteUtils.isAdmin).mockReturnValue(true); | ||
| mockTargetUser.id = mockExecutingUser.id; | ||
| mockGetUser.mockReturnValue(mockTargetUser); | ||
|
|
||
| await smite.execute(context); | ||
|
|
||
| expect(mockReply).toHaveBeenCalledWith( | ||
| expect.objectContaining({ | ||
| embeds: expect.arrayContaining([ | ||
| expect.objectContaining({ | ||
| data: expect.objectContaining({ | ||
| title: 'Wack.', | ||
| }), | ||
| }), | ||
| ]), | ||
| }) | ||
| ); | ||
| expect(smiteUtils.setUserSmitten).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('should smite the executor if they try to smite the bot', async () => { | ||
| vi.mocked(smiteUtils.isAdmin).mockReturnValue(true); | ||
| mockTargetUser.id = mockClient.user?.id ?? ''; | ||
| mockGetUser.mockReturnValue(mockTargetUser); | ||
|
|
||
| await smite.execute(context); | ||
|
|
||
| expect(smiteUtils.setUserSmitten).toHaveBeenCalledWith( | ||
| mockExecutingUser.id, | ||
| 'test-guild-id', | ||
| true | ||
| ); | ||
| expect(mockReply).toHaveBeenCalledWith( | ||
| expect.objectContaining({ | ||
| embeds: expect.arrayContaining([ | ||
| expect.objectContaining({ | ||
| data: expect.objectContaining({ | ||
| title: 'You fool!', | ||
| }), | ||
| }), | ||
| ]), | ||
| }) | ||
| ); | ||
| }); | ||
|
|
||
| it('should throw error if target is an admin', async () => { | ||
| vi.mocked(smiteUtils.isAdmin).mockReturnValueOnce(true).mockReturnValueOnce(true); | ||
|
|
||
| await expect(smite.execute(context)).rejects.toThrow(UserMessageError); | ||
| await expect(smite.execute(context)).rejects.toThrow('cannot smite an administrator'); | ||
| }); | ||
|
|
||
| it('should successfully smite a regular user', async () => { | ||
| vi.mocked(smiteUtils.isAdmin).mockReturnValueOnce(true).mockReturnValueOnce(false); | ||
|
|
||
| await smite.execute(context); | ||
|
|
||
| expect(smiteUtils.setUserSmitten).toHaveBeenCalledWith( | ||
| mockTargetUser.id, | ||
| 'test-guild-id', | ||
| true | ||
| ); | ||
| expect(mockReply).toHaveBeenCalledWith( | ||
| expect.objectContaining({ | ||
| embeds: expect.arrayContaining([ | ||
| expect.objectContaining({ | ||
| data: expect.objectContaining({ | ||
| title: '⚡ SMITTEN! ⚡', | ||
| }), | ||
| }), | ||
| ]), | ||
| }) | ||
| ); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'; | ||
| import { isAdmin, setUserSmitten } from '../helpers/smiteUtils.js'; | ||
| import { UserMessageError } from '../helpers/UserMessageError.js'; | ||
|
|
||
| const builder = new SlashCommandBuilder() | ||
| .setName('smite') | ||
| .setDescription('Smite a user, preventing them from using bot commands') | ||
| .addUserOption(option => | ||
| option.setName('user').setDescription('The user to smite').setRequired(true) | ||
| ); | ||
|
|
||
| const ODIN_SMITING_THOR_GIF = | ||
| 'https://cdn.discordapp.com/attachments/781563734098575410/1083403217553084446/smite.gif?ex=68fb93df&is=68fa425f&hm=83b4a865179f0dd6fb1312f535c2a730673709734470c0095dd2a432908bfcba&'; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This link has expired already. We should instead provide the image file directly. |
||
| const WACK_IMAGE = | ||
| 'https://i.kym-cdn.com/entries/icons/original/000/033/758/Screen_Shot_2020-04-28_at_12.21.48_PM.png'; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This link still works, but feels weird to rely on an external service for a file we may frequently access. We should provide this file directly as well
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe even convert to .webp for smaller storage, unless we have ppl using old versions of Safari lol
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should make a simple HTTP server in CSBot that serves static images like these ones 🤔 |
||
| export const smite: GuildedCommand = { | ||
| info: builder, | ||
| requiresGuild: true, | ||
| async execute({ reply, options, member, guild, client, user }): Promise<void> { | ||
| const targetUser = options.getUser('user', true); | ||
| const targetMember = await guild.members.fetch(targetUser.id); | ||
|
|
||
| // Check if the executor is an admin | ||
| if (!isAdmin(member)) { | ||
| // Non-admins get smitten for 60 seconds for trying to use this command | ||
| await setUserSmitten(user.id, guild.id, true); | ||
|
|
||
| // Auto-unsmite after 60 seconds | ||
| setTimeout(() => { | ||
| void setUserSmitten(user.id, guild.id, false); | ||
| }, 60_000); | ||
|
|
||
| await reply({ | ||
| embeds: [ | ||
| new EmbedBuilder() | ||
| .setTitle('⚡ Hubris! ⚡') | ||
| .setDescription( | ||
| `You dare try to wield the power of the gods?\n\nYou have been smitten for 60 seconds for your insolence!` | ||
| ) | ||
| .setImage(ODIN_SMITING_THOR_GIF) | ||
| .setColor(0xff_00_00), // Red | ||
| ], | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| // Check if user is trying to smite themselves | ||
| if (targetUser.id === user.id) { | ||
| await reply({ | ||
| embeds: [ | ||
| new EmbedBuilder().setTitle('Wack.').setImage(WACK_IMAGE).setColor(0xff_a5_00), // Orange | ||
| ], | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| // Check if user is trying to smite the bot | ||
| if (targetUser.id === client.user.id) { | ||
| // Smite the user who tried to smite the bot instead | ||
| await setUserSmitten(user.id, guild.id, true); | ||
|
|
||
| await reply({ | ||
| embeds: [ | ||
| new EmbedBuilder() | ||
| .setTitle('You fool!') | ||
| .setDescription( | ||
| `Only now do you understand.\n\nYou have been smitten for attempting to smite ${client.user.username}.` | ||
| ) | ||
| .setImage(ODIN_SMITING_THOR_GIF) | ||
| .setColor(0xff_00_00), // Red | ||
| ], | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| // Check if target is an admin | ||
| if (isAdmin(targetMember)) { | ||
| throw new UserMessageError('You cannot smite an administrator.'); | ||
| } | ||
|
|
||
| // Smite the target user | ||
| await setUserSmitten(targetUser.id, guild.id, true); | ||
|
|
||
| await reply({ | ||
| embeds: [ | ||
| new EmbedBuilder() | ||
| .setTitle('⚡ SMITTEN! ⚡') | ||
| .setDescription( | ||
| `${targetUser} has been smitten by the gods!\n\nThey can no longer use bot commands for the next hour.` | ||
| ) | ||
| .setImage(ODIN_SMITING_THOR_GIF) | ||
| .setColor(0x58_65_f2), // Blurple | ||
| ], | ||
| }); | ||
| }, | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.