Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/**
* This val creates a webhook endpoint that receives text messages and sends SMS replies using the TextBelt API.
* It uses blob storage to keep track of message history and conversation state.
* The TextBelt API is used for sending SMS messages without requiring an API key.
* The conversation history is stored as an array of message objects containing sender, content, date, and phone number.
* OpenAI's GPT-4 is used to generate contextual responses based on the conversation history.
*/
import { blob } from "https://esm.town/v/std/blob";
import { OpenAI } from "https://esm.town/v/std/openai";
import { Buffer } from "node:buffer";
import { createHmac, timingSafeEqual } from "node:crypto";
// Change this to your own val URL
const selfURL = "https://cephalization-smsjournalertextrelay.web.val.run";
// Set these up
// This is the code that users must enter to register on the registration app
const systemRegistrationKey = Deno.env.get("smsjournalerregistrationkey");
// This is the secret shared with the registration app in order to register users
const sharedSecret = Deno.env.get("smsjournalersecret");
// This is the key for the TextBelt API
const textbeltKey = Deno.env.get("textbeltkey");
// Define the structure for a message object
interface Message {
sender: string;
content: string;
date: string;
phone?: string; // Optional phone number field
}
const makeBlobKey = (phone: string) => `conversation_${phone}`;
// this is either broken or textbelt is not sending me the right stuff
// this is implemented per the textbelt docs but i cant verify the sig...
function validateTextbeltRequestSignature(
apiKey: string,
timestamp: string,
requestSignature: string,
requestPayload: string,
) {
const mySignature = createHmac("sha256", apiKey)
.update(
timestamp + requestPayload,
)
.digest("hex");
return timingSafeEqual(
Buffer.from(requestSignature),
Buffer.from(mySignature),
);
}
// Helper function to send SMS using TextBelt API
async function sendSMS(phone: string, message: string) {
const response = await fetch("https://textbelt.com/text", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
phone,
message,
key: textbeltKey,
// this val url
replyWebhookUrl: selfURL,
}),
});
return response.json();
}
// Helper function to get conversation history
async function getConversationHistory(phone: string): Promise<Message[]> {
const key = makeBlobKey(phone);
const history = await blob.getJSON(key) as Message[] | null;
return history || [];
}
// Helper function to update conversation history
async function updateConversationHistory(phone: string, message: Message) {
const key = makeBlobKey(phone);
const history = await getConversationHistory(phone);
history.push(message);
await blob.setJSON(key, history);
}
// Helper function to generate AI response
async function generateAIResponse(history: Message[]): Promise<string> {
const openai = new OpenAI();
const messages: { role: "user" | "assistant" | "system"; content: string }[] = history.map(msg => ({
role: msg.sender === "User" ? "user" : "assistant",
content: msg.content,
}));
messages.unshift({
role: "system",
content:
"You are an AI assistant communicating via SMS. Keep your responses concise and under 160 characters. Your task is to help the user organize their daily work activities and provide brief, helpful responses.",
});
const completion = await openai.chat.completions.create({
messages,
cephalization-smsjournalertextrelay.web.val.run
September 7, 2024