├── .changeset ├── README.md ├── config.json ├── late-tomatoes-grow.md ├── metal-students-invite.md ├── polite-ties-occur.md ├── pre.json ├── rare-readers-trade.md ├── selfish-oranges-knock.md ├── serious-jeans-hunt.md ├── serious-mice-exist.md ├── small-cameras-knock.md ├── smooth-trains-shout.md ├── three-avocados-return.md └── warm-bulldogs-boil.md ├── .gitattributes ├── .gitignore ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── build-for-release.sh ├── decisions ├── AccessToken renewal.md ├── Context-based-Sockets.md ├── DR-TEMPLATE.md ├── PNs with mixed capabilities.md ├── conversations-over-sync ├── licenses.md └── ports.md ├── deepsource.toml ├── package.json ├── packages ├── dev-phone-ui │ ├── CHANGELOG.md │ ├── README.md │ ├── babel.config.json │ ├── package.json │ ├── public │ │ ├── index.html │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.test.js │ │ ├── actions │ │ │ └── index.js │ │ ├── components │ │ │ ├── App │ │ │ │ └── App.jsx │ │ │ ├── CallHistory │ │ │ │ ├── CallHistory.jsx │ │ │ │ └── CallRecord.jsx │ │ │ ├── DevDisclaimer │ │ │ │ └── DevDisclaimer.jsx │ │ │ ├── Dialer │ │ │ │ ├── Dialer.jsx │ │ │ │ ├── DtmfButton.jsx │ │ │ │ ├── DtmfButton.module.css │ │ │ │ └── StatusMessage.jsx │ │ │ ├── Footer │ │ │ │ └── Footer.jsx │ │ │ ├── Header │ │ │ │ └── Header.jsx │ │ │ ├── Illustrations │ │ │ │ └── SuccessIllustration.jsx │ │ │ ├── PhoneNumberInput │ │ │ │ └── PhoneNumberInput.jsx │ │ │ ├── PhoneNumberPicker │ │ │ │ ├── PhoneNumberPicker.jsx │ │ │ │ └── WelcomeDialog.jsx │ │ │ ├── SendSmsForm │ │ │ │ ├── EmptyMessageList.jsx │ │ │ │ ├── MessageList.jsx │ │ │ │ └── SendSmsForm.jsx │ │ │ ├── Softphone │ │ │ │ ├── MissingDestinationNumber.jsx │ │ │ │ └── Softphone.jsx │ │ │ └── WebsocketManagers │ │ │ │ ├── ConversationsManager.jsx │ │ │ │ └── VoiceManager.jsx │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── reducer │ │ │ └── index.js │ │ └── setupTests.js │ ├── webpack.config.js │ └── webpack.prod.config.js └── plugin-dev-phone │ ├── .editorconfig │ ├── .eslintrc │ ├── .gitignore │ ├── .mocharc.yml │ ├── .twilio-functions │ ├── CHANGELOG.md │ ├── README.md │ ├── appveyor.yml │ ├── bin │ ├── run │ └── run.cmd │ ├── package.json │ ├── src │ ├── commands │ │ └── dev-phone.ts │ ├── serverless │ │ └── functions │ │ │ ├── incoming-call-handler.js │ │ │ ├── incoming-message-handler.js │ │ │ ├── outbound-call-handler.js │ │ │ └── sync-call-history.js │ └── utils │ │ ├── create-serverless-util.ts │ │ ├── helpers.ts │ │ └── phone-number-utils.ts │ ├── test │ ├── .eslintrc │ ├── commands │ │ └── dev-phone.test.js │ └── helpers │ │ └── init.js │ └── tsconfig.json └── turbo.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } -------------------------------------------------------------------------------- /.changeset/late-tomatoes-grow.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@twilio-labs/plugin-dev-phone": patch 3 | --- 4 | 5 | Include the `answerOnBridge` parameter in `` TwiML. This fixes an issue where incoming calls play `` and `` verbs prematurely. 6 | -------------------------------------------------------------------------------- /.changeset/metal-students-invite.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@twilio-labs/plugin-dev-phone": patch 3 | --- 4 | 5 | Use the Dev Phone Name when deleting resources to allow multiple instances to work against the same subaccount. 6 | -------------------------------------------------------------------------------- /.changeset/polite-ties-occur.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@twilio-labs/dev-phone-ui": patch 3 | --- 4 | 5 | Remove logging from Voice SDK component, which was causing errors on startup 6 | -------------------------------------------------------------------------------- /.changeset/pre.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "pre", 3 | "tag": "beta", 4 | "initialVersions": { 5 | "@twilio-labs/dev-phone-ui": "1.0.0-beta.1", 6 | "@twilio-labs/plugin-dev-phone": "1.0.0-beta.1" 7 | }, 8 | "changesets": [ 9 | "late-tomatoes-grow", 10 | "metal-students-invite", 11 | "polite-ties-occur", 12 | "rare-readers-trade", 13 | "selfish-oranges-knock", 14 | "serious-jeans-hunt", 15 | "serious-mice-exist", 16 | "small-cameras-knock", 17 | "smooth-trains-shout", 18 | "three-avocados-return", 19 | "warm-bulldogs-boil" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.changeset/rare-readers-trade.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@twilio-labs/dev-phone-ui": minor 3 | --- 4 | 5 | Replaced custom messaging component with paste chat log. Version bump for paste react, and react dom. 6 | -------------------------------------------------------------------------------- /.changeset/selfish-oranges-knock.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@twilio-labs/dev-phone-ui": patch 3 | --- 4 | 5 | Fixed Overflow in Call History Component 6 | -------------------------------------------------------------------------------- /.changeset/serious-jeans-hunt.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@twilio-labs/plugin-dev-phone": patch 3 | --- 4 | 5 | Update dev phone ui dependency 6 | -------------------------------------------------------------------------------- /.changeset/serious-mice-exist.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@twilio-labs/plugin-dev-phone": minor 3 | --- 4 | 5 | The dev phone plugin is now compatible with Twilio CLI v5. Backwards compatibility is no longer guaranteed, unfortunately, so we recommend bumping to a recent node version and updating the plugin CLI as soon as possible. 6 | -------------------------------------------------------------------------------- /.changeset/small-cameras-knock.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@twilio-labs/plugin-dev-phone": minor 3 | --- 4 | 5 | Include new `clear` flag, enabling the deletion of all dev-phone resources from your Twilio account 6 | -------------------------------------------------------------------------------- /.changeset/smooth-trains-shout.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@twilio-labs/dev-phone-ui": minor 3 | --- 4 | 5 | Added mute button to dialer for calls 6 | -------------------------------------------------------------------------------- /.changeset/three-avocados-return.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@twilio-labs/plugin-dev-phone": patch 3 | --- 4 | 5 | Update services to use v1 endpoints with the helper library. This will quiet noisy setup and teardown of the dev phone. 6 | -------------------------------------------------------------------------------- /.changeset/warm-bulldogs-boil.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@twilio-labs/dev-phone-ui": patch 3 | --- 4 | 5 | Improve resilience of the phone number picker component 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *-debug.log 2 | *-error.log 3 | .nyc_output 4 | dist 5 | tmp 6 | yarn.lock 7 | package-lock.json 8 | node_modules 9 | coverage 10 | *.tgz 11 | *.tar.gz 12 | 13 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 14 | 15 | # dependencies 16 | /node_modules 17 | /.pnp 18 | .pnp.js 19 | 20 | # testing 21 | /coverage 22 | 23 | # production 24 | plugin-dev-phone-client/build 25 | plugin-dev-phone/public 26 | 27 | # misc 28 | .DS_Store 29 | .env.local 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | npm-debug.log* 35 | yarn-debug.log* 36 | yarn-error.log* 37 | 38 | 39 | .idea/ 40 | .turbo 41 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Development with the Dev Phone Services 2 | 3 | ## Prerequisites 4 | 5 | You will need: 6 | - Node development environment (node, npm, npx) 7 | - Twilio CLI 8 | 9 | ## Set up and run locally 10 | 11 | 1. Get the code: 12 | * Clone this repo & `cd` into the directory 13 | 2. Install all of the dependencies using `npm i --workspaces` 14 | 3. Build all of the packages using `npm run build --workspaces` 15 | 16 | ## CLI Plugin 17 | 1. Link the plugin with your Twilio CLI installation 18 | * `twilio plugins:link $(pwd)/packages/plugin-dev-phone` 19 | 2. Start the plugin 20 | * `twilio dev-phone` or use a specific number by using the --phone-number flag. e.g. `twilio dev-phone --phone-number +11234567890` 21 | * Leave this running and continue in a new terminal. 22 | 3. Make changes to the source code. When you're ready to test them, use `CTRL + C` to tear down the Dev Phone if it's still running, then rebuild the plugin with `npm run build --workspace=packages/plugin-dev-phone` 23 | 24 | ## Dev Phone UI 25 | 1. Start the front end 26 | * `npm start --workspace=packages/dev-phone-ui` 27 | * Open `http://localhost:3000/` in your browser 28 | 2. The Dev Phone UI runs a dev server that will hot reload when you make changes. The UI relies on tokens and data provided by the plugin, so you MUST be running the CLI plugin for the local UI to work 29 | 30 | After running the plugin and/or the UI locally, you should see the Dev Phone UI appear with a phone number selector. 31 | 32 | ## How to modify the plugin 33 | 34 | From now on you can edit the plugin server code (in `packages/plugin-dev-phone`, running on port 3001 usually) and front end (in `packages/dev-phone-ui`, running on port 3000 usually) 35 | 36 | ### Local back end 37 | 38 | Work in `packages/plugin-dev-phone/`. Start with `src/commands/dev-phone.js`. 39 | 40 | This does _not_ hot-reload, so whenever you make edits you will need to stop, rebuild with `npm run build --workspace=packages/plugin-dev-phone` and restart with `twilio dev-phone`. 41 | 42 | You can make requests directly to this server without running the front end, eg http://localhost:3001/ping 43 | 44 | Run tests with `npm test`. There are no real tests at the moment but the place to add them is `test/commands/dev-phone.test.js`. 45 | 46 | ### Local front end 47 | 48 | Work in `packages/dev-phone-ui`. Start with `src/App/App.js`. 49 | 50 | This does hot-reload, so changes will be reflected in your browser as soon as you save. 51 | 52 | Run tests with `npm test`. There are no tests at the moment but the place to add them is ` 53 | 54 | ### Documenting Changes 55 | After you've made changes, run: 56 | 57 | `npx changeset` 58 | 59 | to clearly document what changes you've made. This will be included in the future CHANGELOG.md for the appropriate package update. 60 | 61 | ## Build 62 | The Dev Phone CLI plugin uses the Dev Phone UI as a dependency. This means that new versions of the UI need to be released with a version bump AND the latest version needs to be installed and released in the Dev Phone plugin in order to be consumed by end users. 63 | 64 | If you're including new packages in the repository, please include the relevant licenses for said licenses. This may require legal approval to ensure that the Dev Phone open source license stays valid. 65 | 66 | When you're ready for a new deploy, do a fresh build of the `dist` folders with: 67 | 68 | `npx turbo run build` 69 | 70 | Once there's a new build, you can manage a version bump on a new version branch with: 71 | 72 | ``` 73 | npx changesets version 74 | ``` 75 | 76 | As of 30 Sep 2022, the Dev Phone is still in a prerelease, and so all version bumps are beta version bumps (no major, minor, or patch). This should create some changes in the Changelogs and package.json that need to be committed and pushed to GitHub. Be sure to commit both the version branch and the tag. 77 | 78 | ## Release 79 | To release, run: 80 | 81 | `npx changesets publish` 82 | 83 | Note that tests and linting are on the roadmap as a part of the release process. 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Twilio, Inc. 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Dev Phone - Twilio Labs 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/dev-phone-ui/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Dev Phone - Twilio Labs", 3 | "name": "A developer browser phone to test your Twilio applications.", 4 | "icons": [], 5 | "start_url": ".", 6 | "display": "standalone", 7 | "theme_color": "#000000", 8 | "background_color": "#ffffff" 9 | } -------------------------------------------------------------------------------- /packages/dev-phone-ui/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /packages/dev-phone-ui/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('Shows the owlwave', () => { 5 | render(); 6 | const helloElement = screen.getByText(/:owlwave:/i); 7 | expect(helloElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /packages/dev-phone-ui/src/actions/index.js: -------------------------------------------------------------------------------- 1 | export const SET_DESTINATION_NUMBER = "SET_DESTINATION_NUMBER" 2 | 3 | export function setDestinationNumber(number) { 4 | return { 5 | type: SET_DESTINATION_NUMBER, 6 | number 7 | } 8 | } 9 | 10 | // Actions for handling history logic in the UI 11 | export const ADD_MESSAGE = "ADD_MESSAGE" 12 | export const ADD_CALL_RECORD = "ADD_CALL_RECORD" 13 | export const UPDATE_CALL_RECORD = "UPDATE_CALL_RECORD" 14 | 15 | export function addMessage(payload) { 16 | return { 17 | type: ADD_MESSAGE, 18 | payload 19 | } 20 | } 21 | 22 | export function addCallRecord(payload) { 23 | return { 24 | type: ADD_CALL_RECORD, 25 | payload 26 | } 27 | } 28 | 29 | export function updateCallRecord(payload) { 30 | return { 31 | type: UPDATE_CALL_RECORD, 32 | payload 33 | } 34 | } 35 | 36 | // Voice Device Actions 37 | export const UPDATE_CALL_INFORMATION = "UPDATE_CALL_INFORMATION" 38 | 39 | export function updateCallInformation(call) { 40 | return { 41 | type: UPDATE_CALL_INFORMATION, 42 | call 43 | } 44 | } 45 | 46 | export const UPDATE_MUTE_STATUS = "UPDATE_MUTE_STATUS"; 47 | 48 | export function updateMuteStatus(isMuted) { 49 | return { 50 | type: UPDATE_MUTE_STATUS, 51 | isMuted 52 | } 53 | } 54 | 55 | // Logic for communicating with the local backend 56 | export const DEV_PHONE_NUMBER_SELECTED = "DEV_PHONE_NUMBER_SELECTED" 57 | export const CONFIGURE_NUMBER_IN_USE = "CONFIGURE_NUMBER_IN_USE" 58 | export const DEV_PHONE_CONFIG_ERROR = "DEV_PHONE_CONFIG_ERROR" 59 | 60 | export function selectDevPhoneNumberRequest() { 61 | return { 62 | type: DEV_PHONE_NUMBER_SELECTED 63 | } 64 | } 65 | 66 | export function changeNumberInUse(number) { 67 | return { 68 | type: CONFIGURE_NUMBER_IN_USE, 69 | number 70 | } 71 | } 72 | 73 | // TODO: This may be too pessimistic - should we just configure it and warn that inbound may not work? 74 | export function devPhoneConfigError(error) { 75 | return { 76 | type: DEV_PHONE_CONFIG_ERROR, 77 | error 78 | } 79 | } 80 | 81 | export function configureNumberInUse(number) { 82 | return async function (dispatch) { 83 | dispatch(selectDevPhoneNumberRequest()) 84 | try { 85 | const response = await fetch('/choose-phone-number', { 86 | method: 'POST', 87 | headers: { 88 | 'Content-Type': 'application/json', 89 | }, 90 | body: JSON.stringify(number), 91 | }) 92 | 93 | // A success response suggests that the backend was able to configure serverless correctly 94 | const data = await response.json() 95 | dispatch(changeNumberInUse(data.phoneNumber)) 96 | } catch (error) { 97 | dispatch(devPhoneConfigError(error)) 98 | } 99 | } 100 | } 101 | 102 | export const REQUEST_CLIENT_TOKEN = "REQUEST_CLIENT_TOKEN" 103 | export const REQUEST_CLIENT_TOKEN_SUCCESS = "REQUEST_CLIENT_TOKEN_SUCCESS" 104 | export const REQUEST_CLIENT_TOKEN_ERROR = "REQUEST_CLIENT_TOKEN_ERROR" 105 | 106 | export function clientTokenRequest() { 107 | return { 108 | type: REQUEST_CLIENT_TOKEN 109 | } 110 | } 111 | 112 | export function clientTokenRequestSuccess(payload) { 113 | return { 114 | type: REQUEST_CLIENT_TOKEN_SUCCESS, 115 | payload 116 | } 117 | } 118 | 119 | export function clientTokenRequestFailure(error) { 120 | return { 121 | type: REQUEST_CLIENT_TOKEN_ERROR, 122 | error 123 | } 124 | } 125 | 126 | export function fetchClientToken() { 127 | return async function (dispatch) { 128 | dispatch(clientTokenRequest) 129 | try { 130 | const response = await fetch('/client-token') 131 | const data = await response.json() 132 | dispatch(clientTokenRequestSuccess(data.token)) 133 | } catch (error) { 134 | dispatch(clientTokenRequestFailure(error)) 135 | } 136 | } 137 | } 138 | 139 | export const REQUEST_CHANNEL_DATA = "REQUEST_CHANNEL_DATA" 140 | export const REQUEST_CHANNEL_DATA_SUCCESS = "REQUEST_CHANNEL_DATA_SUCCESS" 141 | export const REQUEST_CHANNEL_DATA_ERROR = "REQUEST_CHANNEL_DATA_ERROR" 142 | 143 | export function channelDataRequest() { 144 | return { 145 | type: REQUEST_CHANNEL_DATA 146 | } 147 | } 148 | 149 | export function channelDataRequestSuccess(payload) { 150 | return { 151 | type: REQUEST_CHANNEL_DATA_SUCCESS, 152 | payload 153 | } 154 | } 155 | 156 | export function channelDataRequestFailure(error) { 157 | return { 158 | type: REQUEST_CHANNEL_DATA_ERROR, 159 | error 160 | } 161 | } 162 | 163 | export function fetchChannelData() { 164 | return async function (dispatch) { 165 | dispatch(channelDataRequest) 166 | try { 167 | const response = await fetch('/plugin-settings') 168 | const data = await response.json() 169 | dispatch(channelDataRequestSuccess(data)) 170 | } catch (error) { 171 | dispatch(channelDataRequestFailure(error)) 172 | } 173 | } 174 | } 175 | 176 | export const ADD_DIGIT_TO_DESTINATION_NUMBER = 'ADD_DIGIT_TO_DESTINATION_NUMBER'; 177 | 178 | export function addDigitToDestinationNumber(digit) { 179 | return { 180 | type: ADD_DIGIT_TO_DESTINATION_NUMBER, 181 | digit 182 | } 183 | } -------------------------------------------------------------------------------- /packages/dev-phone-ui/src/components/App/App.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import Konami from "konami"; 3 | import { useSelector, useDispatch } from "react-redux"; 4 | import { changeNumberInUse, configureNumberInUse } from "../../actions"; 5 | import Header from "../Header/Header" 6 | import PhoneNumberPicker from "../PhoneNumberPicker/PhoneNumberPicker"; 7 | import DevDisclaimer from "../DevDisclaimer/DevDisclaimer"; 8 | import Softphone from "../Softphone/Softphone" 9 | 10 | import { Box, Column, Grid, Flex } from "@twilio-paste/core"; 11 | import Footer from "../Footer/Footer"; 12 | 13 | const setupKonamiCode = (setNinetiesMode) => { 14 | const ninetiesMode = new Konami(() => { 15 | // window.alert("Lets party like it's 1991!"); 16 | setNinetiesMode(true) 17 | }); 18 | ninetiesMode.pattern = "383840403739373949575749"; 19 | }; 20 | 21 | function App() { 22 | const channelData = useSelector(state => state.channelData) 23 | const numberInUse = useSelector(state => state.numberInUse ? state.numberInUse.phoneNumber : "") 24 | const dispatch = useDispatch() 25 | 26 | const [ninetiesMode, setNinetiesMode] = useState(false); 27 | 28 | useEffect(() => { 29 | setupKonamiCode(setNinetiesMode); 30 | if (channelData.phoneNumber) { 31 | dispatch(changeNumberInUse(channelData.phoneNumber)); 32 | } 33 | }, [changeNumberInUse, channelData]); 34 | 35 | return ( 36 | 37 |
38 | 39 | {numberInUse ? ( 40 | 41 | ) : ( 42 | 43 | 44 | dispatch(configureNumberInUse(number))} /> 45 | 46 | 47 | )} 48 |