Contrast
< Back to Blog
Original link:

https://www.youtube.com/watch?v=ZwN8uzYaZCU

2023-06-14 17:55:08

Send SMS text messages to ChatGPT with webhooks on localhost - complete development

video content Image generated by Wilowrid

Hi , This is Jim from Conveyor .

In this video , I'm going to create a web application that connects the twill s MS service to chat GP t s API , which will allow anyone with the phone number to text with chat GP t .

I'm imagining this being useful for a business where the customer doesn't want to have to go to a website or install an app .

Text messaging has the benefit of being a familiar interface and low friction .

This project uses Visual studio 2022 a s p dot net seven semantic kernel chat , GP , TT and conveyor .

This is the result of the project .

I'm texting here with the Laundromat assistant I made , but there are probably better business use cases out there .

You can even write plugins for chat GP t to enable business specific knowledge and functions .

video content Image generated by Wilowrid

Making it work is pretty awesome and surprisingly easy , actually , especially with our free tool conveyor to receive s MS Web calls during development .

Although I mentioned to service a lot , there isn't any special reason other than it being probably the most well known .

There are other services such as message bird D seven s MS Intel , Tech s MS s MS , Gateway and Tel .

I'm not endorsing any of them .

Please pick the one that fits you best .

I will also mention chat GP t a lot and things are changing rapidly in the chat bot market .

So I'm sorry if the code in this video becomes outdated and remember to consider other Chatbot API providers .

OK , I'm going to set up chat GP t as a laundromat assistant who can help with washing and drying questions .

I'm going to go through every step from building up the application to connecting to chat GP t and T .

video content Image generated by Wilowrid

So it is going to be really detailed , but please use the chapter markers to skip anything as desired .

I'll create a new Web APP project .

It needs to be dot net six or newer in order to use semantic kernel .

The idea for this project is to first have a simple chat Web page where I can test sending messages to chat GP T and then create an API controller that can receive messages from Twi OS MS service .

This is called a Web hook and works by giving the sending Web application to , in this case a URL to call when an event happens , such as a text message is received .

video content Image generated by Wilowrid

To start , I'll make the Web based chat that sends messages to chat GP t using the semantic kernel package .

So in develop a powershell , install the package by CD into the project folder dot net a package , and it's pre release mode at the moment .

And , of course , you can use the U I .

If you prefer .

Let's have a look at the chat GP t API in Semantic Colonel , how do you send a message ?

Well , here's the basic structure .

video content Image generated by Wilowrid

So to begin with , we create the kernel and then we configure it with the model that we want to use and our open API key and our organisation ID .

Next we set up chat GP t .

So , first of all , we give it its instructions on how to act .

And then here we create the service and send the set up instructions .

And then down here , we have to recreate the conversation history because chat GP t isn't state .

So you can see here The user , uh , asked for something .

And then , um , we had previously received a message back from chat GP t .

Why not try soccer ?

video content Image generated by Wilowrid

And then we can add the next message there , and then we can send that conversation to chat GP t and get the reply .

The main takeaways are that I need to provide credentials and have the conversation resent with every request , so it will need to be persisted somehow between calls .

This is also a good point regarding cost chat .

GP T pricing is based on the number of tokens sent and received .

The number of tokens is similar to the number of characters , but not quite .

There is a calculator on the open a I website , but I would just say that you can have hundreds of short messages for a few US cents .

However , because chat GP t isn't state and the entire conversation is resent with every message , the number of tokens increases with the length of the conversation and therefore becomes more expensive , just something to bear in mind .

video content Image generated by Wilowrid

Uh , there is also a usage limit page where you can set spend limits .

I would recommend that in case something goes wrong , like an infinite loop , it could get expensive .

OK , back to the application .

I'll create a basic data type , uh , for the messages being sent back and forth .

So I think we're going to need to know who the sender is .

And of course , we're gonna need the message , which I'm gonna default to now .

And we need to define message sender .

That's gonna be an ENUM .

video content Image generated by Wilowrid

And it can be the user or chat g BT , which is system .

There's already an index dot CS HTML .

I'll open the model for it and add properties for a list of conversation messages and the new message that's just been entered .

The list can function as a way to persist the conversation and show the user what has already been said each time the page loads .

This is our new message that the user has just entered .

video content Image generated by Wilowrid

And we want razor to bind to these properties in the markup side of things .

I just need to have a basic form to show the past messages and allow the entry of a new message .

So I'll get rid of the boiler plate .

video content Image generated by Wilowrid

We got to begin a form , and we want to display the conversation messages So that property we just added , and we're gonna need an editor for the new message and a button just to send a new message back to the server .

video content Image generated by Wilowrid

The application needs to know how to display for and also edit for the type conversation message because we just created it .

Although the call is to display for a the list of conversation message objects , Razor will automatically iterate the list and call display for on each conversation message .

video content Image generated by Wilowrid

So to do that , make a display templates and edit templates folder under shared and add a razor page to each called conversation message for the display template version .

video content Image generated by Wilowrid

I just want to output the fields for the model conversation message , so I'm gonna change the model to the type conversation message which I created .

Don't eat that , and what I need to do is at hidden fields for December and the message so that they are persisted and sent back to the server when the form is submitted .

video content Image generated by Wilowrid

But I also want to show the user the previous messages as well , so I will actually , uh , Albert those and I need to do the same for the message .

Just had a couple of plank lines there for the editor template .

I'm gonna change the model again .

We want it to be the data type that we created .

video content Image generated by Wilowrid

And we actually just need a text box for the message .

Because this is the new message field .

We don't actually want to , uh , allow the user to specify the sender .

That field will be added by us later .

All right , let's try it out .

That looks about right .

When the send button is clicked , the page model code is called , and it's gonna use the post HTTP method .

video content Image generated by Wilowrid

So in the page model , I'm going to change on , get to on post .

And I'm also gonna make this handler asynchronous because I know that , uh , chat G BT s API is asynchronous .

And I'm just gonna add the new message .

I I'm just gonna add the new message to my list of messages .

There we go and run it .

Just type in anything .

We can see that it works .

Basically , let's have a think how to send the new message to chat GP t and get the response .

I think I will create a class to handle the logic of sending the message .

video content Image generated by Wilowrid

This will definitely be handy when I add the API controller later .

that handles the s MS message since that will use this new class too .

So I'll create a new class , and I'm gonna call it Chatbot session and I can keep the conversation history in here too .

So I'm actually just gonna copy this and put it here , and I'm gonna initialise it as well because this copy of it isn't bound by razor .

And I'm gonna change that property back here in the page model as well .

Uh , what I want to do is actually just map it to the property in Chatbot session .

video content Image generated by Wilowrid

So what I need first is an instance of chatbot session .

I'll I'll do and initialise it , and then I can change this property here .

I don't need the setter anymore .

I'm just going to return what chat that session has .

There we go right close up and I can go back to my on post and we're not gonna do this here now .

Instead , let's create a method in here .

Call it forward Message from the user , a sync .

And yes , I do want that to be asynchronous .

video content Image generated by Wilowrid

So I'm going to That's why I'm going to await it .

Let's create this and then F 12 to get to it .

And we need to make this asynchronous as well .

And then in here , Well , I am going to want to add the message to the list .

Um , and I also do need to set the sender field .

If you remember , that's not coming from the user interface .

So by definition , it's going to be the user who sent that message , and I need to fix the capitalization here now and then .

We want to actually send the messages to chat GP t .

video content Image generated by Wilowrid

So let's call that in the whole history .

So now we actually need to generate this method again .

We have to change this and specify that it's a sync .

I'm just going to copy the first part from the boiler plate .

But there is actually a change already made here .

So if you remember , this argument was for the open A I key .

And this one here was for the organisation , ID .

So you can see that , uh , I'm actually gonna read it from my environment variables .

I'll talk about that in a moment , but for now , I just assume that those environment variables are set correctly .

I'm just gonna change this so it's more visible .

video content Image generated by Wilowrid

OK , so when you work with chat G BT , you need to sort of give it a mission statement .

I guess you could say , um we need to tell it what to do .

Otherwise it will just sort of do the usual , uh , chatbot that it does .

So you've probably seen on the Internet .

So I'm going to tell it that it's a laundromat attendant who gives laundry advice in 20 words or less .

So I don't want the messages to be too lengthy .

So I'm gonna specify 20 words or less , and I can give it more guidelines as need be .

So if I find that it , uh , gives advice , I don't want it to , or if I need it to have any , uh , specific advice I want to give .

I can specify that here .

video content Image generated by Wilowrid

And just remember again that this message does count , uh , to your token count .

So the longer this message is , the more it's gonna cost you , OK ?

And then the next part was to get the service , and then we're gonna create a new chat .

No typo there .

And if you remember , we need to recreate the entire history and send all that again , so we already have obviously the list of messages already sent .

video content Image generated by Wilowrid

So let's iterate those and we're going to switch on the type of sender because we call a different method in the API , depending on whether it's a message from the user or a message from the track .

GP t So in the case , it's the user we're gonna use add user message .

And of course , it's the message that we just received .

That's fine .

And if it's from chat G BT before that will work .

OK , that looks right .

video content Image generated by Wilowrid

So that just loads up the API with all of the messages , including the new one , because we already added that to the conversation .

So let's get the reply now from chat GP t you send the chat and we're gonna add the reply to It's a new conversation message .

The sender is going to be not the user , but the system and the message is , the sister replied , That was very good , and we're just gonna return it in case that becomes useful .

video content Image generated by Wilowrid

So this would be a strength .

So back to those two environment variables , open I a colon key and open .

I a colon or ID .

video content Image generated by Wilowrid

These are stored in the system environment variables rather than in a convict file in the spirit of keeping secrets out of source code so they don't accidentally slip into source control or get shared , as has happened in KUB , for example , these are the credentials you use to authenticate with Open A I to get the org ID , go to platform dot open a i dot com slash account slash org settings , and you can see it here , and I'm gonna copy it , then open the system environment variables and add a variable named open A I colon .

OK , go on .

video content Image generated by Wilowrid

Org ID with the value from the website and then go back to the website and down here in API Keys , I can create a new secret key .

Let's give it a name and I'm going to copy that as it tells you , it's never going to show you that again .

If you did forget the key , it's OK .

You can just delete it here from the list and create a new one and then update your code .

So I need a new environment variable for that key , which is going to be open a I colon , OK ?

And then I'll past the value here .

Here's the important thing to remember and save yourself some time .

If you set environment variables , you have to restart visual studio and maybe you just have to restart .

I s express .

I don't actually know , but you do that to get the new values picked up .

video content Image generated by Wilowrid

Now run it and see if it works .

So this is gonna actually send a message to Jack G .

BT .

There we go .

So we have a basic web based chat interface .

It's time to bolt on the twill part so that the application can handle s MS calls .

How does An s MS API provider like to work at present ?

When you sign up with them , you get some free credit .

It was $15 when I did it , which you use for a phone number for your region .

And any s MS you send and receive is paid for with that credit .

video content Image generated by Wilowrid

Remember , of course , that you are using a metre service and will get stuck with the bill if you send or receive a lot of messages , or if you call your emergency number like 911 .

But are not set up properly in North America .

You're charged $75 .

If you don't have an address in the system for the phone number , you'll see I haven't set it up in my account .

And that's because they expect a US address .

But I'm in Canada anyway .

I just won't call 911 using it from the coding side , you need a public URL .

Most likely a Web API that the S MS provider will call when a text message comes in to your number .

I'll show you how to create the Web API and expose it to the Internet using conveyor during development .

So you don't have to keep publishing your code to a server , which you would have to otherwise .

video content Image generated by Wilowrid

So first thing to do is add required packages .

So I like doing it and develop a powershell dot net a package and also s p net core .

You can probably get away without easing these packages if you want , but they help with formatting s MS replies in this case .

Next , I will add a web API controller to the project .

I'm gonna cool .

It s MS receiver controller .

video content Image generated by Wilowrid

I'm going to change it to a twill controller and decorate the class as API controller and the root I want to be API slash controller and I'll make the default method something I can test out because I know I won't get it right first time .

So there's no view .

It's an API so content Hello .

I need to add controllers to the services in programme .

video content Image generated by Wilowrid

So I'm gonna go there services at control This and I need two map controllers and run and test in the browser .

That's the main page .

But we want to call that controller .

So it's s MS receiver .

So there we go .

Hello , world .

Great .

I just need to create an action method to receive the S MS calls now .

And I got this method signature from Let me just go here .

video content Image generated by Wilowrid

I got this method signature from to documentation where from will be the sender's phone number and of course , body is the text message .

It's decorated HTTP post because by default , twill sends the messages a post request .

The from form attribute is because the post requests coming in will have the data in form of coding for other s MS providers .

The request could be totally different .

It could be json formatted , for example .

And so this code won't work at this point , I have everything I need to send the message on to chat g BT like I was before .

And then whatever I set in the return value , the TT m L result is sent as a response text to the user .

video content Image generated by Wilowrid

So let's write some code to actually use the chatbot session .

Uh , not quite .

We wanna make sure from is not now .

And we're just gonna create a chatbot session like that and the we're gonna create a new conversation message based on the arguments that came in .

So that's an empty message .

We're gonna fill it with the body , and the sender will be .

Obviously , it's not from it's actually the user .

The user wrote the message on the phone and sent it to us .

And now we're sending it to Jack G .

video content Image generated by Wilowrid

BT so we can get the reply and call that method that we wrote before forward message from user as sync with that message we just created .

And then we want to create an object that twill is expecting for the response .

So we send the response back to to and then it sends it as a text message back to the user .

video content Image generated by Wilowrid

So we just need to load it with the reply we got from chat GP t and then return it formatted the correct way , OK ?

And what is wrong here ?

Can I assign void to an implicitly typed variable ?

Ok , did I not specify ?

Yeah , I have to specify the return type over in this method that we wrote earlier and actually return it .

There we go .

And this is not supposed to be message response .

Supposed to be messaging response .

video content Image generated by Wilowrid

I think , um , when you also need to pull in the name space and we better just finish this off .

So if from is now , uh well , for now , we'll just return .

I OK now , to test this , I can either send a post request from a programme like Postman .

I'm just gonna run it , which allows me to create the request manually .

Or I can set up twill with this Web hook and hope it works .

I can't give twilio my local host for the webhook address because to twill , local host is their local machine .

video content Image generated by Wilowrid

Um , so that's why we're going to use the conveyor extension to , uh , create a public URL via tunnelling that we can give to twill .

So because I need to set up conveyor and have it tunnelling as well as setting up twill to point to the correct URL .

And because this introduces more points of failure when I haven't even tried out my method , I'm gonna send a test request from postman first and make sure it works .

Otherwise , I would be banking on twilio being configured properly , conveyor running properly and my API action working properly to do anything , so I'll test it in Postman Postman is a nice tool for testing Web API s and here it is .

So I'm going to create a new request .

video content Image generated by Wilowrid

The URL is this one , and I'm going to set the type to post , and the request that we want to send is formatted like this x w w format R l encoded .

This is according to to Leo's specifications in their documentation .

And there's two fields that we want to send one is from .

So that's gonna be the phone number of the person who sent us a message .

So we'll have to make up something really doesn't matter .

And we need to send the message body .

OK , my application is running , so I'm just gonna try it out .

video content Image generated by Wilowrid

And there we go .

So we can see that it sent my hello from postman message to the , uh , controller just fine .

The controller sent it on to chat G BT and chat GP t replied with its standard ish reply .

Great .

If it didn't work , I would put a break point in the method and try again and see if the request is being mapped at all .

If it isn't even hitting the break point , then I know something is wrong with either my test request .

Perhaps the wrong type of method or form encoding is wrong , or there's a mistake capitalization or spelling .

You can take a bit of trial and error to resolve issues like that without seeing how the A s p dot net middleware is actually handling your request .

So now I see it working .

I feel more confident to try it with twilio .

video content Image generated by Wilowrid

But I know I will need to give twilio a public URL , but without deploying my code , I don't have one .

The easiest way to get a public URL is to use a tunneler like conveyor .

There's a version of conveyor for the command line , which you can get from conveyor dot cloud website .

But in this video , I will set up the V s extension In visual studio , I go to extensions , manage extensions and in the online tab , I can search for conveyor download and close visual studio in order to install it now , I followed the installation process and restarted visual studio .

When I run the application , now I can find the conveyor window for me .

video content Image generated by Wilowrid

It's down here and I can see it's giving me the local URL , a remote URL , which is good for land connections .

And we want the Internet URL , which we can get by clicking access over Internet .

The first time you click access over Internet , you'll see a request to log in and also a register button .

You can click the register button to go to the website and sign up for free .

Once connected , conveyor will set up a tunnel , and I can see here the Internet URL .

This is the free level of service , which gives you a URL that changes every time you connect to tunnelling and only works for four hour stints .

After that , you have to restart V s and get a new URL .

Pay plans are pretty cheap and more convenient .

So now I have the public URL .

I can go to the twill website and log in .

They do have a great onboarding experience , which I have already done and received an s MS number .

video content Image generated by Wilowrid

Hopefully you can do that easily enough then to configure a message webhook I go to phone numbers , manage active numbers and I click the number I want to work with .

There's a warning I was talking about earlier for the 911 .

I'm just gonna ignore that , go down to messaging configurations .

And then here's what What ?

URL will be called when um an s MS comes in so you can see here .

This was the last time I was running it .

So I'm gonna update the URL to the new public URL and save it and remember that with the free conveyor plan , the URL is subject to change .

So keep an eye on that .

And now the moment of truth , I will send an S MS .

And it worked .

If it didn't , I could put a breakpoint in my method and check if the request came in properly .

video content Image generated by Wilowrid

If it didn't , there is a message log in to that can help .

There is one issue that may not have been obvious , and that is that this code treats every incoming message regardless of the sending phone as part of the same conversation , because it only has one instance of chatbot session .

video content Image generated by Wilowrid

It's pretty easy to go back to the controller and change this line here to get or create chat bot session and pass in the number of the cooler , and we're going to return the chatbot session , and what I need is a field to store my sessions in .

So let's make it a dictionary of strings for the from number and chatbot sessions for the corresponding session .

video content Image generated by Wilowrid

Now , if the if there isn't already a session for the from number , we create one and then we return , so it will either return an existing one or create a new one .

So now at least you can have separate chats for separate users to take it to production .

Of course , you would need better error handling logging session , time out , validation to prevent abuse , et cetera .

Well , I hope you found this video helpful and feel free to like and subscribe , Of course .

And thank you for watching Bye for now .

Partnership

Attention YouTube vloggers and media companies!
Are you looking for a way to reach a wider audience and get more views on your videos?
Our innovative video to text transcribing service can help you do just that.
We provide accurate transcriptions of your videos along with visual content that will help you attract new viewers and keep them engaged. Plus, our data analytics and ad campaign tools can help you monetize your content and maximize your revenue.
Let's partner up and take your video content to the next level!
Contact us today to learn more.