From 8bcec211528ddfcacef6330afe90408788e7ea9f Mon Sep 17 00:00:00 2001 From: jrcleber Date: Mon, 28 Nov 2022 00:29:47 +0000 Subject: [PATCH] =?UTF-8?q?codechat=20=F0=9F=94=A5=20whatsapp-api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 25 + .eslintignore | 2 + .eslintrc.js | 42 + .gitignore | 32 + .prettierrc.js | 9 + .vscode/settings.json | 11 + Dockerfile | 16 + LICENSE | 674 +++++++++++++ README.md | 230 +++++ build | 9 + docker-compose.yaml | 18 + index.ts | 3 + instances/.gitkeep | 0 mongodb/docker-compose.yaml | 31 + package.json | 89 ++ postman.json | 630 ++++++++++++ public/images/code.png | Bin 0 -> 14339 bytes public/images/codechat-logo.png | Bin 0 -> 122829 bytes public/images/cover.png | Bin 0 -> 110630 bytes src/config/env.config.ts | 107 ++ src/config/error.config.ts | 21 + src/config/event.config.ts | 7 + src/config/logger.config.ts | 126 +++ src/config/path.config.ts | 6 + src/db/db.connect.ts | 22 + src/env.yml | 103 ++ src/exceptions/400.exeption.ts | 11 + src/exceptions/401.exception.ts | 11 + src/exceptions/403.exception.ts | 11 + src/exceptions/404.exception.ts | 11 + src/exceptions/500.exception.ts | 11 + src/exceptions/index.ts | 5 + src/main.ts | 112 +++ src/utils/use-multi-file-auth-state-db.ts | 102 ++ src/validate/validate.schema.ts | 482 +++++++++ src/whatsapp/abstract/abstract.repository.ts | 55 ++ src/whatsapp/abstract/abstract.router.ts | 96 ++ src/whatsapp/controllers/chat.controller.ts | 37 + src/whatsapp/controllers/group.controller.ts | 44 + .../controllers/instance.controller.ts | 72 ++ .../controllers/sendMessage.controller.ts | 51 + src/whatsapp/controllers/views.controller.ts | 44 + .../controllers/webhook.controller.ts | 20 + src/whatsapp/dto/chat.dto.ts | 27 + src/whatsapp/dto/group.dto.ts | 15 + src/whatsapp/dto/instance.dto.ts | 3 + src/whatsapp/dto/sendMessage.dto.ts | 95 ++ src/whatsapp/dto/webhook.dto.ts | 4 + src/whatsapp/guards/auth.guard.ts | 76 ++ src/whatsapp/guards/instance.guard.ts | 41 + src/whatsapp/models/chat.model.ts | 16 + src/whatsapp/models/contact.model.ts | 19 + src/whatsapp/models/message.model.ts | 62 ++ src/whatsapp/repository/chat.repository.ts | 36 + src/whatsapp/repository/contact.repository.ts | 77 ++ src/whatsapp/repository/index.repository.ts | 13 + src/whatsapp/repository/message.repository.ts | 81 ++ .../repository/messageUp.repository.ts | 77 ++ src/whatsapp/routers/chat.router.ts | 85 ++ src/whatsapp/routers/group.router.ts | 90 ++ src/whatsapp/routers/index.router.ts | 37 + src/whatsapp/routers/instance.router.ts | 83 ++ src/whatsapp/routers/sendMessage.router.ts | 101 ++ src/whatsapp/routers/view.router.ts | 17 + src/whatsapp/routers/webhook.router.ts | 35 + src/whatsapp/services/auth.service.ts | 121 +++ src/whatsapp/services/monitor.service.ts | 166 ++++ src/whatsapp/services/webhook.service.ts | 35 + src/whatsapp/services/whatsapp.service.ts | 916 ++++++++++++++++++ src/whatsapp/types/wa.types.ts | 45 + src/whatsapp/whatsapp.module.ts | 63 ++ store/auth/apikey/.gitkeep | 0 store/auth/jwt/.gitkeep | 0 store/auth/jwt/codechat.json | 1 + store/contacts/.gitkeep | 0 store/message-up/.gitkeep | 0 store/messages/.gitkeep | 0 store/webhook/.gitkeep | 0 tsconfig.json | 22 + views/qrcode.hbs | 82 ++ 80 files changed, 5929 insertions(+) create mode 100644 .dockerignore create mode 100644 .eslintignore create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 .prettierrc.js create mode 100644 .vscode/settings.json create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build create mode 100644 docker-compose.yaml create mode 100644 index.ts create mode 100644 instances/.gitkeep create mode 100644 mongodb/docker-compose.yaml create mode 100644 package.json create mode 100644 postman.json create mode 100644 public/images/code.png create mode 100644 public/images/codechat-logo.png create mode 100644 public/images/cover.png create mode 100644 src/config/env.config.ts create mode 100644 src/config/error.config.ts create mode 100644 src/config/event.config.ts create mode 100644 src/config/logger.config.ts create mode 100644 src/config/path.config.ts create mode 100644 src/db/db.connect.ts create mode 100644 src/env.yml create mode 100644 src/exceptions/400.exeption.ts create mode 100644 src/exceptions/401.exception.ts create mode 100644 src/exceptions/403.exception.ts create mode 100644 src/exceptions/404.exception.ts create mode 100644 src/exceptions/500.exception.ts create mode 100644 src/exceptions/index.ts create mode 100644 src/main.ts create mode 100644 src/utils/use-multi-file-auth-state-db.ts create mode 100644 src/validate/validate.schema.ts create mode 100644 src/whatsapp/abstract/abstract.repository.ts create mode 100644 src/whatsapp/abstract/abstract.router.ts create mode 100644 src/whatsapp/controllers/chat.controller.ts create mode 100644 src/whatsapp/controllers/group.controller.ts create mode 100644 src/whatsapp/controllers/instance.controller.ts create mode 100644 src/whatsapp/controllers/sendMessage.controller.ts create mode 100644 src/whatsapp/controllers/views.controller.ts create mode 100644 src/whatsapp/controllers/webhook.controller.ts create mode 100644 src/whatsapp/dto/chat.dto.ts create mode 100644 src/whatsapp/dto/group.dto.ts create mode 100644 src/whatsapp/dto/instance.dto.ts create mode 100644 src/whatsapp/dto/sendMessage.dto.ts create mode 100644 src/whatsapp/dto/webhook.dto.ts create mode 100644 src/whatsapp/guards/auth.guard.ts create mode 100644 src/whatsapp/guards/instance.guard.ts create mode 100644 src/whatsapp/models/chat.model.ts create mode 100644 src/whatsapp/models/contact.model.ts create mode 100644 src/whatsapp/models/message.model.ts create mode 100644 src/whatsapp/repository/chat.repository.ts create mode 100644 src/whatsapp/repository/contact.repository.ts create mode 100644 src/whatsapp/repository/index.repository.ts create mode 100644 src/whatsapp/repository/message.repository.ts create mode 100644 src/whatsapp/repository/messageUp.repository.ts create mode 100644 src/whatsapp/routers/chat.router.ts create mode 100644 src/whatsapp/routers/group.router.ts create mode 100644 src/whatsapp/routers/index.router.ts create mode 100644 src/whatsapp/routers/instance.router.ts create mode 100644 src/whatsapp/routers/sendMessage.router.ts create mode 100644 src/whatsapp/routers/view.router.ts create mode 100644 src/whatsapp/routers/webhook.router.ts create mode 100644 src/whatsapp/services/auth.service.ts create mode 100644 src/whatsapp/services/monitor.service.ts create mode 100644 src/whatsapp/services/webhook.service.ts create mode 100644 src/whatsapp/services/whatsapp.service.ts create mode 100644 src/whatsapp/types/wa.types.ts create mode 100644 src/whatsapp/whatsapp.module.ts create mode 100644 store/auth/apikey/.gitkeep create mode 100644 store/auth/jwt/.gitkeep create mode 100644 store/auth/jwt/codechat.json create mode 100644 store/contacts/.gitkeep create mode 100644 store/message-up/.gitkeep create mode 100644 store/messages/.gitkeep create mode 100644 store/webhook/.gitkeep create mode 100644 tsconfig.json create mode 100644 views/qrcode.hbs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..578bd7e8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +# Compiled output +/src + +# Dev +/test +/.vs +/.vscode + +# Package +/package-lock.json + +# Docker +/Docker +/Dockerfile +/docker-compose.yml +/.dockerignore + +# Project related +/LICENCE +/README.md +/src +/.eslintignore +/.eslintrc.js +/.pretierrc.js +/.gitignore \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..cc1c56d5 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +/node-modules +/dist \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..d3545e60 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,42 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'CommonJS', + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + 'plugin:prettier/recommended' + ], + globals: { + Atomics: 'readonly', + SharedArrayBuffer: 'readonly', + }, + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: ['.eslintrc.js'], + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/ban-types': [ + 'error', + { + extendDefaults: true, + types: { + '{}': false, + Object: false, + }, + }, + ], + 'prettier/prettier': ['error', { endOfLine: 'auto' }], + }, +}; \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..165fcc2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# compiled output +/dist +/node_modules + +# Logs +logs/**.json +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Package +/yarn.lock +/package-lock.json + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# Prisma +/prisma/migrations + +# Project related +/instances/* +!/instances/.gitkeep +/test/ +/.env \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000..067abea5 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,9 @@ +module.exports = { + semi: true, + trailingComma: 'all', + singleQuote: true, + printWidth: 90, + tabWidth: 2, + bracketSameLine: true, + bracketSpacing: true +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..5bc52817 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "editor.fontSize": 13, + "editor.fontLigatures": true, + "editor.letterSpacing": 0.5, + "editor.smoothScrolling": true, + "editor.tabSize": 2, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true, + "source.fixAll": true + } +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..641b7ef0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM node:16.17.0 + +LABEL version="2.0.0" description="Api to control whatsapp features through http requests." +LABEL maintainer="Cleber Wilson" git="https://github.com/jrCleber" +LABEL contact="suporte@codechat.rest" + +RUN apt-get update -y +RUN apt-get upgrade -y + +WORKDIR /~/codechat + +COPY . . + +EXPOSE 8080 443 + +CMD [ "npm", "run", "start:prod" ] diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0da2f519 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..c46402d9 --- /dev/null +++ b/README.md @@ -0,0 +1,230 @@ +

CodeChat Api Pro

+ +It is a version of this API with advanced features like message queue management, send processing status, integration with **[n8n](https://github.com/code-chat-br/n8n-nodes-codechat)** unique models and much more! **[Check it out](https://github.com/code-chat-br/docs-codechat)** 😉! + + + +
+
+ +
+ +[![Telegram](https://img.shields.io/badge/Group-Telegram-%2333C1FF)](https://t.me/codechatBR) +[![Whatsapp](https://img.shields.io/badge/WhatsApp-message-%2322BC18)](https://api.whatsapp.com/send?phone=5531995918699) +[![License](https://img.shields.io/badge/license-GPL--3.0-orange)](./LICENSE) + +
+ +
+ +## WhatsApp-Api-NodeJs + +This code is an implementation of [Baileys](https://github.com/adiwajshing/Baileys), as a RestFull Api service, which controls whatsapp functions.
+With this one you can create multiservice chats, service bots or any other system that uses whatsapp. With this code you don't need to know javascript for nodejs , just start the server and make the language requests that you feel most comfortable with. + +## Infrastructure + +### Nvm installation + +```sh +$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash +# or +$ wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash +``` +### Docker installation \[optional\] + +```sh +$ curl -fsSL https://get.docker.com -o get-docker.sh + +$ sudo sh get-docker.sh + +$ sudo usermod -aG docker ${USER} + +$ sudo apt-get install docker-compose +``` +### Nodejs installation + +```sh +$ nvm install 16.10.0 +``` +> +> After finishing, restart the terminal to load the new information. +> +```sh +$ docker --version + +$ node --version + +$ docker-compose --version +``` +## MongoDb + +After installing docker and docker-compose, up the container. + - [compose from mongodb](./mongodb/docker-compose.yaml) + +In the same directory where the file is located, run the following command: +```sh +$ docker-compose up -d +``` +Using the database is optional. + +## Application startup + +Go to the project directory and install all dependencies.
+```sh +$ npm i +// or +$ yarn install +``` + +Finally, run the command below to start the application: +```sh +# Under development +$ npm run start +// or +$ yarn start + +# In production +$ npm run build +$ npm run start:prod +// or +$ yarn build +$ yarn start:prod +``` +## Authentication + +You can define two authentication **types** for the routes in the **[env file](./src/env.yml)**. +Authentications must be inserted in the request header. + +1. **apikey** + +2. **jwt:** A JWT is a standard for authentication and information exchange defined with a signature. + +> Authentications are generated at instance creation time. + +**Note:** There is also the possibility to define a global api key, which can access and control all instances. + +### Connection + +#### Create an instance + +##### HTTP + +```http +POST /instance/create HTTP/1.1 +Host: localhost:8080 +Content-Type: application/json +Content-Length: 34 + +{ + "instanceName": "codechat" +} +``` +##### cURL + +```bash +curl --location --request POST 'http://localhost:8080/instance/create' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "instanceName": "codechat" +}' +``` +### Response + +```ts +{ + "instance": { + "instanceName": "codechat", + "status": "created" + }, + "hash": { + "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. [...]" + + // or + // "apikey": "88513847-1B0E-4188-8D76-4A2750C9B6C3" + } +} +``` +#### Connection with qrcode + +##### HTTP + +```http +GET /instance/connect/codechat HTTP/1.1 +Host: localhost:8080 +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. [...] +``` +```http +GET /instance/connect/codechat HTTP/1.1 +Host: localhost:8080 +apikey: 88513847-1B0E-4188-8D76-4A2750C9B6C3 +``` +##### cURL + +```bash +curl --location --request GET 'http://localhost:8080/instance/connect/codechat' \ +--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. [...]' +``` +```bash +curl --location --request GET 'http://localhost:8080/instance/connect/codechat' \ +--header 'apikey: 88513847-1B0E-4188-8D76-4A2750C9B6C3' +``` + +### Response + +```ts +{ + "code": "2@nXSUgRJSBY6T0XJmiFKZ0 [...] ,XsgJhJHYa+0MPpXANdPHHt6Ke/I7O2QyXT/Lsge0uSg=", + "base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhE [...] LkMtqAAAAABJRU5ErkJggg==" +} +``` + +### App in Docker + - [Dockerfile](./Dockerfile) + - [docker-compose](./docker-compose.yaml) + +After building the application, in the same directory as the files above, run the following command: +```sh +$ docker-compose up +``` + +## Routes + - [Postman Json](./postman.json) + +## Webhook Events + +| Name | Event | TypeData | Description | +|------|-------|-----------|------------| +| QRCODE_UPDATED | qrcode.updated | json | Sends the base64 of the qrcode for reading | +| CONNECTION_UPDATE | connection.update | json | Informs the status of the connection with whatsapp | +| INSTANCE | instance | json | Informs instance loading | +| MESSAGES_SET | message.set | json | Sends a list of all your messages uploaded on whatsapp
This event occurs only once | +| MESSAGES_UPSERT | message.upsert | json | Notifies you when a message is received | +| MESSAGES_UPDATE | message.update | json | Tells you when a message is updated | +| SEND_MESSAGE | send.message | json | Notifies when a message is sent | +| CONTACTS_SET | contacts.set | json | Performs initial loading of all contacts
This event occurs only once | +| CONTACTS_UPSERT | contacts.upsert | json | Reloads all contacts with additional information
This event occurs only once | +| CONTACTS_UPDATE | contacts.update | json | Informs you when the chat is updated | +| PRESENCE_UPDATE | presence.update | json | Informs if the user is online, if he is performing some action like writing or recording and his last seen
'unavailable' | 'available' | 'composing' | 'recording' | 'paused' | +| CHATS_SET | chats.set | json | Send a list of all loaded chats | +| CHATS_UPDATE | chats.update | json | Informs you when the chat is updated | +| CHATS_UPSERT | chats.upsert | json | Sends any new chat information | + +## Env File + +See additional settings that can be applied through the **env** file by clicking **[here](./src/env.yml)**. + +## SSL + +To install the SSL certificate, follow the **[instructions](https://certbot.eff.org/instructions?ws=other&os=ubuntufocal)** below. + +# Note + +This code is in no way affiliated with WhatsApp. Use at your own discretion. Don't spam this. + +This code was produced based on the baileys library and it is still under development. +
\ No newline at end of file diff --git a/build b/build new file mode 100644 index 00000000..6b6b55e0 --- /dev/null +++ b/build @@ -0,0 +1,9 @@ +echo +echo > rm -rf ./dist +echo +rm -rf ./dist +echo > tsc +echo +tsc +cp ./src/env.yml ./dist/src +echo build success \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..a46091af --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,18 @@ +version: '3.8' + +networks: + api-net: + drive: bridge + +services: + codechatapi: + container_name: codechat + build: + context: backend + dockerfile: ./Dockerfile + + expose: + 8080:8080 + 443:443 + + command: npm run start:prod diff --git a/index.ts b/index.ts new file mode 100644 index 00000000..0abd25e8 --- /dev/null +++ b/index.ts @@ -0,0 +1,3 @@ +import { bootstrap } from './src/main'; + +bootstrap(); diff --git a/instances/.gitkeep b/instances/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/mongodb/docker-compose.yaml b/mongodb/docker-compose.yaml new file mode 100644 index 00000000..568b421b --- /dev/null +++ b/mongodb/docker-compose.yaml @@ -0,0 +1,31 @@ + +version: '3.8' + +networks: + mongo-net: + driver: bridge + +services: + mongodb: + container_name: mongodb + + # This image already has a single replica set + image: bitnami/mongodb:latest + + restart: always + volumes: + # sudo mkdir -p /data/mongodb + - /data/mongodb:/data/db + ports: + - 26712:27017 + environment: + - MONGODB_ADVERTISED_HOSTNAME=localhost + - MONGODB_REPLICA_SET_MODE=primary + - MONGODB_ROOT_USER=root + # Set a password to access the bank + - MONGODB_ROOT_PASSWORD= + - MONGODB_REPLICA_SET_KEY=9glkjQlr855kU + networks: + - mongo-net + expose: + - 26712 diff --git a/package.json b/package.json new file mode 100644 index 00000000..56535ded --- /dev/null +++ b/package.json @@ -0,0 +1,89 @@ +{ + "name": "whatsapp-api", + "version": "1.0.0", + "description": "Rest api for communication with WhatsApp", + "main": "index.js", + "scripts": { + "build": "bash ./build", + "start": "ts-node --files --transpile-only ./index.ts", + "start:prod": "node ./dist/index.js", + "start:dev": "clear && tsnd --files --transpile-only --respawn --ignore-watch node_modules ./index.ts", + "test": "clear && tsnd --files --transpile-only --respawn --ignore-watch node_modules ./test/all.test.ts" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/code-chat-br/whatsapp-api.git" + }, + "keywords": [ + "chat", + "communication", + "message", + "send message", + "whatsapp", + "js-whatsapp", + "whatsapp-api", + "whatsapp-web", + "whatsapp", + "whatsapp-chat", + "whatsapp-group", + "automation", + "multi-device", + "bot" + ], + "author": { + "name": "jrcleber", + "email": "contato@codechat.dev" + }, + "license": "GPL-3.0", + "bugs": { + "url": "https://github.com/code-chat-br/whatsapp-api/issues" + }, + "homepage": "https://github.com/code-chat-br/whatsapp-api#readme", + "dependencies": { + "@adiwajshing/baileys": "^4.4.0", + "@hapi/boom": "^10.0.0", + "@types/express-session": "^1.17.5", + "axios": "^1.1.3", + "class-validator": "^0.13.2", + "compression": "^1.7.4", + "cors": "^2.8.5", + "cross-env": "^7.0.3", + "dayjs": "^1.11.6", + "eventemitter2": "^6.4.9", + "express": "^4.18.2", + "express-async-errors": "^3.1.1", + "express-session": "^1.17.3", + "hbs": "^4.2.0", + "join": "^3.0.0", + "js-yaml": "^4.1.0", + "jsonschema": "^1.4.1", + "jsonwebtoken": "^8.5.1", + "mongoose": "^6.7.0", + "node-mime-types": "^1.1.0", + "pino": "^8.7.0", + "qrcode": "^1.5.1", + "qrcode-terminal": "^0.12.0", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@types/axios": "^0.14.0", + "@types/compression": "^1.7.2", + "@types/cors": "^2.8.12", + "@types/eventemitter2": "^4.1.0", + "@types/express": "^4.17.14", + "@types/js-yaml": "^4.0.5", + "@types/jsonwebtoken": "^8.5.9", + "@types/mongoose": "^5.11.97", + "@types/node": "^18.11.4", + "@types/qrcode": "^1.5.0", + "@types/uuid": "^8.3.4", + "@typescript-eslint/eslint-plugin": "^5.40.1", + "@typescript-eslint/parser": "^5.40.1", + "eslint": "^8.26.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.2.1", + "prettier": "^2.7.1", + "ts-node-dev": "^2.0.0", + "typescript": "^4.8.4" + } +} diff --git a/postman.json b/postman.json new file mode 100644 index 00000000..f612647c --- /dev/null +++ b/postman.json @@ -0,0 +1,630 @@ +{ + "info": { + "_postman_id": "a346ce06-3faa-47c3-88f0-4924ab4ca92d", + "name": "Api Whatsapp", + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" + }, + "item": [ + { + "name": "Instance Controller", + "item": [ + { + "name": "Views", + "item": [ + { + "name": "QrCode", + "id": "e13e73d1-ca81-46b7-82a9-717e1558284e", + "request": { + "method": "GET", + "header": [], + "url": "{{baseUrl}}/instance/qrcode/{{instance}}" + }, + "response": [] + } + ], + "id": "c58c742f-3010-4e95-99f8-28feeb37891a" + }, + { + "name": "Create Instance", + "id": "0e70f058-7b96-4ca9-a31c-408bb638c00b", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"instanceName\": \"codechat\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/instance/create" + }, + "response": [] + }, + { + "name": "Instance Connect", + "id": "39ef2c5a-bb9e-4996-94b5-689d161f5412", + "request": { + "method": "GET", + "header": [], + "url": "{{baseUrl}}/instance/connect/{{instance}}" + }, + "response": [] + }, + { + "name": "Connection Status", + "id": "7b4d3891-9a72-4b8c-9fa8-393308273b99", + "request": { + "method": "GET", + "header": [], + "url": "{{baseUrl}}/instance/connectionState/{{instance}}" + }, + "response": [] + }, + { + "name": "Logout Instance", + "id": "c852edac-88bc-4b14-8452-dc00749797f1", + "request": { + "method": "DELETE", + "header": [], + "url": "{{baseUrl}}/instance/logout/{{instance}}" + }, + "response": [] + } + ], + "id": "6513b816-9425-4ca7-b350-5f137dcf7239", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "5777fabe-31ff-4f2e-b782-6b089f990b67", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "fe0c8c67-1abf-4863-8a11-8f3dd54e6fa2", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + }, + { + "name": "Send Message Controller", + "item": [ + { + "name": "Send Text", + "id": "0989292a-7aba-4ba8-9b48-7097b3eaf218", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"number\": \"5531900000000\",\r\n \"options\": {\r\n \"delay\": 1200\r\n },\r\n \"textMessage\": {\r\n \"text\": \"ok\"\r\n }\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/message/sendText/{{instance}}" + }, + "response": [] + }, + { + "name": "Send Media", + "id": "4d17edef-f237-4f00-906d-71687a2210d8", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"number\": \"5531900000000\",\r\n \"options\": {\r\n \"delay\": 1200\r\n },\r\n \"mediaMessage\": {\r\n \"mediatype\": \"image | document | video\",\r\n \"fileName\": \"image.png\",\r\n \"caption\": \"Description\",\r\n \"media\": \"https://fonts.freepiklabs.com/storage/4886/conversions/Mistaken-preview.jpg\"\r\n }\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/message/sendMedia/{{instance}}" + }, + "response": [] + }, + { + "name": "Send Buttons", + "id": "6ad2b81b-096c-4889-9c2b-778eab259445", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"number\": \"5531997853327\",\r\n \"options\": {\r\n \"delay\": 1200\r\n },\r\n \"buttonMessage\": {\r\n \"title\": \"Tutle Button\",\r\n \"description\": \"Description Button\",\r\n \"footerText\": \"Footer Button\",\r\n \"buttons\": [\r\n {\r\n \"buttonText\": \"Click here\",\r\n \"buttonId\": \"1\"\r\n },\r\n {\r\n \"buttonText\": \"Click here\",\r\n \"buttonId\": \"2\"\r\n }\r\n ],\r\n \"mediaMessage\": {\r\n \"mediatype\": \"image | document | video\",\r\n \"media\": \"https://fonts.freepiklabs.com/storage/4886/conversions/Mistaken-preview.jpg\"\r\n }\r\n }\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/message/sendButtons/{{instance}}" + }, + "response": [] + }, + { + "name": "Send Location", + "id": "12dcb0d1-d6e1-465c-916e-8a238b1413e9", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"number\": \"5531997853327\",\r\n \"locationMessage\": {\r\n \"name\": \"Moeda - MG\",\r\n \"address\": \"🗺️ Moeda 🗺️\",\r\n \"latitude\": -20.32568196333534,\r\n \"longitude\": -44.016271276581236\r\n },\r\n \"options\": {\r\n \"delay\": 1200\r\n }\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/message/sendLocation/{{instance}}" + }, + "response": [] + }, + { + "name": "Send List", + "id": "fc5feea6-9415-4036-8c5a-c8fccaf35e43", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"number\": \"5531900000000\",\r\n \"listMessage\": {\r\n \"title\": \"List Title\",\r\n \"description\": \"List description\",\r\n \"buttonText\": \"Click Here\",\r\n \"footerText\": \"footer list\\nhttps://examplelink.com.br\",\r\n \"sections\": [\r\n {\r\n \"title\": \"Row tilte 01\",\r\n \"rows\": [\r\n {\r\n \"title\": \"Title row 01\",\r\n \"description\": \"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,\",\r\n \"rowId\": \"rowId 001\"\r\n },\r\n {\r\n \"title\": \"Title row 02\",\r\n \"description\": \"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,\",\r\n \"rowId\": \"rowId 002\"\r\n }\r\n ]\r\n },\r\n {\r\n \"title\": \"Row tilte 02\",\r\n \"rows\": [\r\n {\r\n \"title\": \"Title row 01\",\r\n \"description\": \"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,\",\r\n \"rowId\": \"rowId 001\"\r\n },\r\n {\r\n \"title\": \"Title row 02\",\r\n \"description\": \"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,\",\r\n \"rowId\": \"rowId 002\"\r\n }\r\n ]\r\n }\r\n ]\r\n },\r\n \"options\": {\r\n \"delay\": 1200\r\n }\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/message/sendList/{{instance}}" + }, + "response": [] + }, + { + "name": "Send Contact", + "id": "55802759-f91c-4963-a191-7bc6d58face5", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"number\": \"5531900000000\",\r\n \"contactMessage\": [\r\n {\r\n \"fullName\": \"Contact Name\",\r\n \"wuid\": \"5531988882222\",\r\n \"phoneNumber\": \"+55 31 9 8888-2222\"\r\n },\r\n {\r\n \"fullName\": \"Contact Name\",\r\n \"wuid\": \"5531988882222\",\r\n \"phoneNumber\": \"+55 31 9 8888-2222\"\r\n },\r\n {\r\n \"fullName\": \"Contact Name\",\r\n \"wuid\": \"5531922228888\",\r\n \"phoneNumber\": \"+55 31 9 2222-8888\"\r\n }\r\n ],\r\n \"options\": {\r\n \"delay\": 1200\r\n }\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/message/sendContact/{{instance}}" + }, + "response": [] + }, + { + "name": "Send Reaction", + "id": "94e29b21-410d-470b-925c-0739ad36efcb", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"reactionMessage\": {\r\n \"key\": {\r\n \"remoteJid\": \"123@s.whatsapp.net\",\r\n \"fromMe\": true,\r\n \"id\": \"BAE5A75CB0F39712\"\r\n },\r\n \"reaction\": \"😒\"\r\n }\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/message/sendReaction/{{instance}}" + }, + "response": [] + } + ], + "id": "254b8181-a775-4f4c-b9f8-e7b61d77fed1" + }, + { + "name": "Chat Controller", + "item": [ + { + "name": "WhatsApp Number", + "id": "d75c7dda-1473-401e-9775-69baa4ce3de3", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"numbers\": [\r\n \"5531900000000\",\r\n \"5531911111111\"\r\n ]\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/chat/whatsappNumbers/{{instance}}" + }, + "response": [] + }, + { + "name": "Read Messages", + "id": "32f9cd0c-69c8-4319-8526-a8154b2891c5", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"readMessages\": [\r\n {\r\n \"remoteJid\": \"123@s.whatsapp.net\",\r\n \"fromMe\": false,\r\n \"id\": \"80C4CE9B72F797DBC6ECD8D19B247FC9\"\r\n }\r\n ]\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/chat/markMessageAsRead/{{instance}}" + }, + "response": [] + }, + { + "name": "Archive Chat", + "id": "26364791-b1df-4abe-910d-98b295509754", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"lastMessage\": {\r\n \"key\": {\r\n \"remoteJid\": \"123@s.whatsapp.net\",\r\n \"fromMe\": false,\r\n \"id\": \"80C4CE9B72F797DBC6ECD8D19B247FC9\"\r\n }\r\n },\r\n \"archive\": false\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/chat/archiveChat/{{instance}}" + }, + "response": [] + }, + { + "name": "Find Contacts", + "id": "1ae99ab9-abca-4240-8097-a3aa067ebc90", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"where\": {\r\n \"_id\": \"\",\r\n \"pusName\": \"\",\r\n \"id\": \"\"\r\n }\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/chat/findContacts/{{instance}}" + }, + "response": [] + }, + { + "name": "Find Messages", + "id": "4754ff87-4d2a-4933-9130-769c788cf625", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"where\": {\r\n /* \r\n * \"key.fromMe\": false,\r\n * \"key.remoteJid\": \"553197853327@s.whatsapp.net\",\r\n */\r\n \"key\": {\r\n \"fromMe\": false,\r\n \"remoteJid\": \"553197853327@s.whatsapp.net\"\r\n },\r\n \"message.[...]\": { }\r\n }\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/chat/findMessages/{{instance}}" + }, + "response": [] + }, + { + "name": "Find Status Message", + "id": "b261a9c9-d41c-4e75-a537-dbdafd872d5f", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"where\": {\r\n \"_id\": \"\",\r\n \"remoteJid\": \"\",\r\n \"id\": \"\",\r\n \"fromMe\": true,\r\n \"participant\": \"\",\r\n \"status\": \"'ERROR', 'PENDING', 'SERVER_ACK', 'DELIVERY_ACK', 'READ', 'PLAYED'\"\r\n }\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/chat/findStatusMessage/{{instance}}" + }, + "response": [] + } + ], + "id": "84b58e33-0408-481f-9874-b82ceeb6e1bc" + }, + { + "name": "Group Controller", + "item": [ + { + "name": "Create Group", + "id": "783e0b82-8960-4248-b432-9fa47697dd6a", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"subject\": \"Test 02\",\r\n \"participants\": [\"5531900000000\", \"5531911111111\", \"5531922222222\"],\r\n \"profilePicture\": \"optional: url or base64\",\r\n \"description\": \"optional\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/group/create/{{instance}}" + }, + "response": [] + }, + { + "name": "Find Group", + "id": "bf7c0e39-056e-4d89-acb6-bc5fc850a3eb", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/group/findGroupInfos/{{instance}}?groupJid={{groupJid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "group", + "findGroupInfos", + "{{instance}}" + ], + "query": [ + { + "key": "groupJid", + "value": "{{groupJid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Find Participants", + "id": "326818bf-c75d-4f1a-868e-a5079efff7a5", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/group/participants/{{instance}}?groupJid={{groupJid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "group", + "participants", + "{{instance}}" + ], + "query": [ + { + "key": "groupJid", + "value": "{{groupJid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Invite Code", + "id": "daacb24b-86cb-4a95-abe3-13fd1dfe8304", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/group/inviteCode/{{instance}}?groupJid={{groupJid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "group", + "inviteCode", + "{{instance}}" + ], + "query": [ + { + "key": "groupJid", + "value": "{{groupJid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Revoke Invite Code", + "id": "67291926-2d3e-4b97-911f-7f2760761681", + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{baseUrl}}/group/revokeInviteCode/{{instance}}?groupJid={{groupJid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "group", + "revokeInviteCode", + "{{instance}}" + ], + "query": [ + { + "key": "groupJid", + "value": "{{groupJid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Update Participant", + "id": "aef56a44-c790-45d5-b370-7f5716c59d68", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"action\": \"'add' | 'remove' | 'promote' | 'demote'\",\r\n \"participants\": [\r\n \"5531900000000\",\r\n \"5531911111111\",\r\n \"5531922222222\"\r\n ]\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/group/updateParticipant/{{instance}}?groupJid={{groupJid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "group", + "updateParticipant", + "{{instance}}" + ], + "query": [ + { + "key": "groupJid", + "value": "{{groupJid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Leave Group", + "id": "4918cfc2-7b8c-483b-80a6-ee6c977f0772", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/group/leaveGroup/{{instance}}?groupJid={{groupJid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "group", + "leaveGroup", + "{{instance}}" + ], + "query": [ + { + "key": "groupJid", + "value": "{{groupJid}}" + } + ] + } + }, + "response": [] + } + ], + "id": "e448a85f-92e6-4a3e-b400-1e58bebd1271" + }, + { + "name": "JWT", + "item": [ + { + "name": "Refresh Token", + "id": "8fa47d1f-712d-4138-84cc-2b793e04b838", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"oldToken\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbnN0YW5jZU5hbWUiOiJjb2RlY2hhdCIsImFwaU5hbWUiOiJ3aGF0c2FwcC1hcGkiLCJ0b2tlbklkIjoiZTZjZGY4YzctNWZmZS00MDkxLWEwYjYtOTMzOWQyOGMyNDA3IiwiaWF0IjoxNjY4MTkwMTY2LCJleHAiOjE2NjgxOTM3NjYsInN1YiI6ImctdCJ9.O1DJpprdBvBuOH_tW4wqnSXxdX4eVKYTpADH6aIf5rk\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{baseUrl}}/instance/refreshToken/" + }, + "response": [] + } + ], + "id": "088f2e19-b320-4513-ab9b-c19fa45d1b5c" + } + ], + "auth": { + "type": "bearer", + "bearer": { + "token": "{{token}}" + } + }, + "event": [ + { + "listen": "prerequest", + "script": { + "id": "cb1b7a2b-e575-465a-8a72-7335e48c52ee", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "8c3975e6-8d65-48b8-b113-c38ec42095eb", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "id": "661d5580-fb5b-46df-b342-6133829c2b62", + "key": "baseUrl", + "value": "http://localhost:8080", + "type": "string" + }, + { + "id": "68c25258-b79c-47fc-ac27-00c899926d75", + "key": "instance", + "value": "codechat", + "type": "string" + }, + { + "id": "7de13c92-f388-4cf9-80e6-f80898587701", + "key": "groupJid", + "value": "120363026700826424@g.us", + "type": "string" + }, + { + "id": "5f5d457b-fa8c-48ce-8e54-77d1d374f4f6", + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbnN0YW5jZU5hbWUiOiJjb2RlY2hhdCIsImFwaU5hbWUiOiJ3aGF0c2FwcC1hcGkiLCJ0b2tlbklkIjoiZTZjZGY4YzctNWZmZS00MDkxLWEwYjYtOTMzOWQyOGMyNDA3IiwiaWF0IjoxNjY4MTkwMTY2LCJleHAiOjE2NjgxOTM3NjYsInN1YiI6ImctdCJ9.O1DJpprdBvBuOH_tW4wqnSXxdX4eVKYTpADH6aIf5rk", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/public/images/code.png b/public/images/code.png new file mode 100644 index 0000000000000000000000000000000000000000..1eb9306b5ef1e999503c96df5aa7d4179cdebb07 GIT binary patch literal 14339 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 literal 0 HcmV?d00001 diff --git a/public/images/codechat-logo.png b/public/images/codechat-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..af8054b223c5a7a52f8fffb1b7496a2663b57c37 GIT binary patch literal 122829 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 z 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%0A9u|T@l~cz3 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 literal 0 HcmV?d00001 diff --git a/src/config/env.config.ts b/src/config/env.config.ts new file mode 100644 index 00000000..3e83d33f --- /dev/null +++ b/src/config/env.config.ts @@ -0,0 +1,107 @@ +import { readFileSync } from 'fs'; +import { load } from 'js-yaml'; +import { join } from 'path'; +import { SRC_DIR } from './path.config'; + +export type HttpServer = { TYPE: 'http' | 'https'; PORT: number }; + +export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE'; +export type Cors = { + ORIGIN: string[]; + METHODS: HttpMethods; + CREDENTIALS: boolean; +}; + +export type LogLevel = 'ERROR' | 'WARN' | 'DEBUG' | 'INFO' | 'LOG' | 'VERBOSE' | 'DARK'; + +export type SaveData = { + OLD_MESSAGE: boolean; + NEW_MESSAGE: boolean; + MESSAGE_UPDATE: boolean; + CONTACTS: boolean; + CHATS: boolean; +}; + +export type StoreConf = { + CLEANING_INTARVAL: number; + MESSAGES: true; + CONTACTS: true; + CHATS: true; +}; + +export type DBConnection = { + HOST: string; + PORT: number; + USER: string; + PASSWORD: string; + DB_PREFIX_NAME: string; +}; +export type Database = { + CONNECTION: DBConnection; + ENABLED: boolean; + SAVE_DATA: SaveData; +}; + +export type EventsWebhook = { + INSTANCE: boolean; + MESSAGES_SET: boolean; + MESSAGES_UPSERT: boolean; + MESSAGES_UPDATE: boolean; + SEND_MESSAGE: boolean; + CONTACTS_SET: boolean; + CONTACTS_UPDATE: boolean; + CONTACTS_UPSERT: boolean; + PRESENCE_UPDATE: boolean; + CHATS_SET: boolean; + CHATS_UPDATE: boolean; +}; + +export type ApiKey = { KEY: string }; +export type Jwt = { EXPIRIN_IN: number; SECRET: string }; +export type Auth = { API_KEY: ApiKey; JWT: Jwt; TYPE: 'jwt' | 'apikey' }; + +export type GlobalWebhook = { URL: string; ENABLED: boolean }; +export type SslConf = { PRIVKEY: string; FULLCHAIN: string }; +export type Webhook = { GLOBAL?: GlobalWebhook; EVENTS: EventsWebhook }; +export type ConfigSessionPhone = { CLIENT: string; NAME: string }; +export type ExpressSession = { SECRET: string }; +export type QrCode = { LINIT: number }; +export type Production = boolean; + +export interface Env { + SERVER: HttpServer; + CORS: Cors; + SSL_CONF: SslConf; + STORE: StoreConf; + DATABASE: Database; + LOG_LEVEL: LogLevel[]; + WEBHOOK: Webhook; + CONFIG_SESSION_PHONE: ConfigSessionPhone; + EXPRESS_SESSION: ExpressSession; + QRCODE: QrCode; + AUTHENTICATION: Auth; + PRODUCTION: Production; +} + +export type Key = keyof Env; + +export class ConfigService { + constructor() { + this.env = this.configEnv(); + } + + private env: Env; + + public get(key: Key) { + return this.env[key] as T; + } + + private configEnv(): Env { + const YAML_PATH = join(SRC_DIR, 'env.yml'); + const configYml = load(readFileSync(YAML_PATH, { encoding: 'utf-8' })) as Env; + configYml.PRODUCTION = process.env.NODE_ENV === 'PROD' ? true : false; + return configYml; + } +} + +export const configService = new ConfigService(); diff --git a/src/config/error.config.ts b/src/config/error.config.ts new file mode 100644 index 00000000..6449d52e --- /dev/null +++ b/src/config/error.config.ts @@ -0,0 +1,21 @@ +import { Logger } from './logger.config'; + +export function onUnexpectedError() { + process.on('uncaughtException', (error, origin) => { + const logger = new Logger('uncaughtException'); + logger.error({ + origin, + stderr: process.stderr.fd, + error, + }); + }); + + process.on('unhandledRejection', (error, origin) => { + const logger = new Logger('unhandledRejection'); + logger.error({ + origin, + stderr: process.stderr.fd, + error, + }); + }); +} diff --git a/src/config/event.config.ts b/src/config/event.config.ts new file mode 100644 index 00000000..c3a02ece --- /dev/null +++ b/src/config/event.config.ts @@ -0,0 +1,7 @@ +import EventEmitter2 from 'eventemitter2'; + +export const eventeEmitter = new EventEmitter2({ + delimiter: '.', + newListener: false, + ignoreErrors: false, +}); diff --git a/src/config/logger.config.ts b/src/config/logger.config.ts new file mode 100644 index 00000000..9e2495e2 --- /dev/null +++ b/src/config/logger.config.ts @@ -0,0 +1,126 @@ +import { configService, LogLevel } from './env.config'; +import dayjs from 'dayjs'; + +export const formatDateLog = (timestamp: number) => + dayjs(timestamp) + .toDate() + .toString() + .replace(/\sGMT.+/, ''); + +enum Color { + LOG = '\x1b[32m', + INFO = '\x1b[34m', + WARN = '\x1b[33m', + ERROR = '\x1b[31m', + DEBUG = '\x1b[36m', + VERBOSE = '\x1b[37m', + DARK = '\x1b[30m', +} + +enum Command { + RESET = '\x1b[0m', + BRIGTH = '\x1b[1m', + UNDERSCORE = '\x1b[4m', +} + +enum Level { + LOG = Color.LOG + '%s' + Command.RESET, + DARK = Color.DARK + '%s' + Command.RESET, + INFO = Color.INFO + '%s' + Command.RESET, + WARN = Color.WARN + '%s' + Command.RESET, + ERROR = Color.ERROR + '%s' + Command.RESET, + DEBUG = Color.DEBUG + '%s' + Command.RESET, + VERBOSE = Color.VERBOSE + '%s' + Command.RESET, +} + +enum Type { + LOG = 'LOG', + WARN = 'WARN', + INFO = 'INFO', + DARK = 'DARK', + ERROR = 'ERROR', + DEBUG = 'DEBUG', + VERBOSE = 'VERBOSE', +} + +enum Background { + LOG = '\x1b[42m', + INFO = '\x1b[44m', + WARN = '\x1b[43m', + DARK = '\x1b[40m', + ERROR = '\x1b[41m', + DEBUG = '\x1b[46m', + VERBOSE = '\x1b[47m', +} + +export class Logger { + private readonly configService = configService; + constructor(private context = 'Logger') {} + + public setContext(value: string) { + this.context = value; + } + + private console(value: any, type: Type) { + const types: Type[] = []; + + this.configService + .get('LOG_LEVEL') + .forEach((level) => types.push(Type[level])); + + const typeValue = typeof value; + + if (types.includes(type)) { + console.log( + /*Command.UNDERSCORE +*/ Command.BRIGTH + Level[type], + '[CodeChat]', + Command.BRIGTH + Color[type], + process.pid.toString(), + Command.RESET, + Command.BRIGTH + Color[type], + '-', + Command.BRIGTH + Color.VERBOSE, + `${formatDateLog(Date.now())} `, + Command.RESET, + Color[type] + Background[type] + Command.BRIGTH, + `${type} ` + Command.RESET, + Color.WARN + Command.BRIGTH, + `[${this.context}]` + Command.RESET, + Color[type] + Command.BRIGTH, + `[${typeValue}]` + Command.RESET, + Color[type], + typeValue !== 'object' ? value : '', + Command.RESET, + ); + typeValue === 'object' ? console.log(/*Level.DARK,*/ value, '\n') : ''; + } + } + + public log(value: any) { + this.console(value, Type.LOG); + } + + public info(value: any) { + this.console(value, Type.INFO); + } + + public warn(value: any) { + this.console(value, Type.WARN); + } + + public error(value: any) { + this.console(value, Type.ERROR); + } + + public verbose(value: any) { + this.console(value, Type.VERBOSE); + } + + public debug(value: any) { + this.console(value, Type.DEBUG); + } + + public dark(value: any) { + this.console(value, Type.DARK); + } +} diff --git a/src/config/path.config.ts b/src/config/path.config.ts new file mode 100644 index 00000000..3fb00e49 --- /dev/null +++ b/src/config/path.config.ts @@ -0,0 +1,6 @@ +import { join } from 'path'; + +export const ROOT_DIR = process.cwd(); +export const INSTANCE_DIR = join(ROOT_DIR, 'instances'); +export const SRC_DIR = join(ROOT_DIR, 'src'); +export const AUTH_DIR = join(ROOT_DIR, 'store', 'auth'); diff --git a/src/db/db.connect.ts b/src/db/db.connect.ts new file mode 100644 index 00000000..d15b4b86 --- /dev/null +++ b/src/db/db.connect.ts @@ -0,0 +1,22 @@ +import mongoose from 'mongoose'; +import { configService, Database } from '../config/env.config'; +import { Logger } from '../config/logger.config'; + +const logger = new Logger('Db Connection'); + +const db = configService.get('DATABASE'); + +export const dbserver = db.ENABLED + ? mongoose.createConnection( + `mongodb://${db.CONNECTION.HOST}:${db.CONNECTION.PORT}?authSource=admin&directConnection=true`, + { + user: db.CONNECTION.USER, + pass: db.CONNECTION.PASSWORD, + dbName: db.CONNECTION.DB_PREFIX_NAME + '-whatsapp-api', + }, + ) + : null; + +export const mongoClient = dbserver?.getClient(); + +db.ENABLED ? logger.info('ON - dbName: ' + dbserver['$dbName']) : null; diff --git a/src/env.yml b/src/env.yml new file mode 100644 index 00000000..dbd5b829 --- /dev/null +++ b/src/env.yml @@ -0,0 +1,103 @@ +# ⚠️ +# ⚠️ ALL SETTINGS DEFINED IN THIS FILE ARE APPLIED TO ALL INSTANCES. +# ⚠️ + +# Choose the server type for the application +SERVER: + TYPE: http # https + PORT: 8080 # 443 + +CORS: + ORIGIN: + - '*' + # - yourdomain.com + METHODS: + - POST + - GET + - PUT + - DELETE + CREDENTIALS: true + +# Install ssl certificate and replace string with domain name +# Access: https://certbot.eff.org/instructions?ws=other&os=ubuntufocal +SSL_CONF: + PRIVKEY: /etc/letsencrypt/live//privkey.pem + FULLCHAIN: /etc/letsencrypt/live//fullchain.pem + +# Determine the logs to be displayed +LOG_LEVEL: + - ERROR + - WARN + - DEBUG + - INFO + - LOG + - VERBOSE + - DARK + +# Temporary data storage +STORE: + CLEANING_INTARVAL: 7200 # seconds === 2h + MESSAGE: true + CONTACTS: true + CHATS: true + +# Permanent data storage +DATABASE: + ENABLED: false + CONNECTION: + HOST: + PORT: + USER: + PASSWORD: + DB_PREFIX_NAME: codechat + # Choose the data you want to save in the database + SAVE_DATA: + OLD_MESSAGE: false + NEW_MESSAGE: true + MESSAGE_UPDATE: true + CONTACTS: true + CHATS: true + +# Webhook Settings +WEBHOOK: + # Define a global webhook that will listen for enabled events from all instances + GLOBAL: + URL: + ENABLED: false + # Set the events you want to hear + EVENTS: + INSTANCE: true + MESSAGES_SET: true + MESSAGES_UPSERT: true + MESSAGES_UPDATE: true + SEND_MESSAGE: true + CONTACTS_SET: true + CONTACTS_UPSERT: true + CONTACTS_UPDATE: true + PRESENCE_UPDATE: true + CHATS_SET: true + CHATS_UPDATE: true + + +CONFIG_SESSION_PHONE: + # Name that will be displayed on smartphone connection + CLIENT: CodeChat + NAME: Chrome # firefox | edge | opera | safari + +EXPRESS_SESSION: + SECRET: C%I6IkpXV*CJ9.eyJjb2RlI + +# Set qrcode display limit +QRCODE: + LIMIT: 6 + +# Defines an authentication type for the api +AUTHENTICATION: + TYPE: jwt # or apikey + # Define a global apikey to access all instances + API_KEY: + KEY: t8OOEeISKzpmc3jjcMqBWYSaJH2PIxns + # Set the secret key to encrypt and decrypt your token and its expiration time. + JWT: + EXPIRIN_IN: 3600 # seconds - 3600s === 1h + SECRET: L=0YWt]b2w[WF>#>:&E` diff --git a/src/exceptions/400.exeption.ts b/src/exceptions/400.exeption.ts new file mode 100644 index 00000000..833295c1 --- /dev/null +++ b/src/exceptions/400.exeption.ts @@ -0,0 +1,11 @@ +import { HttpStatus } from '../whatsapp/routers/index.router'; + +export class BadRequestException { + constructor(...objectError: any[]) { + throw { + status: HttpStatus.BAD_REQUEST, + error: 'Bad Request', + message: objectError.length > 0 ? objectError : undefined, + }; + } +} diff --git a/src/exceptions/401.exception.ts b/src/exceptions/401.exception.ts new file mode 100644 index 00000000..72734d4e --- /dev/null +++ b/src/exceptions/401.exception.ts @@ -0,0 +1,11 @@ +import { HttpStatus } from '../whatsapp/routers/index.router'; + +export class UnauthorizedException { + constructor(...objectError: any[]) { + throw { + status: HttpStatus.UNAUTHORIZED, + error: 'Unauthorized', + message: objectError.length > 0 ? objectError : undefined, + }; + } +} diff --git a/src/exceptions/403.exception.ts b/src/exceptions/403.exception.ts new file mode 100644 index 00000000..bd5d9b89 --- /dev/null +++ b/src/exceptions/403.exception.ts @@ -0,0 +1,11 @@ +import { HttpStatus } from '../whatsapp/routers/index.router'; + +export class ForbidenException { + constructor(...objectError: any[]) { + throw { + status: HttpStatus.FORBIDDEN, + error: 'Forbiden', + message: objectError.length > 0 ? objectError : undefined, + }; + } +} diff --git a/src/exceptions/404.exception.ts b/src/exceptions/404.exception.ts new file mode 100644 index 00000000..1119d1a1 --- /dev/null +++ b/src/exceptions/404.exception.ts @@ -0,0 +1,11 @@ +import { HttpStatus } from '../whatsapp/routers/index.router'; + +export class NotFoundException { + constructor(...objectError: any[]) { + throw { + status: HttpStatus.NOT_FOUND, + error: 'Not Found', + message: objectError.length > 0 ? objectError : undefined, + }; + } +} diff --git a/src/exceptions/500.exception.ts b/src/exceptions/500.exception.ts new file mode 100644 index 00000000..2a41dfa5 --- /dev/null +++ b/src/exceptions/500.exception.ts @@ -0,0 +1,11 @@ +import { HttpStatus } from '../whatsapp/routers/index.router'; + +export class InternalServerErrorException { + constructor(...objectError: any[]) { + throw { + status: HttpStatus.INTERNAL_SERVER_ERROR, + error: 'Internal Server Error', + message: objectError.length > 0 ? objectError : undefined, + }; + } +} diff --git a/src/exceptions/index.ts b/src/exceptions/index.ts new file mode 100644 index 00000000..cf75e9f6 --- /dev/null +++ b/src/exceptions/index.ts @@ -0,0 +1,5 @@ +export * from './400.exeption'; +export * from './401.exception'; +export * from './403.exception'; +export * from './404.exception'; +export * from './500.exception'; diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 00000000..3381d4f1 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,112 @@ +import compression from 'compression'; +import { + configService, + Cors, + Production, + HttpServer, + ExpressSession, + SslConf, +} from './config/env.config'; +import cors from 'cors'; +import express, { + Express, + json, + NextFunction, + Request, + Response, + urlencoded, +} from 'express'; +import { join } from 'path'; +import session from 'express-session'; +import * as https from 'https'; +import * as http from 'http'; +import { onUnexpectedError } from './config/error.config'; +import { Logger } from './config/logger.config'; +import { ROOT_DIR } from './config/path.config'; +import { waMonitor } from './whatsapp/whatsapp.module'; +import { HttpStatus, router } from './whatsapp/routers/index.router'; +import 'express-async-errors'; + +const serverUp = { + https(app: Express) { + const { FULLCHAIN, PRIVKEY } = configService.get('SSL_CONF'); + const server = https.createServer({ cert: FULLCHAIN, key: PRIVKEY }, app); + return server; + }, + + http(app: Express) { + const server = http.createServer(app); + return server; + }, +}; + +function initWA() { + waMonitor.loadInstance(); +} + +export function bootstrap() { + initWA(); + + const logger = new Logger('SERVER'); + const app = express(); + + app.use( + cors({ + origin(requestOrigin, callback) { + const { ORIGIN } = configService.get('CORS'); + !requestOrigin ? (requestOrigin = '*') : undefined; + if (ORIGIN.indexOf(requestOrigin) !== -1) { + return callback(null, true); + } + return callback(new Error('Not allowed by CORS')); + }, + methods: [...configService.get('CORS').METHODS], + credentials: configService.get('CORS').CREDENTIALS, + }), + urlencoded({ extended: true, limit: '50mb' }), + json({ limit: '50mb' }), + compression(), + ); + + if (!configService.get('PRODUCTION')) { + app.set('view engine', 'hbs'); + app.set('views', join(ROOT_DIR, 'views')); + app.use( + express.static(join(ROOT_DIR, 'public')), + session({ + secret: configService.get('EXPRESS_SESSION').SECRET, + resave: false, + saveUninitialized: false, + }), + ); + } + + app.use('/', router); + + app.use( + (err: Error, req: Request, res: Response, next: NextFunction) => { + if (err) { + return res.status(err['status'] || 500).json(err); + } + }, + (req: Request, res: Response, next: NextFunction) => { + const { method, url } = req; + + res.status(HttpStatus.NOT_FOUND).json({ + status: HttpStatus.NOT_FOUND, + message: `Cannot ${method.toUpperCase()} ${url}`, + error: 'Not Found', + }); + + next(); + }, + ); + + const httpServer = configService.get('SERVER'); + + serverUp[httpServer.TYPE](app).listen(httpServer.PORT, () => + logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT), + ); + + onUnexpectedError(); +} diff --git a/src/utils/use-multi-file-auth-state-db.ts b/src/utils/use-multi-file-auth-state-db.ts new file mode 100644 index 00000000..aca070d4 --- /dev/null +++ b/src/utils/use-multi-file-auth-state-db.ts @@ -0,0 +1,102 @@ +import { + AuthenticationCreds, + AuthenticationState, + BufferJSON, + initAuthCreds, + proto, + SignalDataTypeMap, +} from '@adiwajshing/baileys'; +import { configService, Database } from '../config/env.config'; +import { Logger } from '../config/logger.config'; +import { mongoClient } from '../db/db.connect'; + +export async function useMultiFileAuthStateDb( + coll: string, +): Promise<{ state: AuthenticationState; saveCreds: () => Promise }> { + const logger = new Logger(useMultiFileAuthStateDb.name); + + const collection = mongoClient + .db(configService.get('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances') + .collection(coll); + + const writeData = async (data: any, key: string): Promise => { + try { + await mongoClient.connect(); + return await collection.replaceOne( + { _id: key }, + JSON.parse(JSON.stringify(data, BufferJSON.replacer)), + { upsert: true }, + ); + } catch (error) { + return logger.error({ localError: 'writeData', error }); + } + }; + + const readData = async (key: string): Promise => { + try { + await mongoClient.connect(); + const data = await collection.findOne({ _id: key }); + if (data) { + const creds = JSON.stringify(data); + if (creds) { + return JSON.parse(creds, BufferJSON.reviver); + } + } + return; + } catch (error) { + logger.error({ readData: 'writeData', error }); + return; + } + }; + + const removeData = async (key: string) => { + try { + await mongoClient.connect(); + return await collection.deleteOne({ _id: key }); + } catch (error) { + logger.error({ readData: 'removeData', error }); + } + }; + + const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds(); + + return { + state: { + creds, + keys: { + get: async (type, ids: string[]) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const data: { [_: string]: SignalDataTypeMap[type] } = {}; + await Promise.all( + ids.map(async (id) => { + let value = await readData(`${type}-${id}`); + if (type === 'app-state-sync-key' && value) { + value = proto.Message.AppStateSyncKeyData.fromObject(value); + } + + data[id] = value; + }), + ); + + return data; + }, + set: async (data: any) => { + const tasks: Promise[] = []; + for (const category in data) { + for (const id in data[category]) { + const value = data[category][id]; + const key = `${category}-${id}`; + tasks.push(value ? await writeData(value, key) : await removeData(key)); + } + } + + await Promise.all(tasks); + }, + }, + }, + saveCreds: async () => { + return await writeData(creds, 'creds'); + }, + }; +} diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts new file mode 100644 index 00000000..5670ea0d --- /dev/null +++ b/src/validate/validate.schema.ts @@ -0,0 +1,482 @@ +import { JSONSchema7, JSONSchema7Definition } from 'json-schema'; +import { v4 } from 'uuid'; + +const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => { + const properties = {}; + propertyNames.forEach( + (property) => + (properties[property] = { + minLength: 1, + description: `The "${property}" cannot be empty`, + }), + ); + return { + if: { + propertyNames: { + enum: [...propertyNames], + }, + }, + then: { properties }, + }; +}; + +// Instance Schema +export const instanceNameSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + instanceName: { type: 'string' }, + }, + ...isNotEmpty('instanceName'), +}; + +export const oldTokenSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + oldToken: { type: 'string' }, + }, + required: ['oldToken'], + ...isNotEmpty('oldToken'), +}; + +// Send Message Schema +const optionsSchema: JSONSchema7 = { + properties: { + delay: { + type: 'integer', + description: 'Enter a value in milliseconds', + title: 'Delay', + }, + presence: { + type: 'string', + enum: ['unavailable', 'available', 'composing', 'recording', 'paused'], + }, + }, +}; + +const numberDefinition: JSONSchema7Definition = { + type: 'string', + pattern: '^\\d+$', + description: 'The number property must be a numeric string', +}; + +export const textMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + number: { ...numberDefinition }, + options: { ...optionsSchema }, + textMessage: { + type: 'object', + properties: { + text: { type: 'string' }, + }, + required: ['text'], + ...isNotEmpty('text'), + }, + }, + required: ['textMessage', 'number'], +}; + +export const mediaMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + number: { ...numberDefinition }, + options: { ...optionsSchema }, + mediaMessage: { + type: 'object', + properties: { + mediatype: { type: 'string', enum: ['image', 'document', 'video'] }, + media: { type: 'string' }, + fileName: { type: 'string' }, + caption: { type: 'string' }, + }, + required: ['mediatype', 'media'], + ...isNotEmpty('fileName', 'caption'), + }, + }, + required: ['mediaMessage', 'number'], +}; + +export const buttonMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + number: { ...numberDefinition }, + options: { ...optionsSchema }, + buttonMessage: { + type: 'object', + properties: { + title: { type: 'string' }, + description: { type: 'string' }, + footerText: { type: 'string' }, + buttons: { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'object', + properties: { + buttonText: { type: 'string' }, + buttonId: { type: 'string' }, + }, + required: ['buttonText', 'buttonId'], + ...isNotEmpty('buttonText', 'buttonId'), + }, + }, + mediaMessage: { + type: 'object', + properties: { + media: { type: 'string' }, + mediatype: { type: 'string', enum: ['image', 'document', 'video'] }, + }, + required: ['media', 'mediatype'], + }, + }, + required: ['title', 'description', 'buttons'], + ...isNotEmpty('title', 'description'), + }, + }, + required: ['number', 'buttonMessage'], +}; + +export const locationMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + number: { ...numberDefinition }, + options: { ...optionsSchema }, + locationMessage: { + type: 'object', + properties: { + latitude: { type: 'number' }, + longitude: { type: 'number' }, + name: { type: 'string' }, + address: { type: 'string' }, + }, + required: ['latitude', 'longitude'], + ...isNotEmpty('name', 'addresss'), + }, + }, + required: ['number', 'locationMessage'], +}; + +export const listMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + number: { ...numberDefinition }, + options: { ...optionsSchema }, + listMessage: { + type: 'object', + properties: { + title: { type: 'string' }, + description: { type: 'string' }, + footerText: { type: 'string' }, + buttonText: { type: 'string' }, + sections: { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'object', + properties: { + title: { type: 'string' }, + rows: { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'object', + properties: { + title: { type: 'string' }, + description: { type: 'string' }, + rowId: { type: 'string' }, + }, + required: ['title', 'description', 'rowId'], + ...isNotEmpty('title', 'description', 'rowId'), + }, + }, + }, + required: ['title', 'rows'], + ...isNotEmpty('title'), + }, + }, + }, + required: ['title', 'description', 'buttonText', 'sections'], + ...isNotEmpty('title', 'description', 'buttonText', 'footerText'), + }, + }, + required: ['number', 'listMessage'], +}; + +export const contactMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + number: { ...numberDefinition }, + options: { ...optionsSchema }, + contactMessage: { + type: 'array', + items: { + type: 'object', + properties: { + fullName: { type: 'string' }, + wuid: { + type: 'string', + minLength: 10, + pattern: '\\d+', + description: '"wuid" must be a numeric string', + }, + phoneNumber: { type: 'string', minLength: 10 }, + }, + required: ['fullName', 'wuid', 'phoneNumber'], + ...isNotEmpty('fullName'), + }, + minItems: 1, + uniqueItems: true, + }, + }, + required: ['number', 'contactMessage'], +}; + +export const reactionMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + reactionMessage: { + type: 'object', + properties: { + key: { + type: 'object', + properties: { + id: { type: 'string' }, + remoteJid: { type: 'string' }, + fromMe: { type: 'boolean', enum: [true, false] }, + }, + required: ['id', 'remoteJid', 'fromMe'], + ...isNotEmpty('id', 'remoteJid'), + }, + reaction: { type: 'string' }, + }, + required: ['key', 'reaction'], + ...isNotEmpty('reaction'), + }, + }, + required: ['reactionMessage'], +}; + +// Chat Schema +export const whatsappNumberSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + numbers: { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'string', + pattern: '^\\d+', + description: '"numbers" must be an array of numeric strings', + }, + }, + }, +}; + +export const readMessageSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + readMessages: { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + properties: { + id: { type: 'string' }, + fromMe: { type: 'boolean', enum: [true, false] }, + remoteJid: { type: 'string' }, + }, + required: ['id', 'fromMe', 'remoteJid'], + ...isNotEmpty('id', 'remoteJid'), + }, + }, + }, + required: ['readMessages'], +}; + +export const archiveChatSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + lastMessage: { + type: 'object', + properties: { + key: { + type: 'object', + properties: { + id: { type: 'string' }, + remoteJid: { type: 'string' }, + fromMe: { type: 'boolean', enum: [true, false] }, + }, + required: ['id', 'fromMe', 'remoteJid'], + ...isNotEmpty('id', 'remoteJid'), + }, + messageTimestamp: { type: 'integer', minLength: 1 }, + }, + required: ['key'], + ...isNotEmpty('messageTimestamp'), + }, + archive: { type: 'boolean', enum: [true, false] }, + }, + required: ['lastMessage', 'archive'], +}; + +export const contactValidateSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + where: { + type: 'object', + properties: { + _id: { type: 'string', minLength: 1 }, + pushName: { type: 'string', minLength: 1 }, + id: { type: 'string', minLength: 1 }, + }, + ...isNotEmpty('_id', 'id', 'pushName'), + }, + }, +}; + +export const messaseValidateSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + where: { + type: 'object', + properties: { + _id: { type: 'string', minLength: 1 }, + key: { + type: 'object', + if: { + propertyNames: { + enum: ['fromMe', 'remoteJid', 'id'], + }, + }, + then: { + properties: { + remoteJid: { + type: 'string', + minLength: 1, + description: 'The property cannot be empty', + }, + id: { + type: 'string', + minLength: 1, + description: 'The property cannot be empty', + }, + fromMe: { type: 'boolean', enum: [true, false] }, + }, + }, + }, + message: { type: 'object' }, + }, + ...isNotEmpty('_id'), + }, + }, +}; + +export const messageUpSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + where: { + type: 'object', + properties: { + _id: { type: 'string' }, + remoteJid: { type: 'string' }, + id: { type: 'object' }, + fromMe: { type: 'boolean', enum: [true, false] }, + participant: { type: 'string' }, + status: { + type: 'string', + enum: ['ERROR', 'PENDING', 'SERVER_ACK', 'DELIVERY_ACK', 'READ', 'PLAYED'], + }, + }, + ...isNotEmpty('_id', 'remoteJid', 'id', 'status'), + }, + }, +}; + +// Group Schema +export const createGroupSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + subject: { type: 'string' }, + description: { type: 'string' }, + profilePicture: { type: 'string' }, + participants: { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'string', + minLength: 10, + pattern: '\\d+', + description: '"participants" must be an array of numeric strings', + }, + }, + }, + required: ['subject', 'participants'], + ...isNotEmpty('subject', 'description', 'profilePicture'), +}; + +export const groupJidSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + groupJid: { type: 'string', pattern: '^[\\d]+@g.us$' }, + }, + ...isNotEmpty('groupJid'), +}; + +export const updateGparticipantsSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + groupJid: { type: 'string' }, + action: { + type: 'string', + enum: ['add', 'remove', 'promote', 'demote'], + }, + participants: { + type: 'array', + minItems: 1, + uniqueItems: true, + items: { + type: 'string', + minLength: 10, + pattern: '\\d+', + description: '"participants" must be an array of numeric strings', + }, + }, + }, + required: ['groupJid', 'action', 'participants'], + ...isNotEmpty('groupJid', 'action'), +}; + +// Webhook Schema +export const webhookSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + url: { type: 'string' }, + enabled: { type: 'boolean', enum: [true, false] }, + }, + required: ['url', 'enabled'], + ...isNotEmpty('url'), +}; diff --git a/src/whatsapp/abstract/abstract.repository.ts b/src/whatsapp/abstract/abstract.repository.ts new file mode 100644 index 00000000..682614cb --- /dev/null +++ b/src/whatsapp/abstract/abstract.repository.ts @@ -0,0 +1,55 @@ +import { writeFileSync } from 'fs'; +import { join } from 'path'; +import { ConfigService, Database } from '../../config/env.config'; +import { ROOT_DIR } from '../../config/path.config'; + +export type IInsert = { insertCount: number }; + +export interface IRepository { + insert(data: any, saveDb?: boolean): Promise; + find(query: any): Promise; + delete(query: any, force?: boolean): Promise; + + dbSettings: Database; + readonly storePath: string; +} + +type WriteStore = { + path: string; + fileName: string; + data: U; +}; + +export abstract class Repository implements IRepository { + constructor(configService: ConfigService) { + this.dbSettings = configService.get('DATABASE'); + } + + dbSettings: Database; + readonly storePath = join(ROOT_DIR, 'store'); + + public writeStore = (create: WriteStore) => { + try { + writeFileSync( + join(create.path, create.fileName + '.json'), + JSON.stringify({ ...create.data }), + { encoding: 'utf-8' }, + ); + + return { message: 'create - success' }; + } finally { + create.data = undefined; + } + }; + + public insert(data: any, saveDb = false): Promise { + throw new Error('Method not implemented.'); + } + public find(query: any): Promise { + throw new Error('Method not implemented.'); + } + + delete(query: any, force?: boolean): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/src/whatsapp/abstract/abstract.router.ts b/src/whatsapp/abstract/abstract.router.ts new file mode 100644 index 00000000..dd9b0f9c --- /dev/null +++ b/src/whatsapp/abstract/abstract.router.ts @@ -0,0 +1,96 @@ +import { InstanceDto } from '../dto/instance.dto'; +import { JSONSchema7 } from 'json-schema'; +import { Request } from 'express'; +import { validate } from 'jsonschema'; +import { BadRequestException } from '../../exceptions'; +import 'express-async-errors'; +import { Logger } from '../../config/logger.config'; +import { GroupJid } from '../dto/group.dto'; + +type DataValidate = { + request: Request; + schema: JSONSchema7; + ClassRef: any; + execute: (instance: InstanceDto, data: T) => Promise; +}; + +const logger = new Logger('Validate'); + +export abstract class RouterBroker { + constructor() {} + public routerPath(path: string) { + return '/' + path + '/:instanceName'; + } + + public async dataValidate(args: DataValidate) { + const { request, schema, ClassRef, execute } = args; + + const ref = new ClassRef(); + const body = request.body; + const instance = request.params as unknown as InstanceDto; + + if (Object.keys(instance).length === 0) { + Object.assign(instance, body); + Object.assign(ref, instance); + } else { + Object.assign(ref, body); + } + + const v = validate(ref, schema); + + logger.error(!v.valid ? v.errors : [null]); + + if (!v.valid) { + const message: any[] = v.errors.map(({ property, stack, schema }) => { + let message: string; + if (schema['description']) { + message = schema['description']; + } else { + message = stack.replace('instance.', ''); + } + return { + property: property.replace('instance.', ''), + message, + }; + }); + throw new BadRequestException(...message); + } + + return await execute(instance, ref); + } + + public async groupValidate(args: DataValidate) { + const { request, ClassRef, schema, execute } = args; + + const instance = request.params as unknown as InstanceDto; + const groupJid = request.query as unknown as GroupJid; + const body = request.body; + + const ref = new ClassRef(); + + Object.assign(body, groupJid); + Object.assign(ref, body); + + const v = validate(ref, schema); + + logger.error(!v.valid ? v.errors : [null]); + + if (!v.valid) { + const message: any[] = v.errors.map(({ property, stack, schema }) => { + let message: string; + if (schema['description']) { + message = schema['description']; + } else { + message = stack.replace('instance.', ''); + } + return { + property: property.replace('instance.', ''), + message, + }; + }); + throw new BadRequestException(...message); + } + + return await execute(instance, ref); + } +} diff --git a/src/whatsapp/controllers/chat.controller.ts b/src/whatsapp/controllers/chat.controller.ts new file mode 100644 index 00000000..dc3dea27 --- /dev/null +++ b/src/whatsapp/controllers/chat.controller.ts @@ -0,0 +1,37 @@ +import { ArchiveChatDto, ReadMessageDto, WhatsAppNumberDto } from '../dto/chat.dto'; +import { InstanceDto } from '../dto/instance.dto'; +import { ContactQuery } from '../repository/contact.repository'; +import { MessageQuery } from '../repository/message.repository'; +import { MessageUpQuery } from '../repository/messageUp.repository'; +import { WAMonitoringService } from '../services/monitor.service'; + +export class ChatController { + constructor(private readonly waMonitor: WAMonitoringService) {} + + public async whatsappNumber({ instanceName }: InstanceDto, data: WhatsAppNumberDto) { + return await this.waMonitor.waInstances[instanceName].whatsappNumber(data); + } + + public async readMessage({ instanceName }: InstanceDto, data: ReadMessageDto) { + return await this.waMonitor.waInstances[instanceName].markMessageAsRead(data); + } + + public async archiveChat({ instanceName }: InstanceDto, data: ArchiveChatDto) { + return await this.waMonitor.waInstances[instanceName].archiveChat(data); + } + + public async fetchContacts({ instanceName }: InstanceDto, query: ContactQuery) { + query.where.instanceName = instanceName; + return await this.waMonitor.waInstances[instanceName].fetchContacts(query); + } + + public async fetchMessages({ instanceName }: InstanceDto, query: MessageQuery) { + query.where.instanceName = instanceName; + return await this.waMonitor.waInstances[instanceName].fetchMessages(query); + } + + public async fetchStatusMessage({ instanceName }: InstanceDto, query: MessageUpQuery) { + query.where.instanceName = instanceName; + return await this.waMonitor.waInstances[instanceName].findStatusMessage(query); + } +} diff --git a/src/whatsapp/controllers/group.controller.ts b/src/whatsapp/controllers/group.controller.ts new file mode 100644 index 00000000..78cb6736 --- /dev/null +++ b/src/whatsapp/controllers/group.controller.ts @@ -0,0 +1,44 @@ +import { CreateGroupDto, GroupJid, GroupUpdateParticipantDto } from '../dto/group.dto'; +import { InstanceDto } from '../dto/instance.dto'; +import { WAMonitoringService } from '../services/monitor.service'; + +export class GroupController { + constructor(private readonly waMonitor: WAMonitoringService) {} + + public async createGroup(instance: InstanceDto, create: CreateGroupDto) { + return await this.waMonitor.waInstances[instance.instanceName].createGroup(create); + } + + public async findGroupInfo(instance: InstanceDto, groupJid: GroupJid) { + return await this.waMonitor.waInstances[instance.instanceName].findGroup(groupJid); + } + + public async inviteCode(instance: InstanceDto, groupJid: GroupJid) { + return await this.waMonitor.waInstances[instance.instanceName].invitCode(groupJid); + } + + public async revokeInviteCode(instance: InstanceDto, groupJid: GroupJid) { + return await this.waMonitor.waInstances[instance.instanceName].revokeInviteCode( + groupJid, + ); + } + + public async findParticipnats(instance: InstanceDto, groupJid: GroupJid) { + return await this.waMonitor.waInstances[instance.instanceName].findParticipants( + groupJid, + ); + } + + public async updateGParticipat( + instance: InstanceDto, + update: GroupUpdateParticipantDto, + ) { + return await this.waMonitor.waInstances[instance.instanceName].updateGParticipant( + update, + ); + } + + public async leaveGroup(instance: InstanceDto, groupJid: GroupJid) { + return await this.waMonitor.waInstances[instance.instanceName].leaveGroup(groupJid); + } +} diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts new file mode 100644 index 00000000..5182194d --- /dev/null +++ b/src/whatsapp/controllers/instance.controller.ts @@ -0,0 +1,72 @@ +import { delay } from '@adiwajshing/baileys'; +import EventEmitter2 from 'eventemitter2'; +import { ConfigService } from '../../config/env.config'; +import { InternalServerErrorException } from '../../exceptions'; +import { InstanceDto } from '../dto/instance.dto'; +import { RepositoryBroker } from '../repository/index.repository'; +import { AuthService, OldToken } from '../services/auth.service'; +import { WAMonitoringService } from '../services/monitor.service'; +import { WAStartupService } from '../services/whatsapp.service'; + +export class InstanceController { + constructor( + private readonly waMonitor: WAMonitoringService, + private readonly configService: ConfigService, + private readonly repository: RepositoryBroker, + private readonly eventEmitter: EventEmitter2, + private readonly authService: AuthService, + ) {} + + public async createInstance({ instanceName }: InstanceDto) { + const instance = new WAStartupService( + this.configService, + this.eventEmitter, + this.repository, + ); + instance.instanceName = instanceName; + this.waMonitor.waInstances[instance.instanceName] = instance; + + const hash = this.authService.generateHash({ instanceName: instance.instanceName }); + + return { + instance: { + instanceName: instance.instanceName, + status: 'created', + }, + hash, + }; + } + + public async connectToWhatsapp({ instanceName }: InstanceDto) { + const instance = this.waMonitor.waInstances[instanceName]; + const state = instance.connectionStatus?.state; + + switch (state) { + case 'close': + await instance.connectToWhatsapp(); + await delay(1200); + return instance.qrCode; + case 'connecting': + return instance.qrCode; + default: + return {}; + } + } + + public async connectionState({ instanceName }: InstanceDto) { + return this.waMonitor.waInstances[instanceName].connectionStatus; + } + + public async logout({ instanceName }: InstanceDto) { + try { + this.eventEmitter.emit('remove.instance', instanceName); + return { error: false, message: 'Instance logged out' }; + } catch (error) { + throw new InternalServerErrorException(error.toString()); + } + } + + public async refreshToken(_: InstanceDto, oldToken: OldToken) { + return this.authService.refreshToken(oldToken); + } +} diff --git a/src/whatsapp/controllers/sendMessage.controller.ts b/src/whatsapp/controllers/sendMessage.controller.ts new file mode 100644 index 00000000..5dd0d10b --- /dev/null +++ b/src/whatsapp/controllers/sendMessage.controller.ts @@ -0,0 +1,51 @@ +import { isBase64, isURL } from 'class-validator'; +import { BadRequestException } from '../../exceptions'; +import { InstanceDto } from '../dto/instance.dto'; +import { + SendButtonDto, + SendContactDto, + SendListDto, + SendLocationDto, + SendMediaDto, + SendReactionDto, + SendTextDto, +} from '../dto/sendMessage.dto'; +import { WAMonitoringService } from '../services/monitor.service'; + +export class SendMessageController { + constructor(private readonly waMonitor: WAMonitoringService) {} + + public async sendText({ instanceName }: InstanceDto, data: SendTextDto) { + return await this.waMonitor.waInstances[instanceName].textMessage(data); + } + + public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto) { + if (!isURL(data?.mediaMessage?.media) || !isBase64(data?.mediaMessage?.media)) { + throw new BadRequestException('Owned media must be a url or base64'); + } + return await this.waMonitor.waInstances[instanceName].mediaMessage(data); + } + + public async sendButtons({ instanceName }: InstanceDto, data: SendButtonDto) { + return await this.waMonitor.waInstances[instanceName].buttonMessage(data); + } + + public async sendLocation({ instanceName }: InstanceDto, data: SendLocationDto) { + return await this.waMonitor.waInstances[instanceName].locationMessage(data); + } + + public async sendList({ instanceName }: InstanceDto, data: SendListDto) { + return await this.waMonitor.waInstances[instanceName].listMessage(data); + } + + public async sendContact({ instanceName }: InstanceDto, data: SendContactDto) { + return await this.waMonitor.waInstances[instanceName].contactMessage(data); + } + + public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) { + if (!data.reactionMessage.reaction.match(/[^\(\)\w\sà-ú"-\+]+/)) { + throw new BadRequestException('"reaction" must be an emoji'); + } + return await this.waMonitor.waInstances[instanceName].reactionMessage(data); + } +} diff --git a/src/whatsapp/controllers/views.controller.ts b/src/whatsapp/controllers/views.controller.ts new file mode 100644 index 00000000..c96072a6 --- /dev/null +++ b/src/whatsapp/controllers/views.controller.ts @@ -0,0 +1,44 @@ +import { Request, Response } from 'express'; +import { readFileSync } from 'fs'; +import { join } from 'path'; +import { Auth, ConfigService } from '../../config/env.config'; +import { AUTH_DIR } from '../../config/path.config'; +import { BadRequestException } from '../../exceptions'; +import { InstanceDto } from '../dto/instance.dto'; +import { HttpStatus } from '../routers/index.router'; +import { JwtPayload } from '../services/auth.service'; +import { WAMonitoringService } from '../services/monitor.service'; + +export class ViewsController { + constructor( + private readonly waMonit: WAMonitoringService, + private readonly configService: ConfigService, + ) {} + + public qrcode(request: Request, response: Response) { + const param = request.params as unknown as InstanceDto; + + const instance = this.waMonit.waInstances[param.instanceName]; + if (instance.connectionStatus.state === 'open') { + throw new BadRequestException('The instance is already connected'); + } + + let auth: { type: 'jwt' | 'apikey'; hash: string } = + request.session[param.instanceName]; + + if (!auth) { + const type = this.configService.get('AUTHENTICATION').TYPE; + const data: JwtPayload = JSON.parse( + readFileSync(join(AUTH_DIR, type, param.instanceName + '.json'), { + encoding: 'utf-8', + }), + ); + + const hash = type === 'jwt' ? `Bearer ${data[type]}` : data[type]; + + auth = { type, hash }; + } + + return response.status(HttpStatus.OK).render('qrcode', { ...auth, ...param }); + } +} diff --git a/src/whatsapp/controllers/webhook.controller.ts b/src/whatsapp/controllers/webhook.controller.ts new file mode 100644 index 00000000..b91a0db6 --- /dev/null +++ b/src/whatsapp/controllers/webhook.controller.ts @@ -0,0 +1,20 @@ +import { isURL } from 'class-validator'; +import { BadRequestException } from '../../exceptions'; +import { InstanceDto } from '../dto/instance.dto'; +import { WebhookDto } from '../dto/webhook.dto'; +import { WebhookService } from '../services/webhook.service'; + +export class WebhookController { + constructor(private readonly webhookService: WebhookService) {} + + public async createWebhook(instance: InstanceDto, data: WebhookDto) { + if (!isURL(data.url)) { + throw new BadRequestException('Invalid "url" property'); + } + return this.webhookService.create(instance, data); + } + + public async findWebhook(instance: InstanceDto) { + return this.webhookService.find(instance); + } +} diff --git a/src/whatsapp/dto/chat.dto.ts b/src/whatsapp/dto/chat.dto.ts new file mode 100644 index 00000000..9a0c7e33 --- /dev/null +++ b/src/whatsapp/dto/chat.dto.ts @@ -0,0 +1,27 @@ +import { proto } from '@adiwajshing/baileys'; + +export class OnWhatsAppDto { + constructor(public readonly jid: string, public readonly exists: boolean) {} +} +export class WhatsAppNumberDto { + numbers: string[]; +} + +class Key { + id: string; + fromMe: boolean; + remoteJid: string; +} +export class ReadMessageDto { + readMessages: Key[]; +} + +class LastMessage { + key: Key; + messageTimestamp?: number; +} + +export class ArchiveChatDto { + lastMessage: LastMessage; + archive: boolean; +} diff --git a/src/whatsapp/dto/group.dto.ts b/src/whatsapp/dto/group.dto.ts new file mode 100644 index 00000000..44e1c6d0 --- /dev/null +++ b/src/whatsapp/dto/group.dto.ts @@ -0,0 +1,15 @@ +export class CreateGroupDto { + subject: string; + description?: string; + participants: string[]; + profilePicture: string; +} + +export class GroupJid { + groupJid: string; +} + +export class GroupUpdateParticipantDto extends GroupJid { + action: 'add' | 'remove' | 'promote' | 'demote'; + paticipants: string[]; +} diff --git a/src/whatsapp/dto/instance.dto.ts b/src/whatsapp/dto/instance.dto.ts new file mode 100644 index 00000000..e4f929dc --- /dev/null +++ b/src/whatsapp/dto/instance.dto.ts @@ -0,0 +1,3 @@ +export class InstanceDto { + instanceName: string; +} diff --git a/src/whatsapp/dto/sendMessage.dto.ts b/src/whatsapp/dto/sendMessage.dto.ts new file mode 100644 index 00000000..aead15e7 --- /dev/null +++ b/src/whatsapp/dto/sendMessage.dto.ts @@ -0,0 +1,95 @@ +import { proto, WAPresence } from '@adiwajshing/baileys'; + +export class Options { + delay?: number; + presence?: WAPresence; +} +class OptionsMessage { + options: Options; +} + +export class Metadata extends OptionsMessage { + number: string; +} + +class TextMessage { + text: string; +} +export class SendTextDto extends Metadata { + textMessage: TextMessage; +} + +export type MediaType = 'image' | 'document' | 'video'; +export class MediaMessage { + mediatype: MediaType; + caption?: string; + // for document + fileName?: string; + // url or base64 + media: string; +} +export class SendMediaDto extends Metadata { + mediaMessage: MediaMessage; +} + +class Button { + buttonText: string; + buttonId: string; +} +class ButtonMessaqge { + title: string; + description: string; + footerText?: string; + buttons: Button[]; + mediaMessage?: MediaMessage; +} +export class SendButtonDto extends Metadata { + buttonMessage: ButtonMessaqge; +} + +class LocationMessage { + latitude: number; + longitude: number; + name?: string; + address?: string; +} +export class SendLocationDto extends Metadata { + locationMessage: LocationMessage; +} + +class Row { + title: string; + desctiption: string; + rowId: string; +} +class Section { + title: string; + rows: Row[]; +} +class ListMessage { + title: string; + description: string; + footerText?: string; + buttonText: string; + sections: Section[]; +} +export class SendListDto extends Metadata { + listMessage: ListMessage; +} + +export class ContactMessage { + fullName: string; + wuid: string; + phoneNumber: string; +} +export class SendContactDto extends Metadata { + contactMessage: ContactMessage[]; +} + +class ReactionMessage { + key: proto.IMessageKey; + reaction: string; +} +export class SendReactionDto { + reactionMessage: ReactionMessage; +} diff --git a/src/whatsapp/dto/webhook.dto.ts b/src/whatsapp/dto/webhook.dto.ts new file mode 100644 index 00000000..95b0c0d7 --- /dev/null +++ b/src/whatsapp/dto/webhook.dto.ts @@ -0,0 +1,4 @@ +export class WebhookDto { + enabled: boolean; + url: string; +} diff --git a/src/whatsapp/guards/auth.guard.ts b/src/whatsapp/guards/auth.guard.ts new file mode 100644 index 00000000..6c5b9ee3 --- /dev/null +++ b/src/whatsapp/guards/auth.guard.ts @@ -0,0 +1,76 @@ +import { isJWT } from 'class-validator'; +import { NextFunction, Request, Response } from 'express'; +import jwt from 'jsonwebtoken'; +import { Auth, configService } from '../../config/env.config'; +import { Logger } from '../../config/logger.config'; +import { name } from '../../../package.json'; +import { InstanceDto } from '../dto/instance.dto'; +import { JwtPayload } from '../services/auth.service'; +import { UnauthorizedException } from '../../exceptions'; +import { readFileSync } from 'fs'; +import { join } from 'path'; +import { AUTH_DIR } from '../../config/path.config'; + +const logger = new Logger('GUARD'); + +function jwtGuard(req: Request, res: Response, next: NextFunction) { + if (req.originalUrl.includes('/instance/create')) { + return next(); + } + + const key = req.get('apikey'); + if (configService.get('AUTHENTICATION').API_KEY.KEY === key) { + return next(); + } + + const jwtOpts = configService.get('AUTHENTICATION').JWT; + try { + const [bearer, token] = req.get('authorization').split(' '); + + if (bearer.toLowerCase() !== 'bearer') { + throw new UnauthorizedException(); + } + + if (!isJWT(token)) { + throw new UnauthorizedException(); + } + + const param = req.params as unknown as InstanceDto; + const decode = jwt.verify(token, jwtOpts.SECRET, { + ignoreExpiration: false, + }) as JwtPayload; + + if (param.instanceName !== decode.instanceName || name !== decode.apiName) { + throw new UnauthorizedException(); + } + + return next(); + } catch (error) { + logger.error(error); + throw new UnauthorizedException(); + } +} + +function apiKey(req: Request, res: Response, next: NextFunction) { + const env = configService.get('AUTHENTICATION').API_KEY; + const key = req.get('apikey'); + + try { + const param = req.params as unknown as InstanceDto; + const storeKey = JSON.parse( + readFileSync(join(AUTH_DIR, 'apikey', param.instanceName + '.json'), { + encoding: 'utf-8', + }), + ) as Pick; + if (storeKey.apiKey === 'key') { + return next(); + } + } catch (error) {} + + if (key === env.KEY) { + return next(); + } + throw new UnauthorizedException(); +} + +export const authGuard = { jwt: jwtGuard, apiKey }; diff --git a/src/whatsapp/guards/instance.guard.ts b/src/whatsapp/guards/instance.guard.ts new file mode 100644 index 00000000..25b82537 --- /dev/null +++ b/src/whatsapp/guards/instance.guard.ts @@ -0,0 +1,41 @@ +import { NextFunction, Request, Response } from 'express'; +import { + BadRequestException, + ForbidenException, + NotFoundException, +} from '../../exceptions'; +import { InstanceDto } from '../dto/instance.dto'; +import { waMonitor } from '../whatsapp.module'; + +export function instanceExistsGuard(req: Request, res: Response, next: NextFunction) { + if (req.originalUrl.includes('/instance/create')) { + return next(); + } + + const param = req.params as unknown as InstanceDto; + if (!param?.instanceName) { + throw new BadRequestException('"instanceName" not provided.'); + } + + const instance = waMonitor.waInstances[param.instanceName]; + + if (!instance) { + throw new NotFoundException(`The "${param.instanceName}" instance does not exist`); + } + + next(); +} + +export function instanceLoggedGuard(req: Request, res: Response, next: NextFunction) { + if (req.originalUrl.includes('/instance/create')) { + const instance = req.body as InstanceDto; + const waInstance = waMonitor.waInstances[instance.instanceName]; + if (waInstance) { + throw new ForbidenException( + `This name "${instance.instanceName}" is already in use.`, + ); + } + } + + next(); +} diff --git a/src/whatsapp/models/chat.model.ts b/src/whatsapp/models/chat.model.ts new file mode 100644 index 00000000..bbb98e27 --- /dev/null +++ b/src/whatsapp/models/chat.model.ts @@ -0,0 +1,16 @@ +import { Schema } from 'mongoose'; +import { dbserver } from '../../db/db.connect'; + +export class ChatRaw { + _id?: string; + id?: string; + isnatnceName: string; +} + +const chatSchema = new Schema({ + id: { type: String, required: true, minlength: 1 }, + instanceName: { type: String, required: true, minlength: 1 }, +}); + +export const ChatModel = dbserver?.model(ChatRaw.name, chatSchema, 'chats'); +export type IChatModel = typeof ChatModel; diff --git a/src/whatsapp/models/contact.model.ts b/src/whatsapp/models/contact.model.ts new file mode 100644 index 00000000..a12e3796 --- /dev/null +++ b/src/whatsapp/models/contact.model.ts @@ -0,0 +1,19 @@ +import { Schema } from 'mongoose'; +import { dbserver } from '../../db/db.connect'; + +export class ContactRaw { + _id?: string; + pushName?: string; + id?: string; + profilePictureUrl?: string; + instanceName: string; +} + +const contactSchema = new Schema({ + pushName: { type: String, minlength: 1 }, + id: { type: String, required: true, minlength: 1 }, + profilePictureUrl: { type: String, minlength: 1 }, +}); + +export const ContactModel = dbserver?.model(ContactRaw.name, contactSchema, 'contacts'); +export type IContactModel = typeof ContactModel; diff --git a/src/whatsapp/models/message.model.ts b/src/whatsapp/models/message.model.ts new file mode 100644 index 00000000..bc277b3c --- /dev/null +++ b/src/whatsapp/models/message.model.ts @@ -0,0 +1,62 @@ +import { Schema } from 'mongoose'; +import { dbserver } from '../../db/db.connect'; +import { wa } from '../types/wa.types'; + +class Key { + id?: string; + remoteJid?: string; + fromMe?: boolean; + participant?: string; +} + +export class MessageRaw { + _id?: string; + key?: Key; + message?: object; + messageTimestamp?: number | Long.Long; + instanceName: string; + source?: 'android' | 'web' | 'ios'; +} + +const messageSchema = new Schema({ + key: { + id: { type: String, required: true, minlength: 1 }, + remoteJid: { type: String, required: true, minlength: 1 }, + fromMe: { type: Boolean, required: true }, + participant: { type: String, minlength: 1 }, + }, + message: { type: Object }, + source: { type: String, minlength: 3, enum: ['android', 'web', 'ios'] }, + messageTimestamp: { type: Number, required: true }, + instanceName: { type: String, required: true, minlength: 1 }, +}); + +export const MessageModel = dbserver?.model(MessageRaw.name, messageSchema, 'messages'); +export type IMessageModel = typeof MessageModel; + +export class MessageUpdateRaw { + _id?: string; + remoteJid?: string; + id?: string; + fromMe?: boolean; + participant?: string; + datetime?: number; + status: wa.StatusMessage; + instanceName: string; +} + +const messageUpdateSchema = new Schema({ + remoteJid: { type: String, required: true, min: 1 }, + id: { type: String, required: true, min: 1 }, + fromMe: { type: Boolean, required: true }, + participante: { type: String, min: 1 }, + datetime: { type: Number, required: true }, + instanceName: { type: String, required: true, min: 1 }, +}); + +export const MessageUpModel = dbserver?.model( + MessageUpdateRaw.name, + messageUpdateSchema, + 'messageUpdate', +); +export type IMessageUpModel = typeof MessageUpModel; diff --git a/src/whatsapp/repository/chat.repository.ts b/src/whatsapp/repository/chat.repository.ts new file mode 100644 index 00000000..5b16f35a --- /dev/null +++ b/src/whatsapp/repository/chat.repository.ts @@ -0,0 +1,36 @@ +import { join } from 'path'; +import { ConfigService } from '../../config/env.config'; +import { ChatRaw, IChatModel } from '../models/chat.model'; +import { IInsert, Repository } from '../abstract/abstract.repository'; + +export class ChatRepository extends Repository { + constructor( + private readonly chatModel: IChatModel, + private readonly configService: ConfigService, + ) { + super(configService); + } + + public async insert(data: ChatRaw[], saveDb = false): Promise { + try { + if (this.dbSettings.ENABLED) { + const insert = await this.chatModel.insertMany([...data]); + return { insertCount: insert.length }; + } + + data.forEach((chat) => { + this.writeStore({ + path: join(this.storePath, 'chats'), + fileName: chat.id, + data: chat, + }); + }); + + return { insertCount: data.length }; + } catch (error) { + return error; + } finally { + data = undefined; + } + } +} diff --git a/src/whatsapp/repository/contact.repository.ts b/src/whatsapp/repository/contact.repository.ts new file mode 100644 index 00000000..50a2f1f0 --- /dev/null +++ b/src/whatsapp/repository/contact.repository.ts @@ -0,0 +1,77 @@ +import { opendirSync, readFileSync } from 'fs'; +import { join } from 'path'; +import { ConfigService } from '../../config/env.config'; +import { ContactRaw, IContactModel } from '../models/contact.model'; +import { IInsert, Repository } from '../abstract/abstract.repository'; + +export class ContactQuery { + where: ContactRaw; +} + +export class ContactRepository extends Repository { + constructor( + private readonly contactModel: IContactModel, + private readonly configService: ConfigService, + ) { + super(configService); + } + + public async insert(data: ContactRaw[], saveDb = false): Promise { + try { + if (this.dbSettings.ENABLED) { + const insert = await this.contactModel.insertMany([...data]); + return { insertCount: insert.length }; + } + + data.forEach((contact) => { + this.writeStore({ + path: join(this.storePath, 'contacts'), + fileName: contact.id, + data: contact, + }); + }); + + return { insertCount: data.length }; + } catch (error) { + return error; + } finally { + data = undefined; + } + } + + public async find(query: ContactQuery): Promise { + try { + if (this.dbSettings.ENABLED) { + return await this.contactModel.find({ ...query.where }); + } + const contacts: ContactRaw[] = []; + if (query?.where?.id) { + contacts.push( + JSON.parse( + readFileSync(join(this.storePath, 'contacts', query.where.id + '.json'), { + encoding: 'utf-8', + }), + ), + ); + } else { + const openDir = opendirSync(join(this.storePath, 'contacts'), { + encoding: 'utf-8', + }); + for await (const dirent of openDir) { + if (dirent.isFile()) { + contacts.push( + JSON.parse( + readFileSync(join(this.storePath, 'contacts', dirent.name), { + encoding: 'utf-8', + }), + ), + ); + } + } + } + return contacts; + } catch (error) { + return []; + } + } +} diff --git a/src/whatsapp/repository/index.repository.ts b/src/whatsapp/repository/index.repository.ts new file mode 100644 index 00000000..48bf9e77 --- /dev/null +++ b/src/whatsapp/repository/index.repository.ts @@ -0,0 +1,13 @@ +import { MessageRepository } from './message.repository'; +import { ChatRepository } from './chat.repository'; +import { ContactRepository } from './contact.repository'; +import { MessageUpRepository } from './messageUp.repository'; + +export class RepositoryBroker { + constructor( + public readonly message: MessageRepository, + public readonly chat: ChatRepository, + public readonly contact: ContactRepository, + public readonly messageUpdate: MessageUpRepository, + ) {} +} diff --git a/src/whatsapp/repository/message.repository.ts b/src/whatsapp/repository/message.repository.ts new file mode 100644 index 00000000..811ef937 --- /dev/null +++ b/src/whatsapp/repository/message.repository.ts @@ -0,0 +1,81 @@ +import { ConfigService } from '../../config/env.config'; +import { join } from 'path'; +import { IMessageModel, MessageRaw } from '../models/message.model'; +import { IInsert, Repository } from '../abstract/abstract.repository'; +import { opendirSync, readFileSync } from 'fs'; + +export class MessageQuery { + where: MessageRaw; +} + +export class MessageRepository extends Repository { + constructor( + private readonly messageModel: IMessageModel, + private readonly configService: ConfigService, + ) { + super(configService); + } + + public async insert(data: MessageRaw[], saveDb = false): Promise { + try { + if (this.dbSettings.ENABLED && saveDb) { + const insert = await this.messageModel.insertMany([...data]); + return { insertCount: insert.length }; + } + + data.forEach((msg) => + this.writeStore({ + path: join(this.storePath, 'messages'), + fileName: msg.key.id, + data: msg, + }), + ); + + return { insertCount: data.length }; + } catch (error) { + console.log('ERROR: ', error); + return error; + } finally { + data = undefined; + } + } + + public async find(query: MessageQuery): Promise { + try { + if (this.dbSettings.ENABLED) { + return await this.messageModel.find({ ...query.where }); + } + + const messages: MessageRaw[] = []; + if (query?.where?.key?.id) { + messages.push( + JSON.parse( + readFileSync(join(this.storePath, 'messages', query.where.key.id + '.json'), { + encoding: 'utf-8', + }), + ), + ); + } else { + const openDir = opendirSync(join(this.storePath, 'messages'), { + encoding: 'utf-8', + }); + + for await (const dirent of openDir) { + if (dirent.isFile()) { + messages.push( + JSON.parse( + readFileSync(join(this.storePath, 'messages', dirent.name), { + encoding: 'utf-8', + }), + ), + ); + } + } + } + + return messages; + } catch (error) { + return []; + } + } +} diff --git a/src/whatsapp/repository/messageUp.repository.ts b/src/whatsapp/repository/messageUp.repository.ts new file mode 100644 index 00000000..83a47130 --- /dev/null +++ b/src/whatsapp/repository/messageUp.repository.ts @@ -0,0 +1,77 @@ +import { Model } from 'mongoose'; +import { ConfigService } from '../../config/env.config'; +import { IMessageUpModel, MessageUpdateRaw } from '../models/message.model'; +import { IInsert, Repository } from '../abstract/abstract.repository'; +import { join } from 'path'; +import { opendirSync, readFileSync } from 'fs'; + +export class MessageUpQuery { + where: MessageUpdateRaw; +} + +export class MessageUpRepository extends Repository { + constructor( + private readonly messageUpModel: Model, + private readonly configService: ConfigService, + ) { + super(configService); + } + + public async insert(data: MessageUpdateRaw[], saveDb?: boolean): Promise { + try { + if (this.dbSettings.ENABLED && saveDb) { + const insert = await this.messageUpModel.insertMany([...data]); + return { insertCount: insert.length }; + } + + data.forEach((update) => { + this.writeStore({ + path: join(this.storePath, 'message-up'), + fileName: update.id, + data: update, + }); + }); + } catch (error) { + return error; + } + } + + public async find(query: MessageUpQuery): Promise { + try { + if (this.dbSettings.ENABLED) { + return await this.messageUpModel.find({ ...query.where }); + } + + const messageUpdate: MessageUpdateRaw[] = []; + if (query?.where?.id) { + messageUpdate.push( + JSON.parse( + readFileSync(join(this.storePath, 'message-up', query.where.id + '.json'), { + encoding: 'utf-8', + }), + ), + ); + } else { + const openDir = opendirSync(join(this.storePath, 'message-up'), { + encoding: 'utf-8', + }); + + for await (const dirent of openDir) { + if (dirent.isFile()) { + messageUpdate.push( + JSON.parse( + readFileSync(join(this.storePath, 'message-up', dirent.name), { + encoding: 'utf-8', + }), + ), + ); + } + } + + return messageUpdate; + } + } catch (error) { + return []; + } + } +} diff --git a/src/whatsapp/routers/chat.router.ts b/src/whatsapp/routers/chat.router.ts new file mode 100644 index 00000000..c53ddedb --- /dev/null +++ b/src/whatsapp/routers/chat.router.ts @@ -0,0 +1,85 @@ +import { RequestHandler, Router } from 'express'; +import { + archiveChatSchema, + contactValidateSchema, + messageUpSchema, + messaseValidateSchema, + readMessageSchema, + whatsappNumberSchema, +} from '../../validate/validate.schema'; +import { ArchiveChatDto, ReadMessageDto, WhatsAppNumberDto } from '../dto/chat.dto'; +import { ContactQuery } from '../repository/contact.repository'; +import { MessageQuery } from '../repository/message.repository'; +import { chatController } from '../whatsapp.module'; +import { RouterBroker } from '../abstract/abstract.router'; +import { HttpStatus } from './index.router'; +import { MessageUpQuery } from '../repository/messageUp.repository'; + +export class ChatRouter extends RouterBroker { + constructor(...guards: RequestHandler[]) { + super(); + this.router + .post(this.routerPath('whatsappNumbers'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: whatsappNumberSchema, + ClassRef: WhatsAppNumberDto, + execute: (instance, data) => chatController.whatsappNumber(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) + .put(this.routerPath('markMessageAsRead'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: readMessageSchema, + ClassRef: ReadMessageDto, + execute: (instance, data) => chatController.readMessage(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) + .put(this.routerPath('archiveChat'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: archiveChatSchema, + ClassRef: ArchiveChatDto, + execute: (instance, data) => chatController.archiveChat(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) + .post(this.routerPath('findContacts'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: contactValidateSchema, + ClassRef: ContactQuery, + execute: (instance, data) => chatController.fetchContacts(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) + .post(this.routerPath('findMessages'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: messaseValidateSchema, + ClassRef: MessageQuery, + execute: (instance, data) => chatController.fetchMessages(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) + .post(this.routerPath('findStatusMessage'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: messageUpSchema, + ClassRef: MessageUpQuery, + execute: (instance, data) => chatController.fetchStatusMessage(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }); + } + + public readonly router = Router(); +} diff --git a/src/whatsapp/routers/group.router.ts b/src/whatsapp/routers/group.router.ts new file mode 100644 index 00000000..52fc519d --- /dev/null +++ b/src/whatsapp/routers/group.router.ts @@ -0,0 +1,90 @@ +import { RequestHandler, Router } from 'express'; +import { + createGroupSchema, + groupJidSchema, + updateGparticipantsSchema, +} from '../../validate/validate.schema'; +import { RouterBroker } from '../abstract/abstract.router'; +import { CreateGroupDto, GroupJid, GroupUpdateParticipantDto } from '../dto/group.dto'; +import { groupController } from '../whatsapp.module'; +import { HttpStatus } from './index.router'; + +export class GroupRouter extends RouterBroker { + constructor(...guards: RequestHandler[]) { + super(); + this.router + .post(this.routerPath('create'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: createGroupSchema, + ClassRef: CreateGroupDto, + execute: (instance, data) => groupController.createGroup(instance, data), + }); + + res.status(HttpStatus.CREATED).json(response); + }) + .get(this.routerPath('findGroupInfos'), ...guards, async (req, res) => { + const response = await this.groupValidate({ + request: req, + schema: groupJidSchema, + ClassRef: GroupJid, + execute: (instance, data) => groupController.findGroupInfo(instance, data), + }); + + res.status(HttpStatus.OK).json(response); + }) + .get(this.routerPath('participants'), ...guards, async (req, res) => { + const response = await this.groupValidate({ + request: req, + schema: groupJidSchema, + ClassRef: GroupJid, + execute: (instance, data) => groupController.findParticipnats(instance, data), + }); + + res.status(HttpStatus.OK).json(response); + }) + .get(this.routerPath('inviteCode'), ...guards, async (req, res) => { + const response = await this.groupValidate({ + request: req, + schema: groupJidSchema, + ClassRef: GroupJid, + execute: (instance, data) => groupController.inviteCode(instance, data), + }); + + res.status(HttpStatus.OK).json(response); + }) + + .put(this.routerPath('revokeInviteCode'), ...guards, async (req, res) => { + const response = await this.groupValidate({ + request: req, + schema: groupJidSchema, + ClassRef: GroupJid, + execute: (instance, data) => groupController.revokeInviteCode(instance, data), + }); + + res.status(HttpStatus.CREATED).json(response); + }) + .put(this.routerPath('updateParticipant'), ...guards, async (req, res) => { + const response = await this.groupValidate({ + request: req, + schema: updateGparticipantsSchema, + ClassRef: GroupUpdateParticipantDto, + execute: (instance, data) => groupController.revokeInviteCode(instance, data), + }); + + res.status(HttpStatus.CREATED).json(response); + }) + .delete(this.routerPath('leaveGroup'), ...guards, async (req, res) => { + const response = await this.groupValidate({ + request: req, + schema: updateGparticipantsSchema, + ClassRef: GroupJid, + execute: (instance, data) => groupController.revokeInviteCode(instance, data), + }); + + res.status(HttpStatus.OK).json(response); + }); + } + + public readonly router = Router(); +} diff --git a/src/whatsapp/routers/index.router.ts b/src/whatsapp/routers/index.router.ts new file mode 100644 index 00000000..363377d1 --- /dev/null +++ b/src/whatsapp/routers/index.router.ts @@ -0,0 +1,37 @@ +import { Router } from 'express'; +import { Auth, configService } from '../../config/env.config'; +import { instanceExistsGuard, instanceLoggedGuard } from '../guards/instance.guard'; +import { authGuard } from '../guards/auth.guard'; +import { ChatRouter } from './chat.router'; +import { GroupRouter } from './group.router'; +import { InstanceRouter } from './instance.router'; +import { MessageRouter } from './sendMessage.router'; +import { ViewslRouter } from './view.router'; +import { WebhookRouter } from './webhook.router'; + +enum HttpStatus { + OK = 200, + CREATED = 201, + NOT_FOUND = 404, + FORBIDDEN = 403, + BAD_REQUEST = 400, + UNAUTHORIZED = 401, + INTERNAL_SERVER_ERROR = 500, +} + +const router = Router(); +const authType = configService.get('AUTHENTICATION').TYPE; +const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]]; + +router + .use( + '/instance', + new InstanceRouter(configService, ...guards).router, + new ViewslRouter(configService, instanceExistsGuard, instanceLoggedGuard).router, + ) + .use('/message', new MessageRouter(...guards).router) + .use('/chat', new ChatRouter(...guards).router) + .use('/group', new GroupRouter(...guards).router) + .use('/webhook', new WebhookRouter(...guards).router); + +export { router, HttpStatus }; diff --git a/src/whatsapp/routers/instance.router.ts b/src/whatsapp/routers/instance.router.ts new file mode 100644 index 00000000..7eb7decb --- /dev/null +++ b/src/whatsapp/routers/instance.router.ts @@ -0,0 +1,83 @@ +import { RequestHandler, Router } from 'express'; +import { instanceNameSchema, oldTokenSchema } from '../../validate/validate.schema'; +import { InstanceDto } from '../dto/instance.dto'; +import { instanceController } from '../whatsapp.module'; +import { RouterBroker } from '../abstract/abstract.router'; +import { HttpStatus } from './index.router'; +import { OldToken } from '../services/auth.service'; +import { Auth, ConfigService } from '../../config/env.config'; + +export class InstanceRouter extends RouterBroker { + constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) { + super(); + const auth = configService.get('AUTHENTICATION'); + this.router + .post('/create', ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: instanceNameSchema, + ClassRef: InstanceDto, + execute: (instance) => instanceController.createInstance(instance), + }); + + let session: any = {}; + + auth.TYPE === 'jwt' + ? (session = { type: 'jwt', hash: response.hash.jwt }) + : (session = { type: 'apikey', hash: response.hash.apikey }); + req.session[response.instance.instanceName] = session; + + return res.status(HttpStatus.CREATED).json(response); + }) + .get(this.routerPath('connect'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: instanceNameSchema, + ClassRef: InstanceDto, + execute: (instance) => instanceController.connectToWhatsapp(instance), + }); + + return res.status(HttpStatus.OK).json(response); + }) + .get(this.routerPath('connectionState'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: instanceNameSchema, + ClassRef: InstanceDto, + execute: (instance) => instanceController.connectionState(instance), + }); + + return res.status(HttpStatus.OK).json(response); + }) + .delete(this.routerPath('logout'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: instanceNameSchema, + ClassRef: InstanceDto, + execute: (instance) => instanceController.logout(instance), + }); + + return res.status(HttpStatus.OK).json(response); + }); + + if (auth.TYPE === 'jwt') { + this.router.put('/refreshToken', async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: oldTokenSchema, + ClassRef: OldToken, + execute: (_, data) => instanceController.refreshToken(_, data), + }); + + req.session[response.instanceName] = { + header: 'Authorization', + hash: response.jwt, + } as any; + + return res.status(HttpStatus.CREATED).json(response); + }); + } + } + + public readonly router = Router(); +} diff --git a/src/whatsapp/routers/sendMessage.router.ts b/src/whatsapp/routers/sendMessage.router.ts new file mode 100644 index 00000000..bba1b7b0 --- /dev/null +++ b/src/whatsapp/routers/sendMessage.router.ts @@ -0,0 +1,101 @@ +import { RequestHandler, Router } from 'express'; +import { + buttonMessageSchema, + contactMessageSchema, + listMessageSchema, + locationMessageSchema, + mediaMessageSchema, + reactionMessageSchema, + textMessageSchema, +} from '../../validate/validate.schema'; +import { + SendButtonDto, + SendContactDto, + SendListDto, + SendLocationDto, + SendMediaDto, + SendReactionDto, + SendTextDto, +} from '../dto/sendMessage.dto'; +import { sendMessageController } from '../whatsapp.module'; +import { RouterBroker } from '../abstract/abstract.router'; +import { HttpStatus } from './index.router'; + +export class MessageRouter extends RouterBroker { + constructor(...guards: RequestHandler[]) { + super(); + this.router + .post(this.routerPath('sendText'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: textMessageSchema, + ClassRef: SendTextDto, + execute: (instance, data) => sendMessageController.sendText(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) + .post(this.routerPath('sendMedia'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: mediaMessageSchema, + ClassRef: SendMediaDto, + execute: (instance, data) => sendMessageController.sendMedia(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) + .post(this.routerPath('sendButtons'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: buttonMessageSchema, + ClassRef: SendButtonDto, + execute: (instance, data) => sendMessageController.sendButtons(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) + .post(this.routerPath('sendLocation'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: locationMessageSchema, + ClassRef: SendLocationDto, + execute: (instance, data) => sendMessageController.sendLocation(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) + .post(this.routerPath('sendList'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: listMessageSchema, + ClassRef: SendListDto, + execute: (instance, data) => sendMessageController.sendList(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) + .post(this.routerPath('sendContact'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: contactMessageSchema, + ClassRef: SendContactDto, + execute: (instance, data) => sendMessageController.sendContact(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }) + .post(this.routerPath('sendReaction'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: reactionMessageSchema, + ClassRef: SendReactionDto, + execute: (instance, data) => sendMessageController.sendReaction(instance, data), + }); + + return res.status(HttpStatus.CREATED).json(response); + }); + } + + public readonly router = Router(); +} diff --git a/src/whatsapp/routers/view.router.ts b/src/whatsapp/routers/view.router.ts new file mode 100644 index 00000000..2324cb00 --- /dev/null +++ b/src/whatsapp/routers/view.router.ts @@ -0,0 +1,17 @@ +import { RequestHandler, Router } from 'express'; +import { ConfigService, Production } from '../../config/env.config'; +import { RouterBroker } from '../abstract/abstract.router'; +import { viewsController } from '../whatsapp.module'; + +export class ViewslRouter extends RouterBroker { + constructor(configService: ConfigService, ...guards: RequestHandler[]) { + super(); + if (!configService.get('PRODUCTION')) { + this.router.get(this.routerPath('qrcode'), ...guards, (req, res) => { + return viewsController.qrcode(req, res); + }); + } + } + + public readonly router = Router(); +} diff --git a/src/whatsapp/routers/webhook.router.ts b/src/whatsapp/routers/webhook.router.ts new file mode 100644 index 00000000..9aa5ab4a --- /dev/null +++ b/src/whatsapp/routers/webhook.router.ts @@ -0,0 +1,35 @@ +import { RequestHandler, Router } from 'express'; +import { webhookSchema } from '../../validate/validate.schema'; +import { RouterBroker } from '../abstract/abstract.router'; +import { WebhookDto } from '../dto/webhook.dto'; +import { webhookController } from '../whatsapp.module'; +import { HttpStatus } from './index.router'; + +export class WebhookRouter extends RouterBroker { + constructor(...guards: RequestHandler[]) { + super(); + this.router + .post(this.routerPath('set'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: webhookSchema, + ClassRef: WebhookDto, + execute: (instance, data) => webhookController.createWebhook(instance, data), + }); + + res.status(HttpStatus.CREATED).json(response); + }) + .get(this.routerPath('find'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: webhookSchema, + ClassRef: WebhookDto, + execute: (instance) => webhookController.findWebhook(instance), + }); + + res.status(HttpStatus.OK).json(response); + }); + } + + public readonly router = Router(); +} diff --git a/src/whatsapp/services/auth.service.ts b/src/whatsapp/services/auth.service.ts new file mode 100644 index 00000000..20cb485d --- /dev/null +++ b/src/whatsapp/services/auth.service.ts @@ -0,0 +1,121 @@ +import { Auth, ConfigService } from '../../config/env.config'; +import { InstanceDto } from '../dto/instance.dto'; +import { name as apiName } from '../../../package.json'; +import { verify, sign } from 'jsonwebtoken'; +import { readFileSync, writeFile } from 'fs'; +import { join } from 'path'; +import { AUTH_DIR } from '../../config/path.config'; +import { Logger } from '../../config/logger.config'; +import { v4 } from 'uuid'; +import { isJWT } from 'class-validator'; +import { BadRequestException } from '../../exceptions'; + +export type JwtPayload = { + instanceName: string; + apiName: string; + jwt?: string; + apiKey?: string; + tokenId: string; +}; + +export class OldToken { + oldToken: string; +} + +export class AuthService { + constructor(private readonly configService: ConfigService) {} + + private readonly logger = new Logger(AuthService.name); + + private jwt(instance: InstanceDto) { + const jwtOpts = this.configService.get('AUTHENTICATION').JWT; + const token = sign( + { + instanceName: instance.instanceName, + apiName, + tokenId: v4(), + }, + jwtOpts.SECRET, + { expiresIn: jwtOpts.EXPIRIN_IN, encoding: 'utf8', subject: 'g-t' }, + ); + + writeFile( + join(AUTH_DIR, 'jwt', instance.instanceName + '.json'), + JSON.stringify({ instance: instance.instanceName, jwt: token }), + (error) => { + if (error) { + this.logger.error({ + localError: AuthService.name + '.jwt', + error, + }); + } + }, + ); + + return { jwt: token }; + } + + private apikey(instance: InstanceDto) { + const apikey = v4().toUpperCase(); + + writeFile( + join(AUTH_DIR, 'apikey', instance.instanceName + '.json'), + JSON.stringify({ instance: instance.instanceName, apikey }), + (error) => { + if (error) { + this.logger.error({ + localError: AuthService.name + '.jwt', + error, + }); + } + }, + ); + + return { apikey }; + } + + public generateHash(instance: InstanceDto) { + const options = this.configService.get('AUTHENTICATION'); + return this[options.TYPE](instance) as { jwt: string } | { apikey: string }; + } + + public refreshToken({ oldToken }: OldToken) { + if (!isJWT(oldToken)) { + throw new BadRequestException('Invalid "oldToken"'); + } + + try { + const jwtOpts = this.configService.get('AUTHENTICATION').JWT; + const decode = verify(oldToken, jwtOpts.SECRET, { + ignoreExpiration: true, + }) as Pick; + + const tokenStore = JSON.parse( + readFileSync(join(AUTH_DIR, 'jwt', decode.instanceName + '.json'), { + encoding: 'utf-8', + }), + ) as Pick; + + const decodeTokenStore = verify(tokenStore.jwt, jwtOpts.SECRET, { + ignoreExpiration: true, + }) as Pick; + + if (decode.tokenId !== decodeTokenStore.tokenId) { + throw new BadRequestException('Invalid "oldToken"'); + } + + const token = { + jwt: this.jwt({ instanceName: decode.instanceName }).jwt, + instanceName: decode.instanceName, + }; + + return token; + } catch (error) { + this.logger.error({ + localError: AuthService.name + '.refreshToken', + error, + }); + throw new BadRequestException('Invalid "oldToken"'); + } + } +} diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts new file mode 100644 index 00000000..2925de34 --- /dev/null +++ b/src/whatsapp/services/monitor.service.ts @@ -0,0 +1,166 @@ +import { Dirent, opendirSync, readdirSync, rmSync } from 'fs'; +import { WAStartupService } from './whatsapp.service'; +import { INSTANCE_DIR } from '../../config/path.config'; +import EventEmitter2 from 'eventemitter2'; +import { join } from 'path'; +import { DisconnectReason } from '@adiwajshing/baileys'; +import { Boom } from '@hapi/boom'; +import { Logger } from '../../config/logger.config'; +import { ConfigService, Database } from '../../config/env.config'; +import { RepositoryBroker } from '../repository/index.repository'; +import { dbserver, mongoClient } from '../../db/db.connect'; + +export class WAMonitoringService { + constructor( + private readonly eventEmitter: EventEmitter2, + private readonly configService: ConfigService, + private readonly repository: RepositoryBroker, + ) { + this.removeInstance(); + this.noConnection(); + this.delInstanceFiles(); + } + + private readonly db = this.configService.get('DATABASE'); + private readonly dbInstance = this.db.ENABLED + ? mongoClient.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances') + : null; + + private readonly logger = new Logger(WAMonitoringService.name); + public readonly waInstances: Record = {}; + + private delInstanceFiles() { + setInterval(async () => { + if (this.db.ENABLED) { + const collections = await this.dbInstance.collections(); + collections.forEach(async (collection) => { + const name = collection.namespace.replace(/^[\w-]+./, ''); + await this.dbInstance.collection(name).deleteMany({ + $or: [ + { _id: { $regex: /^app.state.*/ } }, + { _id: { $regex: /^session-.*/ } }, + ], + }); + }); + } else { + const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' }); + for await (const dirent of dir) { + if (dirent.isDirectory()) { + const files = readdirSync(join(INSTANCE_DIR, dirent.name), { + encoding: 'utf-8', + }); + files.forEach(async (file) => { + if (file.match(/^app.state.*/) || file.match(/^session-.*/)) { + rmSync(join(INSTANCE_DIR, dirent.name, file), { + recursive: true, + force: true, + }); + } + }); + } + } + } + }, 3600 * 1000 * 2); + } + + public async loadInstance() { + const set = async (name: string) => { + const instance = new WAStartupService( + this.configService, + this.eventEmitter, + this.repository, + ); + instance.instanceName = name; + await instance.connectToWhatsapp(); + this.waInstances[name] = instance; + }; + + try { + if (this.db.ENABLED) { + await mongoClient.connect(); + const collections = await this.dbInstance.collections(); + if (collections.length > 0) { + collections.forEach( + async (coll) => await set(coll.namespace.replace(/^[\w-]+\./, '')), + ); + } + return; + } + const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' }); + for await (const dirent of dir) { + if (dirent.isDirectory()) { + const files = readdirSync(join(INSTANCE_DIR, dirent.name), { + encoding: 'utf-8', + }); + if (files.length === 0) { + rmSync(join(INSTANCE_DIR, dirent.name), { recursive: true, force: true }); + break; + } + + await set(dirent.name); + } + } + } finally { + if (this.db.ENABLED) { + await mongoClient.close(); + } + } + } + + private removeInstance() { + this.eventEmitter.on('remove.instance', async (instanceName: string) => { + try { + await this.waInstances[instanceName].client?.logout(); + delete this.waInstances[instanceName]; + } catch (error) {} + + try { + if (this.db.ENABLED) { + await mongoClient.connect(); + return await this.dbInstance.dropCollection(instanceName); + } + rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); + } catch (error) { + this.logger.error({ + localError: 'removeInstance', + warn: `Error deleting ${instanceName} folder with whatsapp connection files, or files do not exist.`, + error, + }); + } finally { + if (this.db.ENABLED) { + await mongoClient.close(); + } + } + }); + } + + private noConnection() { + this.eventEmitter.on('no.connection', async (instanceName) => { + try { + this.waInstances[instanceName].client?.end( + new Boom('QR code limit reached, please login again', { + statusCode: DisconnectReason.badSession, + }), + ); + delete this.waInstances[instanceName]; + + if (this.db.ENABLED) { + await mongoClient.connect(); + return await this.dbInstance.dropCollection(instanceName); + } + + rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); + } catch (error) { + this.logger.error({ + localError: 'noConnection', + warn: 'Error deleting instance from memory.', + error, + }); + } finally { + if (this.db.ENABLED) { + await mongoClient.close(); + } + } + }); + } +} diff --git a/src/whatsapp/services/webhook.service.ts b/src/whatsapp/services/webhook.service.ts new file mode 100644 index 00000000..377bcfe7 --- /dev/null +++ b/src/whatsapp/services/webhook.service.ts @@ -0,0 +1,35 @@ +import { readFileSync, writeFileSync } from 'fs'; +import { join } from 'path'; +import { ROOT_DIR } from '../../config/path.config'; +import { InstanceDto } from '../dto/instance.dto'; +import { WebhookDto } from '../dto/webhook.dto'; +import { WAMonitoringService } from './monitor.service'; + +export class WebhookService { + constructor(private readonly waMonitor: WAMonitoringService) {} + private readonly store = join(ROOT_DIR, 'store', 'webhook'); + + public create(instance: InstanceDto, data: WebhookDto) { + writeFileSync(join(this.store, instance.instanceName), JSON.stringify(data), { + encoding: 'utf-8', + }); + + this.waMonitor.waInstances[instance.instanceName].setWebhook(data); + + return { webhook: { ...instance, webhook: data } }; + } + + public find(instance: InstanceDto): WebhookDto { + try { + const webhook = JSON.parse( + readFileSync(join(this.store, instance.instanceName + '.json'), { + encoding: 'utf-8', + }), + ) as WebhookDto; + + return webhook; + } catch (error) { + return { enabled: null, url: '' }; + } + } +} diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts new file mode 100644 index 00000000..3ae7887a --- /dev/null +++ b/src/whatsapp/services/whatsapp.service.ts @@ -0,0 +1,916 @@ +import makeWASocket, { + BaileysEventEmitter, + delay, + DisconnectReason, + fetchLatestBaileysVersion, + generateWAMessageFromContent, + getDevice, + isJidGroup, + isJidUser, + MessageRetryMap, + prepareWAMessageMedia, + proto, + useMultiFileAuthState, + UserFacingSocketConfig, + WABrowserDescription, + WASocket, +} from '@adiwajshing/baileys'; +import { + ConfigService, + ConfigSessionPhone, + Database, + StoreConf, + Webhook, +} from '../../config/env.config'; +import { Logger } from '../../config/logger.config'; +import { INSTANCE_DIR, ROOT_DIR, SRC_DIR } from '../../config/path.config'; +import { existsSync, readFileSync } from 'fs'; +import { join } from 'path'; +import axios from 'axios'; +import { v4 } from 'uuid'; +import { QRCodeToDataURLOptions, toDataURL } from 'qrcode'; +import { Events, wa } from '../types/wa.types'; +import { Boom } from '@hapi/boom'; +import EventEmitter2 from 'eventemitter2'; +import { release } from 'os'; +import P from 'pino'; +import { execSync } from 'child_process'; +import { RepositoryBroker } from '../repository/index.repository'; +import { MessageRaw, MessageUpdateRaw } from '../models/message.model'; +import { ContactRaw } from '../models/contact.model'; +import { ChatRaw } from '../models/chat.model'; +import { getMIMEType } from 'node-mime-types'; +import { + ContactMessage, + MediaMessage, + Options, + SendButtonDto, + SendContactDto, + SendListDto, + SendLocationDto, + SendMediaDto, + SendReactionDto, + SendTextDto, +} from '../dto/sendMessage.dto'; +import { arrayUnique, isBase64, isURL } from 'class-validator'; +import { + ArchiveChatDto, + OnWhatsAppDto, + ReadMessageDto, + WhatsAppNumberDto, +} from '../dto/chat.dto'; +import { MessageQuery } from '../repository/message.repository'; +import { ContactQuery } from '../repository/contact.repository'; +import { + BadRequestException, + InternalServerErrorException, + NotFoundException, +} from '../../exceptions'; +import { CreateGroupDto, GroupJid, GroupUpdateParticipantDto } from '../dto/group.dto'; +import { WebhookDto } from '../dto/webhook.dto'; +import { MessageUpQuery } from '../repository/messageUp.repository'; +import { useMultiFileAuthStateDb } from '../../utils/use-multi-file-auth-state-db'; + +export class WAStartupService { + constructor( + private readonly configService: ConfigService, + private readonly eventEmitter: EventEmitter2, + private readonly repository: RepositoryBroker, + ) { + this.loadWebhook(); + this.cleanStore(); + this.instance.qrcode = { count: 0 }; + } + + private readonly logger = new Logger(WAStartupService.name); + private readonly instance: wa.Instance = {}; + public client: WASocket; + private readonly localWebhook: wa.LocalWebHook = {}; + private readonly msgRetryCounterMap: MessageRetryMap = {}; + private stateConnection: wa.StateConnection = { + state: 'close', + }; + private readonly storePath = join(ROOT_DIR, 'store'); + + public set instanceName(name: string) { + if (!name) { + this.instance.name = v4(); + return; + } + this.instance.name = name; + } + + public get instanceName() { + return this.instance.name; + } + + public get qrCode(): wa.QrCode { + return { + code: this.instance.qrcode?.code, + base64: this.instance.qrcode?.base64, + }; + } + + private async loadWebhook() { + const path = join(SRC_DIR, 'store', 'webhook', this.instance.name + '.json'); + if (existsSync(path)) { + try { + const data = JSON.parse( + readFileSync(path, { encoding: 'utf-8' }), + ) as wa.LocalWebHook; + this.localWebhook.enabled = data.enabled; + this.localWebhook.url = data.url; + } catch (error) {} + } + } + + public setWebhook(data: WebhookDto) { + Object.assign(this.localWebhook, data); + } + + private async sendDataWebhook(event: Events, data: T) { + const webhook = this.configService.get('WEBHOOK'); + const we = event.replace('.', '_').toUpperCase(); + if (webhook.EVENTS[we]) { + try { + if (this.localWebhook.enabled && isURL(this.localWebhook.url)) { + const httpService = axios.create({ baseURL: this.localWebhook.url }); + await httpService.post( + '', + { event, data: { ...data } }, + { params: '/' + this.instance.name }, + ); + } + + const globalWebhhok = this.configService.get('WEBHOOK').GLOBAL; + if (globalWebhhok && globalWebhhok?.ENABLED && isURL(globalWebhhok.URL)) { + const httpService = axios.create({ baseURL: globalWebhhok.URL }); + await httpService.post( + '', + { event, data: { ...data } }, + { params: '/' + this.instance.name }, + ); + } + } catch (error) { + this.logger.error({ + local: + WAStartupService.name + '.' + WAStartupService.prototype.sendDataWebhook.name, + message: error?.message, + hostName: error?.hostname, + syscall: error?.syscall, + code: error?.code, + error: error?.errno, + stack: error?.stack, + name: error?.name, + }); + } finally { + data = undefined; + } + } + } + + private async connectionUpdate(ev: BaileysEventEmitter) { + ev.on('connection.update', async ({ qr, connection, lastDisconnect }) => { + if (qr) { + if (this.instance.qrcode.count === 6) { + this.sendDataWebhook(Events.QRCODE_UPDATED, { + message: 'QR code limit reached, please login again', + statusCode: DisconnectReason.badSession, + }); + + this.sendDataWebhook(Events.CONNECTION_UPDATE, { + instance: this.instance.name, + state: 'refused', + statusReason: DisconnectReason.connectionClosed, + }); + + return this.eventEmitter.emit('no.connection', this.instance.name); + } + + this.instance.qrcode.count++; + + const optsQrcode: QRCodeToDataURLOptions = { + margin: 3, + scale: 4, + errorCorrectionLevel: 'H', + color: { + light: '#ffffff', + dark: '#198754', + }, + }; + + toDataURL(qr, optsQrcode, (error, base64) => { + if (error) { + this.logger.error('Qrcode generare failed:' + error.toString()); + return; + } + + this.instance.qrcode.base64 = base64; + this.instance.qrcode.code = qr; + + this.sendDataWebhook(Events.QRCODE_UPDATED, { + instance: this.instance.name, + qrcode: { code: qr, base64 }, + }); + }); + } + + if (connection) { + this.stateConnection = { + instance: this.instance.name, + state: connection, + statusReason: (lastDisconnect?.error as Boom)?.output?.statusCode || 200, + }; + } + + if (connection === 'close') { + const shouldRecnnect = + (lastDisconnect.error as Boom)?.output?.statusCode !== + DisconnectReason.loggedOut; + if (shouldRecnnect) { + await this.connectToWhatsapp(); + } else { + return this.eventEmitter.emit('remove.instance', this.instance.name); + } + } + + if (connection === 'open') { + this.setHandles(this.client.ev); + this.instance.wuid = this.client.user.id.replace(/:d+/, ''); + this.logger.info( + ` + ┌──────────────────────────────┐ + │ CONNECTED TO WHATSAPP │ + └──────────────────────────────┘`.replace(/^ +/gm, ' '), + ); + } + }); + } + + private async getMessage(key: proto.IMessageKey): Promise { + try { + const webMessageInfo: proto.IWebMessageInfo = JSON.parse( + readFileSync(join(this.storePath, 'messages', key.id + '.json'), { + encoding: 'utf-8', + }), + ); + return webMessageInfo.message; + } catch (error) { + return { conversation: '' }; + } + } + + private cleanStore() { + const store = this.configService.get('STORE'); + const database = this.configService.get('DATABASE'); + if (store?.CLEANING_INTARVAL && !database.ENABLED) { + setInterval(() => { + try { + for (const [key, value] of Object.entries(store)) { + if (value === true) { + execSync(`rm -rf ${join(this.storePath, key)}/*.json`); + } + } + } catch (error) {} + }, store?.CLEANING_INTARVAL * 1000); + } + } + + public async connectToWhatsapp() { + this.instance.authState = this.configService.get('DATABASE').ENABLED + ? await useMultiFileAuthStateDb(this.instance.name) + : await useMultiFileAuthState(join(INSTANCE_DIR, this.instance.name)); + + const { version } = await fetchLatestBaileysVersion(); + const session = this.configService.get('CONFIG_SESSION_PHONE'); + const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()]; + + const socketConfig: UserFacingSocketConfig = { + auth: this.instance.authState.state, + logger: P({ level: 'error' }), + msgRetryCounterMap: this.msgRetryCounterMap, + printQRInTerminal: true, + browser, + version, + connectTimeoutMs: 60_000, + emitOwnEvents: true, + qrTimeout: 60_000, + getMessage: this.getMessage, + }; + + this.client = makeWASocket(socketConfig); + this.connectionUpdate(this.client.ev); + + this.client.ev.on('creds.update', this.instance.authState.saveCreds); + + return this; + } + + private chatHandle(ev: BaileysEventEmitter) { + ev.on('chats.set', async ({ chats, isLatest }) => { + if (isLatest) { + const chatsRaw: ChatRaw[] = chats.map((chat) => { + return { + id: chat.id, + isnatnceName: this.instance.name, + }; + }); + await this.sendDataWebhook(Events.CHATS_SET, chatsRaw); + // await this.repository.chat.insert(chatsRaw, database.SAVE_DATA.CHATS); + } + }); + + ev.on('chats.upsert', async (chats) => { + const chatsRaw: ChatRaw[] = chats.map((chat) => { + return { + id: chat.id, + isnatnceName: this.instance.name, + }; + }); + await this.sendDataWebhook(Events.CHATS_UPSERT, chatsRaw); + }); + + ev.on('chats.update', async (chats) => { + const chatsRaw: ChatRaw[] = chats.map((chat) => { + return { + id: chat.id, + isnatnceName: this.instance.name, + }; + }); + await this.sendDataWebhook(Events.CHATS_UPDATE, chatsRaw); + }); + } + + private contactHandle(ev: BaileysEventEmitter) { + const database = this.configService.get('DATABASE'); + ev.on('contacts.upsert', async (contacts) => { + const contactsRepository = await this.repository.contact.find({ + where: { instanceName: this.instance.name }, + }); + + const contactsRaw: ContactRaw[] = []; + for await (const contact of contacts) { + if (contactsRepository.find((cr) => cr.id === contact.id)) { + continue; + } + + contactsRaw.push({ + id: contact.id, + pushName: contact?.name || contact?.verifiedName, + profilePictureUrl: (await this.profilePicture(contact.id)).profilePictureUrl, + instanceName: this.instance.name, + }); + } + await this.sendDataWebhook(Events.CONTACTS_UPSERT, [...contactsRaw]); + await this.repository.contact.insert(contactsRaw, database.SAVE_DATA.CONTACTS); + }); + + ev.on('contacts.update', async (contacts) => { + const contactsRaw: ContactRaw[] = []; + for await (const contact of contacts) { + contactsRaw.push({ + id: contact.id, + pushName: contact?.name || contact?.verifiedName, + profilePictureUrl: (await this.profilePicture(contact.id)).profilePictureUrl, + instanceName: this.instance.name, + }); + } + await this.sendDataWebhook(Events.CONTACTS_UPDATE, contactsRaw); + }); + } + + private messageHandle(ev: BaileysEventEmitter) { + const database = this.configService.get('DATABASE'); + ev.on('messages.set', async ({ messages, isLatest }) => { + messages.forEach(async (m) => { + if ( + m.message?.protocolMessage || + m.message?.senderKeyDistributionMessage || + !m.message + ) { + return; + } + + const messagesRepository = await this.repository.message.find({ + where: { instanceName: this.instance.name }, + }); + if ( + messagesRepository.find( + (mr) => mr.instanceName === this.instance.name && mr.key.id === m.key.id, + ) + ) { + return; + } + + const messagesRaw: MessageRaw[] = []; + messagesRaw.push({ + key: m.key, + message: { ...m.message }, + messageTimestamp: m.messageTimestamp, + instanceName: this.instance.name, + }); + + this.sendDataWebhook(Events.MESSAGES_SET, messagesRaw); + await this.repository.message.insert(messagesRaw, database.SAVE_DATA.OLD_MESSAGE); + + messages = undefined; + }); + }); + + ev.on('messages.upsert', async ({ messages, type }) => { + const received = messages[0]; + if ( + type !== 'notify' || + !received?.message || + received.message?.protocolMessage || + received.message.senderKeyDistributionMessage + ) { + return; + } + const messageRaw: MessageRaw = { + key: received.key, + message: { ...received.message }, + messageTimestamp: received.messageTimestamp, + instanceName: this.instance.name, + source: getDevice(received.key.id), + }; + + this.logger.log(received); + + await this.repository.message.insert([messageRaw], database.SAVE_DATA.NEW_MESSAGE); + await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); + }); + + ev.on('messages.update', async (args) => { + const status: Record = { + 0: 'ERROR', + 1: 'PENDING', + 2: 'SERVER_ACK', + 3: 'DELIVERY_ACK', + 4: 'READ', + 5: 'PLAYED', + }; + for await (const { key, update } of args) { + if (key.remoteJid !== 'status@broadcast' && !key?.remoteJid?.match(/(:\d+)/)) { + const message: MessageUpdateRaw = { + ...key, + status: status[update.status], + datetime: Date.now(), + instanceName: this.instance.name, + }; + await this.sendDataWebhook(Events.MESSAGES_UPDATE, message); + await this.repository.messageUpdate.insert( + [message], + database.SAVE_DATA.MESSAGE_UPDATE, + ); + } + } + }); + } + + private setHandles(ev: BaileysEventEmitter) { + this.chatHandle(ev); + this.contactHandle(ev); + this.messageHandle(ev); + } + + private createJid(number: string) { + if (number.includes('@g.us') || number.includes('@s.whatsapp.net')) { + return this.formatBRNumber(number) as string; + } + return number.includes('-') + ? `${number}@g.us` + : `${this.formatBRNumber(number)}@s.whatsapp.net`; + } + + // Check if the number is br + private formatBRNumber(jid: string) { + const regexp = new RegExp(/^(\d{2})(\d{2})\d{1}(\d{8})$/); + if (regexp.test(jid)) { + const match = regexp.exec(jid); + if (match && match[1] === '55' && Number.isInteger(Number.parseInt(match[2]))) { + const ddd = Number.parseInt(match[2]); + if (ddd < 31) { + return match[0]; + } else if (ddd >= 31) { + return match[1] + match[2] + match[3]; + } + } + } else { + return jid; + } + } + + public async profilePicture(number: string) { + const jid = this.createJid(number); + try { + return { + wuid: jid, + profilePictureUrl: await this.client.profilePictureUrl(jid, 'image'), + }; + } catch (error) { + return { + wuid: jid, + profilePictureUrl: null, + }; + } + } + + private async sendMessageWithTyping( + number: string, + message: proto.IMessage, + options?: Options, + ) { + try { + const jid = this.createJid(number); + if (options?.delay) { + await this.client.presenceSubscribe(jid); + await this.client.sendPresenceUpdate(options?.presence || 'composing', jid); + await delay(options.delay); + await this.client.sendPresenceUpdate('paused', jid); + } + + const messageSent = await this.client.sendMessage(jid, { + forward: { + key: { remoteJid: this.instance.wuid, fromMe: true }, + message, + }, + }); + + this.sendDataWebhook(Events.MESSAGES_UPSERT, messageSent).catch((error) => + this.logger.error(error), + ); + this.repository.message + .insert( + [{ ...messageSent, instanceName: this.instance.name }], + this.configService.get('DATABASE').SAVE_DATA.NEW_MESSAGE, + ) + .catch((error) => this.logger.error(error)); + + return messageSent; + } catch (error) { + throw new BadRequestException(error.toString()); + } + } + + // Instance Controller + public get connectionStatus() { + return this.stateConnection; + } + + // Send Message Controller + public async textMessage(data: SendTextDto) { + return await this.sendMessageWithTyping( + data.number, + { + extendedTextMessage: { + text: data.textMessage.text, + }, + }, + data?.options, + ); + } + + private async prepareMediaMessage(mediaMessage: MediaMessage) { + const prepareMedia = await prepareWAMessageMedia( + { + [mediaMessage.mediatype]: isURL(mediaMessage.media) + ? { url: mediaMessage.media } + : Buffer.from(mediaMessage.media, 'base64'), + } as any, + { upload: this.client.waUploadToServer }, + ); + + const type = mediaMessage.mediatype + 'Message'; + + if (mediaMessage.mediatype === 'document' && !mediaMessage.fileName) { + const regex = new RegExp(/.*\/(.+?)\./); + const arryMatch = regex.exec(mediaMessage.media); + mediaMessage.fileName = arryMatch[1]; + } else { + mediaMessage.fileName = type; + } + + let mimetype: string; + + if (isURL(mediaMessage.media)) { + mimetype = getMIMEType(mediaMessage.media); + } else { + mimetype = getMIMEType(mediaMessage.fileName); + } + + prepareMedia[type].caption = mediaMessage?.caption; + prepareMedia[type].mimetype = mimetype; + prepareMedia[type].fileName = mediaMessage.fileName; + + return generateWAMessageFromContent( + '', + { [type]: { ...prepareMedia[type] } }, + { userJid: this.instance.wuid }, + ); + } + + public async mediaMessage(data: SendMediaDto) { + const generate = await this.prepareMediaMessage(data.mediaMessage); + + return await this.sendMessageWithTyping( + data.number, + { ...generate.message }, + data?.options, + ); + } + + public async buttonMessage(data: SendButtonDto) { + const embeddMedia: any = {}; + const mediatype = data.buttonMessage.mediaMessage.mediatype.toUpperCase() || 'TEXT'; + + if (data.buttonMessage?.mediaMessage) { + embeddMedia.mediaKey = mediatype.toLowerCase() + 'Message'; + const generate = await this.prepareMediaMessage(data.buttonMessage.mediaMessage); + embeddMedia.message = generate.message[embeddMedia.mediaKey]; + embeddMedia.contentText = `*${data.buttonMessage.title}*\n\n${data.buttonMessage.description}`; + } + + const btnItens = { + text: data.buttonMessage.buttons.map((btn) => btn.buttonText), + ids: data.buttonMessage.buttons.map((btn) => btn.buttonId), + }; + + if (!arrayUnique(btnItens.text) || !arrayUnique(btnItens.ids)) { + throw new BadRequestException( + 'Button texts cannot be repeated', + 'Button IDs cannot be repeated.', + ); + } + + return await this.sendMessageWithTyping( + data.number, + { + buttonsMessage: { + text: !embeddMedia?.mediaKey ? data.buttonMessage.title : undefined, + contentText: embeddMedia?.contentText || data.buttonMessage.description, + footerText: data.buttonMessage?.footerText, + buttons: data.buttonMessage.buttons.map((button) => { + return { + buttonText: { + displayText: button.buttonText, + }, + buttonId: button.buttonId, + type: 1, + }; + }), + headerType: proto.Message.ButtonsMessage.HeaderType[mediatype], + [embeddMedia?.mediaKey]: embeddMedia?.message, + }, + }, + data?.options, + ); + } + + public async locationMessage(data: SendLocationDto) { + return await this.sendMessageWithTyping( + data.number, + { + locationMessage: { + degreesLatitude: data.locationMessage.latitude, + degreesLongitude: data.locationMessage.longitude, + name: data.locationMessage?.name, + address: data.locationMessage?.address, + }, + }, + data?.options, + ); + } + + public async listMessage(data: SendListDto) { + return await this.sendMessageWithTyping( + data.number, + { + listMessage: { + title: data.listMessage.title, + description: data.listMessage.description, + buttonText: data.listMessage?.buttonText, + footerText: data.listMessage?.footerText, + sections: data.listMessage.sections, + listType: 1, + }, + }, + data?.options, + ); + } + + public async contactMessage(data: SendContactDto) { + const messsage: proto.IMessage = {}; + + const vcard = (contact: ContactMessage) => { + return ( + 'BEGIN:VCARD\n' + + 'VERSION:3.0\n' + + 'FN:' + + contact.fullName + + '\n' + + 'item1.TEL;waid=' + + this.formatBRNumber(contact.wuid) + + ':' + + contact.phoneNumber + + '\n' + + 'item1.X-ABLabel:Celular\n' + + 'END:VCARD' + ); + }; + + if (data.contactMessage.length === 1) { + messsage.contactMessage = { + displayName: data.contactMessage[0].fullName, + vcard: vcard(data.contactMessage[0]), + }; + } else { + messsage.contactsArrayMessage = { + displayName: `${data.contactMessage.length} contacts`, + contacts: data.contactMessage.map((contact) => { + return { + displayName: contact.fullName, + vcard: vcard(contact), + }; + }), + }; + } + + return await this.sendMessageWithTyping(data.number, { ...messsage }, data?.options); + } + + public async reactionMessage(data: SendReactionDto) { + return await this.sendMessageWithTyping(data.reactionMessage.key.remoteJid, { + reactionMessage: { + key: data.reactionMessage.key, + text: data.reactionMessage.reaction, + }, + }); + } + + // Chat Controller + public async whatsappNumber(data: WhatsAppNumberDto) { + const onWhatsapp: OnWhatsAppDto[] = []; + for await (const number of data.numbers) { + const jid = this.createJid(number); + try { + const result = await this.client.onWhatsApp(jid); + onWhatsapp.push(new OnWhatsAppDto(result[0].jid, result[0].exists)); + } catch (error) { + onWhatsapp.push(new OnWhatsAppDto(number, false)); + } + } + + return onWhatsapp; + } + + public async markMessageAsRead(data: ReadMessageDto) { + try { + const keys: proto.IMessageKey[] = []; + data.readMessages.forEach((read) => { + if (isJidGroup(read.remoteJid) || isJidUser(read.remoteJid)) { + keys.push({ + remoteJid: read.remoteJid, + fromMe: read.fromMe, + id: read.id, + }); + } + }); + await this.client.readMessages(keys); + return { message: 'Read messages', read: 'success' }; + } catch (error) { + throw new InternalServerErrorException('Read messages fail', error.toString()); + } + } + + public async archiveChat(data: ArchiveChatDto) { + try { + data.lastMessage.messageTimestamp = + data.lastMessage?.messageTimestamp || Date.now(); + await this.client.chatModify( + { + archive: data.archive, + lastMessages: [data.lastMessage], + }, + data.lastMessage.key.remoteJid, + ); + + return { + chatId: data.lastMessage.key.remoteJid, + archived: true, + }; + } catch (error) { + throw new InternalServerErrorException({ + archived: false, + message: [ + 'An error occurred while archiving the chat. Open a calling.', + error.toString(), + ], + }); + } + } + + public async fetchContacts(query: ContactQuery) { + return await this.repository.contact.find(query); + } + + public async fetchMessages(query: MessageQuery) { + if (query?.where?.key) { + for (const [k, v] of Object.entries(query.where.key)) { + query.where['key.' + k] = v; + delete query.where.key; + } + } + return await this.repository.message.find(query); + } + + public async findStatusMessage(query: MessageUpQuery) { + return await this.repository.messageUpdate.find(query); + } + + // Group + public async createGroup(create: CreateGroupDto) { + try { + const participants = create.participants.map((p) => this.createJid(p)); + const { id } = await this.client.groupCreate(create.subject, participants); + if (create?.description) { + await this.client.groupUpdateDescription(id, create.description); + } + if (create?.profilePicture) { + let picture: any; + if (isURL(create.profilePicture)) { + picture = { url: create.profilePicture }; + } else if (isBase64(create.profilePicture)) { + picture = Buffer.from(create.profilePicture, 'base64'); + } else { + throw new BadRequestException('"profilePicture" must be a url or a base64'); + } + await this.client.updateProfilePicture(id, picture); + } + + const group = await this.client.groupMetadata(id); + + return { groupMetadata: group }; + } catch (error) { + throw new InternalServerErrorException('Error creating group', error.toString()); + } + } + + public async findGroup(id: GroupJid) { + try { + return await this.client.groupMetadata(id.groupJid); + } catch (error) { + throw new InternalServerErrorException('Error fetching group', error.toString()); + } + } + + public async invitCode(id: GroupJid) { + try { + const code = await this.client.groupInviteCode(id.groupJid); + return { inviteUrl: `https://chat.whatsapp.com/${code}`, inviteCode: code }; + } catch (error) { + throw new NotFoundException('No invite code', error.toString()); + } + } + + public async revokeInviteCode(id: GroupJid) { + try { + const inviteCode = await this.client.groupRevokeInvite(id.groupJid); + return { revoked: true, inviteCode }; + } catch (error) { + throw new NotFoundException('Revoke error', error.toString()); + } + } + + public async findParticipants(id: GroupJid) { + try { + const participants = (await this.client.groupMetadata(id.groupJid)).participants; + return { participants }; + } catch (error) { + throw new NotFoundException('No participants', error.toString()); + } + } + + public async updateGParticipant(update: GroupUpdateParticipantDto) { + try { + const participants = update.paticipants.map((p) => this.createJid(p)); + const updatePaticipants = await this.client.groupParticipantsUpdate( + update.groupJid, + participants, + update.action, + ); + return { updatePaticipants }; + } catch (error) { + throw new BadRequestException('Error updating participants', error.toString()); + } + } + + public async leaveGroup(id: GroupJid) { + try { + await this.client.groupLeave(id.groupJid); + return { groupJid: id.groupJid, leave: true }; + } catch (error) { + throw new BadRequestException('Unable to leave the group', error.toString()); + } + } +} diff --git a/src/whatsapp/types/wa.types.ts b/src/whatsapp/types/wa.types.ts new file mode 100644 index 00000000..b2b81369 --- /dev/null +++ b/src/whatsapp/types/wa.types.ts @@ -0,0 +1,45 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +import { AuthenticationState, WAConnectionState } from '@adiwajshing/baileys'; + +export enum Events { + QRCODE_UPDATED = 'qrcode.updated', + CONNECTION_UPDATE = 'connection.update', + INSTANCE = 'instance', + MESSAGES_SET = 'messages.set', + MESSAGES_UPSERT = 'messges.upsert', + MESSAGES_UPDATE = 'messages.update', + SEND_MESSAGE = 'send.message', + CONTACTS_SET = 'contacts.set', + CONTACTS_UPSERT = 'contacts.upsert', + CONTACTS_UPDATE = 'contacts.update', + PRESENCE_UPDATE = 'presence.update', + CHATS_SET = 'chats.set', + CHATS_UPDATE = 'chats.update', + CHATS_UPSERT = 'chats.upsert', +} + +export declare namespace wa { + export type QrCode = { count?: number; base64?: string; code?: string }; + export type Instance = { + qrcode?: QrCode; + authState?: { state: AuthenticationState; saveCreds: () => void }; + name?: string; + wuid?: string; + }; + + export type LocalWebHook = { enabled?: boolean; url?: string }; + + export type StateConnection = { + instance?: string; + state?: WAConnectionState | 'refused'; + statusReason?: number; + }; + + export type StatusMessage = + | 'ERROR' + | 'PENDING' + | 'SERVER_ACK' + | 'DELIVERY_ACK' + | 'READ' + | 'PLAYED'; +} diff --git a/src/whatsapp/whatsapp.module.ts b/src/whatsapp/whatsapp.module.ts new file mode 100644 index 00000000..1843c973 --- /dev/null +++ b/src/whatsapp/whatsapp.module.ts @@ -0,0 +1,63 @@ +import { configService, Database } from '../config/env.config'; +import { Logger } from '../config/logger.config'; +import { eventeEmitter } from '../config/event.config'; +import { RepositoryBroker } from './repository/index.repository'; +import { MessageRepository } from './repository/message.repository'; +import { WAMonitoringService } from './services/monitor.service'; +import { MessageModel, MessageUpModel } from './models/message.model'; +import { ChatRepository } from './repository/chat.repository'; +import { ChatModel } from './models/chat.model'; +import { ContactRepository } from './repository/contact.repository'; +import { ContactModel } from './models/contact.model'; +import { MessageUpRepository } from './repository/messageUp.repository'; +import { ChatController } from './controllers/chat.controller'; +import { InstanceController } from './controllers/instance.controller'; +import { SendMessageController } from './controllers/sendMessage.controller'; +import { AuthService } from './services/auth.service'; +import { GroupController } from './controllers/group.controller'; +import { ViewsController } from './controllers/views.controller'; +import { WebhookService } from './services/webhook.service'; +import { WebhookController } from './controllers/webhook.controller'; + +const logger = new Logger('WA MODULE'); + +const messageRepository = new MessageRepository(MessageModel, configService); +const chatRepository = new ChatRepository(ChatModel, configService); +const contactRepository = new ContactRepository(ContactModel, configService); +const messageUpdateRepository = new MessageUpRepository( + MessageUpModel as any, + configService, +); + +const repository = new RepositoryBroker( + messageRepository, + chatRepository, + contactRepository, + messageUpdateRepository, +); + +const authService = new AuthService(configService); + +export const waMonitor = new WAMonitoringService( + eventeEmitter, + configService, + repository, +); + +const webhookService = new WebhookService(waMonitor); + +export const webhookController = new WebhookController(webhookService); + +export const instanceController = new InstanceController( + waMonitor, + configService, + repository, + eventeEmitter, + authService, +); +export const viewsController = new ViewsController(waMonitor, configService); +export const sendMessageController = new SendMessageController(waMonitor); +export const chatController = new ChatController(waMonitor); +export const groupController = new GroupController(waMonitor); + +logger.info('Module - ON'); diff --git a/store/auth/apikey/.gitkeep b/store/auth/apikey/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/store/auth/jwt/.gitkeep b/store/auth/jwt/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/store/auth/jwt/codechat.json b/store/auth/jwt/codechat.json new file mode 100644 index 00000000..770367c1 --- /dev/null +++ b/store/auth/jwt/codechat.json @@ -0,0 +1 @@ +{"instance":"codechat","jwt":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbnN0YW5jZU5hbWUiOiJjb2RlY2hhdCIsImFwaU5hbWUiOiJ3aGF0c2FwcC1hcGkiLCJ0b2tlbklkIjoiMzhlZWEyZWMtNmU3Mi00YTg1LWJiYjgtNDY3NzE5YWFiMzRlIiwiaWF0IjoxNjY5NTgzODczLCJleHAiOjE2Njk1ODc0NzMsInN1YiI6ImctdCJ9.3O0XrL_YcBamW0pHqItqZmGqnqoqIPc3OAC0fh0uonI"} \ No newline at end of file diff --git a/store/contacts/.gitkeep b/store/contacts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/store/message-up/.gitkeep b/store/message-up/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/store/messages/.gitkeep b/store/messages/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/store/webhook/.gitkeep b/store/webhook/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..4183b6ff --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "declaration": true, + "target": "ES6", + "module": "commonjs", + "rootDir": "./", + "resolveJsonModule": true, + "removeComments": true, + "outDir": "./dist", + "noEmitOnError": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": false, + "skipLibCheck": true, + "strictNullChecks": false, + "incremental": true, + "noImplicitAny": false, + }, + "exclude": ["node_modules", "./test", "./dist", "./prisma"] +} \ No newline at end of file diff --git a/views/qrcode.hbs b/views/qrcode.hbs new file mode 100644 index 00000000..d8d90b30 --- /dev/null +++ b/views/qrcode.hbs @@ -0,0 +1,82 @@ + + + + + + + + + + + + Generate QRCode + + + + +

+ +
+

Connect to whatsapp

+
+ + +
+
+
+ +
+ +
+ + + + + + \ No newline at end of file