diff --git a/docs/assets/bongo.png b/docs/assets/bongo.png deleted file mode 100644 index 4c59ffa..0000000 Binary files a/docs/assets/bongo.png and /dev/null differ diff --git a/docs/assets/build_a_bot.gif b/docs/assets/build_a_bot.gif deleted file mode 100644 index c80db63..0000000 Binary files a/docs/assets/build_a_bot.gif and /dev/null differ diff --git a/docs/assets/card_nosubmit.gif b/docs/assets/card_nosubmit.gif deleted file mode 100644 index ed91917..0000000 Binary files a/docs/assets/card_nosubmit.gif and /dev/null differ diff --git a/docs/assets/deno/deno_addcode.png b/docs/assets/deno/deno_addcode.png deleted file mode 100644 index 36fc3fc..0000000 Binary files a/docs/assets/deno/deno_addcode.png and /dev/null differ diff --git a/docs/assets/deno/deno_playground.png b/docs/assets/deno/deno_playground.png deleted file mode 100644 index f9e455a..0000000 Binary files a/docs/assets/deno/deno_playground.png and /dev/null differ diff --git a/docs/assets/deno/deno_webhook.png b/docs/assets/deno/deno_webhook.png deleted file mode 100644 index da14863..0000000 Binary files a/docs/assets/deno/deno_webhook.png and /dev/null differ diff --git a/docs/assets/deno/set_secrets.png b/docs/assets/deno/set_secrets.png deleted file mode 100644 index 5df12bc..0000000 Binary files a/docs/assets/deno/set_secrets.png and /dev/null differ diff --git a/docs/assets/deno/set_secrets_saved.png b/docs/assets/deno/set_secrets_saved.png deleted file mode 100644 index 4bb1741..0000000 Binary files a/docs/assets/deno/set_secrets_saved.png and /dev/null differ diff --git a/docs/assets/download.png b/docs/assets/download.png deleted file mode 100644 index d35ee71..0000000 Binary files a/docs/assets/download.png and /dev/null differ diff --git a/docs/assets/download2.png b/docs/assets/download2.png deleted file mode 100644 index 05da32b..0000000 Binary files a/docs/assets/download2.png and /dev/null differ diff --git a/docs/assets/regen_token.gif b/docs/assets/regen_token.gif deleted file mode 100644 index e9bfa70..0000000 Binary files a/docs/assets/regen_token.gif and /dev/null differ diff --git a/docs/assets/speedy.jpeg b/docs/assets/speedy.jpeg deleted file mode 100644 index 060f7d9..0000000 Binary files a/docs/assets/speedy.jpeg and /dev/null differ diff --git a/docs/examples/deno/README.md b/docs/examples/deno/README.md index c3b8652..376d23b 100644 --- a/docs/examples/deno/README.md +++ b/docs/examples/deno/README.md @@ -16,192 +16,11 @@ Note: The steps below assume you have a **[working WebEx account](https://develo - Press the blue "New Playground" button -![sb](./../../assets/deno/deno_playground.png) +![sb](https://raw.githubusercontent.com/valgaze/speedybot-utils/main/assets/various/deno/deno_playground.png) -Note: There are many (better) ways to setup Deno, but for now we can just use the Playground and copy/paste everything in the panel below: +Note: There are many (better) ways to setup Deno, but for now we can just use the Playground and copy/paste everything in **[index.ts](./index.ts)** -## 2a) Copy the code below into the playground - -
💡 Tap here to open code (copy me) - -```ts -import { SpeedyBot, logoRoll } from "https://cdn.skypack.dev/speedybot@latest"; - -Deno.serve(async (req: Request) => { - if (req.method === "GET") { - return new Response(`Server is running ${new Date()} ${logoRoll()}`); - } - - const CONFIG = { - token: Deno.env.get("token"), - webhookSecret: Deno.env.get("webhookSecret"), - }; - - if (req.method === "POST") { - const signature = - req.headers.get("x-spark-signature") || - req.headers.get("X-Spark-Signature"); - const Bot = new SpeedyBot(CONFIG.token); - const json = await req.json(); - - if (signature) { - const validateWebhook = async ( - requestData: T, - secret: string, - signature: string - ): Promise => { - const stringyBody = - typeof requestData !== "string" - ? JSON.stringify(requestData) - : requestData; - const algo = { - name: "HMAC", - hash: "SHA-1", - }; - const enc = { - name: "UTF-8", - }; - const hmacKey = await crypto.subtle.importKey( - "raw", - new TextEncoder().encode(secret), - algo, - false, - ["sign"] - ); - const hmacData = await crypto.subtle.sign( - algo, - hmacKey, - new TextEncoder().encode(stringyBody) - ); - - const bufferToHex = (buffer: ArrayBufferLike) => { - return Array.prototype.map - .call(new Uint8Array(buffer), (x) => - ("00" + x.toString(16)).slice(-2) - ) - .join(""); - }; - const hmacDataHex = bufferToHex(hmacData); - return hmacDataHex === signature; - }; - const proceed = await validateWebhook( - json, - CONFIG.webhookSecret, - signature - ); - if (proceed === false) { - return new Response("Webhook Secret Rejected"); - } - } - - Bot.exact("$clear", async ($) => { - await $.clearScreen(); - return $.end; - }); - - Bot.addStep(async ($) => { - if ($.data && !$.data.showCard) { - const dataSnippet = $.buildDataSnippet($.data); - await $.send(`This data was submitted:`); - await $.send(dataSnippet); - return $.end; - } else { - return $.next; - } - }); - - Bot.addStep(async ($) => { - await $.send(`helllllooo, you said "${$.text}"`); - const card = $.card().survey([ - { - type: "text", - question: "What is the name of your company?", - id: "company_name", - }, - { - type: "text", - question: "Describe the service performed by the vendor.", - id: "service_type", - }, - { - type: "picker-date", - question: "When was the service provided?", - id: "service_date", - }, - { - type: "single-select", - question: "How would you rate the quality of service?", - choices: ["Excellent", "Good", "Average", "Poor", "Very poor"], - id: "service_quality", - }, - { - type: "multi-select", - question: "What were the highlights of the service?", - choices: [ - "Communication", - "Punctuality", - "Time to Resolution", - "Friendliness", - "Cost", - ], - id: "service_highlights", - }, - { - type: "single-select", - question: - "Would you consider using our services again in the future?", - choices: [ - "Definitely", - "Probably", - "Not sure", - "Probably not", - "Definitely not", - ], - id: "future_use", - }, - { - type: "textarea", - question: - "Please provide any other comments or suggestions for improvement.", - id: "other_comments", - }, - { - type: "picker-time", - question: "What time of day is preferable for future contact?", - id: "preferred_contact_time", - }, - { - type: "picker-dropdown", - question: "Preferred method of communication for future updates?", - choices: ["Email", "Phone", "Text"], - id: "communication_method", - }, - ]); - - await $.send(card); - - return $.next; - }); - - Bot.captureError(async (payload) => { - const { roomId } = payload; - if (roomId) { - await Bot.sendTo( - roomId, - `Whoops, there was a problem: ${payload.message}` - ); - } - }); - - await Bot.runMiddleware(json); - } - return new Response(`Request processed`); // webhooks should return **something** -}); -``` - -
- -![sb](./../../assets/deno/deno_addcode.png) +![sb](https://raw.githubusercontent.com/valgaze/speedybot-utils/main/assets/various/deno/deno_addcode.png) ## 3) Expose your bot access token to Deno @@ -211,51 +30,24 @@ Deno.serve(async (req: Request) => { - If you're using a webhook secret (which you should), add it as a secret `webhookSecret` -![sb](./../../assets/deno/set_secrets.png) +![sb](https://raw.githubusercontent.com/valgaze/speedybot-utils/main/assets/various/deno/set_secrets.png) Verify you hit save underneath each secret you add to the playground -![sb](./../../assets/deno/set_secrets_saved.png) +![sb](https://raw.githubusercontent.com/valgaze/speedybot-utils/main/assets/various/deno/set_secrets_saved.png) ## 4) Register your webhook -- Right now if you try to interact with your "deployed" agent nothing happens, nobody is "home" to answer the knock at the door +- Grab your playground's URL (it'll be a strange name like https://noisy-bongodrum-75.deno.dev) and register your webhook using SpeedyBot Garage -- Grab your playground's URL (it'll likely be a strange/random name like https://noisy-bongodrum-75.deno.dev) and register your webhook using SpeedyBot Garage +- Visit https://speedybot.js.org/garage and select **webhooks** and add your URL and optional (though highly recommeneded) webhook secret -- Hop on over to the **[SpeedyBot Garage (https://speedybot.js.org/garage)](https://speedybot.js.org/garage)**, enter your access token, select the Webhooks tab, and then **Add New Webhook** and add your Worker's URL and (optionally but hopefully) a webhook secret +- If all went well you should see this and your bot is up and running on Deno! - +![sb](https://raw.githubusercontent.com/valgaze/speedybot-utils/main/assets/various/deno/deno_webhook.png) ## 5) Take it for a spin After connecting webhooks, send your bot a message to take it for a spin - - - + diff --git a/docs/new.md b/docs/new.md index 305a31a..7f8419d 100644 --- a/docs/new.md +++ b/docs/new.md @@ -13,7 +13,7 @@ Follow the quick setup below to go from zero to a SpeedyBot running on your loca The flow to get a token will look roughly like this: settings page https://speedybot.js.org/new - - + diff --git a/examples/deno/README.md b/examples/deno/README.md index 5e4b5eb..376d23b 100644 --- a/examples/deno/README.md +++ b/examples/deno/README.md @@ -16,11 +16,11 @@ Note: The steps below assume you have a **[working WebEx account](https://develo - Press the blue "New Playground" button -![sb](./../../docs/assets/deno/deno_playground.png) +![sb](https://raw.githubusercontent.com/valgaze/speedybot-utils/main/assets/various/deno/deno_playground.png) Note: There are many (better) ways to setup Deno, but for now we can just use the Playground and copy/paste everything in **[index.ts](./index.ts)** -![sb](./../../docs/assets/deno/deno_addcode.png) +![sb](https://raw.githubusercontent.com/valgaze/speedybot-utils/main/assets/various/deno/deno_addcode.png) ## 3) Expose your bot access token to Deno @@ -30,11 +30,11 @@ Note: There are many (better) ways to setup Deno, but for now we can just use th - If you're using a webhook secret (which you should), add it as a secret `webhookSecret` -![sb](./../../docs/assets/deno/set_secrets.png) +![sb](https://raw.githubusercontent.com/valgaze/speedybot-utils/main/assets/various/deno/set_secrets.png) Verify you hit save underneath each secret you add to the playground -![sb](./../../docs/assets/deno/set_secrets_saved.png) +![sb](https://raw.githubusercontent.com/valgaze/speedybot-utils/main/assets/various/deno/set_secrets_saved.png) ## 4) Register your webhook @@ -44,7 +44,7 @@ Verify you hit save underneath each secret you add to the playground - If all went well you should see this and your bot is up and running on Deno! -![sb](./../../docs/assets/deno/deno_webhook.png) +![sb](https://raw.githubusercontent.com/valgaze/speedybot-utils/main/assets/various/deno/deno_webhook.png) ## 5) Take it for a spin diff --git a/examples/llm-stream/settings/bot.ts b/examples/llm-stream/settings/bot.ts index f6bf259..e5ea317 100644 --- a/examples/llm-stream/settings/bot.ts +++ b/examples/llm-stream/settings/bot.ts @@ -1,9 +1,9 @@ import { SpeedyBot } from "speedybot"; import { OpenAIStream } from "./llm-stream"; -const Bot = new SpeedyBot(); +const Bot = new SpeedyBot<"OPEN_AI_KEY">(); -const showDebug = false; +const showDebug = false; // show debug info flag Bot.exact("$clear", async ($) => { await $.clearScreen(); @@ -34,6 +34,7 @@ Bot.addStep(async ($) => { if ($.text) { const rootMsg = await $.reply("Thinking..."); OpenAIStream( + Bot.getSecret("OPEN_AI_KEY") as string, $.text, async (curr, isFinal) => { await $.edit(rootMsg, curr); diff --git a/examples/llm-stream/settings/launch.ts b/examples/llm-stream/settings/launch.ts index 494fb6e..b08a228 100644 --- a/examples/llm-stream/settings/launch.ts +++ b/examples/llm-stream/settings/launch.ts @@ -4,11 +4,22 @@ import { config } from "dotenv"; import { resolve } from "path"; config({ path: resolve(__dirname, "..", ".env") }); import { announceExit, websocketLauncher } from "../util"; +import Bot from "./bot"; process.on("exit", announceExit); -import Bot from "./bot"; +// Assert these are available on process.env yadda-yadda, otherwise would have to `process.env.BOT_TOKEN as string` +declare global { + namespace NodeJS { + interface ProcessEnv { + BOT_TOKEN: string; + OPEN_AI_KEY: string; + } + } +} +// Add secrets Bot.setToken(process.env.BOT_TOKEN as string); +Bot.addSecret("OPEN_AI_KEY", process.env.OPEN_AI_KEY); // Pass in your SpeedyBot websocketLauncher(Bot).catch((e) => console.log("##", e)); diff --git a/examples/llm-stream/settings/llm-stream.ts b/examples/llm-stream/settings/llm-stream.ts index 5b65bcd..ca434c2 100644 --- a/examples/llm-stream/settings/llm-stream.ts +++ b/examples/llm-stream/settings/llm-stream.ts @@ -18,6 +18,7 @@ type Chunk = { }; export const OpenAIStream = async ( + apiKey: string, prompt: string, cb: (currentChunk: string, isFinale: boolean) => void, config: Partial = {}, @@ -32,7 +33,7 @@ export const OpenAIStream = async ( content: prompt, }), }, - { apiKey: process.env.OPEN_AI_KEY } + { apiKey } ); // type handler = (chunk: string, isFinished?: boolean) => void; diff --git a/examples/speedybot-starter/settings/launch.ts b/examples/speedybot-starter/settings/launch.ts index 494fb6e..f6ebfd2 100644 --- a/examples/speedybot-starter/settings/launch.ts +++ b/examples/speedybot-starter/settings/launch.ts @@ -4,9 +4,8 @@ import { config } from "dotenv"; import { resolve } from "path"; config({ path: resolve(__dirname, "..", ".env") }); import { announceExit, websocketLauncher } from "../util"; -process.on("exit", announceExit); - import Bot from "./bot"; +process.on("exit", announceExit); Bot.setToken(process.env.BOT_TOKEN as string); diff --git a/examples/voiceflow-kb/settings/launch.ts b/examples/voiceflow-kb/settings/launch.ts index 494fb6e..7cf5ab6 100644 --- a/examples/voiceflow-kb/settings/launch.ts +++ b/examples/voiceflow-kb/settings/launch.ts @@ -4,11 +4,22 @@ import { config } from "dotenv"; import { resolve } from "path"; config({ path: resolve(__dirname, "..", ".env") }); import { announceExit, websocketLauncher } from "../util"; +import Bot from "./bot"; process.on("exit", announceExit); -import Bot from "./bot"; +// Assert these are available on process.env yadda-yadda, otherwise would have to `process.env.BOT_TOKEN as string` +declare global { + namespace NodeJS { + interface ProcessEnv { + BOT_TOKEN: string; + VOICEFLOW_API_KEY: string; + } + } +} +// Add secrets Bot.setToken(process.env.BOT_TOKEN as string); +Bot.addSecret("VOICEFLOW_API_KEY", process.env.VOICEFLOW_API_KEY); // Pass in your SpeedyBot websocketLauncher(Bot).catch((e) => console.log("##", e)); diff --git a/examples/voiceflow/settings/launch.ts b/examples/voiceflow/settings/launch.ts index 494fb6e..7cf5ab6 100644 --- a/examples/voiceflow/settings/launch.ts +++ b/examples/voiceflow/settings/launch.ts @@ -4,11 +4,22 @@ import { config } from "dotenv"; import { resolve } from "path"; config({ path: resolve(__dirname, "..", ".env") }); import { announceExit, websocketLauncher } from "../util"; +import Bot from "./bot"; process.on("exit", announceExit); -import Bot from "./bot"; +// Assert these are available on process.env yadda-yadda, otherwise would have to `process.env.BOT_TOKEN as string` +declare global { + namespace NodeJS { + interface ProcessEnv { + BOT_TOKEN: string; + VOICEFLOW_API_KEY: string; + } + } +} +// Add secrets Bot.setToken(process.env.BOT_TOKEN as string); +Bot.addSecret("VOICEFLOW_API_KEY", process.env.VOICEFLOW_API_KEY); // Pass in your SpeedyBot websocketLauncher(Bot).catch((e) => console.log("##", e)); diff --git a/package.json b/package.json index 1f0e0c9..fe53c2e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "speedybot", - "version": "2.0.2", + "version": "2.0.3", "main": "dist/cjs/src/index.js", "module": "dist/mjs/src/index.js", "types": "dist/mjs/src/index.d.ts", diff --git a/src/cards.ts b/src/cards.ts index 0454af6..5c84e2d 100644 --- a/src/cards.ts +++ b/src/cards.ts @@ -453,8 +453,9 @@ export class SpeedyCard { } addHeader(text: string, config: HeaderConfig = {}) { + const hasURL = config.iconURL?.includes("http"); + const textPayload = { - width: "stretch", items: [ { type: "TextBlock", @@ -475,18 +476,30 @@ export class SpeedyCard { }; const iconPayload = config.iconURL - ? { - width: "32px", - items: [ - { - type: "Image", - horizontalAlignment: config.iconAlignment ?? "Left", - url: config.iconURL, - ...(config.iconRound && { style: "person" }), - width: `${config.iconWidth ?? "16"}px`, - }, - ], - } + ? hasURL + ? { + width: "32px", + items: [ + { + type: "Image", + horizontalAlignment: config.iconAlignment ?? "Left", + url: config.iconURL, + ...(config.iconRound && { style: "person" }), + width: `${config.iconWidth ?? "16"}px`, + }, + ], + } + : { + type: "Column", + width: "auto", + items: [ + { + type: "TextBlock", + text: config.iconURL, + verticalContentAlignment: "Center", + }, + ], + } : null; const headerPayload: { @@ -494,6 +507,7 @@ export class SpeedyCard { columns: (typeof textPayload | typeof iconPayload)[]; } = { type: "ColumnSet", + columns: config.rtl ? [textPayload, iconPayload].filter(Boolean) : [iconPayload, textPayload].filter(Boolean),