Build Your Own AI Agent – Full Course with OpenAI, Langchain, Render Deployment

#LangChain #AI Agents
💬 Chat with this Video
Ask anything about this video…
Learn to build and deploy intelligent AI agents that bridge the gap between LLMs and real world automation to handle complex research and data analysis. This comprehensive course takes you from zero to deployment using OpenAI and Node.js to create a production ready tool complete with a PostgreSQL database on Render and thanks to Render for providing a grant to make this course possible. Hey everyone, my name is Anna Kuber and today I'll be showing you how to win an AI powered Slackbot that automatically researches new community members and scores their fit for your business all in just one file. So for example, this is me running my own Slack channel called Code with Anna that I use for keeping students of my code with Anna course informed. If a new member joins, say John Durr with the email johnetcp.com, I'm going to use AI to do an internet search on him and get a fit score to see how well he will fit into my community and for my products. Pretty cool, right? So that research and recap gets messaged to me in a private Slack channel. Today I'm going to show you exactly how to do this using Node.js, JS, OpenAI, the Slack API, and deploy this project onto Render. We will also learn how to use databases. Okay. And we're going to do this using render and another separate file that will then send our research essentially into a database, a Postgress database to be exact. Okay. So, in fact, here's exactly what we will cover. We're creating a smart Slack AI agent that automatically detects when new members join. When new members join your Slack community, researches their background using their email and GitHub profile. Uses OpenAI's GBT4 to analyze how well they'd fit as customers for your product. Post a beautiful analysis with fit score and recommendations to a private channel. Uses a database on render.com. And finally, we will deploy on render.com too. This is perfect for SAS founders, community managers, or anyone who wants to automatically qualify leads from their Slack community. The code is super clean, minimal, and essentially ready for you to take, make it your own, improve on it, add features, you name it. So, what are we waiting for? Let's do it. Okay, so first off, in order to build our Slack AI agent, we are going to have a database where we're going to save all our signups along with the compatibility scores for our Slack channel. So, let's do it. I'm just going to use render for this. So, here we are. I'm going to head over to the dashboard and here in my workspace, I'm just going to create a Postgress database. So this is a super easy way to create Postgress databases. Let's just go ahead and click new Postgress. And I'm just going to give this a unique name for my Postgress instance. I'm going to go ahead and use the database name is going to be Slack DB. The user I will keep blank. This is randomly generated unless specified. The region I'm going to keep as the default. The version as well. We're not going to use data dog or data dog region. And we're just going to use the free option for this. So great. And again, if you want to take this further and use this in production or in real life, in other words, then you would go for some of the more heavier lifting options. There is a lot. But for now, we're just testing. So I'm just going to go ahead and click create database. Great. And this will spin up a URL for us. Okay. So now let's go down here and once this is available. So once this has been created, you just scroll down here and this is all the information that you're going to need. We're just going to use the external database URL. So if you go ahead and view mine, this is it. Let's go ahead and copy that and let's spin up our project. Okay, so I've already created a folder essentially or a directory called Slack AI agent. And at the moment, it doesn't have any files in it, but we're going to go ahead and create av file because we can already store our database URL here because we just got it. So mine is this. Yours will be unique and will not be the same. So please go ahead and post yours in here. do not use mine. But of course, you can't really see it. So great, that's all we're going to do here. Let's save that and let's move on. The next thing I'm going to do, this is just a NodeJS project. So I'm just going to initialize a Node project by writing npm in it. If you don't have npm installed, please go ahead and do that. I will go over quickly how to do this if you are not familiar with npm and note. So, at the moment, we're just going to hit enter for all of these. It's just asking us to pick a package name. And I'm happy with Slack AI agent. I'm happy with calling this version one. I'm happy with no description. I'm happy with the entry point being an index.js file, which we are yet to create. And I'm happy to just keep these blank for now. And is this okay? Yes. So, now you'll see that package JSON file be created right here. So, at the moment there of course is not much in it. Don't worry, we will be filling this up full with stuff that is useful. The first thing we'll do is just going to add type module. So we can use modules in this project. So in other words, use the import keyword in the index.js file. Let's also have a start script. So I'm just going to create my own script to start the app. And it's just going to be to run the index.js file just like so. Uh and we can also have a dev mode as well. So this is just going to be node watch file indexjs just like that. Now now we are going to have to install some dependencies. So let's go ahead and do so now. Okay. So we mentioned npm before. Hopefully you are familiar with it. But for those of you who are not, npm is the node package manager and is a command line tool used to manage JavaScript libraries and project dependencies. So it's automatically included when you install NodeJS. So if you don't have NodeJS installed, go ahead and do that now by visiting this URL and getting NodeJS here. Great. So this will allow me to use npmi for install. And I'm going to install a few things. Let's start with express for managing roots env for having secret variables. Axios for helping us with requests. We're also going to do lang chain. So at Langchain OpenAI and at Langchain core as well as Slack Bolt and Slack web API and hit enter. Okay, so this is going to install those dependencies along with their versions. Whatever the versions show up for you, make sure they are the same as this video. So, if you're watching this in the future, perhaps you're working with version two and we're working with version one. Something for you might not work. So, make sure yours are exactly the same as my versions here. Okay? If they're not, just go ahead. Let's just say this was a two. Just change it to a one and run npmi again to install these. Great. Let's continue. So, now let's go ahead and create our index.js file. We did specify this in our package JSON. So, let's keep it the same. I'm also going to go ahead and create a DBJS file right now for our database. And that should be okay. Great. So, let's go back in here. Okay. So, first off, we're just going to do a bunch of stuff to essentially connect to our Slack apps and our OpenAI account. So, you're going to have to sign into both a Slack account and an OpenAI account. And let's continue. So the first thing we're going to need is to import pkg from slackbot. So this imports the slackbot framework. And next we're going to dstructure the app class from the bot package just like so. So this is going to help us create a slack app instance. Next we're going to import Slack's web client for making direct Slack API calls. So for example posting messages or fetching user info. And then we're going to import Lang Chain's OpenAI wrapper which will help us provide an interface to OpenAI's chat models. We are then also going to import a chat prompt template. So this is going to help us build structured prompts with variable placeholders and then a bunch of stuff that you should be used to. So we're going to import Express from Express which of course is going to help us with health checks and testing endpoints. We are then also going to import which is going to help us load environment variables from the EMV file you just saw. And then we're going to import Axios from Axios which is going to help us make external API requests. So now that we've done that, I'm just going to first off read the MV file that holds our database URL. So I'm going to do so using this piece of code right there. And we can start. Okay, our setup is kind of done. Before we get going, actually, I also want to create a simple logger utility object with three log levels. So, this is going to help us log info messages, error messages, and debugging messages. So, let's do it. const log and info is the first one. So, this is going to logformational messages with an info prefix. So, here is the info prefix and it's just going to log out the messages thanks to the console log. Next, we're going to do error as I mentioned. So, let's just paste that again. And I'm going to change this to error prefix. And then we also want a debug one. So, just like that. And you guessed it, debug. But we only really want to console log out debugging messages if the node environment is set to development. So I'm going to do process environment equals and if it deeply equals the string development then so doubleand then we can console log out the debug message. Okay so there we go. So that is my simple logger utility. Okay. We've got a bunch of other stuff we're going to add to thev file coming up specifically when we make our main application class that encapsulate the Slackbot Express server and AI logic. So I'm going to call this class Slack AI agent and let's get up our constructor which is needed for making classes. This will initialize all services and wire up event handlers. Okay, so first we're going to create an app instance for the HTTP server. So this app equals express and just call it. So all of the wonderfulness from the express package is going to saved under app. Next, we're going to create a Slack bolt app instance using socket mode. So no public URL needed. So this Slack equals new app. And then I'm just going to pass through the token. This is going to live in our env file as well. So I'm going to do process env. Let's call this slackbot token. So we're going to have to get a bunch of stuff from our Slack apps. I'm also going to have a signing secret. So we're going to have to find that as well. Uh let's call this Slack signing secret. So very obvious there. Socket mode as we mentioned is going to be true. I am defining that so that it uses websocket connection instead of HTTP. And the app token we're going to pass through as well. And we're also going to save it in ourv file as Slack app token. So a bunch of variables we're going to have to add. But for now, I'm just going to move on and finish this before I show you where to find all of these in Slack. Uh, we're also going to create a standalone Slack web client for direct API calls outside of Bolt event system. So, this web client equals new web client process env. Okay, great. And now we've kind of done with Slack for the moment. We're going to initialize the lang chain open air chat model with GP4 and low temperature for consistent output. So I'm going to say this is this open AI equals new chat open AI and we're just going to pass through some info into here. So like the model that we want to use which is G PT4 the temperature which is 0.3 and the API key. So again we're going to have to get our open AI key from our open AI apps. Open AI API key. Great. Okay. So nearly done. Now we're going to register Slack event listener. So, we're going to listen out for things such as when someone joins your team or a new member joins a channel. Uh, and we're going to write a method or a function for this. Set up Slack events. Okay, so we're yet to write that. And then we're going to write a function to set up. Okay, just like so. Just make sure to keep this a capital W right here. So, great. that is going to help us configure express roots for health checks, testing endpoints and error handling. So now that we've done that, we have finished this as you can see here. I'm going to write this function. Okay, so right after here, set up Slack events and let's go. Let's do it. So as I mentioned, we're going to listen out for teams joining. So let's listen out for team join. Just like that. I have not made that up. You will find that in the Slack documentation. And let's pass through the event. Okay. So just like so. And we're going to try something and we're going to catch any error. So catch error console error or you can do log error so that it uses the logger we wrote previously. And we're just going to pass through, be very specific, error processing team join. And then we'll just pass through the error message. Just like that. Great. Seems we have cut that out by mistake. So that is looking better. Okay. So we're catching the error. However, what do we want to try? Well, we just want to essentially fetch the full user profile info from Slack AI. But first, I'm just going to log the new member's name. So again, I'm going to use log info from the logger we wrote previously and pass through. I'm going to use back ticks new member joined and pass through their name. So, we're going to get the event and from the event we're going to get the user and the real name and just going to put that in there. Alternatively, if that doesn't return anything, we're just going to get the event user name. Okay, so either one of those. Great. So, I'm just going to put this on another line for you to make that easier to read. Wonderful. So now in order to fetch the full user profile info from the Slack API, I'm just going to save this as con user info. Just like that. I'm going to do await this and get user info and just pass through the event user ID. Okay. And then we're going to run the analysis pipeline and push results to Slack. So await this Anna lies and post member user info. Okay, cool. So that is looking good. Once we are done with that. Okay, but still in setup Slack events, I'm going to listen out for a member joined channel event this time. And this fires when a user joins a channel. So just as before, this Slack event and the event we're going to listen out for is member joined channel. Okay, this is an async function. We're going to pass through the event and then once again we're going to do try catch error log error and then we're going to pass through a message of error processing member joined and channel an error message. And this time what we're going to try is well we're only going to process public channels channels with type C and skip DM and private channels. So if event channel type deeply equals C, then we know that's a public channel and we're going to log which member joined which channel. So log info again. Let's do back ticks and member and then let's get the event user joined channel and then the event channel. Now we can fetch the full user profiles from the slack API just like we did previously. So const user info await this get user info and pass through the event user okay to get that back and I'm going to run the analysis pipeline and post results so await this analyze and post member and pass through the user info just like that. Okay, great. And then we catch any errors. Uh I will be providing all this code in the video description. So if you get lost, you can always check that out. So yeah, this is long. Uh the last thing we're going to do for this before configuring the express HTTP server with roots and middleware is add a global error handler for the Slackbot app, which catches unhandled Slack errors. So this slack error, it's an async. We're going to pass through the error and then we're just going to log. So using our log object error and we're going to pass through slack error and the error itself. So error me. So the error message that comes back from Slack. Great. I'm happy with that. So now we can configure the Express HP server. So set up Express and we're just going to pass incoming JSON request bodies. Let's do this app use express JSON. And let's just write a super simple health check. So this app get and the root for this is going to be health. Let's pass through the request and the response. And then I'm going to do res JSON. So it's just going to return back the status as health C. And then let's add a time stamp as well. time stamp new date to ISO string. Okay, great. I'm also just going to quickly write something to unexpose the test endpoint in development mode. So process envy equals development. I'm going to do this app post and then test analyze member async request and response. We'll do try and once again catch error log error pass through the message of test analysis error error message just like so. And I'm also just going to send back a status. So rest status this would be a 500 error. JSON error analysis failed. So this will really help us know exactly what is going on or times when building our app. Okay, lots of error messages everywhere and lots of logging happening. Okay, but first we're going to try something and that's extract member info from the request body. So const uh from the request body, we're going to get the member info. Okay, so that's from the request body. And if member info does not exist, so does not exist, we're just going to return res status 400. Okay, because something's missing. And then we're just going to pass through the error message of member info is required. So I'm happy with that and now we can run the full analysis pipeline with the provided amendment info. So const analysis equals await this analyze and post member pass through member info. Okay. And then I'm just going to return the analysis result as JSON. So res JSON success true analysis. So we're just passing it back. And then let's also add a timestamp just for us. New date to ISO string. Okay. And great. This is looking good. Okay, let's move on right here. So now we did a global express error handling middleware. So this app use pass through the error, the request, the response and next. And we're just going to log error express error if any and just pass through the error message just like that. Apart from it's spelled E R R. So let's do the same. And then let's just send back a status of 500 and the JSON of error internal server error. Okay, before we move on, I'm just going to move this outside of the health check. So just remove everything of this if statement and this app use. Take it out of the health check and just put it on a fresh new line. This is looking good right now. Now let's focus on fetching detailed user profile information from the Slack API itself. So we're going to define the function that we've been writing a lot. So get user info. Okay. And of course we pass through something. Mostly it's the user ID. And now we're going to call Slack's user info API method with the users ID. So const result await this web client users info. Okay. And as the user, we're just going to pass through the user ID that we passed into this function. And then we're going to extract the user object from the API response and save it as user. So result user. Great. Okay. So we've done that. We've saved everything under user. And now we want to return a normalized user info object with only the fields we actually need. So I'm going to return and I'm going to get the ID from the user ID. So the user object has an ID that I've just saved as ID. We're going to get a name which is either going to be the user real name or if that doesn't exist the user name. Then the username which is going to be the users's name. Okay. So the Slack username or handle. Next, we're going to do the email, which is saved under the user profile. And if that exists, we're going to get the email like that. Then the title, which just the same as before, user profile, and we're going to get the title if the profile exists, something like a job title. We're then also going to get the time zone, which is saved under user TZ, and we're going to create a profile. So essentially we're getting the user object and mixing it around to create our own object that we want. The first name lives under the user objects profile if it exists. First name last name is the same. So I'm just going to copy that. Paste it. Change this to say last and this to say last. And then we have a status text which is a custom status text. User profile if it exists and status text. Great. This is looking good. Okay. So we can move on with that. The other function that you saw us using a lot is analyze and post member. So let's write it. analyze and post me. Okay. And this is the main analysis pipeline that researches a member, runs AI analysis, saves this to a database, and post to Slack. So, this is quite heavy lifting. And we usually pass something through into this function. Uh, it's usually something like member info. So, let's save it as that. And first, let's track the database record ID so we can update it later. So I'm going to save it as let analysis ID and keep it as null to start try something and then let's you guessed it catch error and log error log error. The log object is helping us do this. So what should we pass through? Let's pass through the message. I'm going to do error process sync and then let's put the member info's name. Member info name K error message and we are not done yet because also if analysis ID exists. So if analysis was saved to the database but Slack post failed, we want to log that fact as well. Okay, we're really being super pedantic about what we want to log just so it's super easy for us to know where something went wrong. So analysis and then let's pass through the analysis ID saved to data base but not sent to Slack due to error. Okay, great. And that should be it. Let's try write in our try. Okay. Uh and in fact I'm also just going to throw errors. So throw error. So rethrow so callers can handle the error. And in the try now let's do it. I'm going to just log some info for us. So log info processing member and then let's pass through the member name. So member info name and as a step one we're going to gather basic research. So like the company website or the GitHub profile. So const research data and we're going to write a function for this. It's going to be called do basic rearch and we're just going to pass through the member info so we know who to do that research on. Step two, we're going to send member info and research to GBT4 for fit analysis. So let's say this is analysis uh and again we're going to write a function for this called analyze with AI and then pass through the member info as well as the research data we just got above. And step three, we're going to persist the analysis to the database before posting. So it ensures no data loss. So log info saving analysis to database for and then let's just pass through the member info name so we know who we're dealing with. Great. and analysis ID. We can now save something to that. Uh I'm going to write a function called save member analysis. Pass through the member info, pass through the analysis, and pass through the research data. So we're passing through these things. Okay, great. So, a bunch of functions that we are yet to write. And finally, we're going to post the formatted analysis to the private slack channel. So, await this post analysis to channel. And what shall we pass into this? Well, it's going to be the same things. Okay. So, just like that. Great. Okay. So, that is the fourth thing that we're going to try. Okay. So, those are the four steps that we're going to try. One, two, three, four. Uh there is a fifth and that is to update the database records themselves to mark the slack posting has succeeded. So if analysis ID exists my then we're going to await mark as sent to slack and just pass through that again. So another function to write a lot more functions. So let's do it. So after we throw the error here, let's start with the first function which is do basic research. So async do basic re search. And as a reminder, this gathers publicly available info about the member. So like the company website or GitHub. And let's save it to an array called results. So at the moment it's empty, but it shouldn't be when we're done. And once again, let's try something and catch any error. Catch any error. Log error pass through a message. So, I'm just going to go with a basic research error. And then just the error message. But at least we know it's a research error thanks to this piece of text right here. And what shall we try? Well, we're going to see if the member has a non-personal email and try to look up their company website. So, if member info email exists and we check if this is not a personal email. So, we're going to write a function called personal email pass through this member info email. Okay. Then we're going to extract the domain from the email address. So everything after the at. So if it's at google.com, we know they work at google. If it's at apple.com, we know they work at apple and so on. So const domain equals member info email. And I'm going to split at the at just like that and get the second half. So that is a great way to do it. Okay, now we're going to attempt to fetch and pass the company's website. So, const company info equals await this get company info. A lot of functions that we are yet to write domain. We're just going to pass through that domain that we got from this step right here. And then if company info does exist, so we managed to get something back, we're going to push that into our results array. So company info. Okay. And great. Now also if we have the member's name, we're going to search GitHub for any matching profile. So if member info name uh const get hub info equals await this get hub info and pass through the member info not email but name if GitHub info exists exists, we'll push it into the array. So, just like that. Great. So, we're just collecting things and pushing them into the array. And that's it. That's for our do basic research function. So, essentially, we're just checking the domain of their email if it's not a personal email. And we are also checking their GitHub profile and seeing if it was found. We just need to pass through members info into here. And the function that we're going to write is actually is personal email as that is what we are checking. We want a true or false response. Okay, great. And then return whatever research data we collected. So return results. Return that array which to be honest may be empty. We don't. No. Cool. So of course we wrote some functions such as get company info. Let's write them now. So async get company info. Uh we did pass through the domain. So if it's if it's apple.com for example that will be passed through. try and once again catch error log error some message which in this case is could not fetch and then let's put domain and then the error message. But first, we need to try stuff. And we're going to make a HTTP get request to the company's website with a 5-second timer. So, what I'm going to do is const response equals 08xio get https wain. Okay, so that's what we're doing. timeout 5,000s pass through some headers as well. Okay, so if it's apple.com, we're literally going to ww.apple.com. Okay, we're going to do this because we want to extract the title tag from the HTML of that website. So the headers we're going to use for this are user agent mo 5.0. So we're just setting a browser like user agent to avoid blocks. And now we're going to use reject to extract the title tag content from the HTML. So, con title match equals response data match. And then I'm just going to literally search for any title tag like so. And let's also put a fallback to just the domain name if no title tag is found. So, con title equals title match. So if that exists, we get the title match and get the second item from it. Or if it doesn't exist, we're just going to do company is domain. Okay. So hopefully by doing this if we get a better actual title from the company website or if not we'll just use whatever the user gave us as the end of their email address. It's fine. Okay. Now we're going to return a structures research results object. So the URL is going to be well this. Just make sure this only has two double slashes. Okay. So, if it's Apple, once again, it'll be ww.apple.com. The title is going to be whatever we got from the HTML page. So, if it's, you know, if apple.com has a title on there of the Apple company, that will come back. And if there's nothing there, they'll just come back with apple.com. And as the content we're going to do company website for then and the domain of apple.com and type well we know this is a company so I'm just going to put the string of company just like that. Great. So we've dealt with a company. Now, uh I'm just going to return null here. Okay. And now the second function that we used is one that searches GitHub's API for user profiles matching the member's name. So unlikely that someone has a Slack name the same as their GitHub user profile, but you never know. Why not try get hub info pass through the name try something catch error and this time we're going to do log debug get hub search error and then the error error message. Okay, so we're going to log the GitHub API errors at debug level so that we see stuff like maybe there's a rate limit or network issues and so on. Okay, and return null. So we return null if no GitHub profile was found or an error occurred. But first, let's just try it. So in order to try this, we're going to call GitHub's user search API with the member's name URL encoded. Uh, and to do that, I'm just going to do const response just like that. Oh, wait. Axios get and then https api get hub.com search users Q equals and then encoded URI component and pass through the name. Okay. And let's also add a timeout. Time out 5,000. Great. And if there are any matching users that were found, if response data items exists and response data items length is larger than zero, then we're going to take the first best match from the search results. So const user equals response data items the first item. Okay. And then we're going to return a structured research result with the GitHub profile info. So return URL is the user.html URL. The title is going to be back slashggetub user login. So just like that. So the GitHub username content is going to be back tick user public repos. So we're going to save them so we can pass them through to be analyze public repositories and the source type well we know this is GitHub great okay so that is looking good and let's move down okay so now we're going to write the analyze with AI function which uses GB4 via lang chain to analyze a member's fit with the company products. We've got all the info about the person or as much as we can. Let's analyze with AI. Analyze with AI, pass through the member info, pass through the research data, and let's build a prompt template with placeholders such as name, email, etc. that get filled at invoke time. So let's write our prompt con prompt equals chat prompt template from template and then I'm just going to post the prompt because I pre-wrote it previously. So here we go. Analyze this new community member for fit with our commercial product. So here we're going to put our company name and our company product. So for example, we are a database company called Anna's databases. We would save that in our file. So that is another thing we have to remember to do. Or if that's missing from thev file, it'll just say your company or your product. Okay. So this is the prompt. We're then going to also make sure we pass through that member's name, email, and title, as well as the research data to provide a JSON response with a fit score from 0 to 100 to check if they'd be interested in our product insights. So such as an array of three to five key observations and recommendations, an array of two to four engagement suggestions. Consider job title, company size, technical background, and budget authority when checking if this new member is a fit for our community. Great. That prompt is good. So now once again, let's try and catch any errors. Catch error log error. Let's pass through the message AI analysis error and just pass through the error message. Okay, you can also return a safe fallback analysis so the pipeline doesn't break. You could just do something like return fit score 50 insight. We'll just pass through unable to complete full analysis. Okay. So, we're just going to give them a 50 fit score because we don't know if they fit or not. and recommendations will also in the way just have one item manual reviewed. Okay, so that's quite a good message to send. Okay, but first we're going to try and actually you know get a proper fit score. So in order to do this we're going to format the research data as a new line separated string or fallback message. So const research summary equals rearch data length bigger than zero. If that is true, we're going to get the research data and map. Just put this on a new line for you. Map each research data item. Okay. Okay. And I'm just going to do back ticks research data title and get the research data content and join it together. Okay. In a string. So just like that. Okay. Otherwise, if there's no research data, we just return limited research data available. Cool. That looks good. Now, we're going to create a line chain chain. So let's do a const chain equals prompt pipe this open AI and we're going to invoke the chain with the members details filling in the template placeholder. So const result equals await chain invoke and then pass through the name. It's going to be the member info name. The email it's going to be member info email. And if that doesn't exist, we're just going to put not provided. And then the title, which is going to be the member info title. And if that doesn't exist, we'll put not proided. And then the research, which is going to be the research summary that we changed into a string. Great. Now we're going to extract the text content from the lang chain response object. So const response text, let's save it as and we're going to get the result content. If that doesn't exist, we will get the result. Okay. And once we have that, we're going to strip markdown code. So for example, you know, the piece of text that defines that it's JSON, we'll take that out cuz sometimes GPT wraps that around JSON. So let's get rid of it. const claimed response equals and we'll get the response text and take out so literally take out the thing that I was talking about which looks like this and replace it just with an empty string like that and then trim. Okay. So if you want to view it better, there we go. And now we're going to pass a clean string into a JavaScript object. So const analysis equals JSON parse and then we'll get that cleaned response and just pass it through like that. Great. And finally, we can return a normalized analysis object with clamped fit score and validated arrays. So let's go ahead and do it. Return fit score. We're also going to return insights and we're also going to return recommendations. Just like that. The fit score we're going to clamp score to 0 to 100. So, here we go. I'm just going to do that. For insights, we're going to ensure this is an array first. And if it is, we're going to get the analysis insights. Otherwise, we're just going to return back something that says analysis completed. And for recommendations again we're going to ensure it's an array. So we'll pass through the analysis recommendations into array is array and then return the analysis recommendations. Otherwise, we'll put follow up is recommended. Just like that. Great. So now we are done with that. Okay. So we have finished getting a fit score, getting insights and getting recommendations into members that have joined the channel. Now I'm going to post analysis to the channel. So let's write that function async post analysis to channel exactly like we wrote it before. Uh I'm just defining the function really and we're going to pass through member analysis and research data. Okay. Um, we can choose a color based on the fit score. So, we can do green for over 80. I'm going to just show you how to do that. const color equals analysis fit score. And if it's higher or equal to 80 and that's true, well, we want the color of green, which is this hex code right here. Otherwise, if analysis fit score is larger or equal to 60 and that is true, we're going to go with this kind of yellow. Otherwise, if analysis fit score is larger or equal to 40 and that is true, we're going to go with this orange. And then finally, if it's lower, we'll go with a red. So, if it's below 40, like that. Great. I'm just going to move that down there so that looks better for you to read. Wonderful. So now we're going to build the Slack block kit message structure. Okay. So const blocks equals and then make an array and then we're going to write the header block which shows the member name. So type of this is going to be header and then the text is going to be type plain text text text. So, we're just essentially styling what is going to look like the message in Slack and the text itself, which is going to have an emoji. And I'm going to do new member and then just show the member name like that. And because it's a new member, I'm going to put this cute emoji of a looking glass. Okay, just like as if you're a detective or something. Great. Now, we're going to write the section blocks with fields. So, type section fields are going to be type mark down. So, we're going to do markdown here. The text itself is going to be use back tick uh fit score and of course we're going to pass through the actual fit score itself. So I'm just formatting that a bit analysis fit score. So, that should be bold. And then I'm going to say out of 100. Um, I'm just going to actually copy that because we're going to do three of these. So, to the text. However, we are going to replace. So, copy that and copy it again. So, this time it's going to be for email. Okay, make it bold and we're going to do member email or if that doesn't exist, we'll just put not provided. And same for here this text we'll do title make it bold and we'll do the member title or not profile just like that. Okay. Great. And now we're just going to show the insight sections if there's any insights to display at all. So if analysis insights length is larger than zero, I'm going to get blocks push type section. The text is going to be type mark done and the text itself is going to be insights and then analysis insights map. I back tick. Just going to put this on a new line for you so it's more readable. And there we go. So, we're getting each insight and essentially joining it together in bullet point format. We will do the same for any recommendations. So, in fact, I'm just going to copy this whole thing. So, copy all of that and paste it once more. So, there we go. And this time if analysis recommendations recommendations just going to copy that type section recommendations with a capital R analysis recommendations map as well and create into a bullet point list. Okay. And now we're going to add a context block at the bottom with timestamps of the analysis. So blocks push type content elements. Make it an array and then type mark text analized and then whatever the date to ISO string is. So new date to ISO string. Okay. You can even add a fun emoji if you want. Great. So, this is all looking good to me. Just make sure that says context and not content. We just need to finally send all of that. So, send the formatted message to the configured private slack channel. So await this web client chat post message channel we're going to pass through what else we're going to pass through the actual text itself and the blocks. So all the blocks from above uh the channel I'm going to get that from our process env. So it's going to be a Slack private channel ID. So we're going to have to add that to our MV file. The text is just going to be fallback text for notification. So new member analysis and we're going to pass through the member name. Member name like that. And then let's put also the fit score. So analysis. Let's wrap that fit score and make it out of 100. Okay. And of course the block. So the rich block kit layout that we did earlier. Great. While we are here, don't forget to call this new date object as well. That's the full thing there. Once again, this code will be available in the description below. And before we move on, I'm just going to log info. I'm just going to log that the message was successfully posted. So using back tick, I'm going to do analysis posted to channel 4 and the member name. Great. I believe there is one more function that we are yet to write that we haven't covered yet and that is checking whether an email address belongs to a common personal free email provider. So is personal email. Let's check by passing through the email. So if it's bobapple.com it's not going to be personal. But if it's bobgmail.com then yeah it's likely personal. So, const personal domains, this would include I'm just going to create an array to check for. Um, of course, there is gmail.com. There's also yahoo.com. There ismail.com, outlook.com, and iicloud.com. Okay. And great. Okay. And great. Const domain equals email split. And then we're going to take out the at from the email that's been passed through. Get the second half of it. So essentially if it's gmail.com and just in case. So if I check for that exist, we're going to change it to lowerase. So we can compare to our array just in case it was you know put in not lower case. Great. And we're going to return true if the domain is in the personal domains list. So return personal domains includes the domain that we extracted. Great. So if it exists in that array, it will come back with true. Otherwise, it will come back with false. Phew, this has been a lot. I'm actually going to write a function to start all the services. So the database, express server, and the Slackbot. And it's just going to be called start. Okay. So here a sync start just like so. And let's again try catch error catch error log error pass through the message of failed to start and then the error message and we'll exit. So process exit just like that. But first let's try something. So I'm going to initialize the database. So the Postgress database and create tables, indexes, and so on if needed. Um for that we're going to have to write some more code that will help us initialize the database. We'll do that in another file, but for now I'm going to log info initializing data base just like that. Let's do a dot dot dot. We can even put an emoji and await in it data base. Okay, so that we are yet to write and then we're going to start the express server on the configured port. So const port. If we do put something in the env file, that will be picked up first under the variable port. Otherwise, let's just run it on port 3000. This server, this app listen to port and then we're going to pass through a call back and we're just going to log info express server running on port and then whatever the port is. If it's 30,000 then that's what will show up. Now we're going to connect the Slackbolt app via socket mode websockets. Await this Slack start and call it. And again, let's log some info. Log info something like Slack connected. Okay. So there we go. And now we're going to log that everything is up and running. So log info Slack AI agent is up and running and if in development so process environment deeply equals development log info and pass through the test endpoint. Just like so. Development. Great. This is looking good. And now we need to write a function to gracefully shut down all services. So async stop log info shutting down or something similar. Okay. So that is our logged message. But let's actually shut it down. So try catch error log error just like so. shut down error and error message. Okay. So, we're going to disconnect the Slackbot 08 this Slack shop. If this server await new promise resolve this server close resolve We also need to close the Postgrass SQL connection pool. So await close data base another function we are going to have to write log info stopped successfully. Okay. And then we're going to exit the process cleanly. So process exit just like so. Okay. Amazing. So that's really it. All that's left to do is instantiate the agent. Um so we can finally do that outside of here. So pass the final curly brace const agent equals new slack agent slack AI agent actually. And now we're going to listen for control C to shut down. Okay, so that's going to help us do it. We're also going to listen for termination signals from Docker or render to gracefully shut down. And we're going to start the agent. So agent start catch error. Console error start up failed. error message and process exit one. Okay. And now we're going to export default the agent. And that is our index.js file done. Before we write our database JS file, I think let's take a quick break to fill out the variables that we covered in the index.js file. So, let's do it. At the moment, of course, we've got our database URL, but we did cover a lot lot more. So, we had stuff from the Slack configuration. We also had stuff from the Open AI configuration. We also added our own company information such as you know your company name and the products you sell. And then we also have the application configuration. So the slack configuration this consisted of a Slackbot token. So I'm just going to take that. It also consisted of a Slack app token. It also consisted of a Slack signing secret and a Slack private channel ID where we can get our analysis posted to where only we can see it. We're also going to have to get an open AI API key and we're also going to have to supply our company name and the company products. Okay, because obviously we are selling stuff and that's why we want to see if these new joiners are a match for our products. We also then have the port that we want to run the app on and the node environment. Okay, so let's fill this out. Uh let's start with the easy one. So yes, let's make our port 3000. Let's make this development environment. So development, my company name is going to be code with Anna and the product I'm selling is coding courses. Okay, great. So now let's work on the others. I'm going to show you where to get the OpenAI configuration now. Okay. So in order to get your OpenAI API key, you just head over to openai.com and under login, you just click on the API platform. Now you are going to be asked to log in. So please go ahead and do that using your credentials. And once you are logged in, your dashboard should look like this. Okay. So this is what mine looks like. And under here you will see API keys. So in order to start using this, you just go ahead and create a new secret key. You can call this whatever you wish. So I can call this Slack key and create a secret key. Now just copy that like so. And you're just going to, you guessed it, paste it in here and save. Please go ahead and use your own because I will be revoking my one so it's not usable. Great. So now that you have your open AI API key, let's move on to the Slack configuration and getting your Slackbot token, your Slack app token, your Slack signing secret, and your Slack private channel ID. So all I'm going to do is head over to Slack. So this is my Slack channel is called Code with Anna. And in order to get a channel ID, you can already see I have created one here. So if you click on here in order to get the ID, you can open channel details and get the channel ID from here. Okay. So now by grabbing that channel ID I will be using in my project to post messages from my app into as you can see I've already tried it and it worked here. So let's go ahead and post that as the Slack private ID channel like so. Let's go back. Now, in order to get the others, you're going to have to go to api.slack.com/apps. I already have an app here, but I'm going to go ahead and create a new one just you can see. So, let's click from manifest. I'm going to select my workspace, which is code with Anna, and click next. And let's go ahead and click next again. So, let's create that app. Okay, so here we go. This is some basic information, but what you are going to need is your signing secret. So, make sure to copy that and paste it in like so. Next, we're going to get the Slack app token. So, we're just going to carry on going down and generate a token. We're going to call this token anything. So, Slack token if you want. We're going to add a scope and you're going to be able to write, but also if you want, you can add all of them if you are not sure. So, it's up to you what you want to do. And we're just going to click generate. So, now we have that token. I'm just going to copy it and paste it in like so. It should start with XAPP. Great. Let's continue. Now, if you go all the way back up under socket mode, you're going to have to enable socket mode. And finally, under or permission, you're going to have to add an or scope. So, let's go ahead and do read. Let's go ahead and do write channels read. chat, right? Users read users read email. And now let's install two code with Anna. Let's go. allow and our bot or token. Let's copy that and paste it in like so. It should start with XOXB. Amazing. So, that looks good to me. One thing you're also going to have to do is under event subscriptions, you're going to have to turn this on. You can also do delayed events and subscribe to bot events, add bot user event. And then you're just going to have to find team join and member joined channel. Okay, so make sure to add those. Great. And reinstall your app. So let's just allow. Okay, so those two are now added. We're done with our env file. Let's now work on our database. Okay. So, we're going to have to import the PG node Postgress library from Postgress SQL database and then we're going to structure the pool class from it in order to use it. We're also going to have to import to load the variables that we just saw. And of course, we're going to load environment variables from the MMV file into process EMV using this line of code. Now, let's create a pool. So, new pool and we're going to pass through a connection string. This is going to be our database URL which is the first thing that we did from render.com. We're then going to put SSL as reject unauthorized false. So we're going to allow SSL connections without verifying the certificate. We're going to define the maximum number of clients in the pool which is going to be 20. Then we're going to also add a close idle client after 30 seconds. So let's go ahead and do that. And we're also going to add a connection timeout. So fail if a connection can't be established within 2 seconds. There we go. Okay. Okay. Now, we're going to add an event listener which fires each time a new client connects to the database. So, pull on and then we'll do connect. Okay. And then we'll write a call back. And essentially this is just going to say console log info database connected. Okay. So, we're kind of matching the errors from the previous file. Now, we're going to write an event listener which fires unexpected errors from idle clients in the pool. So this time we're just going to do error unexpected database error. And now we're going to write a function essentially that creates the database table and indexes if they don't already exist. So export a sync function init data base. So we're going to export this as we're going to use this in the index.js file. First, we're going to acquire a client from the pool for running multiple queries in sequence. We're then going to create a member analysis table if one does not already exist. Uh I'm just going to try that and catch any errors, of course. So my error this time is just going to be console log this error throw error and then finally client release. Okay, so always release the client back to the pool even if an error occurred. Okay. And we're going to create the member analysis table if one doesn't exist as I tried. So we're going to await client query. We're going to use back tick. And I'm just going to paste this because I don't really want to make any mistakes. So this is how you create a table. Okay. So that is the full thing. Create table if not exists. You've got the ID. You've got the member ID. You've got the member name. member email, member title, member time zone, the fit score, the insights as a JSON array, recommendations, research data, you got analyze with a timestamp, sent to Slack, SL to Slack at, and the time stamp created at and updated at. Okay, so a lot of information is being saved in our database as well. Okay, great. Once again, this will be available in the video description. Now we're going to create an index on member ID for the last lookups by the Slack user ID. So we're going to create index if not exists. Okay. On the member analysis and now we're going to create an index on analyze at for efficient time range queries as well. Okay. So a bit of formatting has happened but essentially that is it. Great. And once that is done, I'm just going to console log info database schema initialized. Wonderful. So we are done with that. Next, we're going to write another function and export it. And this is a save member analysis. So export async function. Save member analysis. Just like so. Let's pass through the member info analysis and research data. We're going to once again acquire a client from the pool and try and catch catch error. The error is going to be error fail to save analysis to database and we're going to throw error and finally we're going to release the client back to the port and in the try well we're going to insert the analysis data using parameterized queries to prevent SQL injection. So, const result equals await client query. Making sure to spell client correctly. And once again, use the back ticks. I'm going to insert into member analysis the member ID, the member name, the member email, the member title, the member time zone, the fit score, the insights, the recommendations, the research data, and the values returning the ID. Okay. And here are all the variables I'm going to pass through. We have the Slack user ID. And if it doesn't exist, just null. We're also going to pass through the member info name. We're also going to pass through the member info email or null if it doesn't exist. Same for the member info title. Same for the member info time zone. And same for the analysis fit score. We also are going to serialize insights array to a JSON string and also the recommendations as well as the research data. Great. So that looks good. Let's also just log the ID of the newly created records so we know everything's gone correctly as well as return the new records ID so callers can reference it. Okay, so there we go. I love it. Okay, so let's move on because we also need to mark as sent to Slack. So export async function mark as sent to Slack just like that. And we're going to pass through the analysis ID. So essentially we just want to update a record to indicate the analysis was successfully posted to Slack. So once again let's acquire a client from the pool. Let's try let's catch error finally and client release. The error is just going to say failed to mark as sent to Slack and actually show the error message. We're also going to throw the error. And what we're going to try is well, we're just going to update the center slack flag and timestamps for the given analysis ID. So await client query. Use back ticks again and update member analysis. Send set to Slack as true. center to slack at the current time stamp and update it at the current time stamp. Okay. And we're just going to pass through the ID so it can replace here as well as the variable. So let's pass through the analysis ID just like that. Great. So once we've done all that, we can gracefully close all connections in the pool. So export async function close data base 08 pool end and then let's console log info database connection pool closed and now we can export Default default port great so amazing we have done all this let's import pg so let's open up our package JSON as you will see pg is not there so let's do npm pg so that it shows up let's also import all those exported functions into here. So right at the top of this file, I'm going to import init data base save member analysis mark as sent to Slack and close data base from the file dbjs. Okay, wonderful. Okay, great. I'm just going to shut all these down and get up my terminal and clear it. So, the first thing we're going to do, making sure we are in Slack agent AI, is just run this app. Okay, so it's injected the variables from the MV file. It's initialized the database. The database is connected. Database schema initialized. Express server running on port 3000. And Slackbot connected. The Slack AI agent is running. I did tell you all those logs were super useful. Amazing. So now let's get up a new terminal. And this time I'm just going to test the agent. So what I'm going to do is just paste this command. Please go ahead and paste the same one we're posting to 3000/estanalyzemember which is the route that we wrote. And I'm just sending over the name John Doe with the email johncorp.com and his title of senior software engineer at techp and pressing enter. And great success true. We've got a fit score of 85 for John. We've got some insights that say John Doe is a senior software engineer at Tech Corp, which suggests he has a strong technical background. His email domain is associated with Tech Corp, indicating he likely works in a techrelated role. The lack of public repositories on GitHub for Stanislaf web could suggest that J's work is private or proprietary. The title senior software engineer suggests that Jon may have influence over technical decisions within his team or department, but it's unclear if he has the budget authority. recommendations. Reach out to John by email with information about our courses focusing on those that are more relevant to senior software engineers. Offer John a free trial of one of those courses or give him a taste of the content and teaching style. So, thanks for the recommendations for selling my courses from code with Anna or invite John to a webinar or online event where he can learn more about our product and interact with other community members. If possible, find out more about Techov's training budget and whether John has authority to make purchasing decisions with a time stamp. So amazing. That is so so cool. And if we now head over to Slack, so let's go to our private channel. So that was our message from previous. Tada. I can see that right now cuz I can look at the time. Amazing. That has been posted to my private Slack channel. Everything has been hooked up correctly. I love it. I'm so so happy with this. Now, I'm not the biggest fan of the formatting that happened. So, actually, we're just going to get rid of one of these forward slashes. So, the final thing should be like this. Let's do the same throughout the file. So, just do the same for insights. So, get rid of those. and the same for the cleaned response. So just get rid of those like that. One other thing you can do is make use of the color code that you wrote and when you essentially post a message. So here instead of blocks you can send through attachments which is an array that will have this object right here. Okay. Now those colors will be passed through as well. And if we go to our database, as you will see here, if we scroll down, you will see that some of our 1 gigabyte has now been used. We have saved John to our database on render.com. The last thing to do is just to deploy this. So let's do it. So for this, I'm first going to save my project onto GitHub. So I've already gone ahead and added that project to my GitHub repository. It is a public project I will be sharing with you. But for now, I'm just going to get that URL. And in render under blueprints, I'm going to create a blueprints instance. Now, what exactly are render blueprints? Well, you can essentially manage your whole infrastructure with a single YAML file. And that's what we're going to do because blueprints are Redd's infrastructure as code model or IA for short. And it helps define, deploy, and manage multiple resources with a single YAML file. So essentially acts like a single source of truth for configuring an interconnected set of services, databases, and environment groups. Whenever you update a blueprint, Render automatically redeploys any affected services to apply the new configuration. And as your infrastructure grows over time, blueprints become more and more helpful for managing changes and additions to it. This is going to help me with automatic syncing of my services. So here I've got to connect a repository. I can post in the URL here because it is a public repository and click continue. I'm going to call this blueprint slack AI agent. And one thing we are going to add is a render YAML file. And as it's going to be in the root of my project, it's going to default to this here. So that looks good. So let's do it. Let's add a render YAML file. So I'm just going to go ahead and do this directly on my GitHub. So let's create a new file. It's going to be a render YAML file and it's just going to look like this. Okay. So it's got the build command, start commands, got some environment variables, too. I'm also just going to add the database URL and sync false to that. And I'm just going to commit that. Okay. So that has now been added. Okay. you will see it right here. So now I know that the repository branch has a render YAML file and it is in the root of the repository. Just going to retry. And amazing. So now we just have to fill this in because of course the project doesn't have this. These secrets only live on our computer. So we need to fill this out. So, I'm just going to put in my Slackbot token. I'm also going to put in my Slack app token. So, just whack it in here just like we do in our env file. Let's also pass through the Slack signing secret, the Slack private channel ID, the open AI API key, and my database URL, which actually comes from render itself. Okay, so great. And I'm just going to deploy this. So, this will be a total of $7 a month, but I will give you all these free credits. You shouldn't have to spend anything when you're testing this out. And we are done. We are deployed. Our web service is working. Everything is looking good. It is live on this URL right here. So, let's check it out. Status healthy. Everything is looking great. So, now let's put it to the test. I'm just going to manually add someone to my channel. That person's going to be Anna@homeb brew.e. So not a private account. It is a work domain. And then I'm just going to send over the invite and click finish. So that person has been invited. And once they accept your invitation that went to their mail. So as you could see home account accepted your invitation to join Slack. Tada. We get a fit score. So great. Our new member analysis has showed up. We got a fit score of 65 out of 100. Title was not provided. We have some insight. So the member is associated with company named home.e which could potentially be a tech company considering thee domain is often used by tech companies. So it gives you some insights and it's also given some recommendations. I love it. So this is working. I'm so happy with this. Our member analysis is working as it should. I love it.

Generated algorithmically for Search Engine Indexing.

Summarize Another Video