Overview

The email skill is written to send an email when an opportunity is saved in PrecisionLender. The email will be sent to the designated recipients in the skill code, and will contain the opportunity id and a link to the opportunity. This article will walk through the email skill template. 

 

In this Article

 

Run

This is main execution of the skill which is called after the Andi skills platform determines if it should run. This particular skill will send an email with information pertaining to the opportunity saved model.

export async function run(skillContext: ISkillContext): Promise<ISkillActivity> {

Gets the Opportunity Model using the getSavedOpportunityModel power.

 const opportunitySavedModel: PrecisionLender.Opportunity.OpportunityResponseModel.SavedOpportunity
        = await skillContext.powers.precisionLender.opportunity.getSavedOpportunityModel();

Gets the user-defined value from the skill config property `opportunityNetCommitmentThreshold`.

const netCommitmentThreshold = skillContext.skillConfig.opportunityNetCommitmentThreshold;

Setting up the arguments for the email power.

 const emailSubject = `Opportunity with more than ${netCommitmentThreshold.toLocaleString()} loan commitment`;
    const emailMessage = `<ul><li>Opportunity : ${opportunitySavedModel.name} with total loan commitment amount of ` +
        `${opportunitySavedModel.financialStatement.loanNetCommitment.toLocaleString()} being priced</li></ul>`;
    const fromAddress = "andi@precisionlender.com";

Gets more user-defined values from the skill config. This is optional.

const configuredAddress = skillContext.skillConfig.recipientEmail;
    const configuredName = skillContext.skillConfig.recipientName;

Gets data off of the event attached to the skillContext.

 const eventMetaData = skillContext.event.eventMetaData;
    const toAddress: EmailUserModel[] =
        skillContext.skillConfig.sendEmailToConversationOwner && skillContext.skillConfig.sendEmailToConversationOwner === "No" ?
            [new EmailUserModel(configuredName, configuredAddress)] :
            [new EmailUserModel(configuredName, configuredAddress), new EmailUserModel(eventMetaData.userFullName, eventMetaData.userEmail)];
    const frequency: string = skillContext.skillConfig && skillContext.skillConfig.frequencyOfEmail;

Setting up the frequency in which the email is sent out.

 let frequencyToUse: EmailFrequencyTypes;
    switch (frequency) {
        case "Weekly":
            frequencyToUse = EmailFrequencyTypes.Weekly;
            break;
        case "Daily":
            frequencyToUse = EmailFrequencyTypes.Daily;
            break;
        case "Hourly":
            frequencyToUse = EmailFrequencyTypes.Hourly;
            break;
        case "5-Minute":
            frequencyToUse = EmailFrequencyTypes.FiveMinute;
            break;
        case "1-Minute":
        default:
            frequencyToUse = EmailFrequencyTypes.OneMinute;
    }

Using the debug power to log information to the skills details page.

skillContext.powers.core.log.debug(`A summary tag will be sent to ${eventMetaData.userFullName} who just saved this opporutnity over the threshold.`);

Building a summary tag with information about the email being sent.

const summaryTag: SummaryTag = {
        text: `You have saved an opportunity that is above the ${netCommitmentThreshold.toLocaleString()} loan net commitment threshold. ` +
            `An email sent on a ${frequency} frequency will be sent alerting this action was taken.`,
        tagType: FieldTagTypes.Info,
        key: "emailSentForOpportunitySave",
        loadWhen: [
            {
                query: { name: "getPropertyFromCommercialLoanAccount", args: ["amount"]},
                comparator: "==",
                value: opportunitySavedModel.financialStatement.loanNetCommitment,
                type: "number"
            }
        ]
    };
    skillContext.powers.core.log.debug(`An email will be sent on the ${frequency} frequency.`);

This indicates that the the sendEmail power is being used.

skillContext.powers.andi.email.sendEmail(

Email category to best describe purpose of this skill

"opportunity-changed-email-example",
        frequencyToUse,
        toAddress,
        fromAddress,
        emailSubject,
        emailMessage,
        "",
        ""
    );

Returning a summary tag to the user to let them know an email is being sent.

 return TagPowers.sendTags([summaryTag]);
}

 

In total, the run code is as follows:

import { ISkillContext, ISkillActivity, PrecisionLender, EmailFrequencyTypes, EmailUserModel, FieldTagTypes } from "andiskills";
import { SummaryTag, TagPowers } from "@andi/powers";

// this is main execution of the skill which is called after the andi
// skills platforms determines if it should run
// this particular skill will send an email with information pertaining
// to the opportunity saved model
export async function run(skillContext: ISkillContext): Promise {
    // get opportunity saved model
    const opportunitySavedModel: PrecisionLender.Opportunity.OpportunityResponseModel.SavedOpportunity
        = await skillContext.powers.precisionLender.opportunity.getSavedOpportunityModel();

    const netCommitmentThreshold = skillContext.skillConfig.opportunityNetCommitmentThreshold;
    const emailSubject = `Opportunity with more than ${netCommitmentThreshold.toLocaleString()} loan commitment`;
    // email message - body can be html
    const emailMessage = Opportunity : ${opportunitySavedModel.name} with total loan commitment amount
    of ` + `${opportunitySavedModel.financialStatement.loanNetCommitment.toLocaleString()}
    being priced

    const configuredAddress = skillContext.skillConfig.recipientEmail;
    const configuredName = skillContext.skillConfig.recipientName;
    const fromAddress = "andi@precisionlender.com";
    const eventMetaData = skillContext.event.eventMetaData;
    const toAddress: EmailUserModel[] =
        skillContext.skillConfig.sendEmailToConversationOwner && skillContext.skillConfig.sendEmailToConversationOwner === "No" ?
            [new EmailUserModel(configuredName, configuredAddress)] :
            [new EmailUserModel(configuredName, configuredAddress), new EmailUserModel(eventMetaData.userFullName, eventMetaData.userEmail)];

    const frequency: string = skillContext.skillConfig && skillContext.skillConfig.frequencyOfEmail;
    let frequencyToUse: EmailFrequencyTypes;
    switch (frequency) {
        case "Weekly":
            frequencyToUse = EmailFrequencyTypes.Weekly;
            break;
        case "Daily":
            frequencyToUse = EmailFrequencyTypes.Daily;
            break;
        case "Hourly":
            frequencyToUse = EmailFrequencyTypes.Hourly;
            break;
        case "5-Minute":
            frequencyToUse = EmailFrequencyTypes.FiveMinute;
            break;
        case "1-Minute":
        default:
            frequencyToUse = EmailFrequencyTypes.OneMinute;
    }

    // send a summary tag to the user who just saved an opportunity above the set threshold, letting them know an email wil be sent.
    skillContext.powers.core.log.debug(`A summary tag will be sent to ${eventMetaData.userFullName} who just saved this opporutnity over the threshold.`);
    const summaryTag: SummaryTag = {
        text: `You have saved an opportunity that is above the ${netCommitmentThreshold.toLocaleString()} loan net commitment threshold. ` +
            `An email sent on a ${frequency} frequency will be sent alerting this action was taken.`,
        tagType: FieldTagTypes.Info,
        key: "emailSentForOpportunitySave",
        loadWhen: [
            {
                query: { name: "getPropertyFromCommercialLoanAccount", args: ["amount"]},
                comparator: "==",
                value: opportunitySavedModel.financialStatement.loanNetCommitment,
                type: "number"
            }
        ]
    };

    skillContext.powers.core.log.debug(`An email will be sent on the ${frequency} frequency.`);
    skillContext.powers.andi.email.sendEmail(
        // email category to best describe purpose of this skill 
        "opportunity-changed-email-example",
        frequencyToUse,
        toAddress,
        fromAddress,
        emailSubject,
        emailMessage,
        "",
        ""
    );

    return TagPowers.sendTags([summaryTag]);
} *
        
}

 

Should I Run

The shouldIRun is used by the Andi skills platform to determine if the given skill's current context needs to be executed. This particular skill should run if the event is an Opportunity Saved.

export async function shouldIRun(skillContext: ISkillContext): Promise<ShouldIRunResponseType> {

Using the isOpportunitySavedEvent to determine if the event type coming into the skill is a Opportunity Saved Click Event

  const isOpportunitySavedEvent = skillContext.powers.precisionLender.opportunity.isOpportunitySavedClickEvent();
    if (!isOpportunitySavedEvent) {
        return skillContext.powers.andi.shouldIRun.shouldIRunFalse();
    }
    validateConfiguration(skillContext);

Gets the opportunity model using the getSavedOpportunityModel power.

const opportunitySavedModel: PrecisionLender.Opportunity.OpportunityResponseModel.SavedOpportunity
        = await skillContext.powers.precisionLender.opportunity.getSavedOpportunityModel();
    if (!opportunitySavedModel) {
        return skillContext.powers.andi.shouldIRun.shouldIRunFalse(); 
    }

Tells Andi to only run if loanNetCommitment is greater than threshold.

if (opportunitySavedModel.financialStatement.loanNetCommitment > skillContext.skillConfig.opportunityNetCommitmentThreshold) {
        skillContext.powers.core.log.debug(`Loan Net Commitment is ${opportunitySavedModel.financialStatement.loanNetCommitment.toLocaleString()}`);
        return skillContext.powers.andi.shouldIRun.shouldIRunTrue();
    }
    return skillContext.powers.andi.shouldIRun.shouldIRunFalse();
}
function validateConfiguration(skillContext: ISkillContext) {
    if (!skillContext.skillConfig) {
        throw new Error("Skill has not been configured.");
    }
    if (!skillContext.skillConfig.opportunityNetCommitmentThreshold) {
        throw new Error("Opportunity Net Commitment Threshold is a required configuration value.");
    }
    if (!skillContext.skillConfig.recipientName) {
        throw new Error("Recipient Name is a required configuration value.");
    }
    if (!skillContext.skillConfig.recipientEmail) {
        throw new Error("Recipient Email is a required configuration value.");
    }
}

 

In total, the shouldIRun code is as follows:

import { ISkillContext, ShouldIRunResponseType, PrecisionLender } from "andiskills";

// shouldIRun is used by the andi skills platform to determine if the given
// skill's current context needs to be executed. 
// this particular skill should run if the event is an Opportunity Saved
export async function shouldIRun(skillContext: ISkillContext): Promise {
    const isOpportunitySavedEvent = skillContext.powers.precisionLender.opportunity.isOpportunitySavedClickEvent();

    if (!isOpportunitySavedEvent) {
        return skillContext.powers.andi.shouldIRun.shouldIRunFalse();
    }

    validateConfiguration(skillContext);

     // get opportunity saved model
    const opportunitySavedModel: PrecisionLender.Opportunity.OpportunityResponseModel.SavedOpportunity
        = await skillContext.powers.precisionLender.opportunity.getSavedOpportunityModel();

    if (!opportunitySavedModel) {
        return skillContext.powers.andi.shouldIRun.shouldIRunFalse(); 
    }

    // only run if loanNetCommitment is greater than threshold
    if (opportunitySavedModel.financialStatement.loanNetCommitment > skillContext.skillConfig.opportunityNetCommitmentThreshold) {
        skillContext.powers.core.log.debug(`Loan Net Commitment is ${opportunitySavedModel.financialStatement.loanNetCommitment.toLocaleString()}`);
        return skillContext.powers.andi.shouldIRun.shouldIRunTrue();
    }

    return skillContext.powers.andi.shouldIRun.shouldIRunFalse();
}

function validateConfiguration(skillContext: ISkillContext) {
    if (!skillContext.skillConfig) {
        throw new Error("Skill has not been configured.");
    }

    if (!skillContext.skillConfig.opportunityNetCommitmentThreshold) {
        throw new Error("Opportunity Net Commitment Threshold is a required configuration value.");
    }

    if (!skillContext.skillConfig.recipientName) {
        throw new Error("Recipient Name is a required configuration value.");
    }

    if (!skillContext.skillConfig.recipientEmail) {
        throw new Error("Recipient Email is a required configuration value.");
    }
}   

 

 

Was this article helpful?
0 out of 0 found this helpful