diff --git a/src/assert/assertChat.ts b/src/assert/assertChat.ts new file mode 100644 index 0000000000..3cf129e6cf --- /dev/null +++ b/src/assert/assertChat.ts @@ -0,0 +1,45 @@ +/*! + * Copyright 2021 WPPConnect Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Chat from '../chat'; +import { WPPError } from '../util'; +import { ChatModel, Wid } from '../whatsapp'; + +export class InvalidChat extends WPPError { + constructor(readonly id: string | { _serialized: string }) { + super('chat_not_found', `Chat not found for ${id}`); + } +} + +export async function assertFindChat(id: string | Wid): Promise { + const chat = await Chat.find(id); + + if (!chat) { + throw new InvalidChat(id); + } + + return chat; +} + +export function assertGetChat(id: string | Wid): ChatModel { + const chat = Chat.get(id); + + if (!chat) { + throw new InvalidChat(id); + } + + return chat; +} diff --git a/src/assert/assertWid.ts b/src/assert/assertWid.ts new file mode 100644 index 0000000000..ea8b0ac74a --- /dev/null +++ b/src/assert/assertWid.ts @@ -0,0 +1,34 @@ +/*! + * Copyright 2021 WPPConnect Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createWid, WPPError } from '../util'; +import { Wid } from '../whatsapp'; + +export class InvalidWid extends WPPError { + constructor(readonly id: string | { _serialized: string }) { + super('invalid_wid', `Invalid WID value for ${id}`); + } +} + +export function assertWid(id: string | { _serialized: string }): Wid { + const wid = createWid(id); + + if (!wid) { + throw new InvalidWid(id); + } + + return wid; +} diff --git a/src/assert/index.ts b/src/assert/index.ts new file mode 100644 index 0000000000..c53bc8f26a --- /dev/null +++ b/src/assert/index.ts @@ -0,0 +1,18 @@ +/*! + * Copyright 2021 WPPConnect Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './assertChat'; +export * from './assertWid'; diff --git a/src/chat/Chat.ts b/src/chat/Chat.ts new file mode 100644 index 0000000000..a3b051e15e --- /dev/null +++ b/src/chat/Chat.ts @@ -0,0 +1,135 @@ +/*! + * Copyright 2021 WPPConnect Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Debug from 'debug'; +import Emittery from 'emittery'; + +import { assertFindChat, assertGetChat, assertWid } from '../assert'; +import * as webpack from '../webpack'; +import { + ChatModel, + ChatStore, + ClockSkew, + Constants, + MsgKey, + UserPrefs, + Wid, +} from '../whatsapp'; +import { + addAndSendMsgToChat, + findChat, + randomMessageId, +} from '../whatsapp/functions'; +import { ChatRawMessage } from '.'; +import { ChatEventTypes, ChatSendMessageOptions } from './types'; + +const debugChat = Debug('WPP:chat'); +const debugMessage = Debug('WPP:message'); + +export class Chat extends Emittery { + constructor() { + super(); + webpack.onInjected(() => this.initialize()); + } + + async initialize() { + ChatStore.on(Constants.COLLECTION_HAS_SYNCED, () => { + debugChat(Constants.COLLECTION_HAS_SYNCED); + }); + + debugChat('initialized'); + } + + async find(chatId: string | Wid): Promise { + const wid = assertWid(chatId); + return findChat(wid); + } + + get(chatId: string | Wid): ChatModel | undefined { + const wid = assertWid(chatId); + return ChatStore.get(wid); + } + + async sendRawMessage( + chatId: any, + message: ChatRawMessage, + options: ChatSendMessageOptions = {} + ): Promise { + const chat = options.createChat + ? await assertFindChat(chatId) + : assertGetChat(chatId); + + message = Object.assign( + {}, + { + t: ClockSkew.globalUnixTime(), + from: UserPrefs.getMaybeMeUser(), + to: chat.id, + self: 'out', + isNewMsg: true, + local: true, + ack: Constants.ACK.CLOCK, + }, + message + ); + + if (!message.id) { + message.id = new MsgKey({ + from: UserPrefs.getMaybeMeUser(), + to: chat.id, + id: randomMessageId(), + selfDir: 'out', + }); + } + + debugMessage(`sending message (${message.type}) with id ${message.id}`); + const result = await addAndSendMsgToChat(chat, message); + debugMessage(`message ${message.id} queued`); + + const finalMessage = await result[0]; + + if (options.waitForAck) { + debugMessage(`waiting ack for ${message.id}`); + + await result[1]; + + debugMessage(`ack received for ${message.id} (ACK: ${finalMessage.ack})`); + } + + return { + id: finalMessage.id.toString(), + ack: finalMessage.ack, + }; + } + + async sendTextMessage( + chatId: any, + content: any, + options: ChatSendMessageOptions = {} + ): Promise { + const message: ChatRawMessage = { + body: content, + type: 'chat', + subtype: null, + urlText: null, + urlNumber: null, + }; + + const result = await this.sendRawMessage(chatId, message, options); + + return result; + } +} diff --git a/src/chat/index.ts b/src/chat/index.ts index 0d9e8eda69..f32206edc9 100644 --- a/src/chat/index.ts +++ b/src/chat/index.ts @@ -14,66 +14,9 @@ * limitations under the License. */ -import * as util from '../util'; -import { - ChatModel, - ChatStore, - ClockSkew, - Constants, - ModelPropertiesContructor, - MsgKey, - MsgModel, - UserPrefs, -} from '../whatsapp'; -import { addAndSendMsgToChat, randomMessageId } from '../whatsapp/functions'; +import { Chat } from './Chat'; -export function get(chatId: string): ChatModel | undefined { - const wid = util.createWid(chatId); +export * from './Chat'; +export * from './types'; - if (!wid) { - throw 'invalid id'; - } - - return ChatStore.get(wid); -} - -export async function sendMessage( - chatId: any, - content: any, - options = {} -): Promise { - console.log(options); - - const chat = get(chatId); - - if (!chat) { - return null; - } - - const newMsgId = new MsgKey({ - from: UserPrefs.getMaybeMeUser(), - to: chat.id, - id: randomMessageId(), - selfDir: 'out', - }); - - const message: ModelPropertiesContructor = { - id: newMsgId, - body: content, - type: 'chat', - subtype: null, - t: ClockSkew.globalUnixTime(), - from: UserPrefs.getMaybeMeUser(), - to: chat.id, - self: 'out', - isNewMsg: true, - local: true, - ack: Constants.ACK.CLOCK, - urlText: null, - urlNumber: null, - }; - - const result = await addAndSendMsgToChat(chat, message); - - return result; -} +export default new Chat(); diff --git a/src/chat/types.ts b/src/chat/types.ts new file mode 100644 index 0000000000..b59350dfe7 --- /dev/null +++ b/src/chat/types.ts @@ -0,0 +1,29 @@ +/*! + * Copyright 2021 WPPConnect Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ModelPropertiesContructor, MsgModel } from '../whatsapp'; + +export interface ChatEventTypes { + change: string; + idle: undefined; +} + +export interface ChatSendMessageOptions { + waitForAck?: boolean; + createChat?: boolean; +} + +export type ChatRawMessage = ModelPropertiesContructor; diff --git a/src/index.ts b/src/index.ts index 7845e4ff9a..545b220c98 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,7 +20,7 @@ import * as webpack from './webpack'; export { webpack }; export { default as auth } from './auth'; -export * as chat from './chat'; +export { default as chat } from './chat'; export { isInjected, isReady } from './webpack'; export * as whatsapp from './whatsapp'; diff --git a/src/util/errors.ts b/src/util/errors.ts new file mode 100644 index 0000000000..559a3458f1 --- /dev/null +++ b/src/util/errors.ts @@ -0,0 +1,21 @@ +/*! + * Copyright 2021 WPPConnect Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class WPPError extends Error { + constructor(readonly code: string, message: string) { + super(message); + } +} diff --git a/src/util/index.ts b/src/util/index.ts index 3cd577dffe..bcdf9a7f6d 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -15,4 +15,5 @@ */ export * from './createWid'; +export * from './errors'; export * from './types'; diff --git a/src/whatsapp/functions/addAndSendMsgToChat.ts b/src/whatsapp/functions/addAndSendMsgToChat.ts index b44983282a..2c1af742d9 100644 --- a/src/whatsapp/functions/addAndSendMsgToChat.ts +++ b/src/whatsapp/functions/addAndSendMsgToChat.ts @@ -20,7 +20,7 @@ import { ChatModel, ModelPropertiesContructor, MsgModel } from '../models'; export declare function addAndSendMsgToChat( chat: ChatModel, message: ModelPropertiesContructor -): Promise; +): Promise<[Promise, Promise]>; exportModule( exports, diff --git a/src/whatsapp/functions/findChat.ts b/src/whatsapp/functions/findChat.ts new file mode 100644 index 0000000000..8f74a00aa0 --- /dev/null +++ b/src/whatsapp/functions/findChat.ts @@ -0,0 +1,29 @@ +/*! + * Copyright 2021 WPPConnect Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Wid } from '..'; +import { exportModule } from '../exportModule'; +import { ChatModel } from '../models'; + +export declare function findChat(wid: Wid): Promise; + +exportModule( + exports, + { + findChat: 'findChat', + }, + (m) => m.findChat +); diff --git a/src/whatsapp/functions/index.ts b/src/whatsapp/functions/index.ts index fa2305d683..84365d0243 100644 --- a/src/whatsapp/functions/index.ts +++ b/src/whatsapp/functions/index.ts @@ -15,6 +15,7 @@ */ export * from './addAndSendMsgToChat'; +export * from './findChat'; export * from './getOrGenerate'; export * from './isAuthenticated'; export * from './randomId'; diff --git a/src/whatsapp/misc/Constants.ts b/src/whatsapp/misc/Constants.ts index 06216f6918..4be85a1745 100644 --- a/src/whatsapp/misc/Constants.ts +++ b/src/whatsapp/misc/Constants.ts @@ -186,19 +186,19 @@ export declare const Constants: { CONTACT_US_MIN_DESC_LENGTH: number; CONTACT_US_MAX_DESC_LENGTH: number; ACK: { - MD_DOWNGRADE: number; - INACTIVE: number; - CONTENT_UNUPLOADABLE: number; - CONTENT_TOO_BIG: number; - CONTENT_GONE: number; - EXPIRED: number; - FAILED: number; - CLOCK: number; - SENT: number; - RECEIVED: number; - READ: number; - PLAYED: number; - PEER: number; + MD_DOWNGRADE: -7; + INACTIVE: -6; + CONTENT_UNUPLOADABLE: -5; + CONTENT_TOO_BIG: -4; + CONTENT_GONE: -3; + EXPIRED: -2; + FAILED: -1; + CLOCK: 0; + SENT: 1; + RECEIVED: 2; + READ: 3; + PLAYED: 4; + PEER: 5; }; ACK_STRING: { SENDER: string; diff --git a/src/whatsapp/models/MsgModel.ts b/src/whatsapp/models/MsgModel.ts index 1c126b54c0..567955e242 100644 --- a/src/whatsapp/models/MsgModel.ts +++ b/src/whatsapp/models/MsgModel.ts @@ -19,7 +19,7 @@ */ import { MsgCollection } from '../collections'; import { exportProxyModel } from '../exportModule'; -import { MsgKey } from '../misc'; +import { MsgKey, Wid } from '../misc'; import { Model, ModelOptions, @@ -30,15 +30,15 @@ import { interface Props { id: MsgKey; rowId?: any; - body?: any; - type?: any; - subtype?: any; - t?: any; + body?: string; + type?: string; + subtype?: string | null; + t?: number; notifyName?: any; - from?: any; - to?: any; + from?: Wid; + to?: Wid; author?: any; - self?: any; + self?: string; /** * See {@link Constants} */ @@ -129,8 +129,8 @@ interface Props { selectedId?: any; selectedIndex?: any; multicast?: any; - urlText?: any; - urlNumber?: any; + urlText?: string | null; + urlNumber?: string | null; clearMedia?: any; isVcardOverMmsDocument: boolean; vcardList?: any;