Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/COMMAND-WIKI.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,36 @@
- **Options:** None
- **Subcommands:** None

# PHRASE
## phrase
- **Aliases:** None
- **Description:** Handle phrase functions.
- **Examples:**<br>`.phrase signup`<br>`.phrase s<br>`.phrase create`<br>`.phrase c<br>`.phrase create @Codey`<br>`.phrase c @Codey<br>`.phrase quit<br>`.phrase q`
- **Options:** None
- **Subcommands:** `signup`, `create`, `quit`

## phrase create
- **Aliases:** `c`
- **Description:** Generate phrases of you!
- **Examples:**<br>`.phrase create`<br>`.phrase c`<br>`.phrase create @username`<br>`.phrase c @username`
- **Options:**
- ``user``: User to generate a phrase for (defaults to yourself)
- **Subcommands:** None

## phrase quit
- **Aliases:** `q`
- **Description:** Opt out of phrase generation
- **Examples:**<br>`.phrase quit`<br>`.phrase q`
- **Options:** None
- **Subcommands:** None

## phrase signup
- **Aliases:** `s`
- **Description:** Sign up to generate phrases of you!
- **Examples:**<br>`.phrase signup`<br>`.phrase s`
- **Options:** None
- **Subcommands:** None

# PROFILE
## profile
- **Aliases:** `userprofile`, `aboutme`
Expand Down
107 changes: 107 additions & 0 deletions src/commandDetails/phrase/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { container } from '@sapphire/framework';
import {
CodeyCommandDetails,
CodeyCommandOptionType,
getUserFromMessage,
SapphireMessageExecuteType,
SapphireMessageResponse,
} from '../../codeyCommand';
import { EmbedBuilder, User } from 'discord.js';
import {
checkIfUserSignedUp,
getUserMessages,
generateMarkovPhrase,
} from '../../components/phrase';
import { logger } from '../../logger/default';

const phraseCreateExecuteCommand: SapphireMessageExecuteType = async (
_client,
messageFromUser,
args,
): Promise<SapphireMessageResponse> => {
const caller = getUserFromMessage(messageFromUser);

// Type-check the user argument properly
let targetUser: User;
if (args.user && args.user instanceof User) {
targetUser = args.user;
} else {
targetUser = caller;
}

const guildId = messageFromUser.guild?.id;

if (!guildId) {
const embed = new EmbedBuilder()
.setColor('Red')
.setTitle('Error ❌')
.setDescription('This command can only be used in a server.');
return { embeds: [embed] };
}

try {
// Check if target user has opted in
const isTargetSignedUp = await checkIfUserSignedUp(targetUser.id, guildId);
if (!isTargetSignedUp) {
const embed = new EmbedBuilder()
.setColor('Orange')
.setTitle('User Not Signed Up')
.setDescription(`${targetUser.username} hasn't opted in to phrase generation yet.`);
return { embeds: [embed] };
}

// Get messages for the target user
const messages = await getUserMessages(targetUser.id, guildId);
if (messages.length === 0) {
const embed = new EmbedBuilder()
.setColor('Orange')
.setTitle('No Messages Found')
.setDescription(
`No messages found for ${targetUser.username}. Try again after the next daily sync.`,
);
return { embeds: [embed] };
}

// Generate phrase using Markov chain
const generatedPhrase = generateMarkovPhrase(messages);

const embed = new EmbedBuilder()
.setColor('Green')
.setTitle(`${targetUser.username} says...`)
.setDescription(`"${generatedPhrase}"`)
.setFooter({ text: `Generated from ${messages.length} messages` });

return { embeds: [embed] };
} catch (error) {
logger.error('Error generating phrase:', error);
const embed = new EmbedBuilder()
.setColor('Red')
.setTitle('Error ❌')
.setDescription('An error occurred while generating the phrase. Please try again later.');
return { embeds: [embed] };
}
};

export const phraseCreateCommandDetails: CodeyCommandDetails = {
name: 'create',
aliases: ['c'],
description: 'Generate phrases of you!',
detailedDescription: `**Examples:**
\`${container.botPrefix}phrase create\`
\`${container.botPrefix}phrase c\`
\`${container.botPrefix}phrase create @username\`
\`${container.botPrefix}phrase c @username\``,

isCommandResponseEphemeral: false,
messageWhenExecutingCommand: 'Creating phrase...',
executeCommand: phraseCreateExecuteCommand,
options: [
{
name: 'user',
description: 'User to generate a phrase for (defaults to yourself)',
type: CodeyCommandOptionType.USER,
required: false,
},
],
subcommandDetails: {},
};
77 changes: 77 additions & 0 deletions src/commandDetails/phrase/quit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { container } from '@sapphire/framework';
import {
CodeyCommandDetails,
SapphireMessageExecuteType,
SapphireMessageResponse,
getUserFromMessage,
} from '../../codeyCommand';
import { EmbedBuilder } from 'discord.js';
import { logger } from '../../logger/default';
import { checkIfUserSignedUp, removeUser } from '../../components/phrase';

const phraseQuitExecuteCommand: SapphireMessageExecuteType = async (
_client,
messageFromUser,
_args,
): Promise<SapphireMessageResponse> => {
const title = 'Phrase Quit Information';
const user = getUserFromMessage(messageFromUser);
const userId = user.id;
const guildId = messageFromUser.guild?.id;

// Check if guild ID is available
if (!guildId) {
const embed = new EmbedBuilder()
.setColor('Red')
.setTitle(title)
.setDescription('This command can only be used in a server.');
return { embeds: [embed] };
}

try {
// Check if user is signed up
const isSignedUp = await checkIfUserSignedUp(userId, guildId);
if (!isSignedUp) {
const embed = new EmbedBuilder()
.setColor('Orange')
.setTitle(title)
.setDescription("You're not currently signed up for phrase generation.");
return { embeds: [embed] };
}

// Remove the user
await removeUser(userId, guildId);

const embed = new EmbedBuilder()
.setColor('Green')
.setTitle(title)
.setDescription(
'You have been removed from phrase generation. All your message data has been removed from database.',
);
return { embeds: [embed] };
} catch (error) {
logger.error('Error in phrase quit:', error);
const embed = new EmbedBuilder()
.setColor('Red')
.setTitle(title)
.setDescription(
'An error occurred while removing you from database. Please try again later.',
);
return { embeds: [embed] };
}
};

export const phraseQuitCommandDetails: CodeyCommandDetails = {
name: 'quit',
aliases: ['q'],
description: 'Opt out of phrase generation',
detailedDescription: `**Examples:**
\`${container.botPrefix}phrase quit\`
\`${container.botPrefix}phrase q\``,

isCommandResponseEphemeral: false,
messageWhenExecutingCommand: 'Removing user from database...',
executeCommand: phraseQuitExecuteCommand,
options: [],
subcommandDetails: {},
};
109 changes: 109 additions & 0 deletions src/commandDetails/phrase/signup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { container } from '@sapphire/framework';
import {
CodeyCommandDetails,
getUserFromMessage,
SapphireMessageExecuteType,
SapphireMessageResponse,
} from '../../codeyCommand';
import { checkIfUserSignedUp, signUpUserWithCollection } from '../../components/phrase';
import { logger } from '../../logger/default';
import { EmbedBuilder, Message } from 'discord.js';

const phraseSignupExecuteCommand: SapphireMessageExecuteType = async (
client,
messageFromUser,
_args,
): Promise<SapphireMessageResponse> => {
const title = 'Phrase Signup Information';
const user = getUserFromMessage(messageFromUser);
const userId = user.id;
const username = user.username;
const guildId = messageFromUser.guild?.id;

// Check if guild ID is available
if (!guildId) {
const embed = new EmbedBuilder()
.setColor('Red')
.setTitle(title)
.setDescription('This command can only be used in a server (guild).');
return { embeds: [embed] };
}

try {
// Check if user is already signed up
const isAlreadySignedUp = await checkIfUserSignedUp(userId, guildId);
if (isAlreadySignedUp) {
const embed = new EmbedBuilder()
.setColor('Orange')
.setTitle(title)
.setDescription("You're already signed up for phrase generation!");
return { embeds: [embed] };
}

// For long-running operations, send initial response first
const initialResponse = await messageFromUser.reply({
embeds: [
new EmbedBuilder()
.setColor('Blue')
.setTitle(title)
.setDescription(
'Signing you up and collecting your messages... This may take a few minutes.',
),
],
ephemeral: true,
fetchReply: true,
});

// Sign up the user and collect messages
const result = await signUpUserWithCollection(client, userId, guildId, username);

let finalEmbed: EmbedBuilder;
if (result.success) {
finalEmbed = new EmbedBuilder().setColor('Green').setTitle(title);

if (result.error) {
finalEmbed.setDescription(`You're signed up! ${result.error}`);
} else {
finalEmbed.setDescription(
`You've successfully signed up for phrase generation! Found ${
result.messagesCollected || 0
} messages to use for phrase generation.`,
);
}
} else {
finalEmbed = new EmbedBuilder()
.setColor('Red')
.setTitle(title)
.setDescription(result.error || 'Failed to sign up. Please try again later.');
}

// Update the response based on command type
if (messageFromUser instanceof Message) {
await initialResponse.edit({ embeds: [finalEmbed] });
} else {
await messageFromUser.editReply({ embeds: [finalEmbed] });
}
} catch (error) {
logger.error('Error in phrase signup:', error);
const embed = new EmbedBuilder()
.setColor('Red')
.setTitle(title)
.setDescription('An error occurred while signing you up. Please try again later.');
return { embeds: [embed] };
}
};

export const phraseSignupCommandDetails: CodeyCommandDetails = {
name: 'signup',
aliases: ['s'],
description: 'Sign up to generate phrases of you!',
detailedDescription: `**Examples:**
\`${container.botPrefix}phrase signup\`
\`${container.botPrefix}phrase s\``,

isCommandResponseEphemeral: false,
messageWhenExecutingCommand: 'Signing user up...',
executeCommand: phraseSignupExecuteCommand,
options: [],
subcommandDetails: {},
};
40 changes: 40 additions & 0 deletions src/commands/phrase/phrase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Command, container } from '@sapphire/framework';
import { CodeyCommand, CodeyCommandDetails } from '../../codeyCommand';
import { phraseSignupCommandDetails } from '../../commandDetails/phrase/signup';
import { phraseCreateCommandDetails } from '../../commandDetails/phrase/create';
import { phraseQuitCommandDetails } from '../../commandDetails/phrase/quit';

const phraseCommandDetails: CodeyCommandDetails = {
name: 'phrase',
aliases: [],
description: 'Handle phrase functions.',
detailedDescription: `**Examples:**
\`${container.botPrefix}phrase signup\`
\`${container.botPrefix}phrase s\
\`${container.botPrefix}phrase create\`
\`${container.botPrefix}phrase c\
\`${container.botPrefix}phrase create @Codey\`
\`${container.botPrefix}phrase c @Codey\
\`${container.botPrefix}phrase quit\
\`${container.botPrefix}phrase q\``,
options: [],
subcommandDetails: {
signup: phraseSignupCommandDetails,
create: phraseCreateCommandDetails,
quit: phraseQuitCommandDetails,
},
defaultSubcommandDetails: phraseCreateCommandDetails,
};

export class PhraseCommand extends CodeyCommand {
details = phraseCommandDetails;

public constructor(context: Command.Context, options: Command.Options) {
super(context, {
...options,
aliases: phraseCommandDetails.aliases,
description: phraseCommandDetails.description,
detailedDescription: phraseCommandDetails.detailedDescription,
});
}
}
Loading