diff --git a/.eslintrc.json b/.eslintrc.json index 858624e12d..7cce6a76c7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -11,6 +11,7 @@ "rules": { "@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", "header/header": [ 2, "block", diff --git a/src/group/Group.ts b/src/group/Group.ts new file mode 100644 index 0000000000..29e75dcf59 --- /dev/null +++ b/src/group/Group.ts @@ -0,0 +1,259 @@ +/*! + * 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 { assertGetChat, assertWid } from '../assert'; +import { WPPError } from '../util'; +import * as webpack from '../webpack'; +import { ParticipantModel, Wid } from '../whatsapp'; +import { + addParticipants, + demoteParticipants, + promoteParticipants, + removeParticipants, +} from '../whatsapp/functions'; +import { ChatEventTypes as GroupEventTypes } from './types'; + +const debug = Debug('WPP:group'); + +export class Group extends Emittery { + constructor() { + super(); + webpack.onInjected(() => this.initialize()); + } + + async initialize() { + debug('initialized'); + } + + private ensureGroup(groupId: string | Wid) { + const groupChat = assertGetChat(groupId); + + if (!groupChat.isGroup) { + throw new WPPError( + 'not_a_group', + `Chat ${groupChat.id._serialized} is not a group` + ); + } + + return groupChat; + } + + iAmAdmin(groupId: string | Wid) { + const groupChat = this.ensureGroup(groupId); + return groupChat.groupMetadata!.participants.iAmAdmin(); + } + + iAmMember(groupId: string | Wid) { + const groupChat = this.ensureGroup(groupId); + return groupChat.groupMetadata!.participants.iAmMember(); + } + + iAmRestrictedMember(groupId: string | Wid) { + const groupChat = this.ensureGroup(groupId); + return groupChat.groupMetadata!.participants.iAmRestrictedMember(); + } + + iAmSuperAdmin(groupId: string | Wid) { + const groupChat = this.ensureGroup(groupId); + return groupChat.groupMetadata!.participants.iAmMember(); + } + + private ensureGroupAndParticipants( + groupId: string | Wid, + participantsIds: (string | Wid) | (string | Wid)[], + createIfNotExists = false + ) { + const groupChat = this.ensureGroup(groupId); + + if (!groupChat.groupMetadata!.participants.iAmAdmin()) { + throw new WPPError( + 'group_you_are_not_admin', + `You are not admin in ${groupChat.id._serialized}` + ); + } + + if (!Array.isArray(participantsIds)) { + participantsIds = [participantsIds]; + } + + const wids = participantsIds.map(assertWid); + + const participants = wids.map((wid) => { + let participant = groupChat.groupMetadata?.participants.get(wid); + + if (!participant && createIfNotExists) { + participant = new ParticipantModel({ + id: wid, + }); + } + + if (!participant) { + throw new WPPError( + 'group_participant_not_found', + `Chat ${groupChat.id._serialized} is not a group` + ); + } + + return participant; + }); + + return { + groupChat, + participants, + }; + } + + canAdd(groupId: string | Wid) { + const groupChat = this.ensureGroup(groupId); + return groupChat.groupMetadata!.participants.canAdd(); + } + + canDemote( + groupId: string | Wid, + participantsIds: (string | Wid) | (string | Wid)[] + ) { + const { groupChat, participants } = this.ensureGroupAndParticipants( + groupId, + participantsIds + ); + + return participants.every((p) => + groupChat.groupMetadata!.participants.canDemote(p) + ); + } + + canPromote( + groupId: string | Wid, + participantsIds: (string | Wid) | (string | Wid)[] + ) { + const { groupChat, participants } = this.ensureGroupAndParticipants( + groupId, + participantsIds + ); + + return participants.every((p) => + groupChat.groupMetadata!.participants.canPromote(p) + ); + } + + canRemove( + groupId: string | Wid, + participantsIds: (string | Wid) | (string | Wid)[] + ) { + const { groupChat, participants } = this.ensureGroupAndParticipants( + groupId, + participantsIds + ); + + return participants.every((p) => + groupChat.groupMetadata!.participants.canRemove(p) + ); + } + + async addParticipants( + groupId: string | Wid, + participantsIds: (string | Wid) | (string | Wid)[] + ): Promise { + const { groupChat, participants } = this.ensureGroupAndParticipants( + groupId, + participantsIds, + true + ); + + if ( + participants.some((p) => groupChat.groupMetadata?.participants.get(p.id)) + ) { + throw new WPPError( + 'group_participant_already_a_group_member', + `Group ${groupChat.id._serialized}: Group participant already a group member` + ); + } + + return addParticipants(groupChat, participants); + } + + async removeParticipants( + groupId: string | Wid, + participantsIds: (string | Wid) | (string | Wid)[] + ): Promise { + const { groupChat, participants } = this.ensureGroupAndParticipants( + groupId, + participantsIds + ); + + if ( + participants.some( + (p) => !groupChat.groupMetadata?.participants.canRemove(p) + ) + ) { + throw new WPPError( + 'group_participant_is_not_a_group_member', + `Group ${groupChat.id._serialized}: Group participant is not a group member` + ); + } + + return removeParticipants(groupChat, participants); + } + + async promoteParticipants( + groupId: string | Wid, + participantsIds: (string | Wid) | (string | Wid)[] + ): Promise { + const { groupChat, participants } = this.ensureGroupAndParticipants( + groupId, + participantsIds + ); + + if ( + participants.some( + (p) => !groupChat.groupMetadata?.participants.canPromote(p) + ) + ) { + throw new WPPError( + 'group_participant_is_already_a_group_admin', + `Group ${groupChat.id._serialized}: Group participant is already a group admin` + ); + } + + return promoteParticipants(groupChat, participants); + } + + async demoteParticipants( + groupId: string | Wid, + participantsIds: (string | Wid) | (string | Wid)[] + ): Promise { + const { groupChat, participants } = this.ensureGroupAndParticipants( + groupId, + participantsIds + ); + + if ( + participants.some( + (p) => !groupChat.groupMetadata?.participants.canDemote(p) + ) + ) { + throw new WPPError( + 'group_participant_is_already_not_a_group_admin', + `Group ${groupChat.id._serialized}: Group participant is already not a group admin` + ); + } + + return demoteParticipants(groupChat, participants); + } +} diff --git a/src/group/index.ts b/src/group/index.ts new file mode 100644 index 0000000000..6890efeb8a --- /dev/null +++ b/src/group/index.ts @@ -0,0 +1,22 @@ +/*! + * 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 { Group } from './Group'; + +export * from './Group'; +export * from './types'; + +export default new Group(); diff --git a/src/group/types.ts b/src/group/types.ts new file mode 100644 index 0000000000..95f049d0f4 --- /dev/null +++ b/src/group/types.ts @@ -0,0 +1,63 @@ +/*! + * 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 GetMessagesOptions { + count?: number; + direction?: 'after' | 'before'; + id?: string; +} + +export interface SendMessageOptions { + waitForAck?: boolean; + createChat?: boolean; +} + +export interface MessageButtonsOptions { + buttons?: Array<{ + id: string; + text: string; + }>; + title?: string; + footer?: string; +} + +export interface ListMessageOptions extends SendMessageOptions { + buttonText: string; + description: string; + sections: Array<{ + title: string; + rows: Array<{ + rowId: string; + title: string; + description: string; + }>; + }>; +} + +export type TextMessageOptions = SendMessageOptions & MessageButtonsOptions; + +export type AllMessageOptions = SendMessageOptions & + MessageButtonsOptions & + Partial; + +export type RawMessage = ModelPropertiesContructor; diff --git a/src/index.ts b/src/index.ts index f89e2d78c4..0b556fe70e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,14 +23,16 @@ import './patch'; import auth from './auth'; import blocklist from './blocklist'; import chat from './chat'; +import group from './group'; import status from './status'; import * as webpack from './webpack'; -export { auth, blocklist, chat, status, webpack }; +export { auth, blocklist, chat, group, status, webpack }; export * as Auth from './auth'; export * as Chat from './chat'; export * as config from './config'; +export * as Group from './group'; export * as Status from './status'; export { isInjected, isReady } from './webpack'; export * as whatsapp from './whatsapp'; diff --git a/src/whatsapp/collections/ParticipantCollection.ts b/src/whatsapp/collections/ParticipantCollection.ts new file mode 100644 index 0000000000..45c7465111 --- /dev/null +++ b/src/whatsapp/collections/ParticipantCollection.ts @@ -0,0 +1,46 @@ +/*! + * 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 { exportModule } from '../exportModule'; +import { ParticipantModel } from '../models'; +import { Collection } from './Collection'; + +/** + * @whatsapp 2.2138.10:16748 + */ +export declare class ParticipantCollection extends Collection { + static model: ParticipantModel; + static comparator(): any; + canAdd(): boolean; + canPromote(participant: ParticipantModel): boolean; + canDemote(participant: ParticipantModel): boolean; + canRemove(participant: ParticipantModel): boolean; + canVerifyIdentity(participant: ParticipantModel): boolean; + iAmMember(): boolean; + iAmRestrictedMember(): boolean; + iAmAdmin(): boolean; + iAmSuperAdmin(): boolean; + getAdmins(): ParticipantModel[]; + getSuperAdmin(): ParticipantModel; +} + +exportModule( + exports, + { + ParticipantCollection: 'default', + }, + (m) => m.default.model === ParticipantModel +); diff --git a/src/whatsapp/collections/index.ts b/src/whatsapp/collections/index.ts index e43159a891..98a5e3cab3 100644 --- a/src/whatsapp/collections/index.ts +++ b/src/whatsapp/collections/index.ts @@ -39,6 +39,7 @@ export * from './MsgInfoCollection'; export * from './MuteCollection'; export * from './OrderCollection'; export * from './OrderItemCollection'; +export * from './ParticipantCollection'; export * from './PresenceCollection'; export * from './ProductCollCollection'; export * from './ProductCollection'; diff --git a/src/whatsapp/functions/groupParticipants.ts b/src/whatsapp/functions/groupParticipants.ts new file mode 100644 index 0000000000..84016ab100 --- /dev/null +++ b/src/whatsapp/functions/groupParticipants.ts @@ -0,0 +1,66 @@ +/*! + * 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 { ChatModel, ParticipantModel } from '..'; +import { exportModule } from '../exportModule'; + +/** + * @whatsapp 2.2138.10:97449 + */ +export declare function addParticipants( + group: ChatModel, + participants: ParticipantModel[] +): Promise; + +/** + * @whatsapp 2.2138.10:97449 + */ +export declare function removeParticipants( + group: ChatModel, + participants: ParticipantModel[] +): Promise; + +/** + * @whatsapp 2.2138.10:97449 + */ +export declare function promoteParticipants( + group: ChatModel, + participants: ParticipantModel[] +): Promise; + +/** + * @whatsapp 2.2138.10:97449 + */ +export declare function demoteParticipants( + group: ChatModel, + participants: ParticipantModel[] +): Promise; + +exportModule( + exports, + { + addParticipants: 'addParticipants', + removeParticipants: 'removeParticipants', + promoteParticipants: 'promoteParticipants', + demoteParticipants: 'demoteParticipants', + }, + (m) => + m.addParticipants && + m.removeParticipants && + m.promoteParticipants && + m.demoteParticipants && + !m.updateParticipants +); diff --git a/src/whatsapp/functions/index.ts b/src/whatsapp/functions/index.ts index 165cda6bea..a93f66b9c6 100644 --- a/src/whatsapp/functions/index.ts +++ b/src/whatsapp/functions/index.ts @@ -18,6 +18,7 @@ export * from './addAndSendMsgToChat'; export * from './blockContact'; export * from './findChat'; export * from './getOrGenerate'; +export * from './groupParticipants'; export * from './isAuthenticated'; export * from './msgFindQuery'; export * from './randomId'; diff --git a/src/whatsapp/models/ChatModel.ts b/src/whatsapp/models/ChatModel.ts index 9ad4890e48..7a4d7673da 100644 --- a/src/whatsapp/models/ChatModel.ts +++ b/src/whatsapp/models/ChatModel.ts @@ -17,6 +17,7 @@ import { ChatCollection } from '../collections'; import { exportProxyModel } from '../exportModule'; import { MsgKey, Wid } from '../misc'; +import { GroupMetadataModel } from '.'; import { ModelOptions, ModelPropertiesContructor, ModelProxy } from './Model'; import { ModelChatBase, PropsChatBase, SessionChatBase } from './ModelChatBase'; @@ -64,7 +65,7 @@ interface Session extends SessionChatBase { composeQuotedMsg?: any; composeQuotedMsgRemoteJid?: any; quotedMsgAdminGroupJid?: any; - groupMetadata?: any; + groupMetadata?: GroupMetadataModel; presence?: any; mute?: any; contact?: any; diff --git a/src/whatsapp/models/GroupMetadataModel.ts b/src/whatsapp/models/GroupMetadataModel.ts index 3ec1a50b31..aa349ede88 100644 --- a/src/whatsapp/models/GroupMetadataModel.ts +++ b/src/whatsapp/models/GroupMetadataModel.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { ParticipantCollection } from '..'; import { exportProxyModel } from '../exportModule'; import { Wid } from '../misc'; import { @@ -64,6 +65,7 @@ export declare interface GroupMetadataModel */ export declare class GroupMetadataModel extends Model { idClass: typeof Wid; + participants: ParticipantCollection; constructor( proterties?: ModelPropertiesContructor, options?: ModelOptions