Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic Reply Handling #1526

Open
ajohn25 opened this issue Nov 8, 2022 · 9 comments
Open

Automatic Reply Handling #1526

ajohn25 opened this issue Nov 8, 2022 · 9 comments
Assignees
Labels
additions: feature 🆕 New feature or request feedback: rfc 🙋 Request For Comment platform: node 🔅 Related to Node backend platform: react ⚛️ Related to React client application

Comments

@ajohn25
Copy link
Contributor

ajohn25 commented Nov 8, 2022

Overview

Allow users to set up campaigns so that common inbound messages that occur as a response to any step in the interaction step tree can be automatically handled.

Response keywords are associated with a child interaction step such that inbound messages in response to the parent interaction step's question:
a) automatically mark that question response
b) send the next script automatically (if a script exists for the child step)

Context

For some organizations, many inbound messages that are handled by texters currently do not actually require much thought to respond to, and can be automatically parsed and handled by Spoke automatically.

Goals and Non-Goals

Goals:

  • Allow users to specify what "common inbound responses" are to an interaction step and which child step should be sent automatically as the outbound response. In other words, allow users to specify what inbound messages Spoke should automatically respond to and how.
  • Refactor retry-interaction-step and message-sending.js to allow for sending script_options that are not the root step.

Non-Goals:

  • Handling responses that are not exact matches for tokens in auto_reply_trigger at the current level in the script tree
  • Handling responses once survey questions are fully answered

Milestones

Separate PRs for each of the following:

  1. Schema and task changes
  2. UI and back end changes to handle the sending of auto responses

Existing Solution

Users handle all replies manually.

Proposed Solution

Schema Changes:

Add a auto_reply_trigger table to store the words to respond to for each interaction step.

create table auto_reply_trigger (
  id serial primary key,
  token text not null,

  -- removed
  -- parent_interaction_step_id int not null references interaction_step (id),
  -- child_interaction_step_id int not null references interaction_step (id),

  -- the parent/child relationship is now handled in code instead
  interaction_step_id int not null references interaction_step (id),

  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  constraint unique_token_per_parent_interaction_step_id unique (parent_interaction_step_id, token)
);

Add a is_auto_reply_eligible boolean DEFAULT true column to campaign_contact to keep track of which conversations can be auto replied to. Once an inbound message that is not contained in auto_reply_trigger for the current level in the script tree comes in for a campaign contact, we set this column to false.

Back End Changes:

  1. Add interactionStepId to RetryInteractionStepPayload When this value is passed in, we select a message from the script_options for the interaction step.

  2. For any contacts that is_auto_reply_eligible check in message-sending.js for any response words for any interaction step for the campaign.

2-3 min delay when scheduling the time to send the response.

Front End Changes:

Add "response words" (or better phrase for this) as another field in the Interactions Section

Access to this feature is controlled by a new envvar ENABLE_AUTO_REPLIES

Alternative Solutions

Predetermine a common list of response words as we've done in #1110 Since we will have to store the pairs of keywords and interaction steps to send in response in the database anyway, predetermining a common list does not save much work overall, and limits more flexible use of this feature.

Testability, Monitoring and Alerting

Write test cases as part of this PR.

Cross-Team Impact

Will require documentation updates.

Open Questions

Detailed Scoping and Timeline

@ajohn25 ajohn25 added feedback: rfc 🙋 Request For Comment additions: feature 🆕 New feature or request platform: react ⚛️ Related to React client application platform: node 🔅 Related to Node backend labels Nov 8, 2022
@ajohn25
Copy link
Contributor Author

ajohn25 commented Nov 8, 2022

@hiemanshu This is a lot smaller in scope than #1516 but what it looks like to the user could depend on the changes described there, so I'm curious whether you think this should wait for #1516 to be completed in terms of working on front end changes.

Edit: I realize you explicitly said you're not touching the interactions section so I actually now think working on these issues shouldn't bump into each other. Let me know if you think otherwise!

@bchrobot
Copy link
Member

@ajohn25 a few questions to confirm understanding and a few suggestions:

Question: the overall approach being proposed is that response keywords would be associated with a child interaction step such that inbound messages in response to the parent interaction step's question would a) automatically mark that question response and b) send the next script automatically (if a script exists for the child step), is that right?

Refactor retry-interaction-step and message-sending.js to allow for sending script_options that are not the root step.

Question: the goal here is to support automated sending of non-root interaction steps, is that right?

Add a response_words text[] column to interaction_step to store the words to respond to for each interaction step.

Suggestion: I think its own table is better than an array column. We've been bitten a few times by script_options being an array column and not its own table. This would also allow per-keyword settings similar to troll tokens; I can imagine "case sensitive" vs "case insensitive" for example.

Question: can you say more about the problem that is_auto_reply_eligible is solving and how?

@ajohn25
Copy link
Contributor Author

ajohn25 commented Nov 13, 2022

@bchrobot 👍🏾 on all questions and suggestions above - will update the design doc description to clarify

Question: can you say more about the problem that is_auto_reply_eligible is solving and how?

Convo for example below:

  1. Outbound - "Hi person...Can you join us?"
  2. Inbound - "Yes but how big is the crowd?" (is_auto_reply_eligible = false now because this isn't a response word phrase)
  3. Outbound - Texter marks survey for Yes they will attend the event and sends this response where the strikethrough/bold section in the middle is manually edited by the texter: Great! You can RSVP here: https://link.com One more thing - can you come early and help us set up? We expect tons of people to be there! Would you still be comfortable attending?
  4. Inbound - "Yes"

At step 4, if we were only looking at the inbound text and current level in the script tree, Spoke would try to handle it as a response to the volunteer ask which was never sent to the contact. If we reference is_auto_reply_eligible which was set to false in step 2, we won't auto handle the message (which is intended).

The alternative without adding an extra column to campaign_contact that I've thought of so far would be that every time we're about to auto handle a response, we look up every inbound message for the campaign contact and make sure the message text is in response_words (now aka auto_reply_trigger) for the matching survey, which would be a lot of extra (but pretty quick) database queries.

@bchrobot
Copy link
Member

Where is the volunteer ask in the example?

@ajohn25
Copy link
Contributor Author

ajohn25 commented Nov 14, 2022

Mixing up examples in my head 🤦🏾‍♂️ Edited above to add the volunteer ask in the strikethrough instead, but the idea is that once a texter starts making edits we can't rely on following the script anymore

@ninabaldwin
Copy link

@ajohn25 Let's do a 2.5 minute delay.

@bchrobot
Copy link
Member

Mixing up examples in my head 🤦🏾‍♂️ Edited above to add the volunteer ask in the strikethrough instead, but the idea is that once a texter starts making edits we can't rely on following the script anymore

Got it, so this is tracking going off script. Sounds like two things then:

  1. Texter goes off script -- Spoke Rewired can't be sure the question being posed matches the question value in the script at that level. We may want to set is_auto_reply_eligible = false when an outbound message is sent with edits to the script?
  2. Contact goes off script (e.g. asking a clarifying question) -- this almost certainly leads to 1. ^^

Can a conversation "recover" from either case? E.g. the texter later selects a question response and sends the populated script without any edits

@ajohn25
Copy link
Contributor Author

ajohn25 commented Nov 21, 2022

I think no to recovering, it feels safe to say once we exit the script we can't safely loop back into it. I would reverse 1 + 2 in the sense that 99% of the time a user would only be able to send non-initials if the contact did 2. The 1% being if an admin goes into message review and sends something, since texters shouldn't be seeing the convo once the initial is sent while is_auto_reply_eligible = true. So I think for setting is_auto_reply_eligible = false :

  1. Contact goes off script (e.g. asking a clarifying question)
  2. Message sent from message review

@bchrobot
Copy link
Member

bchrobot commented Dec 3, 2022

That sounds good to me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
additions: feature 🆕 New feature or request feedback: rfc 🙋 Request For Comment platform: node 🔅 Related to Node backend platform: react ⚛️ Related to React client application
Projects
None yet
Development

No branches or pull requests

3 participants