├── .env.sample ├── .eslintignore ├── integrations └── symbl │ ├── types.d.ts │ ├── config.ts │ └── utils.ts ├── .prettierrc ├── .prettierignore ├── serverless.yml ├── next-env.d.ts ├── docs ├── imgs │ ├── audio.png │ ├── call.png │ ├── chat.png │ ├── fetch.png │ ├── nodeui.png │ ├── result.png │ ├── submit.png │ ├── text.png │ ├── text2.png │ ├── text3.png │ ├── video.png │ ├── notlogged.png │ ├── submitbutton.png │ ├── audioprocessed.png │ ├── conversational.png │ ├── conversational2.png │ ├── videoprocessed.png │ └── audioconversational.png ├── how-to-conversation-data.md ├── how-to-nextjs-telephony-rest.md ├── how-to-text-processing.md ├── how-to-audio-processing.md ├── how-to-video-processing.md └── how-to-nextjs-node-sdk.md ├── .babelrc ├── next.config.js ├── components ├── Divider.tsx ├── TypingIntro.tsx ├── Link.tsx ├── ConnectionLabel.tsx ├── Container.tsx ├── Button.tsx ├── ParamsToggle.tsx ├── Card.tsx ├── index.tsx ├── JsonPayloadCard.tsx ├── ProtectedPage.tsx ├── UrlFileToggle.tsx ├── VideoMessages.tsx ├── FileOrUrlInput.tsx ├── ConversationCard.tsx ├── AsyncParamsUI.tsx ├── ChatComponent.tsx ├── PhoneConfigurations.tsx └── Header.tsx ├── styles └── tailwind.base.css ├── tailwind.config.js ├── .gitignore ├── tsconfig.json ├── pages ├── api │ ├── subscribeToPhoneEvents.ts │ └── call.ts ├── _document.tsx ├── _app.tsx ├── text │ └── index.tsx ├── phone │ └── index.tsx ├── index.tsx ├── conversations │ └── index.tsx ├── audio │ └── index.tsx └── video │ └── index.tsx ├── .eslintrc.json ├── package.json ├── README.md └── hooks └── index.tsx /.env.sample: -------------------------------------------------------------------------------- 1 | APP_ID= 2 | APP_SECRET= -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules/* 2 | **/out/* 3 | **/.next/* 4 | -------------------------------------------------------------------------------- /integrations/symbl/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'symbl-node' 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | yarn.lock 4 | package-lock.json 5 | public 6 | -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | symblaiDemoApp: 2 | component: '@sls-next/serverless-component@1.18.0' 3 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /docs/imgs/audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/audio.png -------------------------------------------------------------------------------- /docs/imgs/call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/call.png -------------------------------------------------------------------------------- /docs/imgs/chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/chat.png -------------------------------------------------------------------------------- /docs/imgs/fetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/fetch.png -------------------------------------------------------------------------------- /docs/imgs/nodeui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/nodeui.png -------------------------------------------------------------------------------- /docs/imgs/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/result.png -------------------------------------------------------------------------------- /docs/imgs/submit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/submit.png -------------------------------------------------------------------------------- /docs/imgs/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/text.png -------------------------------------------------------------------------------- /docs/imgs/text2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/text2.png -------------------------------------------------------------------------------- /docs/imgs/text3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/text3.png -------------------------------------------------------------------------------- /docs/imgs/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/video.png -------------------------------------------------------------------------------- /docs/imgs/notlogged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/notlogged.png -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel", "@emotion/babel-preset-css-prop"], 3 | "plugins": ["macros"] 4 | } 5 | -------------------------------------------------------------------------------- /docs/imgs/submitbutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/submitbutton.png -------------------------------------------------------------------------------- /docs/imgs/audioprocessed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/audioprocessed.png -------------------------------------------------------------------------------- /docs/imgs/conversational.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/conversational.png -------------------------------------------------------------------------------- /docs/imgs/conversational2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/conversational2.png -------------------------------------------------------------------------------- /docs/imgs/videoprocessed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/videoprocessed.png -------------------------------------------------------------------------------- /docs/imgs/audioconversational.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symblai/nextjs-symblai-demo/HEAD/docs/imgs/audioconversational.png -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | 3 | module.exports = { 4 | env: { 5 | APP_ID: process.env.APP_ID, 6 | APP_SECRET: process.env.APP_SECRET 7 | 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /components/Divider.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css' 2 | import tw from '@tailwindcssinjs/macro' 3 | 4 | export const Divider = () => ( 5 |
6 | ) 7 | -------------------------------------------------------------------------------- /styles/tailwind.base.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | 4 | @tailwind utilities; 5 | .bg-black-alt { 6 | background: #191919; 7 | } 8 | .text-black-alt { 9 | color: #191919; 10 | } 11 | .border-black-alt { 12 | border-color: #191919; 13 | } 14 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const defaultTheme = require('tailwindcss/defaultTheme') 2 | 3 | module.exports = { 4 | theme: { 5 | extend: { 6 | fontFamily: { 7 | sans: ['Inter var', ...defaultTheme.fontFamily.sans], 8 | }, 9 | }, 10 | }, 11 | variants: {}, 12 | plugins: [require('@tailwindcss/ui')], 13 | } 14 | -------------------------------------------------------------------------------- /integrations/symbl/config.ts: -------------------------------------------------------------------------------- 1 | export const apiBase = 'https://api.symbl.ai' 2 | export const appId = process.env.APP_ID as string 3 | export const appSecret = process.env.APP_SECRET as string 4 | export const intents = [ 5 | { intent: 'answering_machine' }, 6 | { intent: 'interested' }, 7 | { intent: 'not_interested' }, 8 | { intent: 'do_not_call' }, 9 | ] 10 | -------------------------------------------------------------------------------- /components/TypingIntro.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from '@emotion/css' 3 | import tw from '@tailwindcssinjs/macro' 4 | import Typist from 'react-typist' 5 | 6 | export const TypingIntro: React.FC = ({ children }) => ( 7 |
8 | {children} 9 |
10 | ) 11 | -------------------------------------------------------------------------------- /components/Link.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from '@emotion/css' 3 | import tw from '@tailwindcssinjs/macro' 4 | 5 | export const Link: React.FC<{ href: string }> = ({ href, children }) => ( 6 |
7 | {children} 8 | 9 | 10 | here 11 | 12 |
13 | ) 14 | -------------------------------------------------------------------------------- /components/ConnectionLabel.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from '@emotion/css' 3 | import tw from '@tailwindcssinjs/macro' 4 | 5 | import { useConnection } from '../hooks' 6 | 7 | export const ConnectionLabel = () => { 8 | const { connectionId } = useConnection() 9 | return connectionId ? ( 10 | 13 | ) : null 14 | } 15 | -------------------------------------------------------------------------------- /components/Container.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from '@emotion/css' 3 | import tw from '@tailwindcssinjs/macro' 4 | 5 | export const Container = ({ children }: any) => ( 6 |
7 |
12 | {children} 13 |
14 |
15 | ) 16 | -------------------------------------------------------------------------------- /components/Button.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/css' 2 | import tw from '@tailwindcssinjs/macro' 3 | 4 | export const Button = ({ children, ...props }: any) => ( 5 | 13 | ) 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | .serverless 36 | .serverless_nextjs 37 | .env -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve" 16 | }, 17 | "exclude": ["node_modules", ".next", "out", "docs"], 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js", "components"] 19 | } 20 | -------------------------------------------------------------------------------- /components/ParamsToggle.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { css } from '@emotion/css' 3 | import tw from '@tailwindcssinjs/macro' 4 | 5 | export const ParamsToggle = ({ children }: any) => { 6 | const [isShown, setIsShown] = useState(false) 7 | return ( 8 |
9 |
setIsShown(!isShown)} 14 | > 15 | Advanced Params 16 |
17 |
{isShown && children}
18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /components/Card.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from '@emotion/css' 3 | import tw from '@tailwindcssinjs/macro' 4 | 5 | export const Card = ({ 6 | title, 7 | children, 8 | }: { 9 | title: string 10 | children: JSX.Element 11 | }) => ( 12 |
13 |
16 |
17 |
18 |
19 | {title} 20 |
21 |
22 |
23 |
{children}
24 |
25 |
26 | ) 27 | -------------------------------------------------------------------------------- /components/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from '@emotion/css' 3 | import tw from '@tailwindcssinjs/macro' 4 | export const FlexWrap: React.FC = ({ children }) => ( 5 |
{children}
6 | ) 7 | 8 | export * from './AsyncParamsUI' 9 | export * from './Header' 10 | export * from './Button' 11 | export * from './Card' 12 | export * from './Container' 13 | export * from './Divider' 14 | export * from './PhoneConfigurations' 15 | export * from './ProtectedPage' 16 | export * from './TypingIntro' 17 | export * from './ConversationCard' 18 | export * from './FileOrUrlInput' 19 | export * from './JsonPayloadCard' 20 | export * from './UrlFileToggle' 21 | export * from './ConnectionLabel' 22 | export * from './VideoMessages' 23 | export * from './Link' 24 | export * from './ChatComponent' 25 | export * from './ParamsToggle' 26 | -------------------------------------------------------------------------------- /pages/api/subscribeToPhoneEvents.ts: -------------------------------------------------------------------------------- 1 | import Server from 'socket.io' 2 | import { 3 | subscribeToRealtimeEvents, 4 | stopEndpoint, 5 | } from '../../integrations/symbl/utils' 6 | const ioHandler = (req: any, res: any) => { 7 | if (!res.socket.server.io) { 8 | const io = new Server(res.socket.server) 9 | io.on('connection', (socket: any) => { 10 | socket.on('subscribeToEvents', (msg: any) => { 11 | console.log(`Subscribe to realtime events of ${JSON.stringify(msg)}`) 12 | subscribeToRealtimeEvents(msg.connectionId, (data) => { 13 | socket.emit('realtimeEvent', data) 14 | }) 15 | }) 16 | socket.on('endCall', (msg: any) => { 17 | console.log('stopSubscription for the connection') 18 | stopEndpoint(msg.connectionId) 19 | }) 20 | }) 21 | 22 | res.socket.server.io = io 23 | } else { 24 | console.log('socket.io already running') 25 | } 26 | res.end() 27 | } 28 | 29 | export const config = { 30 | api: { 31 | bodyParser: false, 32 | }, 33 | } 34 | 35 | export default ioHandler 36 | -------------------------------------------------------------------------------- /components/JsonPayloadCard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from '@emotion/css' 3 | import tw from '@tailwindcssinjs/macro' 4 | 5 | export const JsonPayloadCard = ({ 6 | title, 7 | children, 8 | json, 9 | }: { 10 | title: string 11 | children: JSX.Element 12 | json?: Record | null 13 | }) => ( 14 |
15 |
18 |
19 |
20 |
21 | {title} 22 |
23 |
24 |
25 |
26 |
27 | 57 |
58 | 59 | 69 | 70 | 71 | 72 |
Job Processing data
73 |
74 | 75 |
76 | Here you will see Connection data. To check out API check here: 77 | 83 | here 84 | 85 |
86 |
87 |
88 | 89 | 90 | 91 |
92 |
93 | This transcript are using predefined Transcripts element from{' '} 94 | 98 | @symblai/react-elements 99 | 100 |
101 | {jobStatus && (jobStatus as any).status === 'completed' && ( 102 | 103 | )} 104 |
105 |
106 | 107 |
108 |
109 | This transcript are using predefined Transcripts element from{' '} 110 | 114 | @symblai/react-elements 115 | 116 |
117 | {jobStatus && (jobStatus as any).status === 'completed' && ( 118 | 119 | )} 120 |
121 |
122 |
123 | 124 | 125 | ) 126 | } 127 | 128 | export default Index 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Symbl.ai Next.js Demo 2 | 3 | [![telephony](https://img.shields.io/badge/symbl-telephony-blue)](https://docs.symbl.ai/docs/telephony/overview/post-api) 4 | [![async-text](https://img.shields.io/badge/symbl-async--text-9cf)](https://docs.symbl.ai/docs/async-api/overview/text/post-text) 5 | [![async-video](https://img.shields.io/badge/symbl-async--video-%231F618D)](https://docs.symbl.ai/docs/async-api/overview/video/post-video) 6 | [![async-audio](https://img.shields.io/badge/symbl-async--audio-%2358D68D)](https://docs.symbl.ai/docs/async-api/overview/audio/post-audio) 7 | [![symbl-react-elements](https://img.shields.io/badge/symbl-react--elements-yellow)](https://docs.symbl.ai/docs/symbl-elements) 8 | [![sdk](https://img.shields.io/badge/symbl-sdk-blueviolet)](https://docs.symbl.ai/docs/javascript-sdk/overview/introduction) 9 | 10 | 11 | Symbl's APIs empower developers to enable: 12 | - **Real-time** analysis of free-flowing discussions to automatically surface highly relevant summary discussion topics, contextual insights, suggestive action items, follow-ups, decisions, and questions. 13 | - **Voice APIs** that makes it easy to add AI-powered conversational intelligence to either [telephony][telephony] or [WebSocket][websocket] interfaces. 14 | - **Conversation APIs** that provide a REST interface for managing and processing your conversation data. 15 | - **Summary UI** with a fully customizable and editable reference experience that indexes a searchable transcript and shows generated actionable insights, topics, timecodes, and speaker information. 16 | 17 |
18 | 19 | ## This is a Demo app showcasing Symbl.ai capabilities using [ReactJS](https://reactjs.org/), [Typescript](https://www.typescriptlang.org/), and [NextJS](https://nextjs.org/) 20 | 21 |
22 | 23 | * [Setup](#setup) 24 | * [Integration](#integration) 25 | * [Conclusion](#conclusion) 26 | * [Community](#community) 27 | 28 | ## Setup 29 | The first step to getting setup is to [sign up][signup] with Symbl.ai. 30 | 31 | ### Authorization 32 | 33 | 1. Create a .env file in the root directory using the [.env.sample](./.env.sample) as an example. 34 | 2. Update the .env file with the following: 35 | * Your App Id that you can get from [Platform](https://platform.symbl.ai) 36 | * Your App Secret that you can get from [Platform](https://platform.symbl.ai) 37 | 38 | ### Install & Deploy 39 | 40 | 1. Run `yarn install` or `npm install`. 41 | 2. To run the app use `yarn dev`. 42 | 3. Navigate to `localhost:3000` to view the app. 43 | 44 | 45 | ## Integration 46 | 47 | ### Project Structure 48 | 49 | * `pages` In Next.js, a page is a React Component exported from a `.js`, `.jsx`, `.ts`, or `.tsx` file in the pages directory. Each page is associated with a route based on its file name. You can read more about how works with [pages](https://nextjs.org/docs/basic-features/pages). Each tab in the application corresponds to the following pages subdirectories 50 | * `pages/api` => Phone Call 51 | * `pages/audio` => Phone(Client Only) 52 | * `pages/conversations` => Conversation data 53 | * `pages/text` => Text 54 | * `pages/audio` => Audio 55 | * `pages/video` => Video 56 | 57 | ## Conclusion 58 | 59 | When implemented this application offers options to explore the following Symbl.ai features accessible via tabs at the top of the page. 60 | 61 | 1. PSTN call implemented with the [NodeSDK] featuring real time transcription and insights. 62 | 2. PSTN call implemented with the [Telephony API][telephony] featuring real time transcription, insights, and [Symbl React Elements][reactelements]. 63 | 3. Processed conversation data accessed from the [Conversation API](https://docs.symbl.ai/docs/conversation-api/overview/introduction) featuring participants and insights. 64 | 4. [Async Text API](https://docs.symbl.ai/docs/async-api/overview/text/post-text) featuring [Symbl React Elements][reactelements], transcription, and insights. 65 | 5. [Async Audio API](https://docs.symbl.ai/docs/async-api/overview/audio/post-audio) featuring [Symbl React Elements][reactelements], transcription, and insights. 66 | 6. [Async Video API](https://docs.symbl.ai/docs/async-api/overview/video/post-video) featuring [Symbl React Elements][reactelements], transcription, and insights. 67 | 68 | ## Community 69 | 70 | If you have any questions, feel free to reach out to us at devrelations@symbl.ai, through our Community [Slack][slack], or [developer community][developer_community] 71 | 72 | This guide is actively developed, and we love to hear from you! Please feel free to [create an issue][issues] or [open a pull request][pulls] with your questions, comments, suggestions and feedback. If you liked our integration guide, please star our repo! 73 | 74 | This library is released under the [MIT License][license] 75 | 76 | [license]: LICENSE.txt 77 | [telephony]: https://docs.symbl.ai/docs/telephony/overview/post-api 78 | [websocket]: https://docs.symbl.ai/docs/streamingapi/overview/introduction 79 | [developer_community]: https://community.symbl.ai/?_ga=2.134156042.526040298.1609788827-1505817196.1609788827 80 | [signup]: https://platform.symbl.ai/?_ga=2.63499307.526040298.1609788827-1505817196.1609788827 81 | [issues]: https://github.com/symblai/symbl-for-zoom/issues 82 | [pulls]: https://github.com/symblai/symbl-for-zoom/pulls 83 | [slack]: https://join.slack.com/t/symbldotai/shared_invite/zt-4sic2s11-D3x496pll8UHSJ89cm78CA 84 | [NodeSDK]: https://docs.symbl.ai/docs/javascript-sdk/overview/introduction 85 | [reactelements]: https://docs.symbl.ai/docs/symbl-elements 86 | -------------------------------------------------------------------------------- /pages/phone/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useRef } from 'react' 2 | import { PhoneConfigurations } from '../../components/PhoneConfigurations' 3 | import { css } from '@emotion/css' 4 | import tw from '@tailwindcssinjs/macro' 5 | import { 6 | Card, 7 | JsonPayloadCard, 8 | TypingIntro, 9 | FlexWrap, 10 | Divider, 11 | ProtectedPage, 12 | ConnectionLabel, 13 | Link, 14 | } from '../../components' 15 | import { Transcripts, Topics } from '@symblai/react-elements' 16 | import { useConnection, useConversation, useAuth } from '../../hooks' 17 | 18 | const INSIGHT_TYPES = ['question', 'action_item'] 19 | const Index = () => { 20 | const [connectionId, setConnectionId] = useConnection() 21 | const { token } = useAuth() 22 | const { conversationData, setConversationData } = useConversation() 23 | const [liveTranscript, setLiveTranscript] = useState({}) 24 | const [realtimeData, setRealTimeData] = useState({}) 25 | const [messages, setMessages] = useState([]) 26 | const messagesList = useRef(messages) 27 | 28 | useEffect(() => { 29 | messagesList.current = messages 30 | }, [messages]) 31 | 32 | function handleConnection(data: any) { 33 | console.log('Connection Data', data) 34 | setConnectionId(data.connectionId) 35 | setConversationData(data) 36 | } 37 | 38 | useEffect(() => { 39 | let ws: any = null 40 | 41 | if (conversationData && !ws) { 42 | ws = new WebSocket( 43 | `wss://api.symbl.ai/session/subscribe/${connectionId}?access_token=${token}` 44 | ) 45 | 46 | ws.onmessage = (event: MessageEvent) => { 47 | const data = JSON.parse(event.data) 48 | if (data.type === 'message_response') { 49 | const oldMsgs = messagesList.current 50 | setMessages([...oldMsgs, ...data.messages]) 51 | } 52 | if (data.type === 'transcript_response') { 53 | console.log(data.payload.content) 54 | setLiveTranscript(data) 55 | } 56 | 57 | setRealTimeData(data) 58 | console.log('Realtime data', data) 59 | } 60 | } 61 | 62 | // cleanup method which will be called before next execution. in your case unmount. 63 | return () => { 64 | ws && ws.close 65 | } 66 | }, [conversationData]) 67 | 68 | return ( 69 | 70 | 71 | This is automatic calling machine to showcase Symbl.ai REST telephony 72 | API. 73 | 74 | 75 | 76 | You can use Symbl REST API to call to your phone. You can test it there. 77 | After calling a phone you will get a conversationId that will help you 78 | retrieve bunch of insights from live coversation 79 | 80 | 81 | 86 | 87 | 88 | 89 |
90 |
91 | here you see live transcripts as they are parsed by symbl. 92 | Meaningful ones will be parsed as messages and appear in messages 93 | stream 94 |
95 |
96 | {liveTranscript.payload && liveTranscript.payload.content} 97 |
98 |
99 |
100 | 101 |
102 |
103 | Meaningful messages that are parsed from transcriptions 104 |
105 |
    106 | {messages.map((message, index) => ( 107 |
  • 108 | {message.payload && message.payload.content} 109 |
  • 110 | ))} 111 |
112 |
113 |
114 |
115 | 116 |

117 | Symbl React Elements library 118 |

119 | 120 | 121 | 122 |
123 |
124 | This UI is using predefined Transcripts element from{' '} 125 | 129 | @symblai/react-elements 130 | 131 |
132 |
133 | {messages && } 134 |
135 |
136 |
137 | 138 |
139 |
140 | This UI is using predefined Topics element from{' '} 141 | 145 | @symblai/react-elements 146 | 147 |
148 |
149 | {conversationData && conversationData.conversationId && ( 150 | 151 | )} 152 |
153 |
154 |
155 |
156 | 157 | 158 |

159 | Raw Data 160 |

161 | 162 | 163 | 167 |
Realtime data
168 |
169 | 170 |
Connection data
171 |
172 |
173 |
174 | ) 175 | } 176 | 177 | export default Index 178 | -------------------------------------------------------------------------------- /components/ChatComponent.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { css } from '@emotion/css' 3 | import tw from '@tailwindcssinjs/macro' 4 | import { Button } from '../components' 5 | 6 | const Chat = ({ messages }: any) => { 7 | return ( 8 |
    9 | {messages.map((message: any, index: any) => ( 10 |
  • 11 |
    12 |
    18 | 19 |
    userId: {message.from.userId}
    20 |
    21 |
    26 | {message.payload.content} 27 |
    28 |
    29 |
  • 30 | ))} 31 |
32 | ) 33 | } 34 | 35 | export const ChatComponent = ({ 36 | onChange, 37 | payload, 38 | }: { 39 | onChange: (jsonPayload: any) => void 40 | payload: string 41 | }) => { 42 | const [speaker, setSpeaker] = useState('') 43 | const [userId, setUserId] = useState('') 44 | const [message, setMessage] = useState('') 45 | const [ 46 | detectActionPhraseForMessages, 47 | setDetectActionPhraseForMessages, 48 | ] = useState(false) 49 | const [conversationType, setConversationType] = useState('') 50 | const jsonPayload = JSON.parse(payload || '{}') 51 | 52 | const addMessage = () => { 53 | const messages = jsonPayload.messages || [] 54 | if (!message || !speaker) { 55 | alert('No Speaker or Message') 56 | } else { 57 | const metadata = conversationType 58 | ? { 59 | metadata: { 60 | conversationType: conversationType.split(','), 61 | }, 62 | } 63 | : {} 64 | const newData = { 65 | ...metadata, 66 | detectActionPhraseForMessages, 67 | confidenceThreshold: 0.5, 68 | messages: [ 69 | ...messages, 70 | { 71 | payload: { 72 | content: message, 73 | }, 74 | from: { 75 | userId, 76 | name: speaker, 77 | }, 78 | duration: { 79 | startTime: new Date().toISOString(), 80 | endTime: new Date().toISOString(), 81 | }, 82 | }, 83 | ], 84 | } 85 | onChange(newData) 86 | } 87 | } 88 | return ( 89 |
90 |
95 |
96 |
97 |
98 | Chat Example 99 |
100 | { 109 | setConversationType(e.target.value) 110 | }} 111 | /> 112 | { 116 | setDetectActionPhraseForMessages(!detectActionPhraseForMessages) 117 | }} 118 | > 119 | 126 |
127 |
128 |
129 | {jsonPayload.messages && } 130 |
131 |
132 | { 141 | setSpeaker(e.target.value) 142 | }} 143 | /> 144 | { 153 | setUserId(e.target.value) 154 | }} 155 | /> 156 |