Skip to content

[FEAT]: send email & summary as email using slash command. #13

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

Merged
merged 23 commits into from
Jul 23, 2025

Conversation

priyanshuharshbodhi1
Copy link
Contributor

@priyanshuharshbodhi1 priyanshuharshbodhi1 commented Jul 12, 2025

Description

This PR enables user to do following things:

  1. send an email to any email address or any user(s) present on the workspace by tagging them in the slash commmand.
    Ex:

    • /email send an congratulatory email to priyanshu@gmail.com --> sends email to priyanshu@gmail.com
    • /email send an congratulatory email to @priyanshu.harshbodhi and @sing.li --> sends email to sing and priyanshu on their registered emails in RC(in case of more than 1 emails are verified of a user then mail will be send to all of those emails)
  2. User can summarise conversations in channel OR thread send it as email. User can also mention date range and which user's messages to summarise by tagging them)
    Ex:

    • /email summarise the conversation between @sing.li and @priyanshu.harshbodhi in the last week and send it to @vipin.chaudhary --> summarises the conversation between ONLY sing and priyanshu in last 7 days by extracting messages between only them and sends it email address of vipin
  3. User can edit emails before sending it to recipient(s)

  4. If user types anything which our app cant do or understand then iut shows error response:
    Ex:

    • /email fhsd foo baar sdhsd ---> app replies: "I can't understand your request. Could you please rephrase it with more details?"

Videos:

Screencast.from.2025-07-13.01-08-05.webm
Screencast.from.2025-07-13.01-06-10.webm
Screencast.from.2025-07-13.02-02-17.webm

@priyanshuharshbodhi1
Copy link
Contributor Author

@VipinDevelops PTAL

@priyanshuharshbodhi1
Copy link
Contributor Author

image Current tool calling workflow

@VipinDevelops
Copy link
Collaborator

Add more info in this message as discussed earlier
image
update the message instead of sending new one's
image

@VipinDevelops
Copy link
Collaborator

VipinDevelops commented Jul 14, 2025

image

if you have user's name you can also fetch there avatar use that to show in the modal, it would be better UX for sending mail.

// Retrieve stored email data
const emailData = await this.getStoredEmailData(room.id);

if (!emailData) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User should get a error message when these error's occur that explain what went wrong.

throw new Error('Email data not found');
}

if (!triggerId) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for this too

}
}

public async ProcessNaturalLanguageQuery(query: string): Promise<void> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To many thing happening in this method refactor it to smaller chunks

Comment on lines 556 to 574

fixedArguments = fixedArguments.replace(/("content"\s*:\s*")([^"]*)\n([^"]*?)(")/g, (match, start, content1, content2, end) => {
const fixedContent = (content1 + content2).replace(/\n/g, '\\n').replace(/\r/g, '\\r');
return start + fixedContent + end;
});

fixedArguments = fixedArguments.replace(/("content"\s*:\s*")([^"]*"[^"]*?)(")/g, (match, start, content, end) => {
const fixedContent = content.replace(/"/g, '\\"');
return start + fixedContent + end;
});

try {
args = JSON.parse(fixedArguments);
} catch (secondParseError) {
resultMessageBuilder.setText(t(Translations.LLM_PARSING_ERROR, this.language));
return this.read.getNotifier().notifyUser(this.sender, resultMessageBuilder.getMessage());
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this piece of code doing to many regex no explanation try to give me some example in comment its hard to understand

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have improved the prompt, now we dont need this regex in this method.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then why is it still in code ?

}

// Handle send-email and summarize-and-send-email tools with buttons instead of immediate modal
if (toolCall.function.name === 'send-email' || toolCall.function.name === 'summarize-and-send-email') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't use string use enums or type

if (toolCall.function.name === 'send-email' || toolCall.function.name === 'summarize-and-send-email') {
let emailData: ISendEmailData;

if (toolCall.function.name === 'send-email') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again no strings

}

// Format messages for summarization
const formattedMessages = messageService.formatMessagesForSummary(messages);
Copy link
Collaborator

@VipinDevelops VipinDevelops Jul 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put all the formatted message thing in different file.

// Show simplified email ready message with buttons
const block = this.modify.getCreator().getBlockBuilder();

const messageText = toolCall.function.name === 'summarize-and-send-email'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again no string

);

// Create formatted message showing the tool execution result
const block = this.modify.getCreator().getBlockBuilder();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole method is TOO long it should be a lot smaller divide this in different files and handlers

@VipinDevelops
Copy link
Collaborator

VipinDevelops commented Jul 14, 2025

@priyanshuharshbodhi1 make the requested changes.

@VipinDevelops VipinDevelops changed the title [FEAT]: send email & summarise conversations and send it as email using slash command. [FEAT]: send email & summary as email using slash command. Jul 14, 2025
@priyanshuharshbodhi1
Copy link
Contributor Author

Add more info in this message as discussed earlier image update the message instead of sending new one's image

Messages can be updated, Notifications can't be updated once sent. So we will have 2 messages as earlier.

…essage formatting, and additional translation constants for recipient display
@VipinDevelops
Copy link
Collaborator

Add more info in this message as discussed earlier image update the message instead of sending new one's image

Messages can be updated, Notifications can't be updated once sent. So we will have 2 messages as earlier.

Then check If we can convert a message to read only message and then first send a message and then update it and then make it read only.

@priyanshuharshbodhi1
Copy link
Contributor Author

Add more info in this message as discussed earlier image update the message instead of sending new one's image

Messages can be updated, Notifications can't be updated once sent. So we will have 2 messages as earlier.

Then check If we can convert a message to read only message and then first send a message and then update it and then make it read only.

I checked it we cant do that.

@VipinDevelops
Copy link
Collaborator

Add more info in this message as discussed earlier image update the message instead of sending new one's image

Messages can be updated, Notifications can't be updated once sent. So we will have 2 messages as earlier.

Then check If we can convert a message to read only message and then first send a message and then update it and then make it read only.

I checked it we cant do that.

any other alternative to this ? we shouldn't give this bad UX try to find some way around it and share you research.

@priyanshuharshbodhi1
Copy link
Contributor Author

Add more info in this message as discussed earlier image update the message instead of sending new one's image

Messages can be updated, Notifications can't be updated once sent. So we will have 2 messages as earlier.

Then check If we can convert a message to read only message and then first send a message and then update it and then make it read only.

I checked it we cant do that.

any other alternative to this ? we shouldn't give this bad UX try to find some way around it and share you research.

No I didn't find something else for it, I also had a discussion with App.Receipt.Processor's mentee, he is also using similar method.

…rmatting interfaces, and enhance translation constants for improved user feedback
@priyanshuharshbodhi1
Copy link
Contributor Author

Hey @VipinDevelops PTAL!

Screencast.from.2025-07-19.02-12-42.webm

…andling with username extraction for display, and improve message formatting with avatars in SendEmailModal
@priyanshuharshbodhi1
Copy link
Contributor Author

I have updated the PR with changes you mentioned.

@VipinDevelops
Copy link
Collaborator

Add more info in this message as discussed earlier image update the message instead of sending new one's image

Messages can be updated, Notifications can't be updated once sent. So we will have 2 messages as earlier.

Then check If we can convert a message to read only message and then first send a message and then update it and then make it read only.

I checked it we cant do that.

any other alternative to this ? we shouldn't give this bad UX try to find some way around it and share you research.

No I didn't find something else for it, I also had a discussion with App.Receipt.Processor's mentee, he is also using similar method.

Alright then maybe this is one of few cases where app engine fails

@VipinDevelops
Copy link
Collaborator

Hey @VipinDevelops PTAL!

Screencast.from.2025-07-19.02-12-42.webm

We can improve the messages you show for AI thinking it should be just "thinking"

Remove " AI response" from second message
It makes it feel like a robot not a assistant which we aim to show

How you are handling cases when there would be alot of email and images ? There should be a threshold

Like above 3 we only show images
And above 10 we just show 3 images and one count like +5 after images check some examples you'll understand what I mean

Current modal image UI might break in case of multiple mails

Copy link
Collaborator

@VipinDevelops VipinDevelops left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR is improved but still needs some changes
@priyanshuharshbodhi1

@@ -0,0 +1,157 @@
export const LlmPrompts = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really long prompt divided this into seperate parts to make it easy to use

Like one constant name response example so on after that spread it in here.


---

CRITICAL JSON REQUIREMENTS:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be common in all AI prompt make a constant for this

messageBuilder.setText(
t(Translations.ERROR_PROCESSING_LOGIN, this.language, { error: error.message })
);
this.app.getLogger().error('Login processing error:', error);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a common error handlerand use that in all the places

this.app.getLogger().error('Logout preparation error:', error);

// Provide user-friendly error message
let userMessage: string;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Common error handler

@@ -351,7 +387,17 @@ export class Handler implements IHandler {
.setRoom(this.room)
.setGroupable(false);

messageBuilder.setText(t(Translations.CONFIG_ERROR, this.language, { error: error.message }));
// Categorize error and provide user-friendly message
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same error handler

t(Translations.REPORT_ERROR, this.language, { error: error.message })

// Provide user-friendly error message based on error type
let userMessage: string;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well

.setGroupable(false);

// Provide specific error message based on error type
let userMessage: string;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error thing

- implement centralized error handling across the app
- divide tool calling prompt for future use.
@priyanshuharshbodhi1
Copy link
Contributor Author

@VipinDevelops, PTAL, I have made all the changes.

I will raise the "Change LLM" PR once you merge this one. Or make it draft one.

@VipinDevelops
Copy link
Collaborator

@VipinDevelops, PTAL, I have made all the changes.

I will raise the "Change LLM" PR once you merge this one. Or make it draft one.

You can raise that PR right now and the branch of that can be from this branch similar to how we did previously

@VipinDevelops
Copy link
Collaborator

Don't wait for Merges to make PR or stop working because of that PR will only get merged when its complete and tested.

@@ -469,6 +469,7 @@
"version": "3.0.0-beta2",
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.0.0-beta2.tgz",
"integrity": "sha512-y+l1PNV0XDyY8sM3YtuMLK5vE3/hkfId+Do8pLo/OPxfxuFAUwcGz3oiiUuV46/aBpwTzZ+mRWVMtlSKbradhw==",
"deprecated": "too old",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you installed new package or what why we have this ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. the authers of the package may have marked this version of package "too old" and thus it shows it here.

text: string;
}

export interface ISummarizeParams {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add comments why we have all things optional

Copy link
Contributor Author

@priyanshuharshbodhi1 priyanshuharshbodhi1 Jul 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because, the query from user may or may not those fields:

  1. if it contains start and end date for conversation then LLM responds with those. in that case days will be optional
  2. if user mentioned conversation from last 5 days then LLM responds with 5 in days field.
  3. if user didn't mentioned any date in the query then all three( days, start date, end date) would be empty and in that case app will just summarise last 25 messages in the channel or thread.
  4. for person, if user mentioned any person(s) in the query then app will only summarise messages of that person(s).
  5. if user mentioned like he wants the summary in bullet format then summary will be in bullett format. If nothing is mentioned then the summary will be generic

format?: 'bullet' | 'paragraph' | 'detailed' | 'brief';
}

export interface ISummarizeAndSendEmailData {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for here

};
}

export interface ILLMResponse {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLM response from different providers would be different right? you have to define for all the different provider you been using

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, this interface structure will be same for all LLMs.

// Get user's preferred email provider from their personal settings
const data = await this.read.getPersistenceReader().readByAssociation(association);

// Handle both array and single object responses
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good are we handling any case in which LLM provide a response that don't fit any of the type swe defined because of hallucination

Copy link
Contributor Author

@priyanshuharshbodhi1 priyanshuharshbodhi1 Jul 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its explicitely mentioned in the prompt that it should respond with only tool mentioned in the prompt and if no tool fits for the query enterd by user then app gives messsage: I can't understand your request. Please rewrite your query with more details.

If LLM halicunates and dont responds with plain text instead of json or incorrect json then it shows: LLM parse error in message

@priyanshuharshbodhi1
Copy link
Contributor Author

Don't wait for Merges to make PR or stop working because of that PR will only get merged when its complete and tested.

Yes its done 90%.

@VipinDevelops
Copy link
Collaborator

Don't wait for Merges to make PR or stop working because of that PR will only get merged when its complete and tested.

Yes its done 90%.

all the changes and comments resolved ?

@VipinDevelops VipinDevelops merged commit db40cc1 into RocketChat:main Jul 23, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants