kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
refactor: avoid deleting conversations
rodzic
91d3d21854
commit
9bcc624569
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue