refactor: avoid deleting conversations

old-agentic-v1^2
Philipp Burckhardt 2023-06-19 11:24:21 -04:00
rodzic 91d3d21854
commit 9bcc624569
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: A2C3BCA4F31D1DDD
3 zmienionych plików z 196 dodań i 29 usunięć

Wyświetl plik

@ -33,10 +33,10 @@ export interface Logger {
* Log at `'fatal'` level the given msg. If the first argument is an object, all its properties will be included in the JSON line.
* If more args follows `msg`, these will be used to format `msg` using `util.format`.
*
* @typeParam T: the interface of the object being serialized. Default is object.
* @param obj: object to be serialized
* @param msg: the log message to write
* @param ...args: format string values when `msg` is a format string
* @typeParam T - the interface of the object being serialized. Default is object.
* @param obj - object to be serialized
* @param msg - the log message to write
* @param args - format string values when `msg` is a format string
*/
fatal: LogFn
@ -44,10 +44,10 @@ export interface Logger {
* Log at `'error'` level the given msg. If the first argument is an object, all its properties will be included in the JSON line.
* If more args follows `msg`, these will be used to format `msg` using `util.format`.
*
* @typeParam T: the interface of the object being serialized. Default is object.
* @param obj: object to be serialized
* @param msg: the log message to write
* @param ...args: format string values when `msg` is a format string
* @typeParam T - the interface of the object being serialized. Default is object.
* @param obj - object to be serialized
* @param msg - the log message to write
* @param args - format string values when `msg` is a format string
*/
error: LogFn
@ -55,10 +55,10 @@ export interface Logger {
* Log at `'warn'` level the given msg. If the first argument is an object, all its properties will be included in the JSON line.
* If more args follows `msg`, these will be used to format `msg` using `util.format`.
*
* @typeParam T: the interface of the object being serialized. Default is object.
* @param obj: object to be serialized
* @param msg: the log message to write
* @param ...args: format string values when `msg` is a format string
* @typeParam T - the interface of the object being serialized. Default is object.
* @param obj - object to be serialized
* @param msg - the log message to write
* @param args - format string values when `msg` is a format string
*/
warn: LogFn
@ -66,10 +66,10 @@ export interface Logger {
* Log at `'info'` level the given msg. If the first argument is an object, all its properties will be included in the JSON line.
* If more args follows `msg`, these will be used to format `msg` using `util.format`.
*
* @typeParam T: the interface of the object being serialized. Default is object.
* @param obj: object to be serialized
* @param msg: the log message to write
* @param ...args: format string values when `msg` is a format string
* @typeParam T - the interface of the object being serialized. Default is object.
* @param obj - object to be serialized
* @param msg - the log message to write
* @param args - format string values when `msg` is a format string
*/
info: LogFn
@ -77,10 +77,10 @@ export interface Logger {
* Log at `'debug'` level the given msg. If the first argument is an object, all its properties will be included in the JSON line.
* If more args follows `msg`, these will be used to format `msg` using `util.format`.
*
* @typeParam T: the interface of the object being serialized. Default is object.
* @param obj: object to be serialized
* @param msg: the log message to write
* @param ...args: format string values when `msg` is a format string
* @typeParam T - the interface of the object being serialized. Default is object.
* @param obj - object to be serialized
* @param msg - the log message to write
* @param args - format string values when `msg` is a format string
*/
debug: LogFn
@ -88,10 +88,10 @@ export interface Logger {
* Log at `'trace'` level the given msg. If the first argument is an object, all its properties will be included in the JSON line.
* If more args follows `msg`, these will be used to format `msg` using `util.format`.
*
* @typeParam T: the interface of the object being serialized. Default is object.
* @param obj: object to be serialized
* @param msg: the log message to write
* @param ...args: format string values when `msg` is a format string
* @typeParam T - the interface of the object being serialized. Default is object.
* @param obj - object to be serialized
* @param msg - the log message to write
* @param args - format string values when `msg` is a format string
*/
trace: LogFn

Wyświetl plik

@ -1,23 +1,70 @@
import defaultKy from 'ky'
/**
* Base URL endpoint for the Novu API.
*/
export const NOVU_API_BASE_URL = 'https://api.novu.co/v1'
export type NovuSubscriber = {
/**
* Unique identifier for the subscriber. This can be any value that is meaningful to your application such as a user ID stored in your database or a unique email address.
*/
subscriberId: string
/**
* Email address of the subscriber.
*/
email?: string
/**
* First name of the subscriber.
*/
firstName?: string
/**
* Last name of the subscriber.
*/
lastName?: string
/**
* Phone number of the subscriber.
*/
phone?: string
}
/**
* Response from the Novu API when triggering an event.
*/
export type NovuTriggerEventResponse = {
/**
* Data about the triggered event.
*/
data: {
/**
* Whether the trigger was acknowledged or not.
*/
acknowledged?: boolean
/**
* Status for trigger.
*/
status?: string
/**
* Transaction id for trigger.
*/
transactionId?: string
/**
* In case of an error, this field will contain the error message.
*/
error?: Array<any>
}
}
/**
* Options for triggering an event in Novu.
*/
export type NovuTriggerOptions = {
/**
* Name of the event to trigger. This should match the name of an existing notification template in Novu.
@ -30,17 +77,33 @@ export type NovuTriggerOptions = {
payload: Record<string, unknown>
/**
* List of subscribers to send the notification to
* List of subscribers to send the notification to. Each subscriber must at least have a unique `subscriberId` to identify them in Novu and, if not already known to Novu, an `email` address or `phone` number depending on the notification template being used.
*/
to: NovuSubscriber[]
}
/**
* Client for interacting with the Novu API.
*/
export class NovuClient {
/**
* Instance of ky for making requests to the Novu API.
*/
api: typeof defaultKy
/**
* API key to use for authenticating requests to the Novu API.
*/
apiKey: string
/**
* Base URL endpoint for the Novu API.
*/
apiBaseUrl: string
/**
* Novu API client constructor.
*/
constructor({
apiKey = process.env.NOVU_API_KEY,
apiBaseUrl = NOVU_API_BASE_URL,

Wyświetl plik

@ -94,6 +94,70 @@ export interface TwilioConversationMessages {
}
}
/**
* Participant Conversation Resource.
*
* This interface represents a participant in a conversation, along with the conversation details.
*/
interface ParticipantConversation {
/** The unique ID of the Account responsible for this conversation. */
account_sid: string
/** The unique ID of the Conversation Service this conversation belongs to. */
chat_service_sid: string
/** The unique ID of the Participant. */
participant_sid: string
/** The unique string that identifies the conversation participant as Conversation User. */
participant_user_sid: string
/**
* A unique string identifier for the conversation participant as Conversation User.
* This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate.
*/
participant_identity: string
/**
* Information about how this participant exchanges messages with the conversation.
* A JSON parameter consisting of type and address fields of the participant.
*/
participant_messaging_binding: object
/** The unique ID of the Conversation this Participant belongs to. */
conversation_sid: string
/** An application-defined string that uniquely identifies the Conversation resource. */
conversation_unique_name: string
/** The human-readable name of this conversation, limited to 256 characters. */
conversation_friendly_name: string
/**
* An optional string metadata field you can use to store any data you wish.
* The string value must contain structurally valid JSON if specified.
*/
conversation_attributes: string
/** The date that this conversation was created, given in ISO 8601 format. */
conversation_date_created: string
/** The date that this conversation was last updated, given in ISO 8601 format. */
conversation_date_updated: string
/** Identity of the creator of this Conversation. */
conversation_created_by: string
/** The current state of this User Conversation. One of inactive, active or closed. */
conversation_state: 'inactive' | 'active' | 'closed'
/** Timer date values representing state update for this conversation. */
conversation_timers: object
/** Contains absolute URLs to access the participant and conversation of this conversation. */
links: { participant: string; conversation: string }
}
export type TwilioSendAndWaitOptions = {
/**
* The recipient's phone number in E.164 format (e.g. +14565551234).
@ -204,6 +268,31 @@ export class TwilioConversationClient {
return this.api.delete(`Conversations/${conversationSid}`)
}
/**
* Removes a participant from a conversation.
*/
async removeParticipant({
conversationSid,
participantSid
}: {
conversationSid: string
participantSid: string
}) {
return this.api.delete(
`Conversations/${conversationSid}/Participants/${participantSid}`
)
}
/**
* Fetches all conversations a participant as identified by their phone number is a part of.
*/
async findParticipantConversations(participantPhoneNumber: string) {
const encodedPhoneNumber = encodeURIComponent(participantPhoneNumber)
return this.api
.get(`ParticipantConversations?Address=${encodedPhoneNumber}`)
.json<{ conversations: ParticipantConversation[] }>()
}
/**
* Creates a new conversation.
*/
@ -333,7 +422,22 @@ export class TwilioConversationClient {
)
const { sid: conversationSid } = await this.createConversation(name)
await this.addParticipant({ conversationSid, recipientPhoneNumber })
// Find and remove participant from conversation they are currently in, if any:
const { conversations } = await this.findParticipantConversations(
recipientPhoneNumber
)
for (const conversation of conversations) {
await this.removeParticipant({
conversationSid: conversation.conversation_sid,
participantSid: conversation.participant_sid
})
}
const { sid: participantSid } = await this.addParticipant({
conversationSid,
recipientPhoneNumber
})
await this.sendTextWithChunking({ conversationSid, text })
const start = Date.now()
@ -341,7 +445,7 @@ export class TwilioConversationClient {
do {
if (aborted) {
await this.deleteConversation(conversationSid)
await this.removeParticipant({ conversationSid, participantSid })
const reason = stopSignal?.reason || 'Aborted waiting for reply'
if (reason instanceof Error) {
@ -360,7 +464,7 @@ export class TwilioConversationClient {
const candidate = candidates[candidates.length - 1]
if (validate(candidate)) {
await this.deleteConversation(conversationSid)
await this.removeParticipant({ conversationSid, participantSid })
return candidate
}
@ -377,7 +481,7 @@ export class TwilioConversationClient {
await sleep(intervalMs)
} while (Date.now() - start < timeoutMs)
await this.deleteConversation(conversationSid)
await this.removeParticipant({ conversationSid, participantSid })
throw new Error('Twilio timeout waiting for reply')
}
}