Library to communicate with responders and staff when action is required.
On your local machine, in the brave-alert-lib
repository:
-
Pull the latest code for release:
git checkout main && git pull origin main
-
Decide on an appropriate version number for the new version
-
Update
CHANGELOG.md
by moving everything inUnreleased
to a section for the new version -
Update the version in
package.json
andpackage-lock.json
-
Make a new commit directly on
main
with those updates -
Tag the new commit - for example, if the version number is v1.0.0 use
git tag v1.0.0
-
Push the new version to GitHub:
git push origin main --tags
- Must have a
.env
file containing following environment variables:TWILIO_SID
: The Twilio SID to use in productionTWILIO_SID_TEST
: The Twilio SID to use in testingTWILIO_MESSAGING_SERVICE_SID
: The Twilio Messaging Service SID to use in productionTWILIO_MESSAGING_SERVICE_SID_TEST
: The Twilio Messaging Service SID to use in testingTWILIO_TOKEN
: The Twilio token to use in productionTWILIO_TOKEN_TEST
: The Twilio token to use in testingDOMAIN
: The domain name pointing to this server in productionDOMAIN_TEST
: The domain name pointing to this server in testingIS_DB_LOGGING
: Whether (true) or not (false) we are printing DB debug logs to the consolePA_CLIENT_ID
: The client ID of PA. Can be found under the Brave PA Sign-In resource in Google Cloud.PA_CLIENT_ID_TEST
: The client ID of PA. Can be found under the Brave PA Sign-In resource in Google Cloud.PA_CLIENT_SECRET
: The client secret of PA. Can be found under the Brave PA Sign-In resource in Google Cloud.PA_CLIENT_SECRET_TEST
: The client secret of PA. Can be found under the Brave PA Sign-In resource in Google Cloud.
-
clone this repository
-
Copy
.env.example
to.env
and fill out variables appropriately for your local environment -
run
npm install
to install the dependencies -
run
npm run lint
to run the linter -
run
npm test
to run the tests
Reference: https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions
-
Go to the GitHub repo in your web browser
-
Go to the settings page
-
Under the Security section, click on Secrets and variables, then click Actions
-
Under Repository secrets, click New repository secret or edit an existing one with the pen symbol
-
if you are upgrading from a previous version of
brave-alert-lib
, runnpm uninstall brave-alert-lib
to remove it frompackage.json
andpackage-lock.json
-
in the
package.json
file of the other code base, add the following whereVERSION
is the tag that you've chosen (for examplev0.1.0
):``` "dependencies": { ... "brave-alert-lib": "https://github.com/bravetechnologycoop/brave-alert-lib#<VERSION>", ... } ```
-
Run
npm install
to download the library and include it inpackage-lock.json
The main class of this library. It is used to send single alerts or to start alert sessions with the responders.
constructor(getAlertSession, getAlertSessionByPhoneNumbers, alertSessionChangedCallback, getReturnMessageToRespondedByPhoneNumber, getReturnMessageToOtherResponderPhoneNumbers, getClientMessageForRequestToReset)
getAlertSession (async function(sessionId)): function that returns the AlertSession object with the given sessionId
getAlertSessionByPhoneNumbers (async function(devicePhoneNumber, responderPhoneNumber)): function that returns the AlertSession object for the most recent unfinished session for the device with the given devicePhoneNumber and client with the given responderPhoneNumber
alertSessionChangedCallback (async function(alertSession)): function that will be called whenever an alertSession's values change; should be used to update the session in the DB. Will return an object containing the fields: respondedByPhoneNumber, replacementReturnMessageToRespondedByPhoneNumber, replacementReturnMessageToOtherResponderPhoneNumbers. The two replacement fields should be left as undefined to use messages determined through the below two functions, null to send no message at all, or other for a custom message.
getReturnMessageToRespondedByPhoneNumber (function(language, fromAlertState, toAlertState, validIncidentCategories)): function that returns the message to send to the RespondedByPhoneNumber when there is a transition from fromAlertState
to toAlertState
(note that fromAlertState
and toAlertState
will have the same value for cases where a transition doesn't change the alert state). Sometimes this message needs to know the validIncidentCategories
for the particular session.
getReturnMessageToOtherResponderPhoneNumbers (function(language, fromAlertState, toAlertState, selectedIncidentCategory)): function that returns the message to send to all the other Responder Phone Numbers (i.e. not the RespondedByPhoneNumber) when there is a transition from fromAlertState
to toAlertState
(note that fromAlertState
and toAlertState
will have the same value for cases where a transition doesn't change the alert state). Sometimes this message needs to know which incidentCategory was chosen by the respondedByPhoneNumber for the particular session.
getClientMessageForRequestToReset (function(language)): function that returns the message that is considered a request to reset. This is checked against a message received from the client, while the chatbot is in the STARTED
or WAITING_FOR_REPLY
states. If the client message equals the return value of this function, then the client has performed a request to reset, and the alert state machine will transition to the RESET
state. If this function returns null
, then the alert state machine will not transition from STARTED
or WAITING_FOR_REPLY
to the RESET
state, and all messages will transition into the WAITING_FOR_CATEGORY
state.
The BraveAlerter's Express Router contains the route
-
POST /alert/sms
Generally, a call to the
POST /alert/sms
endpoint results in a call to the BraveAlerter'salertSessionChangedCallback
with anAlertSession
object as a parameter. TheAlertSession.sessionId
field will always be present. Other fields will only be present if they have updated. This parameter should be used to update the session's DB.
which can be added to an existing Express app by:
const BraveAlerter = require('brave-alert-lib')
let express = require('express')
const braveAlerter = new BraveAlerter(...)
express.use(braveAlerter.getRouter())
Returns: The BraveAlerter's Express Router
Sends the given message
to the toPhoneNumber
from the fromPhoneNumber
.
toPhoneNumber (string): Phone number to send the message to
fromPhoneNumber (string): Phone number to send the message from
message (string): The message to send
Returns: A promise that is resolved when the message is sent.
Updates an ongoing alert session.
sessionId (GUID): Unique identifier for the alert session that was updated; this should match the session ID in the DB
toPhoneNumbers (array of strings): The phone numbers to send text message alerts to.
fromPhoneNumber (string): The phone number to send text message alerts from.
textMessage (string): Message containing the update to be sent over SMS.
Starts a full alert session configured with the given alertInfo
object.
alertInfo.sessionId (GUID): Unique identifier for the session; this should match the session ID in the DB
alertInfo.toPhoneNumbers (string): The phone numbers to send text message alerts to.
alertInfo.fromPhoneNumber (string): The phone number to send text message alerts from.
alertInfo.message (string): First message to send as part of this session
alertInfo.deviceName (string): The display name of the device that intiated the alert. This is often the unit number of the room.
alertInfo.alertType (ALERT_TYPE): The Alert Type.
alertInfo.reminderTimeoutMillis (int): How long to wait after initial alert before sending a reminder message; if falsy or not positive, will not send a reminder message
alertInfo.fallbackTimeoutMillis (int): How long to wait after initial alert before sending the fallback message; if falsy or not positive, will not send a fallback message
alertInfo.reminderMessage (string): Message for the reminder
alertInfo.fallbackMessage (string): Message for the fallback
alertInfo.fallbackToPhoneNumbers (array of strings): The phone numbers to send fallback text messages to
alertInfo.fallbackFromPhoneNumber (string): The phone number to send fallback text messages from
Returns: A promise that is resolved when the first message is sent, the reminder is scheduled, and the fallback is scheduled.
An object representing an alert session. Contains the following fields:
sessionId (GUID): Unique identifier for the alert session; should be the session ID from the DB
alertState (CHATBOT_STATE): Thc current alert state of the alert session
incidentCategoryKey (string): The string representing the incident category associated with the alert session
respondedByPhoneNumber (string): The phone number of the Responded Phone who first responded to the sessions and that will be the only one to continue progressing the chatbot
responderPhoneNumbers (array of string): The phone numbers of the Responder Phones associated with the alert session
validIncidentCategories (array of strings): The valid incident cateogries for this session. These are the values that will be stored in the DB. For example:
['Accidental', 'Safer Use', 'Overdose', 'Other']
Note that these line up one-to-one with the validIncidentCategoryKeys
. So for any i
, validIncidentCategories[i]
is the human-readable DB value for the validIncidentCategoryKeys[i]
value given by the Responder in a text message.
validIncidentCategoryKeys (array of strings): The valid incident cateogry keys for this session. These are the values that the responder will use to select an incident category through text message. For example:
['1', '2', '3', '4']
Note that these line up one-to-one with the validIncidentCategoryKeys
. So for any i
, validIncidentCategories[i]
is the
human-readable DB value for the validIncidentCategoryKeys[i]
value given by the Responder in a text message.
language (string): The language code in which client-facing messages should be sent.
An object representing a client. Contains the following fields:
id (GUID): Unique identifier for the client; should be the database value client.id
displayName (string): Client name is a displayable format; should be the database value client.display_name
responderPhoneNumbers (array of string): Client's responder phone numbers in the form +133344455555
; should be the database value client.responder_phone_numbers
reminderTimeout (int): Number of seconds after the initial alert before a reminder is sent; should be the database value client.reminder_timeout
fallbackPhoneNumbers (array of strings): Array of the Client's fallback phone numbers in the form '["+12223334444","+19998887777"]'
; should be the database value client.fallback_phone_numbers
fromPhoneNumber (string): Twilio phone number that is used to send fallback and vitals messages in the form +12223334444
; should be the database value client.from_phone_number
fallbackTimeout (int): Number of seconds after the initial alert before a fallback message is sent; should be the database value client.fallback_timeout
heartbeatPhoneNumbers (array of strings): Array of phone numbers to receive vitals/heartbeat messages about this Client, in the form '["+12223334444","+19998887777"]'
; should be the database value client.heartbeat_phone_numbers
incidentCategories (array of strings): Array of incident categories the Client's responder will be asked to choose from for each alert; should be the database value client.incident_categories
isDisplayed (boolean): If this Client is displayed on the dashboard by default; should be the database value client.is_displayed
isSendingAlerts (boolean): If this Client should send alert messages; should be the database value client.is_sending_alerts
isSendingVitals (boolean): If this Client is should send vitals messages; should be the database value client.is_sending_vitals
createdAt (Date): When the Client was created in the database; should be the database value client.created_at
updatedAt (Date): When the Client was last updated in the database; should be the database value client.updated_at
An object representing a session. Contains the following fields:
id (GUID): Unique identifier of the session; should be the database value session.id
chatbotState (CHATBOT_STATE enum): The chatbot state of the session; should be the database value session.chatbot_state
alertType (ALERT_TYPE enum): The alert type of the session; should be the database value session.alert_type
numberOfAlerts (int): The number of alerts sent during the session; should be the database value session.number_of_alerts
createdAt (Date): When the Session was created in the database; should be the database value session.created_at
updatedAt (Date): When the Session was last updated in the database; should be the database value session.updated_at
incidentCategory (string): The chosen incident category of the session; should be the database value session.incident_category
respondedAt (Date): When the Session was responded to; should be the database value session.responded_at
respondedByPhoneNumber (string): The phone number of the responder that replied to the alert this session describes; should be the database value session.responded_by_phone_number
device (object): The device which this session belongs to. Note: button, and location represent the same object as device.
isResettable (boolean): Whether or not this device can be reset by the client. This should be set to true if the device is acting strange.
An enum of the possible types of alert that can be triggered.
An enum of the possible states the Alert Session could be in at any given time.
Small collection of functions that handle the interaction with the Twilio client.
Buys and configures a Twilio Phone number for use with the current server (i.e. using the Buttons project, webhook, and messaging service if deployed on Buttons, and using the Sensor project, webhook, and messaging service if deployed on Sensor).
** areaCode (string):** the three-digit US or Canadian area code of the phone number to buy and configure
** friendlyName (string):** the friendly name to assign to the phone number within Twilio
** Returns:** if successful, an object with message: 'success'
and other key/values of interest; if not successful, an object with message
explaining the error and other key/values of interest
A collection of functions that are useful across the Brave NodeJS applications.
Format the given date
into English and Pacific Time
** date (Date):** the JS Date object to format
** Returns:** a string with the given date
formatted into English and Pacific Time
A function that can be sent as an argument to the Express Validation formatWith
function (https://express-validator.github.io/docs/validation-result-api.html#formatwithformatter). It takes an Express error object and returns a consistent, readable string to be used for error logs and sending in error HTTP messages.
Get a human-readable display name for the given alertType
in the given language.
alertType (ALERT_TYPE): the alert type whose display name to get
language (String): the language the return value would be in
t (function): the i18Next function that handles the translation
Returns: the display name corresponding to the given alertType
in the given language
In the test environment, returns the test version of the environment variable with the given name. Otherwise, returns the production version of the environment variable with the given name.
name (string): the name of the environment variable
Returns: the correct environment variable for the situation.
Returns an English string containing the time difference between the given timeToCompare
and now according to an async function db.getCurrentTime()
.
timeToCompare (Date): the date to compare to
db (object): object containing an async function getCurrentTime()
that resolves to a Date respresenting the current time
Returns: the English string containing the time difference between the given timeToCompare
and the date returned by db.getCurrentTime
Controls whether the program should output DB debug logs.
Returns: true
if the program should output DB debug logs. false
otherwise. Under normal circumstances, this should be false
.
Determines whether we are executing in a test environment (i.e. with the value of NODE_ENV === 'test').
Returns: true
if the current execution is in a test environment. false
otherwise.
Determines whether the given Express request is valid if the given set of properties are required.
req (Express Request object): the request to validate
properties (Array of strings): the body parameters that must be given in the request in order for it to be valid.
Returns: true
if the given request's body contains all of the given properties. false
otherwise.
Logs the given string to stderr as appropriate for the situation. Also forwards the error to Sentry.
logString (string): The string to log
Returns: nothing
Logs the given string to stdout as appropriate for the situation.
logString (string): The string to log
Returns: nothing
Logs the given string to stdout as appropriate for the situation. Also forwards the string to Sentry.
logString (string): The string to log
Returns: nothing
Runs a database query with correct transaction- and error-handling if required.
functionName (string): The name of the function that called runQuery
. Used to make good error messages.
queryString (string): The pg
-style query string to run in the database.
queryParams (array): The pg
-style query parameters to be used in the queryString
.
pool (pg.Pool): The pool of pg
database connections.
clientParam (pg.PoolClient): The database connection client to be used if the query should be part of an ongoing transactions. Undefined if the query should not be part of an ongoing transaction.
Sleep for the given number of milliseconds. Must use await
when calling this function, so much be called from an async
function. Reference: https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep
millis (int): The number of milliseconds to sleep for
Returns: a promise that will resolve after the given number of milliseconds
Set up Sentry for tracking errors and outages. NOTE: this needs to be called after all other app.use() calls
app (express()): The express instance that you want to track errors in
dsnString (string): The Sentry data source name you'd like to connect to
env (string): The environment you are tracking errors in
releaseName (string): The name of the release you are tracking errors for
Returns: nothing
A collection of functions that are used to create valid, customizable model objects or insert models into the database
Insert a row into the clients
table in the given db
with valid default values unless they are overridden by values in the given overrides
array.
db (Buttons or Sensors db object): The database that contains a createClient
function usable to insert a row into the clients
table
overrides (object): Any custom values to use for the new client
row other than the defaults
Create a new Client
object with valid default values unless they are overridden by the values in the given overries
array
overrides (object): Any custom values to use for the new Client
object other than the defaults
A collection of functions providing authentication for PA.
Gets payload contained in a given Google ID token. If the Google ID token is invalid, this will throw an Error.
An ID token is deemed valid if:
- It isn't expired
- It was created for PA
- It was signed by Google
- It is for a Brave Google account
- It contains email and name fields
googleIdToken (string): ID token as given from Google. Should be retrieved using paGetTokens
.
Returns: Payload information contained in the provided ID token. More information can be read in this Google documentation.
Gets tokens (Google access token and Google ID token) from Google using an authorization code.
If the authorization code is invalid, then this function will throw a GaxiosError
.
googleAuthCode (string): Authorization code from Google retrieved in the frontend application (PA).
Returns: Object containing access token (googleAccessToken) and ID token (googleIdToken).
Express middleware function to authorize a request to a PA API call.
Attempts to authorize the request using a submitted Google ID token in the Authorization header or body of the request.
The criteria for a valid Google ID token is defined under the paGetPayload
function.
req (Request): The Express Request object. Should contain googleIdToken in the Authorization header or body of the request.
res (Response): The Express Response object.
next (function): The next function to run if this request is authorized.