whatsapp-callbackindex.tsx26 matches
8// Environment variables should be set in your Val Town val's settings
9const WHATSAPP_WEBHOOK_VERIFY_TOKEN = Deno.env.get("WHATSAPP_WEBHOOK_VERIFY_TOKEN");
10const WHATSAPP_GRAPH_API_TOKEN = Deno.env.get("WHATSAPP_GRAPH_API_TOKEN");
11const WHATSAPP_BUSINESS_ID = Deno.env.get("WHATSAPP_BUSINESS_ID");
12const GROQ_API_KEY = Deno.env.get("GROQ_API_KEY"); // For the new quick ack
1314// URL of YOUR Cloudflare Worker that orchestrates MCP calls & has /initiate-async-chat
46// --- Helper Functions ---
47async function sendWhatsAppMessage(phoneNumber, businessId, text, originalMessageId) {
48if (!WHATSAPP_GRAPH_API_TOKEN || !businessId) {
49console.error("WhatsApp API token or business ID is missing. Cannot send message.");
50return;
51}
58method: "POST",
59headers: {
60"Authorization": `Bearer ${WHATSAPP_GRAPH_API_TOKEN}`,
61"Content-Type": "application/json",
62},
90let messageToSend = "Hmm, let me look into that for you!"; // Default deferral message
9192if (!GROQ_API_KEY) {
93console.warn("[SmartAck] GROQ_API_KEY missing. Sending static deferral ack.");
94await sendWhatsAppMessage(phoneNumber, businessId, messageToSend, originalMessageId);
95return { action: "defer_to_worker", messageSent: messageToSend };
177try {
178console.log(`[SmartAck] Attempting to generate smart ack for: "${userQuery.substring(0, 50)}..." with ${QUICK_ACK_MODEL}`);
179const groqResponse = await fetch("https://api.groq.com/openai/v1/chat/completions", {
180method: "POST",
181headers: {
182"Authorization": `Bearer ${GROQ_API_KEY}`,
183"Content-Type": "application/json",
184},
198if (!groqResponse.ok) {
199const errorText = await groqResponse.text().catch(() => "Could not retrieve error text");
200console.error(`[SmartAck] Error from Groq API: ${groqResponse.status} ${groqResponse.statusText}`, errorText);
201// Fallback to deferral message
202} else {
308console.log(`[initiateChat] Sending to CF Orchestrator (${CLOUDFLARE_WORKER_ORCHESTRATOR_URL}/initiate-async-chat) for ${userIdentifier}:`, JSON.stringify(mcpPayload, null, 2));
309310const mcpApiResponse = await fetch(`${CLOUDFLARE_WORKER_ORCHESTRATOR_URL}/initiate-async-chat`, {
311method: "POST",
312headers: { "Content-Type": "application/json" },
314});
315316if (!mcpApiResponse.ok) {
317const errorText = await mcpApiResponse.text();
318console.error(`[initiateChat] Error from CF Orchestrator for ${clientRequestId}: ${mcpApiResponse.status} ${mcpApiResponse.statusText}`, errorText);
319await sendWhatsAppMessage(userIdentifier, businessPhoneNumberId, "Sorry, I couldn't start a session with the assistant.", originalMessageId);
320return { error: true, details: errorText };
321} else {
322const mcpResponseData = await mcpApiResponse.json();
323console.log(`[initiateChat] Response from CF Orchestrator for ${clientRequestId} (Status ${mcpApiResponse.status}):`, mcpResponseData);
324return { error: false, data: mcpResponseData };
325}
458console.log(`[PROCESS] Smartly Acknowledging for ${userPhoneNumber} (hash: ${queryHash || 'N/A'}). Effective Query for Ack: "${userQueryForSmartAck.substring(0,50)}..."`);
459460if (WHATSAPP_GRAPH_API_TOKEN && business_phone_number_id) {
461// 1. Mark as read (sends blue check for the CURRENT incoming message)
462try {
463console.log(`Marking read for ${userPhoneNumber} (msg_id: ${currentMessageId})`);
464const markReadResp = await fetch(`https://graph.facebook.com/v22.0/${business_phone_number_id}/messages`, {
465method: "POST", headers: {"Authorization": `Bearer ${WHATSAPP_GRAPH_API_TOKEN}`, "Content-Type": "application/json"},
466body: JSON.stringify({messaging_product: "whatsapp", status: "read", message_id: currentMessageId}),
467});
494495} else {
496console.error("WHATSAPP_GRAPH_API_TOKEN or business_phone_number_id missing.");
497}
498} else if (waMessage) {
534// This means for direct GET, you'd need to monitor this Val's `/cf-worker-updates` or provide one.
535536const userIdentifier = "direct_api_user_" + crypto.randomUUID();
537const clientRequestId = crypto.randomUUID(); // Not used by initiateChat directly, but good practice
538653// Handle sending the welcome message
654app.post("/send-welcome", async (c) => {
655if (!WHATSAPP_GRAPH_API_TOKEN || !WHATSAPP_BUSINESS_ID) {
656console.error("WhatsApp API token or Business ID is not configured.");
657return c.json({ error: "Server configuration error: WhatsApp API token or Business ID is missing." }, 500);
658}
659686method: "POST",
687headers: {
688"Authorization": `Bearer ${WHATSAPP_GRAPH_API_TOKEN}`,
689"Content-Type": "application/json",
690},
697);
698699const responseData = await response.json().catch(() => ({ error: { message: "Received non-JSON response from WhatsApp API" } }));
700701if (!response.ok) {
702console.error(`Error sending custom message to ${phoneNumber}: ${response.status} ${response.statusText}`, responseData);
703return c.json({ error: `WhatsApp API Error: ${responseData.error?.message || response.statusText}`, details: responseData }, response.status > 0 ? response.status : 500);
704}
705
TownieLoginRoute.tsx8 matches
8const { isAuthenticated, authenticate, error } = useAuth();
9const [tokenValue, setTokenValue] = useState("");
10const [apiKey, setApiKey] = useState("");
11// const [invalid, setInvalid] = useState(""); // TODO
1213const handleSubmit = (e) => {
14e.preventDefault();
15authenticate(tokenValue, apiKey);
16};
1736>
37<div>
38<label htmlFor="valtown-token" className="label">Val Town API Token</label>
39<div style={{ fontSize: "0.8em", color: "#666" }}>
40<p>
41<a href="https://www.val.town/settings/api/new" target="_blank" rel="noreferrer">
42Create a Val Town token here
43</a>
58</div>
59<div>
60<label htmlFor="anthropic-api-key" className="label">Anthropic API Key (optional)</label>
61<input
62type="password"
63id="anthropic-api-key"
64name="anthropic-key"
65value={apiKey}
66onChange={e => {
67setApiKey(e.target.value);
68}}
69/>
122async function getDiscussionPosts(discussionId: string): Promise<PostT[]> {
123// Used to get the list of post id's for the discussion.
124const discussionRes = await fetch(`${server}/api/discussions/${discussionId}`);
125const discussionResJson = await discussionRes.json();
126134135await Promise.all(chunks.map(async (c: string[]) => {
136const postRes = await fetch(`${server}/api/posts?filter[id]=${c.join(",")}`);
137const postJson = await postRes.json();
138
222223try {
224const response = await fetch("/api/share", {
225method: "POST",
226headers: {
GitHub-Release-Notesgithub.ts20 matches
26let hasMorePages = true;
2728// GitHub API headers
29const headers = {
30"Accept": "application/vnd.github.v3+json",
31"Authorization": `Bearer ${token}`,
32"User-Agent": "GitHub-Release-Notes-Generator",
33"X-GitHub-Api-Version": "2022-11-28",
34};
3539while (hasMorePages) {
40const url =
41`https://api.github.com/repos/${owner}/${repo}/commits?since=${sinceISO}&until=${untilISO}&per_page=100&page=${page}`;
42console.log(`Fetching page ${page}...`);
4346if (!response.ok) {
47const error = await response.text();
48throw new Error(`GitHub API error: ${response.status} - ${error}`);
49}
5077prNumber: number,
78): Promise<GitHubPR | null> {
79const url = `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}`;
8081const response = await fetch(url, {
84"Authorization": `Bearer ${token}`,
85"User-Agent": "GitHub-Release-Notes-Generator",
86"X-GitHub-Api-Version": "2022-11-28",
87},
88});
94if (!response.ok) {
95const error = await response.text();
96throw new Error(`GitHub API error: ${response.status} - ${error}`);
97}
98109commitSha: string,
110): Promise<GitHubPR[]> {
111const url = `https://api.github.com/repos/${owner}/${repo}/commits/${commitSha}/pulls`;
112113const response = await fetch(url, {
116"Authorization": `Bearer ${token}`,
117"User-Agent": "GitHub-Release-Notes-Generator",
118"X-GitHub-Api-Version": "2022-11-28",
119},
120});
126if (!response.ok) {
127const error = await response.text();
128throw new Error(`GitHub API error: ${response.status} - ${error}`);
129}
130133134/**
135* Fetches issues referenced by a commit or PR using GitHub API
136*/
137async function fetchIssueReferences(
147// If we have a PR number, check for issues closed by the PR
148if (prNumber) {
149const prIssuesUrl = `https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/commits`;
150151const prResponse = await fetch(prIssuesUrl, {
154"Authorization": `Bearer ${token}`,
155"User-Agent": "GitHub-Release-Notes-Generator",
156"X-GitHub-Api-Version": "2022-11-28",
157},
158});
161const prCommits = await prResponse.json();
162163// Extract issue numbers from commit messages using the GitHub API pattern
164for (const commit of prCommits) {
165// Look for "Fixes #X", "Closes #X", "Resolves #X" patterns that GitHub recognizes
180// Also check the timeline of the PR to find linked issues
181if (prNumber) {
182const timelineUrl = `https://api.github.com/repos/${owner}/${repo}/issues/${prNumber}/timeline`;
183184const timelineResponse = await fetch(timelineUrl, {
187"Authorization": `Bearer ${token}`,
188"User-Agent": "GitHub-Release-Notes-Generator",
189"X-GitHub-Api-Version": "2022-11-28",
190},
191});
213* Fetches commits and their associated PRs
214* Includes progress tracking for large repositories
215* Uses GitHub API to find PR and issue references
216*/
217export async function fetchCommitsWithPRs(
231232// Process commits in batches to not get rate limited
233const BATCH_SIZE = 5; // Reduced batch size since we're making more API calls per commit
234for (let i = 0; i < commits.length; i += BATCH_SIZE) {
235const batch = commits.slice(i, i + BATCH_SIZE);
236const batchPromises = batch.map(async (commit) => {
237// Use GitHub API to find associated PRs for this commit
238const associatedPRs = await fetchPRsForCommit(token, owner, repo, commit.sha);
239245pr = associatedPRs[0]; // Use the first PR if multiple exist
246247// Get issue references using the GitHub API
248const prIssueRefs = await fetchIssueReferences(token, owner, repo, commit.sha, pr.number);
249issueRefs = [...prIssueRefs];
GitHub-Release-Notesllm.ts1 match
186`;
187188// Call the OpenAI API
189const completion = await openai.chat.completions.create({
190model: "gpt-4o",
Townieuser-summary.ts1 match
20SUM(num_images) as total_images
21FROM ${USAGE_TABLE}
22WHERE our_api_token = 1
23GROUP BY user_id, username
24ORDER BY total_price DESC
TownieuseProject.tsx2 matches
2import { useAuth } from "./useAuth.tsx";
34const PROJECT_ENDPOINT = "/api/project";
5const FILES_ENDPOINT = "/api/project-files";
67export function useProject (projectId: string, branchId?: string) {
TownieuseProjects.tsx1 match
2import { useAuth } from "./useAuth.tsx";
34const ENDPOINT = "/api/projects-loader";
56export function useProjects () {
TownieuseCreateProject.tsx1 match
3import { useAuth } from "./useAuth.tsx";
45const ENDPOINT = "/api/create-project";
67export function useCreateProject () {