diff --git a/codechat-docker oficial/docker-compose.yml b/codechat-docker oficial/docker-compose.yml deleted file mode 100644 index 14c8bea..0000000 --- a/codechat-docker oficial/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ -version: '3' - -networks: - default: - external: false - kabacorp: - external: true - -services: - codechat: - image: codechat/api:latest - restart: always - container_name: codechat - networks: - kabacorp: - ipv4_address: 172.19.0.4 - tty: true - env_file: - - ./src/.env - volumes: - - ./instances:/home/api/instances - command: [node, './dist/src/main.js'] - ports: - - "8080:8083" \ No newline at end of file diff --git a/codechat-docker oficial/src/.env b/codechat-docker oficial/src/.env deleted file mode 100644 index 9c70f1c..0000000 --- a/codechat-docker oficial/src/.env +++ /dev/null @@ -1,72 +0,0 @@ -CORS_ORIGIN='*' # Or separate by commas - ex.: 'yourdomain1.com, yourdomain2.com' -CORS_METHODS='POST,GET,PUT,DELETE' -CORS_CREDENTIALS=true - -# Determine the logs to be displayed -LOG_LEVEL='ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK' -LOG_COLOR=true - -# Determine how long the instance should be deleted from memory in case of no connection. -# Default time: 5 minutes -# If you don't even want an expiration, enter the value false -DEL_INSTANCE=5 - -# Temporary data storage -STORE_CLEANING_INTERVAL=7200 # seconds ===2h -STORE_MESSAGE=true -STORE_CONTACTS=false -STORE_CHATS=false - -# Permanent data storage -DATABASE_ENABLED=true -DATABASE_CONNECTION_URI='mongodb://root:kabacorp@172.19.0.5:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true' -DATABASE_CONNECTION_DB_PREFIX_NAME='codechat' -DATABASE_SAVE_DATA_INSTANCE=false -DATABASE_SAVE_DATA_OLD_MESSAGE=false -DATABASE_SAVE_DATA_NEW_MESSAGE=true -DATABASE_SAVE_MESSAGE_UPDATE=true -DATABASE_SAVE_DATA_CONTACTS=true -DATABASE_SAVE_DATA_CHATS=true - -REDIS_ENABLED=false -REDIS_URI='' - -# Webhook Settings -## Define a global webhook that will listen for enabled events from all instances -WEBHOOK_GLOBAL_URL=http://172.19.0.2:1234/webhook/codechat -WEBHOOK_GLOBAL_ENABLED=true -## Set the events you want to hear -WEBHOOK_EVENTS_STATUS_INSTANCE=true -WEBHOOK_EVENTS_QRCODE_UPDATED=true -WEBHOOK_EVENTS_MESSAGES_SET=true -WEBHOOK_EVENTS_MESSAGES_UPDATE=true -WEBHOOK_EVENTS_MESSAGES_UPSERT=true -WEBHOOK_EVENTS_SEND_MESSAGE=true -WEBHOOK_EVENTS_CONTACTS_SET=true -WEBHOOK_EVENTS_CONTACTS_UPSERT=true -WEBHOOK_EVENTS_CONTACTS_UPDATE=true -WEBHOOK_EVENTS_PRESENCE_UPDATE=true -WEBHOOK_EVENTS_CHATS_SET=true -WEBHOOK_EVENTS_CHATS_UPSERT=true -WEBHOOK_EVENTS_CHATS_UPDATE=true -WEBHOOK_EVENTS_CONNECTION_UPDATE=true -WEBHOOK_EVENTS_GROUPS_UPSERT=false -WEBHOOK_EVENTS_GROUPS_UPDATE=false -WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=false -## This event fires every time a new token is requested via the refresh route -WEBHOOK_EVENTS_NEW_JWT_TOKEN=true - -CONFIG_SESSION_PHONE_CLIENT='codechat' -CONFIG_SESSION_PHONE_NAME='Chrome' - -# 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. -### OBS: This key must be inserted in the request header to create an instance. -AUTHENTICATION_API_KEY='t8OOEeISKzpmc3jjcMqBWYSaJsafdefer' -## Set the secret key to encrypt and decrypt your token and its expiration time -AUTHENTICATION_JWT_EXPIRIN_IN=0 # seconds - 3600s ===1h | zero (0) - never expires -AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG' diff --git a/codechat/Dockerfile b/codechat/Dockerfile deleted file mode 100644 index 52dfb49..0000000 --- a/codechat/Dockerfile +++ /dev/null @@ -1,95 +0,0 @@ -FROM node:16.19.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="contato@codechat.dev" - -RUN apt-get update -y -RUN apt-get upgrade -y - -WORKDIR /home/api - -COPY ./package.json . - -# see https://github.com/code-chat-br/whatsapp-api/blob/main/Docker/dev.env - -# Set "true" to dynamically insert environment variables as described in the "./dev.env" file -ENV DOCKER_ENV=true - -ENV SERVER_TYPE='http' -ENV SERVER_PORT=8083 - -ENV CORS_ORIGIN='*' -ENV CORS_METHODS='POST,GET,PUT,DELETE' -ENV CORS_CREDENTIALS=true - -ENV SSL_CONF_PRIVKEY='/etc/letsencrypt/live//privkey.pem' -ENV SSL_CONF_FULLCHAIN='/etc/letsencrypt/live//fullchain.pem' - -ENV LOG_LEVEL='ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK' -ENV LOG_COLOR=true - -ENV DEL_INSTANCE=5 - -ENV STORE_CLEANING_INTERVAL=7200 -ENV STORE_MESSAGE=true -ENV STORE_CONTACTS=false -ENV STORE_CHATS=false - -ENV DATABASE_ENABLED=false -ENV DATABASE_CONNECTION_URI='mongodb://:@/?authSource=admin&readPreference=primary&ssl=false&directConnection=true' -ENV DATABASE_CONNECTION_DB_PREFIX_NAME='codechat' -ENV DATABASE_SAVE_DATA_INSTANCE=true -ENV DATABASE_SAVE_DATA_OLD_MESSAGE=false -ENV DATABASE_SAVE_DATA_NEW_MESSAGE=true -ENV DATABASE_SAVE_MESSAGE_UPDATE=true -ENV DATABASE_SAVE_DATA_CONTACTS=true -ENV DATABASE_SAVE_DATA_CHATS=true - -ENV REDIS_ENABLED=false -ENV REDIS_URI='redis://' - -ENV WEBHOOK_GLOBAL_URL='' -ENV WEBHOOK_GLOBAL_ENABLED=false - -ENV WEBHOOK_EVENTS_STATUS_INSTANCE=true -ENV WEBHOOK_EVENTS_QRCODE_UPDATED=true -ENV WEBHOOK_EVENTS_MESSAGES_SET=true -ENV WEBHOOK_EVENTS_MESSAGES_UPDATE=true -ENV WEBHOOK_EVENTS_MESSAGES_UPSERT=true -ENV WEBHOOK_EVENTS_SEND_MESSAGE=true -ENV WEBHOOK_EVENTS_CONTACTS_SET=true -ENV WEBHOOK_EVENTS_CONTACTS_UPSERT=true -ENV WEBHOOK_EVENTS_CONTACTS_UPDATE=true -ENV WEBHOOK_EVENTS_PRESENCE_UPDATE=true -ENV WEBHOOK_EVENTS_CHATS_SET=true -ENV WEBHOOK_EVENTS_CHATS_UPSERT=true -ENV WEBHOOK_EVENTS_CHATS_UPDATE=true -ENV WEBHOOK_EVENTS_CONNECTION_UPDATE=true -ENV WEBHOOK_EVENTS_GROUPS_UPSERT=false -ENV WEBHOOK_EVENTS_GROUPS_UPDATE=false -ENV WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=false - -ENV WEBHOOK_EVENTS_NEW_JWT_TOKEN=true - -ENV CONFIG_SESSION_PHONE_CLIENT='codechat' -ENV CONFIG_SESSION_PHONE_NAME='Chrome' - -ENV QRCODE_LIMIT=6 - -ENV AUTHENTICATION_TYPE='jwt' - -ENV AUTHENTICATION_API_KEY='t8OOEeISKzpmc3jjcMqBWYSaJsafdefer' - -ENV AUTHENTICATION_JWT_EXPIRIN_IN=3600 -ENV AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG' - -RUN npm i - -COPY . . - -EXPOSE 8083 - -RUN npm run build - -CMD [ "node", "./dist/src/main.js" ] diff --git a/codechat/LICENSE b/codechat/LICENSE deleted file mode 100644 index 0da2f51..0000000 --- a/codechat/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - 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/codechat/README.md b/codechat/README.md deleted file mode 100644 index 229287e..0000000 --- a/codechat/README.md +++ /dev/null @@ -1,280 +0,0 @@ -

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 Group](https://img.shields.io/badge/Group-Telegram-%2333C1FF)](https://t.me/codechatBR) -[![Whatsapp Group](https://img.shields.io/badge/Group-WhatsApp-%2322BC18)](https://chat.whatsapp.com/HyO8X8K0bAo0bfaeW8bhY5) -[![License](https://img.shields.io/badge/license-GPL--3.0-orange)](./LICENSE) -[![Support](https://img.shields.io/badge/Buy%20me-coffe-orange)](https://app.picpay.com/user/cleber.wilson.oliveira) -[![Support](https://img.shields.io/badge/Buy%20me%20coffe-pix-blue)](#pix-2b526ada-4ef4-4db4-bbeb-f60da2421fce) - -
- -
- -## 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. - -## [Node n8n](https://github.com/jrCleber/n8n-codechat-wapi) - - - -## 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 -``` -> -> After finishing, restart the terminal to load the new information. -> - -### Docker installation \[optional\] - -```sh -curl -fsSL https://get.docker.com -o get-docker.sh - -sudo sh get-docker.sh - -sudo usermod -aG docker ${USER} -``` -### Nodejs installation - -```sh -nvm install 16.17.0 -``` - -### pm2 installation -```sh -npm i -g pm2 -``` - -```sh -docker --version - -node --version -``` -## MongoDb [optional] - -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 -bash docker.sh -``` -Using the database is optional. - -## Application startup - -Cloning the Repository -``` -git clone https://github.com/code-chat-br/whatsapp-api.git -``` - -Go to the project directory and install all dependencies.
-```sh -cd whatsapp-api - -npm i -``` - -Finally, run the command below to start the application: -```sh -# Under development -npm run start - -# In production -npm run start:prod - -# pm2 -pm2 start 'npm run start:prod' --name ApiCodechat -``` -## Authentication - -You can define two authentication **types** for the routes in the **[env file](./src/dev-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 - -> *NOTE:* This key must be inserted in the request header to create an instance. - -```http -POST /instance/create HTTP/1.1 -Host: localhost:8080 -Content-Type: application/json -apikey: t8OOEeISKzpmc3jjcMqBWYSaJH2PIxns - -{ - "instanceName": "codechat" -} -``` -##### cURL - -```bash -curl --location --request POST 'http://localhost:8080/instance/create' \ ---header 'Content-Type: application/json' \ ---header 'apikey: t8OOEeISKzpmc3jjcMqBWYSaJH2PIxns' \ ---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) - - [.env for docker](./Docker/dev.env) - -After building the application, in the same directory as the files above, run the following command: -```sh -docker-compose up -``` -## Send Messages -| | | -|-----|---| -| Send Text | βœ” | -| Send Buttons | βœ” | -| Send Template | ❌ | -| Send Media: audio - video - image - document - gif

base64: ```true``` | βœ” | -| Send Media File | ❌ | -| Send Audio type WhatsApp | βœ” | -| Send Location | βœ” | -| Send List | βœ” | -| Send Link Preview | ❌ | -| Send Contact | βœ” | -| Send Reaction - emoji | βœ” | - -## Postman collections - - [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 | -| 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 | -| GROUPS_UPSERT | groups.upsert | JSON | Notifies when a group is created | -| GROUPS_UPDATE | groups.update | JSON | Notifies when a group has its information updated | -| GROUP_PARTICIPANTS_UPDATE | group-participants.update | JSON | Notifies when an action occurs involving a participant
'add' | 'remove' | 'promote' | 'demote' | -| NEW_TOKEN | new.jwt | JSON | Notifies when the token (jwt) is updated - -## Env File - -See additional settings that can be applied through the **env** file by clicking **[here](./src/dev-env.yml)**. - -> **⚠️Attention⚠️:** rename the **dev-env.yml** file to **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. - -# Donate to the project. - -#### Pix: 2b526ada-4ef4-4db4-bbeb-f60da2421fce - -#### PicPay - - - -
\ No newline at end of file diff --git a/codechat/docker.sh b/codechat/docker.sh deleted file mode 100644 index bbc55c0..0000000 --- a/codechat/docker.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -NET='codechat-net' - -if !(docker network ls | grep ${NET} > /dev/null) -then - docker network create -d bridge ${NET} -fi - -sudo mkdir -p /data/instances - -docker build -t whatsappapi/codechat . - -docker run --restart 'always' --name 'api-codechat' --mount 'type=bind,source=/data/instances,target=/home/api/instances' --publish '8083:8083' --hostname 'codechat-api' --network ${NET} whatsappapi/codechat:latest - -# docker run --restart 'always' -d --name 'api-codechat' --mount 'type=bind,source=/data/instances,target=/home/api/instances' --publish '8083:8083' --hostname 'codechat-api' --network ${NET} whatsappapi/codechat:latest diff --git a/codechat/package.json b/codechat/package.json deleted file mode 100644 index a8c34f8..0000000 --- a/codechat/package.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "name": "whatsapp-api", - "version": "1.2.0", - "description": "Rest api for communication with WhatsApp", - "main": "index.js", - "scripts": { - "build": "tsc", - "start": "ts-node --files --transpile-only ./src/main.ts", - "start:prod": "bash start.sh", - "start:dev": "clear && tsnd --files --transpile-only --respawn --ignore-watch node_modules ./src/main.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": "github:edcarlosm/Baileys", - "@hapi/boom": "^10.0.1", - "axios": "^1.3.5", - "class-validator": "^0.13.2", - "compression": "^1.7.4", - "cors": "^2.8.5", - "cross-env": "^7.0.3", - "dayjs": "^1.11.7", - "eventemitter2": "^6.4.9", - "express": "^4.18.2", - "express-async-errors": "^3.1.1", - "hbs": "^4.2.0", - "jimp": "^0.16.13", - "join": "^3.0.0", - "js-yaml": "^4.1.0", - "jsonschema": "^1.4.1", - "jsonwebtoken": "^8.5.1", - "mongoose": "^6.10.5", - "node-cache": "^5.1.2", - "node-mime-types": "^1.1.0", - "pino": "^8.11.0", - "qrcode": "^1.5.1", - "qrcode-terminal": "^0.12.0", - "redis": "^4.6.5", - "uuid": "^9.0.0" - }, - "devDependencies": { - "@types/compression": "^1.7.2", - "@types/cors": "^2.8.13", - "@types/express": "^4.17.17", - "@types/js-yaml": "^4.0.5", - "@types/jsonwebtoken": "^8.5.9", - "@types/node": "^18.15.11", - "@types/qrcode": "^1.5.0", - "@types/qrcode-terminal": "^0.12.0", - "@types/uuid": "^8.3.4", - "@typescript-eslint/eslint-plugin": "^5.57.1", - "@typescript-eslint/parser": "^5.57.1", - "eslint": "^8.38.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.7", - "ts-node-dev": "^2.0.0", - "typescript": "^4.9.5" - } -} diff --git a/codechat/postman.json b/codechat/postman.json deleted file mode 100644 index d132b6c..0000000 --- a/codechat/postman.json +++ /dev/null @@ -1,1277 +0,0 @@ -{ - "collection": { - "info": { - "_postman_id": "a346ce06-3faa-47c3-88f0-4924ab4ca92d", - "name": "Api Whatsapp", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "updatedAt": "2023-01-24T22:42:53.000Z" - }, - "item": [ - { - "name": "Instance Controller", - "item": [ - { - "name": "Views", - "item": [ - { - "name": "QrCode", - "id": "e13e73d1-ca81-46b7-82a9-717e1558284e", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/instance/qrcode/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "instance", - "qrcode", - "{{instance}}" - ] - } - }, - "response": [] - } - ], - "id": "c58c742f-3010-4e95-99f8-28feeb37891a" - }, - { - "name": "Create Instance", - "id": "0e70f058-7b96-4ca9-a31c-408bb638c00b", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "auth": { - "type": "apikey", - "apikey": [ - { - "key": "in", - "value": "header", - "type": "string" - }, - { - "key": "key", - "value": "apikey", - "type": "string" - }, - { - "key": "value", - "value": "{{globalApikey}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"instanceName\": \"codechat\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/instance/create", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "instance", - "create" - ] - } - }, - "response": [] - }, - { - "name": "Fetch Instances", - "id": "cf9aeacf-dd2e-4798-8c9a-123420d2b66e", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "auth": { - "type": "apikey", - "apikey": [ - { - "key": "in", - "value": "header", - "type": "string" - }, - { - "key": "key", - "value": "apikey", - "type": "string" - }, - { - "key": "value", - "value": "{{globalApikey}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/instance/fetchInstances?instanceName={{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "instance", - "fetchInstances" - ], - "query": [ - { - "key": "instanceName", - "value": "{{instance}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Instance Connect", - "id": "39ef2c5a-bb9e-4996-94b5-689d161f5412", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/instance/connect/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "instance", - "connect", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Connection Status", - "id": "7b4d3891-9a72-4b8c-9fa8-393308273b99", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/instance/connectionState/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "instance", - "connectionState", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Logout Instance", - "id": "c852edac-88bc-4b14-8452-dc00749797f1", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{baseUrl}}/instance/logout/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "instance", - "logout", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Delete Instance", - "id": "4bb47543-b864-41fe-b53d-4e6fd135764a", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{baseUrl}}/instance/delete/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "instance", - "delete", - "{{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", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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": { - "raw": "{{baseUrl}}/message/sendText/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "message", - "sendText", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Send Media", - "id": "4d17edef-f237-4f00-906d-71687a2210d8", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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 | audio\",\r\n \"fileName\": \"image.png\",\r\n \"caption\": \"Description\",\r\n \"media\": \"url or base64\"\r\n }\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/message/sendMedia/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "message", - "sendMedia", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Send Buttons", - "id": "6ad2b81b-096c-4889-9c2b-778eab259445", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"number\": \"5531900000000\",\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 1\",\r\n \"buttonId\": \"1\"\r\n },\r\n {\r\n \"buttonText\": \"Click here 2\",\r\n \"buttonId\": \"2\"\r\n }\r\n ],\r\n \"mediaMessage\": {\r\n \"mediatype\": \"image\",\r\n \"fileName\": \"image.png\",\r\n \"media\": \"https://images.freeimages.com/images/large-previews/31c/red-fox-in-the-wild-1-1624832.jpg\"\r\n }\r\n }\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/message/sendButtons/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "message", - "sendButtons", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Send Location", - "id": "12dcb0d1-d6e1-465c-916e-8a238b1413e9", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"number\": \"5531900000000\",\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": { - "raw": "{{baseUrl}}/message/sendLocation/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "message", - "sendLocation", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Send List", - "id": "fc5feea6-9415-4036-8c5a-c8fccaf35e43", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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": { - "raw": "{{baseUrl}}/message/sendList/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "message", - "sendList", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Send Contact", - "id": "55802759-f91c-4963-a191-7bc6d58face5", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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": { - "raw": "{{baseUrl}}/message/sendContact/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "message", - "sendContact", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Send Reaction", - "id": "94e29b21-410d-470b-925c-0739ad36efcb", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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": { - "raw": "{{baseUrl}}/message/sendReaction/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "message", - "sendReaction", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Send Audio WhatsApp", - "id": "bb6b9445-7f29-42c7-b1b2-b9b9b9bfa121", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"number\": \"5531900000000\",\r\n \"options\": {\r\n \"delay\": 1200\r\n },\r\n \"audioMessage\": {\r\n \"audio\": \"https://lp.naturefarm.site/wp-content/uploads/2023/01/WhatsApp-Ptt-2022-12-05-at-13.00.53.ogg\"\r\n }\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/message/sendWhatsAppAudio/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "message", - "sendWhatsAppAudio", - "{{instance}}" - ] - } - }, - "response": [] - } - ], - "id": "254b8181-a775-4f4c-b9f8-e7b61d77fed1" - }, - { - "name": "Chat Controller", - "item": [ - { - "name": "WhatsApp Number", - "id": "d75c7dda-1473-401e-9775-69baa4ce3de3", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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": { - "raw": "{{baseUrl}}/chat/whatsappNumbers/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "chat", - "whatsappNumbers", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Read Messages", - "id": "32f9cd0c-69c8-4319-8526-a8154b2891c5", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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": { - "raw": "{{baseUrl}}/chat/markMessageAsRead/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "chat", - "markMessageAsRead", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Archive Chat", - "id": "26364791-b1df-4abe-910d-98b295509754", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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 // false: to unarchive\r\n // true: to archive\r\n \"archive\": false\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/chat/archiveChat/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "chat", - "archiveChat", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Delete Message", - "id": "98b828cf-6eda-4d9c-afc9-5e27f3f06b21", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"id\": \"id\",\r\n \"remoteJid\": \"remoteJid\",\r\n \"fromMe\": true,\r\n // optional\r\n \"paticipant\": \"paticipant\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/chat/deleteMessageForEveryone/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "chat", - "deleteMessageForEveryone", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Fetch Profile Picture", - "id": "0106583a-2842-432f-88bc-f63df3469e1c", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"number\": \"5531900000000\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/chat/fetchProfilePictureUrl/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "chat", - "fetchProfilePictureUrl", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Find Contacts", - "id": "1ae99ab9-abca-4240-8097-a3aa067ebc90", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"where\": {\r\n \"id\": \"5531900000000@s.whatsapp.net\"\r\n }\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/chat/findContacts/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "chat", - "findContacts", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Get Base64 From Media Message", - "id": "0e66db81-8d9c-41b1-bfb6-a274a185f977", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "\r\n{\r\n \"key\": {\r\n \"id\": \"3EB0AB73CDA6834B7C72\"\r\n }\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/chat/getBase64FromMediaMessage/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "chat", - "getBase64FromMediaMessage", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Find Messages", - "id": "4754ff87-4d2a-4933-9130-769c788cf625", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "/*\r\n Each of these properties are optional.\r\n With mongodb disabled, only the \"key.id\" property is available.\r\n Remove all comments before submitting the request.\r\n*/\r\n{\r\n \"where\": {\r\n /* \r\n * \"key.fromMe\": false,\r\n * \"key.remoteJid\": \"123@s.whatsapp.net\",\r\n * \"key.id\": \"1C42B68AFA410DBE198BA54DB7F45762\"\r\n */\r\n \"key\": {\r\n \"fromMe\": false,\r\n \"remoteJid\": \"123@s.whatsapp.net\",\r\n \"id\": \"1C42B68AFA410DBE198BA54DB7F45762\"\r\n },\r\n \"message.[...]\": { }\r\n },\r\n // optional\r\n \"limit\": 10\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/chat/findMessages/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "chat", - "findMessages", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Find Status Message", - "id": "b261a9c9-d41c-4e75-a537-dbdafd872d5f", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "/*\r\n Each of these properties are optional.\r\n With mongodb disabled, only the \"id\" property is available.\r\n Remove all comments before submitting the request.\r\n*/\r\n{\r\n \"where\": {\r\n \"_id\": \"6390fe2eba512dacefa8ce3a\",\r\n \"remoteJid\": \"123@s.whatsapp.net\",\r\n \"id\": \"BAE5959535174C7E\",\r\n \"fromMe\": true\r\n },\r\n // optional\r\n \"limit\": 10\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/chat/findStatusMessage/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "chat", - "findStatusMessage", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Find Chats", - "id": "55345816-b322-47bf-9f66-411c8ead9afb", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/chat/findChats/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "chat", - "findChats", - "{{instance}}" - ] - } - }, - "response": [] - } - ], - "id": "84b58e33-0408-481f-9874-b82ceeb6e1bc" - }, - { - "name": "Group Controller", - "item": [ - { - "name": "Create Group", - "id": "783e0b82-8960-4248-b432-9fa47697dd6a", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"subject\": \"Test 02\", \r\n \"description\": \"optional\",\r\n \"participants\": [\r\n \"5531900000000\",\r\n \"5531900000000\"\r\n ]\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/group/create/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "group", - "create", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Update Group Picture", - "id": "f0288c97-00ea-4790-a510-9810b68775d0", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"image\": \"https://i.pinimg.com/736x/25/78/61/25786134576ce0344893b33a051160b1.jpg\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/group/updateGroupPicture/{{instance}}?groupJid={{groupJid}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "group", - "updateGroupPicture", - "{{instance}}" - ], - "query": [ - { - "key": "groupJid", - "value": "{{groupJid}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Find Group", - "id": "bf7c0e39-056e-4d89-acb6-bc5fc850a3eb", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "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", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "auth": { - "type": "noauth" - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"oldToken\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbnN0YW5jZU5hbWUiOiJjb2RlY2hhdCIsImFwaU5hbWUiOiJ3aGF0c2FwcC1hcGkiLCJ0b2tlbklkIjoiYmYyYTQ1MjQtNzY2Mi00YjBlLTkzNDMtODEwNjNmNmU3MjgwIiwiaWF0IjoxNjczNTI5MDcwLCJleHAiOjE2NzM1MjkwNzAsInN1YiI6ImctdCJ9.RGWWXW_dSQO8yXAeP-UJRLlHR5bSPbGSm3yvcyl5KhE\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/instance/refreshToken/", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "instance", - "refreshToken", - "" - ] - } - }, - "response": [] - } - ], - "id": "088f2e19-b320-4513-ab9b-c19fa45d1b5c" - }, - { - "name": "Webhook", - "item": [ - { - "name": "Set Webhook", - "id": "2b7ce76a-d3ab-4065-a984-3774a35492b9", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"enabled\": true,\r\n \"url\": \"https://webhook.site/67b563b0-92ad-4c83-b622-7cdb52cd3d2b\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/webhook/set/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "webhook", - "set", - "{{instance}}" - ] - } - }, - "response": [] - }, - { - "name": "Find Webhook", - "id": "a3e196de-3bc0-40b6-b907-831e1f2f8a8f", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/webhook/find/{{instance}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "webhook", - "find", - "{{instance}}" - ] - } - }, - "response": [] - } - ], - "id": "700b438c-2364-4445-8f7f-e4eb55277a22" - } - ], - "auth": { - "type": "apikey", - "apikey": [ - { - "key": "value", - "value": "{{apikey}}", - "type": "string" - }, - { - "key": "key", - "value": "apikey", - "type": "string" - } - ] - }, - "event": [ - { - "listen": "prerequest", - "script": { - "id": "e8ad8234-cff6-4f20-87cd-228621878f96", - "type": "text/javascript", - "exec": [ - "" - ] - } - }, - { - "listen": "test", - "script": { - "id": "5b48e3ff-0ad5-4c2c-9060-edf1d55ad2dd", - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ], - "variable": [ - { - "id": "66679b73-9e2b-4527-860c-1244be03ee55", - "key": "baseUrl", - "value": "https://api.codechat.dev", - "type": "string" - }, - { - "id": "86676c38-18a7-4ece-b063-694c995364d2", - "key": "instance", - "value": "codechat", - "type": "string" - }, - { - "id": "0737402e-02d7-412b-ba91-8751a59c4253", - "key": "token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbnN0YW5jZU5hbWUiOiJjb2RlY2hhdCIsImFwaU5hbWUiOiJ3aGF0c2FwcC1hcGkiLCJ0b2tlbklkIjoiMjgyYjQxZmQtOWQ3OC00YzEwLWI0YTAtMGYzZWYyYzFmYjA5IiwiaWF0IjoxNjczNjk1NjAyLCJleHAiOjE2NzM2OTU2MDIsInN1YiI6ImctdCJ9.TVhYtPJmZfIl4YljfNX9I07B-Gu_VUgj7tUWQxxnLJE", - "type": "string", - "disabled": true - }, - { - "id": "8f404ede-6427-4163-9c51-2f220b841a2e", - "key": "apikey", - "value": "7259FC4B-2825-402D-9321-022978061B84", - "type": "string" - }, - { - "id": "4a072016-743f-488b-b990-c0bac04d67f4", - "key": "globalApikey", - "value": "t8OOEeISKzpmc3jjcMqBWYSaJsafdefer", - "type": "string" - }, - { - "id": "f8e70ac8-b445-4dae-8614-d1bb9eddb05f", - "key": "groupJid", - "value": "120363046555718472@g.us", - "type": "string", - "disabled": true - } - ] - } -} \ No newline at end of file diff --git a/codechat/public/images/code.png b/codechat/public/images/code.png deleted file mode 100644 index 1eb9306..0000000 Binary files a/codechat/public/images/code.png and /dev/null differ diff --git a/codechat/public/images/codechat-logo.png b/codechat/public/images/codechat-logo.png deleted file mode 100644 index af8054b..0000000 Binary files a/codechat/public/images/codechat-logo.png and /dev/null differ diff --git a/codechat/public/images/cover.png b/codechat/public/images/cover.png deleted file mode 100644 index 3b4c1fa..0000000 Binary files a/codechat/public/images/cover.png and /dev/null differ diff --git a/codechat/public/images/n8n-codechat-wapi.png b/codechat/public/images/n8n-codechat-wapi.png deleted file mode 100644 index caa7b6e..0000000 Binary files a/codechat/public/images/n8n-codechat-wapi.png and /dev/null differ diff --git a/codechat/public/images/picpay-image.png b/codechat/public/images/picpay-image.png deleted file mode 100644 index eaf89f6..0000000 Binary files a/codechat/public/images/picpay-image.png and /dev/null differ diff --git a/codechat/public/images/video-cover.png b/codechat/public/images/video-cover.png deleted file mode 100644 index 4632f39..0000000 Binary files a/codechat/public/images/video-cover.png and /dev/null differ diff --git a/codechat/src/config/env.config.ts b/codechat/src/config/env.config.ts deleted file mode 100644 index 3395845..0000000 --- a/codechat/src/config/env.config.ts +++ /dev/null @@ -1,228 +0,0 @@ -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 Log = { - LEVEL: LogLevel[]; - COLOR: boolean; -}; - -export type SaveData = { - INSTANCE: boolean; - OLD_MESSAGE: boolean; - NEW_MESSAGE: boolean; - MESSAGE_UPDATE: boolean; - CONTACTS: boolean; - CHATS: boolean; -}; - -export type StoreConf = { - CLEANING_INTERVAL: number; - MESSAGES: boolean; - CONTACTS: boolean; - CHATS: boolean; -}; - -export type DBConnection = { - URI: string; - DB_PREFIX_NAME: string; -}; -export type Database = { - CONNECTION: DBConnection; - ENABLED: boolean; - SAVE_DATA: SaveData; -}; - -export type Redis = { - ENABLED: boolean; - URI: string; -}; - -export type EventsWebhook = { - QRCODE_UPDATED: 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; - CHATS_DELETE: boolean; - CHATS_UPSERT: boolean; - CONNECTION_UPDATE: boolean; - GROUPS_UPSERT: boolean; - GROUP_UPDATE: boolean; - GROUP_PARTICIPANTS_UPDATE: boolean; - NEW_JWT_TOKEN: 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 DelInstance = number | boolean; - -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 QrCode = { LIMIT: number }; -export type Production = boolean; - -export interface Env { - SERVER: HttpServer; - CORS: Cors; - SSL_CONF: SslConf; - STORE: StoreConf; - DATABASE: Database; - REDIS: Redis; - LOG: Log; - DEL_INSTANCE: DelInstance; - WEBHOOK: Webhook; - CONFIG_SESSION_PHONE: ConfigSessionPhone; - QRCODE: QrCode; - AUTHENTICATION: Auth; - PRODUCTION?: Production; -} - -export type Key = keyof Env; - -export class ConfigService { - constructor() { - this.loadEnv(); - } - - private env: Env; - - public get(key: Key) { - return this.env[key] as T; - } - - private loadEnv() { - this.env = !(process.env?.DOCKER_ENV === 'true') ? this.envYaml() : this.envProcess(); - this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD'; - if (process.env?.DOCKER_ENV === 'true') { - this.env.SERVER.TYPE = 'http'; - this.env.SERVER.PORT = Number.parseInt(process.env?.SERVER_PORT ?? '8080'); - } - } - - private envYaml(): Env { - return load(readFileSync(join(SRC_DIR, 'env.yml'), { encoding: 'utf-8' })) as Env; - } - - private envProcess(): Env { - return { - SERVER: { - TYPE: process.env.SERVER_TYPE as 'http' | 'https', - PORT: Number.parseInt(process.env.SERVER_PORT), - }, - CORS: { - ORIGIN: process.env.CORS_ORIGIN.split(','), - METHODS: process.env.CORS_METHODS.split(',') as HttpMethods[], - CREDENTIALS: process.env?.CORS_CREDENTIALS === 'true', - }, - SSL_CONF: { - PRIVKEY: process.env?.SSL_CONF_PRIVKEY, - FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN, - }, - STORE: { - CLEANING_INTERVAL: Number.isInteger(process.env?.STORE_CLEANING_TERMINAL) - ? Number.parseInt(process.env.STORE_CLEANING_TERMINAL) - : undefined, - MESSAGES: process.env?.STORE_MESSAGE === 'true', - CONTACTS: process.env?.STORE_CONTACTS === 'true', - CHATS: process.env?.STORE_CHATS === 'true', - }, - DATABASE: { - CONNECTION: { - URI: process.env.DATABASE_CONNECTION_URI, - DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME, - }, - ENABLED: process.env?.DATABASE_ENABLED === 'true', - SAVE_DATA: { - INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true', - OLD_MESSAGE: process.env?.DATABASE_SAVE_DATA_OLD_MESSAGE === 'true', - NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true', - MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true', - CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true', - CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true', - }, - }, - REDIS: { - ENABLED: process.env?.REDIS_ENABLED === 'true', - URI: process.env.REDIS_URI, - }, - LOG: { - LEVEL: process.env?.LOG_LEVEL.split(',') as LogLevel[], - COLOR: process.env?.LOG_COLOR === 'true', - }, - DEL_INSTANCE: - typeof process.env?.DEL_INSTANCE === 'boolean' - ? process.env.DEL_INSTANCE === 'true' - : Number.parseInt(process.env.DEL_INSTANCE), - WEBHOOK: { - GLOBAL: { - URL: process.env?.WEBHOOK_GLOBAL_URL, - ENABLED: process.env?.WEBHOOK_GLOBAL_ENABLED === 'true', - }, - EVENTS: { - QRCODE_UPDATED: process.env?.WEBHOOK_EVENTS_QRCODE_UPDATED === 'true', - MESSAGES_SET: process.env?.WEBHOOK_EVENTS_MESSAGES_SET === 'true', - MESSAGES_UPSERT: process.env?.WEBHOOK_EVENTS_MESSAGES_UPSERT === 'true', - MESSAGES_UPDATE: process.env?.WEBHOOK_EVENTS_MESSAGES_UPDATE === 'true', - SEND_MESSAGE: process.env?.WEBHOOK_EVENTS_SEND_MESSAGE === 'true', - CONTACTS_SET: process.env?.WEBHOOK_EVENTS_CONTACTS_SET === 'true', - CONTACTS_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true', - CONTACTS_UPSERT: process.env?.WEBHOOK_EVENTS_CONTACTS_UPSERT === 'true', - PRESENCE_UPDATE: process.env?.WEBHOOK_EVENTS_PRESENCE_UPDATE === 'true', - CHATS_SET: process.env?.WEBHOOK_EVENTS_CHATS_SET === 'true', - CHATS_UPDATE: process.env?.WEBHOOK_EVENTS_CHATS_UPDATE === 'true', - CHATS_UPSERT: process.env?.WEBHOOK_EVENTS_CHATS_UPSERT === 'true', - CHATS_DELETE: process.env?.WEBHOOK_EVENTS_CHATS_DELETE === 'true', - CONNECTION_UPDATE: process.env?.WEBHOOK_EVENTS_CONNECTION_UPDATE === 'true', - GROUPS_UPSERT: process.env?.WEBHOOK_EVENTS_GROUPS_UPSERT === 'true', - GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true', - GROUP_PARTICIPANTS_UPDATE: - process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true', - NEW_JWT_TOKEN: process.env?.WEBHOOK_EVENTS_NEW_JWT_TOKEN === 'true', - }, - }, - CONFIG_SESSION_PHONE: { - CLIENT: process.env?.CONFIG_SESSION_PHONE_CLIENT, - NAME: process.env?.CONFIG_SESSION_PHONE_NAME, - }, - QRCODE: { - LIMIT: Number.parseInt(process.env.QRCODE_LIMIT), - }, - AUTHENTICATION: { - TYPE: process.env.AUTHENTICATION_TYPE as 'jwt', - API_KEY: { - KEY: process.env.AUTHENTICATION_API_KEY, - }, - JWT: { - EXPIRIN_IN: Number.isInteger(process.env?.AUTHENTICATION_JWT_EXPIRIN_IN) - ? Number.parseInt(process.env.AUTHENTICATION_JWT_EXPIRIN_IN) - : 3600, - SECRET: process.env.AUTHENTICATION_JWT_SECRET, - }, - }, - }; - } -} - -export const configService = new ConfigService(); diff --git a/codechat/src/config/error.config.ts b/codechat/src/config/error.config.ts deleted file mode 100644 index 6449d52..0000000 --- a/codechat/src/config/error.config.ts +++ /dev/null @@ -1,21 +0,0 @@ -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/codechat/src/config/event.config.ts b/codechat/src/config/event.config.ts deleted file mode 100644 index 8451ffd..0000000 --- a/codechat/src/config/event.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import EventEmitter2 from 'eventemitter2'; - -export const eventEmitter = new EventEmitter2({ - delimiter: '.', - newListener: false, - ignoreErrors: false, -}); diff --git a/codechat/src/config/logger.config.ts b/codechat/src/config/logger.config.ts deleted file mode 100644 index 182194b..0000000 --- a/codechat/src/config/logger.config.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { configService, Log } from './env.config'; -import dayjs from 'dayjs'; - -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', - BRIGHT = '\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)) { - if (configService.get('LOG').COLOR) { - console.log( - /*Command.UNDERSCORE +*/ Command.BRIGHT + Level[type], - '[CodeChat]', - Command.BRIGHT + Color[type], - process.pid.toString(), - Command.RESET, - Command.BRIGHT + Color[type], - '-', - Command.BRIGHT + Color.VERBOSE, - `${formatDateLog(Date.now())} `, - Command.RESET, - Color[type] + Background[type] + Command.BRIGHT, - `${type} ` + Command.RESET, - Color.WARN + Command.BRIGHT, - `[${this.context}]` + Command.RESET, - Color[type] + Command.BRIGHT, - `[${typeValue}]` + Command.RESET, - Color[type], - typeValue !== 'object' ? value : '', - Command.RESET, - ); - typeValue === 'object' ? console.log(/*Level.DARK,*/ value, '\n') : ''; - } else { - console.log( - '[CodeChat]', - process.pid.toString(), - '-', - `${formatDateLog(Date.now())} `, - `${type} `, - `[${this.context}]`, - `[${typeValue}]`, - value, - ); - } - } - } - - 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/codechat/src/config/path.config.ts b/codechat/src/config/path.config.ts deleted file mode 100644 index 3fb00e4..0000000 --- a/codechat/src/config/path.config.ts +++ /dev/null @@ -1,6 +0,0 @@ -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/codechat/src/db/db.connect.ts b/codechat/src/db/db.connect.ts deleted file mode 100644 index fd4149f..0000000 --- a/codechat/src/db/db.connect.ts +++ /dev/null @@ -1,14 +0,0 @@ -import mongoose from 'mongoose'; -import { configService, Database } from '../config/env.config'; -import { Logger } from '../config/logger.config'; - -const logger = new Logger('Db Connection'); - -export const db = configService.get('DATABASE'); -export const dbserver = db.ENABLED - ? mongoose.createConnection(db.CONNECTION.URI, { - dbName: db.CONNECTION.DB_PREFIX_NAME + '-whatsapp-api', - }) - : null; - -db.ENABLED ? logger.info('ON - dbName: ' + dbserver['$dbName']) : null; diff --git a/codechat/src/db/redis.client.ts b/codechat/src/db/redis.client.ts deleted file mode 100644 index 6bf606c..0000000 --- a/codechat/src/db/redis.client.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { createClient, RedisClientType } from '@redis/client'; -import { Logger } from '../config/logger.config'; -import { BufferJSON } from '@adiwajshing/baileys'; - -export class RedisCache { - constructor(uri: string, private instanceName: string) { - this.client = createClient({ url: uri }); - - this.client.connect(); - } - - private readonly logger = new Logger(RedisCache.name); - private client: RedisClientType; - - public async getKeys() { - try { - return await this.client.hKeys(this.instanceName); - } catch (error) { - this.logger.error(error); - } - } - - public async writeData(field: string, data: any) { - try { - const json = JSON.stringify(data, BufferJSON.replacer); - return await this.client.hSet(this.instanceName, field, json); - } catch (error) { - this.logger.error(error); - } - } - - public async readData(field: string) { - try { - const data = await this.client.hGet(this.instanceName, field); - if (data) { - return JSON.parse(data, BufferJSON.reviver); - } - } catch (error) { - this.logger.error(error); - } - } - - public async removeData(field: string) { - try { - return await this.client.hDel(this.instanceName, field); - } catch (error) { - this.logger.error(error); - } - } - - public async delAll(hash?: string) { - try { - return await this.client.del(hash || this.instanceName); - } catch (error) { - this.logger.error(error); - } - } -} diff --git a/codechat/src/exceptions/400.exception.ts b/codechat/src/exceptions/400.exception.ts deleted file mode 100644 index 833295c..0000000 --- a/codechat/src/exceptions/400.exception.ts +++ /dev/null @@ -1,11 +0,0 @@ -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/codechat/src/exceptions/401.exception.ts b/codechat/src/exceptions/401.exception.ts deleted file mode 100644 index 72734d4..0000000 --- a/codechat/src/exceptions/401.exception.ts +++ /dev/null @@ -1,11 +0,0 @@ -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/codechat/src/exceptions/403.exception.ts b/codechat/src/exceptions/403.exception.ts deleted file mode 100644 index f53ca9a..0000000 --- a/codechat/src/exceptions/403.exception.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { HttpStatus } from '../whatsapp/routers/index.router'; - -export class ForbiddenException { - constructor(...objectError: any[]) { - throw { - status: HttpStatus.FORBIDDEN, - error: 'Forbidden', - message: objectError.length > 0 ? objectError : undefined, - }; - } -} diff --git a/codechat/src/exceptions/404.exception.ts b/codechat/src/exceptions/404.exception.ts deleted file mode 100644 index 1119d1a..0000000 --- a/codechat/src/exceptions/404.exception.ts +++ /dev/null @@ -1,11 +0,0 @@ -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/codechat/src/exceptions/500.exception.ts b/codechat/src/exceptions/500.exception.ts deleted file mode 100644 index 2a41dfa..0000000 --- a/codechat/src/exceptions/500.exception.ts +++ /dev/null @@ -1,11 +0,0 @@ -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/codechat/src/exceptions/index.ts b/codechat/src/exceptions/index.ts deleted file mode 100644 index 1657fc2..0000000 --- a/codechat/src/exceptions/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './400.exception'; -export * from './401.exception'; -export * from './403.exception'; -export * from './404.exception'; -export * from './500.exception'; diff --git a/codechat/src/main.ts b/codechat/src/main.ts deleted file mode 100644 index 4b8065a..0000000 --- a/codechat/src/main.ts +++ /dev/null @@ -1,81 +0,0 @@ -import compression from 'compression'; -import { configService, Cors, Production, HttpServer } from './config/env.config'; -import cors from 'cors'; -import express, { json, NextFunction, Request, Response, urlencoded } from 'express'; -import { join } from 'path'; -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'; -import { ServerUP } from './utils/server-up'; - -function initWA() { - waMonitor.loadInstance(); -} - -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'))); - } - - 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.app = app; - const server = ServerUP[httpServer.TYPE]; - - server().listen(httpServer.PORT, () => - logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT), - ); - - onUnexpectedError(); -} - -bootstrap(); diff --git a/codechat/src/utils/server-up.ts b/codechat/src/utils/server-up.ts deleted file mode 100644 index 0f6015b..0000000 --- a/codechat/src/utils/server-up.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Express } from 'express'; -import { readFileSync } from 'fs'; -import { configService, SslConf } from '../config/env.config'; -import * as https from 'https'; -import * as http from 'http'; - -export class ServerUP { - static #app: Express; - - static set app(e: Express) { - this.#app = e; - } - - static https() { - const { FULLCHAIN, PRIVKEY } = configService.get('SSL_CONF'); - return https.createServer( - { - cert: readFileSync(FULLCHAIN), - key: readFileSync(PRIVKEY), - }, - ServerUP.#app, - ); - } - - static http() { - return http.createServer(ServerUP.#app); - } -} diff --git a/codechat/src/utils/use-multi-file-auth-state-db.ts b/codechat/src/utils/use-multi-file-auth-state-db.ts deleted file mode 100644 index 8647ef4..0000000 --- a/codechat/src/utils/use-multi-file-auth-state-db.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { - AuthenticationCreds, - AuthenticationState, - BufferJSON, - initAuthCreds, - proto, - SignalDataTypeMap, -} from '@adiwajshing/baileys'; -import { configService, Database } from '../config/env.config'; -import { Logger } from '../config/logger.config'; -import { dbserver } from '../db/db.connect'; - -export async function useMultiFileAuthStateDb( - coll: string, -): Promise<{ state: AuthenticationState; saveCreds: () => Promise }> { - const logger = new Logger(useMultiFileAuthStateDb.name); - - const client = dbserver.getClient(); - - const collection = client - .db(configService.get('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances') - .collection(coll); - - const writeData = async (data: any, key: string): Promise => { - try { - await client.connect(); - return await collection.replaceOne( - { _id: key }, - JSON.parse(JSON.stringify(data, BufferJSON.replacer)), - { upsert: true }, - ); - } catch {} - }; - - const readData = async (key: string): Promise => { - try { - await client.connect(); - const data = await collection.findOne({ _id: key }); - const creds = JSON.stringify(data); - return JSON.parse(creds, BufferJSON.reviver); - } catch {} - }; - - const removeData = async (key: string) => { - try { - await client.connect(); - return await collection.deleteOne({ _id: key }); - } catch {} - }; - - 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 ? writeData(value, key) : removeData(key)); - } - } - - await Promise.all(tasks); - }, - }, - }, - saveCreds: async () => { - return writeData(creds, 'creds'); - }, - }; -} diff --git a/codechat/src/utils/use-multi-file-auth-state-redis-db.ts b/codechat/src/utils/use-multi-file-auth-state-redis-db.ts deleted file mode 100644 index 51e1b39..0000000 --- a/codechat/src/utils/use-multi-file-auth-state-redis-db.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { - AuthenticationCreds, - AuthenticationState, - initAuthCreds, - proto, - SignalDataTypeMap, -} from '@adiwajshing/baileys'; -import { RedisCache } from '../db/redis.client'; -import { Logger } from '../config/logger.config'; -import { Env } from '../config/env.config'; - -export async function useMultiFileAuthStateRedisDb( - redisUri: string, - instanceName: string, -): Promise<{ - state: AuthenticationState; - saveCreds: () => Promise; -}> { - const logger = new Logger(useMultiFileAuthStateRedisDb.name); - - const cache = new RedisCache(instanceName, redisUri); - - const writeData = async (data: any, key: string): Promise => { - try { - return await cache.writeData(key, data); - } catch (error) { - return logger.error({ localError: 'writeData', error }); - } - }; - - const readData = async (key: string): Promise => { - try { - return await cache.readData(key); - } catch (error) { - logger.error({ readData: 'writeData', error }); - return; - } - }; - - const removeData = async (key: string) => { - try { - return await cache.removeData(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/codechat/src/validate/validate.schema.ts b/codechat/src/validate/validate.schema.ts deleted file mode 100644 index a04ac39..0000000 --- a/codechat/src/validate/validate.schema.ts +++ /dev/null @@ -1,537 +0,0 @@ -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', - }, - presence: { - type: 'string', - enum: ['unavailable', 'available', 'composing', 'recording', 'paused'], - }, - }, -}; - -const numberDefinition: JSONSchema7Definition = { - type: 'string', - pattern: '^\\d+[\\.@\\w-]+', - description: 'Invalid format', -}; - -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', 'audio'] }, - media: { type: 'string' }, - fileName: { type: 'string' }, - caption: { type: 'string' }, - }, - required: ['mediatype', 'media'], - ...isNotEmpty('fileName', 'caption', 'media'), - }, - }, - required: ['mediaMessage', 'number'], -}; - -export const audioMessageSchema: JSONSchema7 = { - $id: v4(), - type: 'object', - properties: { - number: { ...numberDefinition }, - options: { ...optionsSchema }, - audioMessage: { - type: 'object', - properties: { - audio: { type: 'string' }, - }, - required: ['audio'], - ...isNotEmpty('audio'), - }, - }, - required: ['audioMessage', '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' }, - fileName: { type: 'string' }, - mediatype: { type: 'string', enum: ['image', 'document', 'video'] }, - }, - required: ['media', 'mediatype'], - ...isNotEmpty('media', 'fileName'), - }, - }, - required: ['title', '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 deleteMessageSchema: JSONSchema7 = { - $id: v4(), - type: 'object', - properties: { - id: { type: 'string' }, - fromMe: { type: 'boolean', enum: [true, false] }, - remoteJid: { type: 'string' }, - participant: { type: 'string' }, - }, - required: ['id', 'fromMe', 'remoteJid'], - ...isNotEmpty('id', 'remoteJid', 'participant'), -}; - -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 profilePictureSchema: JSONSchema7 = { - $id: v4(), - type: 'object', - properties: { - number: { type: 'string' }, - }, - ...isNotEmpty('number'), -}; - -export const messageValidateSchema: 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'), - }, - limit: { type: 'integer' }, - }, -}; - -export const messageUpSchema: JSONSchema7 = { - $id: v4(), - type: 'object', - properties: { - where: { - type: 'object', - properties: { - _id: { type: 'string' }, - remoteJid: { type: 'string' }, - id: { type: 'string' }, - 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'), - }, - limit: { type: 'integer' }, - }, -}; - -// 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$' }, - }, - required: ['groupJid'], - ...isNotEmpty('groupJid'), -}; - -export const updateParticipantsSchema: 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'), -}; - -export const updateGroupPicture: JSONSchema7 = { - $id: v4(), - type: 'object', - properties: { - groupJid: { type: 'string' }, - image: { type: 'string' }, - }, - required: ['groupJid', 'image'], - ...isNotEmpty('groupJid', 'image'), -}; - -// 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/codechat/src/whatsapp/abstract/abstract.repository.ts b/codechat/src/whatsapp/abstract/abstract.repository.ts deleted file mode 100644 index a3afac2..0000000 --- a/codechat/src/whatsapp/abstract/abstract.repository.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { existsSync, mkdirSync, 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) => { - if (!existsSync(create.path)) { - mkdirSync(create.path, { recursive: true }); - } - 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/codechat/src/whatsapp/abstract/abstract.router.ts b/codechat/src/whatsapp/abstract/abstract.router.ts deleted file mode 100644 index 37dac4b..0000000 --- a/codechat/src/whatsapp/abstract/abstract.router.ts +++ /dev/null @@ -1,108 +0,0 @@ -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, param = true) { - let route = '/' + path; - param ? (route += '/:instanceName') : null; - - return route; - } - - 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 (request?.query && Object.keys(request.query).length > 0) { - Object.assign(instance, request.query); - } - - if (request.originalUrl.includes('/instance/create')) { - Object.assign(instance, body); - } - - Object.assign(ref, body); - - const v = schema ? validate(ref, schema) : { valid: true, errors: [] }; - - 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, - }; - }); - logger.error([...message]); - throw new BadRequestException(...message); - } - - return await execute(instance, ref); - } - - public async groupValidate(args: DataValidate) { - const { request, ClassRef, schema, execute } = args; - - const groupJid = request.query as unknown as GroupJid; - - if (!groupJid?.groupJid) { - throw new BadRequestException( - 'The group id needs to be informed in the query', - 'ex: "groupJid=120362@g.us"', - ); - } - - const instance = request.params as unknown as InstanceDto; - const body = request.body; - - const ref = new ClassRef(); - - Object.assign(body, groupJid); - Object.assign(ref, body); - - const v = validate(ref, schema); - - 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, - }; - }); - logger.error([...message]); - throw new BadRequestException(...message); - } - - return await execute(instance, ref); - } -} diff --git a/codechat/src/whatsapp/controllers/chat.controller.ts b/codechat/src/whatsapp/controllers/chat.controller.ts deleted file mode 100644 index c0fa882..0000000 --- a/codechat/src/whatsapp/controllers/chat.controller.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { proto } from '@adiwajshing/baileys'; -import { - ArchiveChatDto, - DeleteMessage, - NumberDto, - 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 deleteMessage({ instanceName }: InstanceDto, data: DeleteMessage) { - return await this.waMonitor.waInstances[instanceName].deleteMessage(data); - } - - public async fetchProfilePicture({ instanceName }: InstanceDto, data: NumberDto) { - return await this.waMonitor.waInstances[instanceName].profilePicture(data.number); - } - - public async fetchContacts({ instanceName }: InstanceDto, query: ContactQuery) { - return await this.waMonitor.waInstances[instanceName].fetchContacts(query); - } - - public async getBase64FromMediaMessage( - { instanceName }: InstanceDto, - message: proto.IWebMessageInfo, - ) { - return await this.waMonitor.waInstances[instanceName].getBase64FromMediaMessage( - message, - ); - } - - public async fetchMessages({ instanceName }: InstanceDto, query: MessageQuery) { - return await this.waMonitor.waInstances[instanceName].fetchMessages(query); - } - - public async fetchStatusMessage({ instanceName }: InstanceDto, query: MessageUpQuery) { - return await this.waMonitor.waInstances[instanceName].fetchStatusMessage(query); - } - - public async fetchChats({ instanceName }: InstanceDto) { - return await this.waMonitor.waInstances[instanceName].fetchChats(); - } -} diff --git a/codechat/src/whatsapp/controllers/group.controller.ts b/codechat/src/whatsapp/controllers/group.controller.ts deleted file mode 100644 index c05e93f..0000000 --- a/codechat/src/whatsapp/controllers/group.controller.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { - CreateGroupDto, - GroupJid, - GroupPictureDto, - 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 updateGroupPicture(instance: InstanceDto, update: GroupPictureDto) { - return await this.waMonitor.waInstances[instance.instanceName].updateGroupPicture( - update, - ); - } - - 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].inviteCode(groupJid); - } - - public async revokeInviteCode(instance: InstanceDto, groupJid: GroupJid) { - return await this.waMonitor.waInstances[instance.instanceName].revokeInviteCode( - groupJid, - ); - } - - public async findParticipants(instance: InstanceDto, groupJid: GroupJid) { - return await this.waMonitor.waInstances[instance.instanceName].findParticipants( - groupJid, - ); - } - - public async updateGParticipate( - 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/codechat/src/whatsapp/controllers/instance.controller.ts b/codechat/src/whatsapp/controllers/instance.controller.ts deleted file mode 100644 index 5a36d8c..0000000 --- a/codechat/src/whatsapp/controllers/instance.controller.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { delay } from '@adiwajshing/baileys'; -import EventEmitter2 from 'eventemitter2'; -import { ConfigService } from '../../config/env.config'; -import { BadRequestException, InternalServerErrorException } from '../../exceptions'; -import { InstanceDto } from '../dto/instance.dto'; -import { RepositoryBroker } from '../repository/repository.manager'; -import { AuthService, OldToken } from '../services/auth.service'; -import { WAMonitoringService } from '../services/monitor.service'; -import { WAStartupService } from '../services/whatsapp.service'; -import { Logger } from '../../config/logger.config'; - -export class InstanceController { - constructor( - private readonly waMonitor: WAMonitoringService, - private readonly configService: ConfigService, - private readonly repository: RepositoryBroker, - private readonly eventEmitter: EventEmitter2, - private readonly authService: AuthService, - ) {} - - private readonly logger = new Logger(InstanceController.name); - - public async createInstance({ instanceName }: InstanceDto) { - const instance = new WAStartupService( - this.configService, - this.eventEmitter, - this.repository, - ); - instance.instanceName = instanceName; - this.waMonitor.waInstances[instance.instanceName] = instance; - this.waMonitor.delInstanceTime(instance.instanceName); - - const hash = await this.authService.generateHash({ - instanceName: instance.instanceName, - }); - - return { - instance: { - instanceName: instance.instanceName, - status: 'created', - }, - hash, - }; - } - - public async connectToWhatsapp({ instanceName }: InstanceDto) { - try { - const instance = this.waMonitor.waInstances[instanceName]; - const state = instance.connectionStatus?.state; - - switch (state) { - case 'close': - await instance.connectToWhatsapp(); - await delay(2000); - return instance.qrCode; - case 'connecting': - return instance.qrCode; - default: - return await this.connectionState({ instanceName }); - } - } catch (error) { - this.logger.log(error); - } - } - - public async connectionState({ instanceName }: InstanceDto) { - return this.waMonitor.waInstances[instanceName].connectionStatus; - } - - public async fetchInstances({ instanceName }: InstanceDto) { - if (instanceName) { - return this.waMonitor.instanceInfo(instanceName); - } - - return this.waMonitor.instanceInfo(); - } - - public async logout({ instanceName }: InstanceDto) { - try { - await this.waMonitor.waInstances[instanceName]?.client?.logout( - 'Log out instance: ' + instanceName, - ); - return { error: false, message: 'Instance logged out' }; - } catch (error) { - throw new InternalServerErrorException(error.toString()); - } - } - - public async deleteInstance({ instanceName }: InstanceDto) { - const stateConn = await this.connectionState({ instanceName }); - if (stateConn.state === 'open') { - throw new BadRequestException([ - 'Deletion failed', - 'The instance needs to be disconnected', - ]); - } - try { - delete this.waMonitor.waInstances[instanceName]; - return { error: false, message: 'Instance deleted' }; - } catch (error) { - throw new BadRequestException(error.toString()); - } - } - - public async refreshToken(_: InstanceDto, oldToken: OldToken) { - return await this.authService.refreshToken(oldToken); - } -} diff --git a/codechat/src/whatsapp/controllers/sendMessage.controller.ts b/codechat/src/whatsapp/controllers/sendMessage.controller.ts deleted file mode 100644 index 28e9f67..0000000 --- a/codechat/src/whatsapp/controllers/sendMessage.controller.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { isBase64, isURL } from 'class-validator'; -import { BadRequestException } from '../../exceptions'; -import { InstanceDto } from '../dto/instance.dto'; -import { - SendAudioDto, - 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 (isBase64(data?.mediaMessage?.media) && !data?.mediaMessage?.fileName) { - throw new BadRequestException('For bse64 the file name must be informed.'); - } - if (isURL(data?.mediaMessage?.media) || isBase64(data?.mediaMessage?.media)) { - return await this.waMonitor.waInstances[instanceName].mediaMessage(data); - } - throw new BadRequestException('Owned media must be a url or base64'); - } - - public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto) { - if (isURL(data.audioMessage.audio) || isBase64(data.audioMessage.audio)) { - return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data); - } - throw new BadRequestException('Owned media must be a url or base64'); - } - - public async sendButtons({ instanceName }: InstanceDto, data: SendButtonDto) { - if ( - isBase64(data.buttonMessage.mediaMessage?.media) && - !data.buttonMessage.mediaMessage?.fileName - ) { - throw new BadRequestException('For bse64 the file name must be informed.'); - } - 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/codechat/src/whatsapp/controllers/views.controller.ts b/codechat/src/whatsapp/controllers/views.controller.ts deleted file mode 100644 index 52bce49..0000000 --- a/codechat/src/whatsapp/controllers/views.controller.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Request, Response } from 'express'; -import { Auth, ConfigService } from '../../config/env.config'; -import { BadRequestException } from '../../exceptions'; -import { InstanceDto } from '../dto/instance.dto'; -import { HttpStatus } from '../routers/index.router'; -import { WAMonitoringService } from '../services/monitor.service'; - -export class ViewsController { - constructor( - private readonly waMonit: WAMonitoringService, - private readonly configService: ConfigService, - ) {} - - public async 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'); - } - const type = this.configService.get('AUTHENTICATION').TYPE; - - return response.status(HttpStatus.OK).render('qrcode', { type, ...param }); - } -} diff --git a/codechat/src/whatsapp/controllers/webhook.controller.ts b/codechat/src/whatsapp/controllers/webhook.controller.ts deleted file mode 100644 index b91a0db..0000000 --- a/codechat/src/whatsapp/controllers/webhook.controller.ts +++ /dev/null @@ -1,20 +0,0 @@ -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/codechat/src/whatsapp/dto/chat.dto.ts b/codechat/src/whatsapp/dto/chat.dto.ts deleted file mode 100644 index ad2c6e0..0000000 --- a/codechat/src/whatsapp/dto/chat.dto.ts +++ /dev/null @@ -1,41 +0,0 @@ -export class OnWhatsAppDto { - constructor( - public readonly jid: string, - public readonly exists: boolean, - public readonly name?: string, - ) {} -} - -export class WhatsAppNumberDto { - numbers: string[]; -} - -export class NumberDto { - number: 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; -} - -export class DeleteMessage { - id: string; - fromMe: boolean; - remoteJid: string; - participant?: string; -} diff --git a/codechat/src/whatsapp/dto/group.dto.ts b/codechat/src/whatsapp/dto/group.dto.ts deleted file mode 100644 index d934c56..0000000 --- a/codechat/src/whatsapp/dto/group.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -export class CreateGroupDto { - subject: string; - description?: string; - participants: string[]; -} - -export class GroupPictureDto { - groupJid: string; - image: string; -} - -export class GroupJid { - groupJid: string; -} - -export class GroupUpdateParticipantDto extends GroupJid { - action: 'add' | 'remove' | 'promote' | 'demote'; - participants: string[]; -} diff --git a/codechat/src/whatsapp/dto/instance.dto.ts b/codechat/src/whatsapp/dto/instance.dto.ts deleted file mode 100644 index e4f929d..0000000 --- a/codechat/src/whatsapp/dto/instance.dto.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class InstanceDto { - instanceName: string; -} diff --git a/codechat/src/whatsapp/dto/sendMessage.dto.ts b/codechat/src/whatsapp/dto/sendMessage.dto.ts deleted file mode 100644 index a0070a5..0000000 --- a/codechat/src/whatsapp/dto/sendMessage.dto.ts +++ /dev/null @@ -1,102 +0,0 @@ -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' | 'audio'; -export class MediaMessage { - mediatype: MediaType; - caption?: string; - // for document - fileName?: string; - // url or base64 - media: string; -} -export class SendMediaDto extends Metadata { - mediaMessage: MediaMessage; -} - -class Audio { - audio: string; -} -export class SendAudioDto extends Metadata { - audioMessage: Audio; -} - -class Button { - buttonText: string; - buttonId: string; -} -class ButtonMessage { - title: string; - description: string; - footerText?: string; - buttons: Button[]; - mediaMessage?: MediaMessage; -} -export class SendButtonDto extends Metadata { - buttonMessage: ButtonMessage; -} - -class LocationMessage { - latitude: number; - longitude: number; - name?: string; - address?: string; -} -export class SendLocationDto extends Metadata { - locationMessage: LocationMessage; -} - -class Row { - title: string; - description: 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/codechat/src/whatsapp/dto/webhook.dto.ts b/codechat/src/whatsapp/dto/webhook.dto.ts deleted file mode 100644 index b87793f..0000000 --- a/codechat/src/whatsapp/dto/webhook.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class WebhookDto { - enabled?: boolean; - url?: string; -} diff --git a/codechat/src/whatsapp/guards/auth.guard.ts b/codechat/src/whatsapp/guards/auth.guard.ts deleted file mode 100644 index 8607cab..0000000 --- a/codechat/src/whatsapp/guards/auth.guard.ts +++ /dev/null @@ -1,94 +0,0 @@ -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 { ForbiddenException, UnauthorizedException } from '../../exceptions'; -import { repository } from '../whatsapp.module'; - -const logger = new Logger('GUARD'); - -async function jwtGuard(req: Request, res: Response, next: NextFunction) { - const key = req.get('apikey'); - - if (key && configService.get('AUTHENTICATION').API_KEY.KEY !== key) { - throw new UnauthorizedException(); - } - - if (configService.get('AUTHENTICATION').API_KEY.KEY === key) { - return next(); - } - - if ( - (req.originalUrl.includes('/instance/create') || - req.originalUrl.includes('/instance/fetchInstances')) && - !key - ) { - throw new ForbiddenException( - 'Missing global api key', - 'The global api key must be set', - ); - } - - 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: jwtOpts.EXPIRIN_IN === 0, - }) as JwtPayload; - - if (param.instanceName !== decode.instanceName || name !== decode.apiName) { - throw new UnauthorizedException(); - } - - return next(); - } catch (error) { - logger.error(error); - throw new UnauthorizedException(); - } -} - -async function apikey(req: Request, res: Response, next: NextFunction) { - const env = configService.get('AUTHENTICATION').API_KEY; - const key = req.get('apikey'); - - if (env.KEY === key) { - return next(); - } - - if ( - (req.originalUrl.includes('/instance/create') || - req.originalUrl.includes('/instance/fetchInstances')) && - !key - ) { - throw new ForbiddenException( - 'Missing global api key', - 'The global api key must be set', - ); - } - - try { - const param = req.params as unknown as InstanceDto; - const instanceKey = await repository.auth.find(param.instanceName); - if (instanceKey.apikey === key) { - return next(); - } - } catch (error) {} - - throw new UnauthorizedException(); -} - -export const authGuard = { jwt: jwtGuard, apikey }; diff --git a/codechat/src/whatsapp/guards/instance.guard.ts b/codechat/src/whatsapp/guards/instance.guard.ts deleted file mode 100644 index db48e9d..0000000 --- a/codechat/src/whatsapp/guards/instance.guard.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { NextFunction, Request, Response } from 'express'; -import { existsSync } from 'fs'; -import { join } from 'path'; -import { INSTANCE_DIR } from '../../config/path.config'; -import { db, dbserver } from '../../db/db.connect'; -import { - BadRequestException, - ForbiddenException, - NotFoundException, -} from '../../exceptions'; -import { InstanceDto } from '../dto/instance.dto'; -import { waMonitor } from '../whatsapp.module'; - -async function getInstance(instanceName: string) { - const exists = waMonitor.waInstances[instanceName]; - - if (db.ENABLED) { - const collection = dbserver - .getClient() - .db(db.CONNECTION.DB_PREFIX_NAME + '-instances') - .collection(instanceName); - return exists || (await collection.find({}).toArray()).length > 0; - } - - return exists || existsSync(join(INSTANCE_DIR, instanceName)); -} - -export async function instanceExistsGuard(req: Request, _: Response, next: NextFunction) { - if ( - req.originalUrl.includes('/instance/create') || - req.originalUrl.includes('/instance/fetchInstances') - ) { - return next(); - } - - const param = req.params as unknown as InstanceDto; - if (!param?.instanceName) { - throw new BadRequestException('"instanceName" not provided.'); - } - - if (!(await getInstance(param.instanceName))) { - throw new NotFoundException(`The "${param.instanceName}" instance does not exist`); - } - - next(); -} - -export async function instanceLoggedGuard(req: Request, _: Response, next: NextFunction) { - if (req.originalUrl.includes('/instance/create')) { - const instance = req.body as InstanceDto; - if (await getInstance(instance.instanceName)) { - throw new ForbiddenException( - `This name "${instance.instanceName}" is already in use.`, - ); - } - - if (waMonitor.waInstances[instance.instanceName]) { - delete waMonitor.waInstances[instance.instanceName]; - } - } - - next(); -} diff --git a/codechat/src/whatsapp/models/auth.model.ts b/codechat/src/whatsapp/models/auth.model.ts deleted file mode 100644 index b5da800..0000000 --- a/codechat/src/whatsapp/models/auth.model.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Schema } from 'mongoose'; -import { dbserver } from '../../db/db.connect'; - -export class AuthRaw { - _id?: string; - jwt?: string; - apikey?: string; -} - -const authSchema = new Schema({ - _id: { type: String, _id: true }, - jwt: { type: String, minlength: 1 }, - apikey: { type: String, minlength: 1 }, -}); - -export const AuthModel = dbserver?.model(AuthRaw.name, authSchema, 'authentication'); -export type IAuthModel = typeof AuthModel; diff --git a/codechat/src/whatsapp/models/chat.model.ts b/codechat/src/whatsapp/models/chat.model.ts deleted file mode 100644 index 51a53e3..0000000 --- a/codechat/src/whatsapp/models/chat.model.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Schema } from 'mongoose'; -import { dbserver } from '../../db/db.connect'; - -export class ChatRaw { - _id?: string; - id?: string; - owner: string; -} - -const chatSchema = new Schema({ - _id: { type: String, _id: true }, - id: { type: String, required: true, minlength: 1 }, - owner: { type: String, required: true, minlength: 1 }, -}); - -export const ChatModel = dbserver?.model(ChatRaw.name, chatSchema, 'chats'); -export type IChatModel = typeof ChatModel; diff --git a/codechat/src/whatsapp/models/contact.model.ts b/codechat/src/whatsapp/models/contact.model.ts deleted file mode 100644 index c15411f..0000000 --- a/codechat/src/whatsapp/models/contact.model.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Schema } from 'mongoose'; -import { dbserver } from '../../db/db.connect'; - -export class ContactRaw { - _id?: string; - pushName?: string; - id?: string; - profilePictureUrl?: string; - owner: string; -} - -const contactSchema = new Schema({ - _id: { type: String, _id: true }, - pushName: { type: String, minlength: 1 }, - id: { type: String, required: true, minlength: 1 }, - profilePictureUrl: { type: String, minlength: 1 }, - owner: { type: String, required: true, minlength: 1 }, -}); - -export const ContactModel = dbserver?.model(ContactRaw.name, contactSchema, 'contacts'); -export type IContactModel = typeof ContactModel; diff --git a/codechat/src/whatsapp/models/index.ts b/codechat/src/whatsapp/models/index.ts deleted file mode 100644 index 11f760d..0000000 --- a/codechat/src/whatsapp/models/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './chat.model'; -export * from './contact.model'; -export * from './message.model'; -export * from './auth.model'; -export * from './webhook.model'; diff --git a/codechat/src/whatsapp/models/message.model.ts b/codechat/src/whatsapp/models/message.model.ts deleted file mode 100644 index a3ce69a..0000000 --- a/codechat/src/whatsapp/models/message.model.ts +++ /dev/null @@ -1,69 +0,0 @@ -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; - pushName?: string; - participant?: string; - message?: object; - messageTimestamp?: number | Long.Long; - owner: string; - source?: 'android' | 'web' | 'ios'; -} - -const messageSchema = new Schema({ - _id: { type: String, _id: true }, - 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 }, - }, - pushName: { type: String }, - participant: { type: String }, - message: { type: Object }, - source: { type: String, minlength: 3, enum: ['android', 'web', 'ios'] }, - messageTimestamp: { type: Number, required: true }, - owner: { 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; - owner: string; -} - -const messageUpdateSchema = new Schema({ - _id: { type: String, _id: true }, - remoteJid: { type: String, required: true, min: 1 }, - id: { type: String, required: true, min: 1 }, - fromMe: { type: Boolean, required: true }, - participant: { type: String, min: 1 }, - datetime: { type: Number, required: true, min: 1 }, - status: { type: String, required: true }, - owner: { type: String, required: true, min: 1 }, -}); - -export const MessageUpModel = dbserver?.model( - MessageUpdateRaw.name, - messageUpdateSchema, - 'messageUpdate', -); -export type IMessageUpModel = typeof MessageUpModel; diff --git a/codechat/src/whatsapp/models/webhook.model.ts b/codechat/src/whatsapp/models/webhook.model.ts deleted file mode 100644 index 11c7301..0000000 --- a/codechat/src/whatsapp/models/webhook.model.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Schema } from 'mongoose'; -import { dbserver } from '../../db/db.connect'; - -export class WebhookRaw { - _id?: string; - url?: string; - enabled?: boolean; -} - -const webhookSchema = new Schema({ - _id: { type: String, _id: true }, - url: { type: String, required: true }, - enabled: { type: Boolean, required: true }, -}); - -export const WebhookModel = dbserver?.model(WebhookRaw.name, webhookSchema, 'webhook'); -export type IWebhookModel = typeof WebhookModel; diff --git a/codechat/src/whatsapp/repository/auth.repository.ts b/codechat/src/whatsapp/repository/auth.repository.ts deleted file mode 100644 index 2b53a7f..0000000 --- a/codechat/src/whatsapp/repository/auth.repository.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { join } from 'path'; -import { Auth, ConfigService } from '../../config/env.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { IAuthModel, AuthRaw } from '../models'; -import { readFileSync } from 'fs'; -import { AUTH_DIR } from '../../config/path.config'; - -export class AuthRepository extends Repository { - constructor( - private readonly authModel: IAuthModel, - readonly configService: ConfigService, - ) { - super(configService); - this.auth = configService.get('AUTHENTICATION'); - } - - private readonly auth: Auth; - - public async create(data: AuthRaw, instance: string): Promise { - try { - if (this.dbSettings.ENABLED) { - const insert = await this.authModel.replaceOne( - { _id: instance }, - { ...data }, - { upsert: true }, - ); - return { insertCount: insert.modifiedCount }; - } - - this.writeStore({ - path: join(AUTH_DIR, this.auth.TYPE), - fileName: instance, - data, - }); - - return { insertCount: 1 }; - } catch (error) { - return { error } as any; - } - } - - public async find(instance: string): Promise { - try { - if (this.dbSettings.ENABLED) { - return await this.authModel.findOne({ _id: instance }); - } - - return JSON.parse( - readFileSync(join(AUTH_DIR, this.auth.TYPE, instance + '.json'), { - encoding: 'utf-8', - }), - ) as AuthRaw; - } catch (error) { - return {}; - } - } -} diff --git a/codechat/src/whatsapp/repository/chat.repository.ts b/codechat/src/whatsapp/repository/chat.repository.ts deleted file mode 100644 index 9e61e1d..0000000 --- a/codechat/src/whatsapp/repository/chat.repository.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { opendirSync, readFileSync, rmSync } from 'fs'; -import { ChatRaw, IChatModel } from '../models'; - -export class ChatQuery { - where: ChatRaw; -} - -export class ChatRepository extends Repository { - constructor( - private readonly chatModel: IChatModel, - private readonly configService: ConfigService, - ) { - super(configService); - } - - public async insert(data: ChatRaw[], saveDb = false): Promise { - if (data.length === 0) { - return; - } - - try { - if (this.dbSettings.ENABLED && saveDb) { - const insert = await this.chatModel.insertMany([...data]); - return { insertCount: insert.length }; - } - - data.forEach((chat) => { - this.writeStore({ - path: join(this.storePath, 'chats', chat.owner), - fileName: chat.id, - data: chat, - }); - }); - - return { insertCount: data.length }; - } catch (error) { - return error; - } finally { - data = undefined; - } - } - - public async find(query: ChatQuery): Promise { - try { - if (this.dbSettings.ENABLED) { - return await this.chatModel.find({ owner: query.where.owner }); - } - - const chats: ChatRaw[] = []; - const openDir = opendirSync(join(this.storePath, 'chats', query.where.owner)); - for await (const dirent of openDir) { - if (dirent.isFile()) { - chats.push( - JSON.parse( - readFileSync( - join(this.storePath, 'chats', query.where.owner, dirent.name), - { encoding: 'utf-8' }, - ), - ), - ); - } - } - - return chats; - } catch (error) { - return []; - } - } - - public async delete(query: ChatQuery) { - try { - if (this.dbSettings.ENABLED) { - return await this.chatModel.deleteOne({ ...query.where }); - } - - rmSync(join(this.storePath, 'chats', query.where.owner, query.where.id + '.josn'), { - force: true, - recursive: true, - }); - - return { deleted: { chatId: query.where.id } }; - } catch (error) { - return { error: error?.toString() }; - } - } -} diff --git a/codechat/src/whatsapp/repository/contact.repository.ts b/codechat/src/whatsapp/repository/contact.repository.ts deleted file mode 100644 index 954292d..0000000 --- a/codechat/src/whatsapp/repository/contact.repository.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { opendirSync, readFileSync } from 'fs'; -import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { ContactRaw, IContactModel } from '../models'; -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 { - if (data.length === 0) { - return; - } - - try { - if (this.dbSettings.ENABLED && saveDb) { - const insert = await this.contactModel.insertMany([...data]); - return { insertCount: insert.length }; - } - - data.forEach((contact) => { - this.writeStore({ - path: join(this.storePath, 'contacts', contact.owner), - 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.owner, - query.where.id + '.json', - ), - { encoding: 'utf-8' }, - ), - ), - ); - } else { - const openDir = opendirSync(join(this.storePath, 'contacts', query.where.owner), { - encoding: 'utf-8', - }); - for await (const dirent of openDir) { - if (dirent.isFile()) { - contacts.push( - JSON.parse( - readFileSync( - join(this.storePath, 'contacts', query.where.owner, dirent.name), - { encoding: 'utf-8' }, - ), - ), - ); - } - } - } - return contacts; - } catch (error) { - return []; - } - } -} diff --git a/codechat/src/whatsapp/repository/message.repository.ts b/codechat/src/whatsapp/repository/message.repository.ts deleted file mode 100644 index 3eb3763..0000000 --- a/codechat/src/whatsapp/repository/message.repository.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { ConfigService } from '../../config/env.config'; -import { join } from 'path'; -import { IMessageModel, MessageRaw } from '../models'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { opendirSync, readFileSync } from 'fs'; - -export class MessageQuery { - where: MessageRaw; - limit?: number; -} - -export class MessageRepository extends Repository { - constructor( - private readonly messageModel: IMessageModel, - private readonly configService: ConfigService, - ) { - super(configService); - } - - public async insert(data: MessageRaw[], saveDb = false): Promise { - if (data.length === 0) { - return; - } - - try { - if (this.dbSettings.ENABLED && saveDb) { - const insert = await this.messageModel.insertMany([...data]); - return { insertCount: insert.length }; - } - - if (saveDb) { - data.forEach((msg) => - this.writeStore({ - path: join(this.storePath, 'messages', msg.owner), - fileName: msg.key.id, - data: msg, - }), - ); - - return { insertCount: data.length }; - } - - return { insertCount: 0 }; - } catch (error) { - console.log('ERROR: ', error); - return error; - } finally { - data = undefined; - } - } - - public async find(query: MessageQuery) { - try { - if (this.dbSettings.ENABLED) { - 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.messageModel - .find({ ...query.where }) - .sort({ messageTimestamp: -1 }) - .limit(query?.limit ?? 0); - } - - const messages: MessageRaw[] = []; - if (query?.where?.key?.id) { - messages.push( - JSON.parse( - readFileSync( - join( - this.storePath, - 'messages', - query.where.owner, - query.where.key.id + '.json', - ), - { encoding: 'utf-8' }, - ), - ), - ); - } else { - const openDir = opendirSync(join(this.storePath, 'messages', query.where.owner), { - encoding: 'utf-8', - }); - - for await (const dirent of openDir) { - if (dirent.isFile()) { - messages.push( - JSON.parse( - readFileSync( - join(this.storePath, 'messages', query.where.owner, dirent.name), - { encoding: 'utf-8' }, - ), - ), - ); - } - } - } - - return messages - .sort((x, y) => { - return (y.messageTimestamp as number) - (x.messageTimestamp as number); - }) - .splice(0, query?.limit ?? messages.length); - } catch (error) { - return []; - } - } -} diff --git a/codechat/src/whatsapp/repository/messageUp.repository.ts b/codechat/src/whatsapp/repository/messageUp.repository.ts deleted file mode 100644 index ac92850..0000000 --- a/codechat/src/whatsapp/repository/messageUp.repository.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { ConfigService } from '../../config/env.config'; -import { IMessageUpModel, MessageUpdateRaw } from '../models'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { join } from 'path'; -import { opendirSync, readFileSync } from 'fs'; - -export class MessageUpQuery { - where: MessageUpdateRaw; - limit?: number; -} - -export class MessageUpRepository extends Repository { - constructor( - private readonly messageUpModel: IMessageUpModel, - private readonly configService: ConfigService, - ) { - super(configService); - } - - public async insert(data: MessageUpdateRaw[], saveDb?: boolean): Promise { - if (data.length === 0) { - return; - } - - 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', update.owner), - fileName: update.id, - data: update, - }); - }); - } catch (error) { - return error; - } - } - - public async find(query: MessageUpQuery) { - try { - if (this.dbSettings.ENABLED) { - return await this.messageUpModel - .find({ ...query.where }) - .sort({ datetime: -1 }) - .limit(query?.limit ?? 0); - } - - const messageUpdate: MessageUpdateRaw[] = []; - if (query?.where?.id) { - messageUpdate.push( - JSON.parse( - readFileSync( - join( - this.storePath, - 'message-up', - query.where.owner, - query.where.id + '.json', - ), - { encoding: 'utf-8' }, - ), - ), - ); - } else { - const openDir = opendirSync( - join(this.storePath, 'message-up', query.where.owner), - { encoding: 'utf-8' }, - ); - - for await (const dirent of openDir) { - if (dirent.isFile()) { - messageUpdate.push( - JSON.parse( - readFileSync( - join(this.storePath, 'message-up', query.where.owner, dirent.name), - { encoding: 'utf-8' }, - ), - ), - ); - } - } - } - - return messageUpdate - .sort((x, y) => { - return y.datetime - x.datetime; - }) - .splice(0, query?.limit ?? messageUpdate.length); - } catch (error) { - return []; - } - } -} diff --git a/codechat/src/whatsapp/repository/repository.manager.ts b/codechat/src/whatsapp/repository/repository.manager.ts deleted file mode 100644 index 86b8c8b..0000000 --- a/codechat/src/whatsapp/repository/repository.manager.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MessageRepository } from './message.repository'; -import { ChatRepository } from './chat.repository'; -import { ContactRepository } from './contact.repository'; -import { MessageUpRepository } from './messageUp.repository'; -import { MongoClient } from 'mongodb'; -import { WebhookRepository } from './webhook.repository'; -import { AuthRepository } from './auth.repository'; - -export class RepositoryBroker { - constructor( - public readonly message: MessageRepository, - public readonly chat: ChatRepository, - public readonly contact: ContactRepository, - public readonly messageUpdate: MessageUpRepository, - public readonly webhook: WebhookRepository, - public readonly auth: AuthRepository, - dbServer?: MongoClient, - ) { - this.dbClient = dbServer; - } - - private dbClient?: MongoClient; - - public get dbServer() { - return this.dbClient; - } -} diff --git a/codechat/src/whatsapp/repository/webhook.repository.ts b/codechat/src/whatsapp/repository/webhook.repository.ts deleted file mode 100644 index 2a0c12a..0000000 --- a/codechat/src/whatsapp/repository/webhook.repository.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { ConfigService } from '../../config/env.config'; -import { join } from 'path'; -import { readFileSync } from 'fs'; -import { IWebhookModel, WebhookRaw } from '../models'; - -export class WebhookRepository extends Repository { - constructor( - private readonly webhookModel: IWebhookModel, - private readonly configService: ConfigService, - ) { - super(configService); - } - - public async create(data: WebhookRaw, instance: string): Promise { - try { - if (this.dbSettings.ENABLED) { - const insert = await this.webhookModel.replaceOne( - { _id: instance }, - { ...data }, - { upsert: true }, - ); - return { insertCount: insert.modifiedCount }; - } - - this.writeStore({ - path: join(this.storePath, 'webhook'), - fileName: instance, - data, - }); - - return { insertCount: 1 }; - } catch (error) { - return error; - } - } - - public async find(instance: string): Promise { - try { - if (this.dbSettings.ENABLED) { - return await this.webhookModel.findOne({ _id: instance }); - } - - return JSON.parse( - readFileSync(join(this.storePath, 'webhook', instance + '.json'), { - encoding: 'utf-8', - }), - ) as WebhookRaw; - } catch (error) { - return {}; - } - } -} diff --git a/codechat/src/whatsapp/routers/chat.router.ts b/codechat/src/whatsapp/routers/chat.router.ts deleted file mode 100644 index 2e4738c..0000000 --- a/codechat/src/whatsapp/routers/chat.router.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { RequestHandler, Router } from 'express'; -import { - archiveChatSchema, - contactValidateSchema, - deleteMessageSchema, - messageUpSchema, - messageValidateSchema, - profilePictureSchema, - readMessageSchema, - whatsappNumberSchema, -} from '../../validate/validate.schema'; -import { - ArchiveChatDto, - DeleteMessage, - NumberDto, - 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'; -import { proto } from '@adiwajshing/baileys'; -import { InstanceDto } from '../dto/instance.dto'; - -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); - }) - .delete( - this.routerPath('deleteMessageForEveryone'), - ...guards, - async (req, res) => { - const response = await this.dataValidate({ - request: req, - schema: deleteMessageSchema, - ClassRef: DeleteMessage, - execute: (instance, data) => chatController.deleteMessage(instance, data), - }); - - return res.status(HttpStatus.CREATED).json(response); - }, - ) - .post(this.routerPath('fetchProfilePictureUrl'), ...guards, async (req, res) => { - const response = await this.dataValidate({ - request: req, - schema: profilePictureSchema, - ClassRef: NumberDto, - execute: (instance, data) => chatController.fetchProfilePicture(instance, data), - }); - - return res.status(HttpStatus.OK).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.OK).json(response); - }) - .post(this.routerPath('getBase64FromMediaMessage'), ...guards, async (req, res) => { - const response = await this.dataValidate({ - request: req, - schema: null, - ClassRef: Object, - execute: (instance, data) => - chatController.getBase64FromMediaMessage(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: messageValidateSchema, - ClassRef: MessageQuery, - execute: (instance, data) => chatController.fetchMessages(instance, data), - }); - - return res.status(HttpStatus.OK).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.OK).json(response); - }) - .get(this.routerPath('findChats'), ...guards, async (req, res) => { - const response = await this.dataValidate({ - request: req, - schema: null, - ClassRef: InstanceDto, - execute: (instance) => chatController.fetchChats(instance), - }); - - return res.status(HttpStatus.OK).json(response); - }); - } - - public readonly router = Router(); -} diff --git a/codechat/src/whatsapp/routers/group.router.ts b/codechat/src/whatsapp/routers/group.router.ts deleted file mode 100644 index 24db23d..0000000 --- a/codechat/src/whatsapp/routers/group.router.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { RequestHandler, Router } from 'express'; -import { - createGroupSchema, - groupJidSchema, - updateParticipantsSchema, - updateGroupPicture, -} from '../../validate/validate.schema'; -import { RouterBroker } from '../abstract/abstract.router'; -import { - CreateGroupDto, - GroupJid, - GroupPictureDto, - 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); - }) - .put(this.routerPath('updateGroupPicture'), ...guards, async (req, res) => { - const response = await this.groupValidate({ - request: req, - schema: updateGroupPicture, - ClassRef: GroupPictureDto, - execute: (instance, data) => groupController.updateGroupPicture(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.findParticipants(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: updateParticipantsSchema, - ClassRef: GroupUpdateParticipantDto, - execute: (instance, data) => groupController.updateGParticipate(instance, data), - }); - - res.status(HttpStatus.CREATED).json(response); - }) - .delete(this.routerPath('leaveGroup'), ...guards, async (req, res) => { - const response = await this.groupValidate({ - request: req, - schema: {}, - ClassRef: GroupJid, - execute: (instance, data) => groupController.leaveGroup(instance, data), - }); - - res.status(HttpStatus.OK).json(response); - }); - } - - public readonly router = Router(); -} diff --git a/codechat/src/whatsapp/routers/index.router.ts b/codechat/src/whatsapp/routers/index.router.ts deleted file mode 100644 index d16c644..0000000 --- a/codechat/src/whatsapp/routers/index.router.ts +++ /dev/null @@ -1,37 +0,0 @@ -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 { ViewsRouter } 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 ViewsRouter(configService, instanceExistsGuard).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/codechat/src/whatsapp/routers/instance.router.ts b/codechat/src/whatsapp/routers/instance.router.ts deleted file mode 100644 index 5dc1851..0000000 --- a/codechat/src/whatsapp/routers/instance.router.ts +++ /dev/null @@ -1,91 +0,0 @@ -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), - }); - - 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); - }) - .get(this.routerPath('fetchInstances', false), ...guards, async (req, res) => { - const response = await this.dataValidate({ - request: req, - schema: null, - ClassRef: InstanceDto, - execute: (instance) => instanceController.fetchInstances(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); - }) - .delete(this.routerPath('delete'), ...guards, async (req, res) => { - const response = await this.dataValidate({ - request: req, - schema: instanceNameSchema, - ClassRef: InstanceDto, - execute: (instance) => instanceController.deleteInstance(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), - }); - - return res.status(HttpStatus.CREATED).json(response); - }); - } - } - - public readonly router = Router(); -} diff --git a/codechat/src/whatsapp/routers/sendMessage.router.ts b/codechat/src/whatsapp/routers/sendMessage.router.ts deleted file mode 100644 index c3f8b71..0000000 --- a/codechat/src/whatsapp/routers/sendMessage.router.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { RequestHandler, Router } from 'express'; -import { - audioMessageSchema, - buttonMessageSchema, - contactMessageSchema, - listMessageSchema, - locationMessageSchema, - mediaMessageSchema, - reactionMessageSchema, - textMessageSchema, -} from '../../validate/validate.schema'; -import { - SendAudioDto, - 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('sendWhatsAppAudio'), ...guards, async (req, res) => { - const response = await this.dataValidate({ - request: req, - schema: audioMessageSchema, - ClassRef: SendMediaDto, - execute: (instance, data) => - sendMessageController.sendWhatsAppAudio(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/codechat/src/whatsapp/routers/view.router.ts b/codechat/src/whatsapp/routers/view.router.ts deleted file mode 100644 index 2f32cdc..0000000 --- a/codechat/src/whatsapp/routers/view.router.ts +++ /dev/null @@ -1,17 +0,0 @@ -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 ViewsRouter 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/codechat/src/whatsapp/routers/webhook.router.ts b/codechat/src/whatsapp/routers/webhook.router.ts deleted file mode 100644 index 70a65a0..0000000 --- a/codechat/src/whatsapp/routers/webhook.router.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { RequestHandler, Router } from 'express'; -import { instanceNameSchema, webhookSchema } from '../../validate/validate.schema'; -import { RouterBroker } from '../abstract/abstract.router'; -import { InstanceDto } from '../dto/instance.dto'; -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: instanceNameSchema, - ClassRef: InstanceDto, - execute: (instance) => webhookController.findWebhook(instance), - }); - - res.status(HttpStatus.OK).json(response); - }); - } - - public readonly router = Router(); -} diff --git a/codechat/src/whatsapp/services/auth.service.ts b/codechat/src/whatsapp/services/auth.service.ts deleted file mode 100644 index c8e3392..0000000 --- a/codechat/src/whatsapp/services/auth.service.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { Auth, ConfigService, Webhook } from '../../config/env.config'; -import { InstanceDto } from '../dto/instance.dto'; -import { name as apiName } from '../../../package.json'; -import { verify, sign } from 'jsonwebtoken'; -import { Logger } from '../../config/logger.config'; -import { v4 } from 'uuid'; -import { isJWT } from 'class-validator'; -import { BadRequestException } from '../../exceptions'; -import axios from 'axios'; -import { WAMonitoringService } from './monitor.service'; -import { RepositoryBroker } from '../repository/repository.manager'; - -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 waMonitor: WAMonitoringService, - private readonly repository: RepositoryBroker, - ) {} - - private readonly logger = new Logger(AuthService.name); - - private async 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' }, - ); - - const auth = await this.repository.auth.create({ jwt: token }, instance.instanceName); - - if (auth['error']) { - this.logger.error({ - localError: AuthService.name + '.jwt', - error: auth['error'], - }); - throw new BadRequestException('Authentication error', auth['error']?.toString()); - } - - return { jwt: token }; - } - - private async apikey(instance: InstanceDto) { - const apikey = v4().toUpperCase(); - - const auth = await this.repository.auth.create({ apikey }, instance.instanceName); - - if (auth['error']) { - this.logger.error({ - localError: AuthService.name + '.jwt', - error: auth['error'], - }); - throw new BadRequestException('Authentication error', auth['error']?.toString()); - } - - return { apikey }; - } - - public async generateHash(instance: InstanceDto) { - const options = this.configService.get('AUTHENTICATION'); - return (await this[options.TYPE](instance)) as { jwt: string } | { apikey: string }; - } - - public async 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 = await this.repository.auth.find(decode.instanceName); - - const decodeTokenStore = verify(tokenStore.jwt, jwtOpts.SECRET, { - ignoreExpiration: true, - }) as Pick; - - if (decode.tokenId !== decodeTokenStore.tokenId) { - throw new BadRequestException('Invalid "oldToken"'); - } - - const token = { - jwt: (await this.jwt({ instanceName: decode.instanceName })).jwt, - instanceName: decode.instanceName, - }; - - try { - const webhook = await this.repository.webhook.find(decode.instanceName); - if ( - webhook.enabled && - this.configService.get('WEBHOOK').EVENTS.NEW_JWT_TOKEN - ) { - const httpService = axios.create({ baseURL: webhook.url }); - await httpService.post( - '', - { - event: 'new.jwt', - instance: decode.instanceName, - data: token, - }, - { params: { owner: this.waMonitor.waInstances[decode.instanceName].wuid } }, - ); - } - } catch (error) { - this.logger.error(error); - } - - return token; - } catch (error) { - this.logger.error({ - localError: AuthService.name + '.refreshToken', - error, - }); - throw new BadRequestException('Invalid "oldToken"'); - } - } -} diff --git a/codechat/src/whatsapp/services/monitor.service.ts b/codechat/src/whatsapp/services/monitor.service.ts deleted file mode 100644 index 362f9e4..0000000 --- a/codechat/src/whatsapp/services/monitor.service.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { 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 { Logger } from '../../config/logger.config'; -import { ConfigService, Database, DelInstance, Redis } from '../../config/env.config'; -import { RepositoryBroker } from '../repository/repository.manager'; -import { NotFoundException } from '../../exceptions'; -import { Db } from 'mongodb'; -import { RedisCache } from '../../db/redis.client'; - -export class WAMonitoringService { - constructor( - private readonly eventEmitter: EventEmitter2, - private readonly configService: ConfigService, - private readonly repository: RepositoryBroker, - ) { - this.removeInstance(); - this.noConnection(); - this.delInstanceFiles(); - - Object.assign(this.db, configService.get('DATABASE')); - Object.assign(this.redis, configService.get('REDIS')); - - this.dbInstance = this.db.ENABLED - ? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances') - : null; - } - - private readonly db: Partial = {}; - private readonly redis: Partial = {}; - private dbInstance: Db; - - private readonly logger = new Logger(WAMonitoringService.name); - public readonly waInstances: Record = {}; - - public delInstanceTime(instance: string) { - const time = this.configService.get('DEL_INSTANCE'); - if (typeof time === 'number' && time > 0) { - setTimeout(() => { - if (this.waInstances[instance]?.connectionStatus?.state !== 'open') { - delete this.waInstances[instance]; - } - }, 1000 * 60 * time); - } - } - - public async instanceInfo(instanceName?: string) { - if (instanceName && !this.waInstances[instanceName]) { - throw new NotFoundException(`Instance "${instanceName}" not found`); - } - - const instances: any[] = []; - - for await (const [key, value] of Object.entries(this.waInstances)) { - if (value && value.connectionStatus.state === 'open') { - instances.push({ - instance: { - instanceName: key, - owner: value.wuid, - profileName: (await value.getProfileName()) || 'not loaded', - profilePictureUrl: value.profilePictureUrl, - }, - }); - } - } - - return instances.find((i) => i.instance.instanceName === instanceName) ?? instances; - } - - private delInstanceFiles() { - setInterval(async () => { - if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { - 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); - } - - private async cleaningUp(instanceName: string) { - if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { - await this.repository.dbServer.connect(); - const collections: any[] = await this.dbInstance.collections(); - if (collections.length > 0) { - await this.dbInstance.dropCollection(instanceName); - } - return; - } - - if (this.redis.ENABLED) { - const redisCache = new RedisCache(this.redis.URI, instanceName); - await redisCache.delAll(); - return; - } - rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); - } - - 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 && this.db.SAVE_DATA.INSTANCE) { - await this.repository.dbServer.connect(); - const collections: any[] = 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); - } - } - } catch (error) { - this.logger.error(error); - } - } - - private removeInstance() { - this.eventEmitter.on('remove.instance', async (instanceName: string) => { - try { - this.waInstances[instanceName] = undefined; - } catch {} - - try { - this.cleaningUp(instanceName); - } finally { - this.logger.warn(`Instance "${instanceName}" - REMOVED`); - } - }); - } - - private noConnection() { - this.eventEmitter.on('no.connection', async (instanceName) => { - try { - this.waInstances[instanceName] = undefined; - this.cleaningUp(instanceName); - } catch (error) { - this.logger.error({ - localError: 'noConnection', - warn: 'Error deleting instance from memory.', - error, - }); - } finally { - this.logger.warn(`Instance "${instanceName}" - NOT CONNECTION`); - } - }); - } -} diff --git a/codechat/src/whatsapp/services/webhook.service.ts b/codechat/src/whatsapp/services/webhook.service.ts deleted file mode 100644 index 99e9c50..0000000 --- a/codechat/src/whatsapp/services/webhook.service.ts +++ /dev/null @@ -1,21 +0,0 @@ -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) {} - - public create(instance: InstanceDto, data: WebhookDto) { - this.waMonitor.waInstances[instance.instanceName].setWebhook(data); - - return { webhook: { ...instance, webhook: data } }; - } - - public async find(instance: InstanceDto): Promise { - try { - return await this.waMonitor.waInstances[instance.instanceName].findWebhook(); - } catch (error) { - return { enabled: null, url: '' }; - } - } -} diff --git a/codechat/src/whatsapp/services/whatsapp.service.ts b/codechat/src/whatsapp/services/whatsapp.service.ts deleted file mode 100644 index 95bf7cb..0000000 --- a/codechat/src/whatsapp/services/whatsapp.service.ts +++ /dev/null @@ -1,1369 +0,0 @@ -import makeWASocket, { - AnyMessageContent, - BufferedEventData, - BufferJSON, - CacheStore, - Chat, - ConnectionState, - Contact, - delay, - DisconnectReason, - downloadMediaMessage, - fetchLatestBaileysVersion, - generateWAMessageFromContent, - getDevice, - GroupMetadata, - isJidGroup, - isJidUser, - MessageUpsertType, - ParticipantAction, - prepareWAMessageMedia, - proto, - useMultiFileAuthState, - UserFacingSocketConfig, - WABrowserDescription, - WAMediaUpload, - WAMessageUpdate, - WASocket, -} from '@adiwajshing/baileys'; -import { - ConfigService, - ConfigSessionPhone, - Database, - Env, - QrCode, - Redis, - StoreConf, - Webhook, -} from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { INSTANCE_DIR, ROOT_DIR } from '../../config/path.config'; -import { existsSync, readFileSync, writeFileSync } from 'fs'; -import { join } from 'path'; -import axios from 'axios'; -import { v4 } from 'uuid'; -import qrcode, { QRCodeToDataURLOptions } from 'qrcode'; -import qrcodeTerminal from 'qrcode-terminal'; -import { Events, TypeMediaMessage, wa, MessageSubtype } 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/repository.manager'; -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, - SendAudioDto, - SendButtonDto, - SendContactDto, - SendListDto, - SendLocationDto, - SendMediaDto, - SendReactionDto, - SendTextDto, -} from '../dto/sendMessage.dto'; -import { arrayUnique, isBase64, isURL } from 'class-validator'; -import { - ArchiveChatDto, - DeleteMessage, - 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, - GroupPictureDto, - GroupUpdateParticipantDto, -} from '../dto/group.dto'; -import { MessageUpQuery } from '../repository/messageUp.repository'; -import { useMultiFileAuthStateDb } from '../../utils/use-multi-file-auth-state-db'; -import Long from 'long'; -import { WebhookRaw } from '../models/webhook.model'; -import { dbserver } from '../../db/db.connect'; -import NodeCache from 'node-cache'; -import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db'; - -export class WAStartupService { - constructor( - private readonly configService: ConfigService, - private readonly eventEmitter: EventEmitter2, - private readonly repository: RepositoryBroker, - ) { - 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 stateConnection: wa.StateConnection = { state: 'close' }; - private readonly storePath = join(ROOT_DIR, 'store'); - private readonly msgRetryCounterCache: CacheStore = new NodeCache(); - private readonly userDevicesCache: CacheStore = new NodeCache(); - private endSession = false; - - public set instanceName(name: string) { - if (!name) { - this.instance.name = v4(); - return; - } - this.instance.name = name; - this.sendDataWebhook(Events.STATUS_INSTANCE, { - instance: this.instance.name, - status: 'created', - }); - } - - public get instanceName() { - return this.instance.name; - } - s; - public get wuid() { - return this.instance.wuid; - } - - public async getProfileName() { - let profileName = this.client.user?.name ?? this.client.user?.verifiedName; - if (!profileName) { - if (this.configService.get('DATABASE').ENABLED) { - const collection = dbserver - .getClient() - .db( - this.configService.get('DATABASE').CONNECTION.DB_PREFIX_NAME + - '-instances', - ) - .collection(this.instanceName); - const data = await collection.findOne({ _id: 'creds' }); - if (data) { - const creds = JSON.parse(JSON.stringify(data), BufferJSON.reviver); - profileName = creds.me?.name || creds.me?.verifiedName; - } - } else if (existsSync(join(INSTANCE_DIR, this.instanceName, 'creds.json'))) { - const creds = JSON.parse( - readFileSync(join(INSTANCE_DIR, this.instanceName, 'creds.json'), { - encoding: 'utf-8', - }), - ); - profileName = creds.me?.name || creds.me?.verifiedName; - } - } - return profileName; - } - - public get profilePictureUrl() { - return this.instance.profilePictureUrl; - } - - public get qrCode(): wa.QrCode { - return { - code: this.instance.qrcode?.code, - base64: this.instance.qrcode?.base64, - }; - } - - private async loadWebhook() { - const data = await this.repository.webhook.find(this.instanceName); - this.localWebhook.url = data?.url; - this.localWebhook.enabled = data?.enabled; - } - - public async setWebhook(data: WebhookRaw) { - await this.repository.webhook.create(data, this.instanceName); - Object.assign(this.localWebhook, data); - } - - public async findWebhook() { - return await this.repository.webhook.find(this.instanceName); - } - - private async sendDataWebhook(event: Events, data: T) { - const webhook = this.configService.get('WEBHOOK'); - const we = event.replace(/[\.-]/gm, '_').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, - instance: this.instance.name, - data, - }, - { params: { owner: this.instance.wuid } }, - ); - } - } catch (error) { - this.logger.error({ - local: WAStartupService.name + '.sendDataWebhook-local', - message: error?.message, - hostName: error?.hostname, - syscall: error?.syscall, - code: error?.code, - error: error?.errno, - stack: error?.stack, - name: error?.name, - }); - } - - try { - const globalWebhook = this.configService.get('WEBHOOK').GLOBAL; - if (globalWebhook && globalWebhook?.ENABLED && isURL(globalWebhook.URL)) { - const httpService = axios.create({ baseURL: globalWebhook.URL }); - await httpService.post( - '', - { - event, - instance: this.instance.name, - data, - }, - { params: { owner: this.instance.wuid } }, - ); - } - } catch (error) { - this.logger.error({ - local: WAStartupService.name + '.sendDataWebhook-global', - message: error?.message, - hostName: error?.hostname, - syscall: error?.syscall, - code: error?.code, - error: error?.errno, - stack: error?.stack, - name: error?.name, - }); - } - } - } - - private async connectionUpdate({ - qr, - connection, - lastDisconnect, - }: Partial) { - if (qr) { - if (this.instance.qrcode.count === this.configService.get('QRCODE').LIMIT) { - 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, - }); - - this.sendDataWebhook(Events.STATUS_INSTANCE, { - instance: this.instance.name, - status: 'removed', - }); - - this.endSession = true; - - 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' }, - }; - - qrcode.toDataURL(qr, optsQrcode, (error, base64) => { - if (error) { - this.logger.error('Qrcode generate failed:' + error.toString()); - return; - } - - this.instance.qrcode.base64 = base64; - this.instance.qrcode.code = qr; - - this.sendDataWebhook(Events.QRCODE_UPDATED, { - qrcode: { instance: this.instance.name, code: qr, base64 }, - }); - }); - - qrcodeTerminal.generate(qr, { small: true }, (qrcode) => - this.logger.log( - `\n{ instance: ${this.instance.name}, qrcodeCount: ${this.instance.qrcode.count} }\n` + - qrcode, - ), - ); - } - - if (connection) { - this.stateConnection = { - state: connection, - statusReason: (lastDisconnect?.error as Boom)?.output?.statusCode ?? 200, - }; - this.sendDataWebhook(Events.CONNECTION_UPDATE, { - instance: this.instance.name, - ...this.stateConnection, - }); - } - - if (connection === 'close') { - const shouldReconnect = - (lastDisconnect.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut; - if (shouldReconnect) { - await this.connectToWhatsapp(); - } else { - this.sendDataWebhook(Events.STATUS_INSTANCE, { - instance: this.instance.name, - status: 'removed', - }); - return this.eventEmitter.emit('remove.instance', this.instance.name, 'inner'); - } - } - - if (connection === 'open') { - this.instance.wuid = this.client.user.id.replace(/:\d+/, ''); - this.instance.profilePictureUrl = ( - await this.profilePicture(this.instance.wuid) - ).profilePictureUrl; - this.logger.info( - ` - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ CONNECTED TO WHATSAPP β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜`.replace(/^ +/gm, ' '), - ); - } - } - - private async getMessage(key: proto.IMessageKey, full = false) { - try { - const webMessageInfo = (await this.repository.message.find({ - where: { owner: this.instance.wuid, key: { id: key.id } }, - })) as unknown as proto.IWebMessageInfo[]; - if (full) { - return webMessageInfo[0]; - } - return webMessageInfo[0].message; - } catch (error) { - return { conversation: '' }; - } - } - - private cleanStore() { - const store = this.configService.get('STORE'); - const database = this.configService.get('DATABASE'); - if (store?.CLEANING_INTERVAL && !database.ENABLED) { - setInterval(() => { - try { - for (const [key, value] of Object.entries(store)) { - if (value === true) { - execSync( - `rm -rf ${join( - this.storePath, - key.toLowerCase(), - this.instance.wuid, - )}/*.json`, - ); - } - } - } catch (error) {} - }, (store?.CLEANING_INTERVAL ?? 3600) * 1000); - } - } - - public async connectToWhatsapp(): Promise { - this.loadWebhook(); - - const db = this.configService.get('DATABASE'); - const redis = this.configService.get('REDIS'); - - if (db.ENABLED && db.SAVE_DATA.INSTANCE) { - this.instance.authState = await useMultiFileAuthStateDb(this.instance.name); - } - - if (redis.ENABLED && !db.SAVE_DATA.INSTANCE) { - this.instance.authState = await useMultiFileAuthStateRedisDb( - redis.URI, - this.instance.name, - ); - } - - if (!redis.ENABLED) { - this.instance.authState = 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' }), - printQRInTerminal: false, - browser, - version, - connectTimeoutMs: 60_000, - qrTimeout: 10_000, - emitOwnEvents: false, - msgRetryCounterCache: this.msgRetryCounterCache, - getMessage: this.getMessage as any, - generateHighQualityLinkPreview: true, - syncFullHistory: true, - userDevicesCache: this.userDevicesCache, - transactionOpts: { maxCommitRetries: 1, delayBetweenTriesMs: 10 }, - patchMessageBeforeSending: (message) => { - const requiresPatch = !!(message.buttonsMessage || message.listMessage); - if (requiresPatch) { - message = { - editedMessage: { - message: { - messageContextInfo: { - deviceListMetadataVersion: 2, - deviceListMetadata: {}, - }, - ...message, - }, - }, - }; - } - - return message; - }, - }; - - this.endSession = false; - - this.client = makeWASocket(socketConfig); - - this.eventHandler(); - - return this.client; - } - - private readonly chatHandle = { - 'chats.upsert': async (chats: Chat[], database: Database) => { - const chatsRepository = await this.repository.chat.find({ - where: { owner: this.instance.wuid }, - }); - - const chatsRaw: ChatRaw[] = []; - for await (const chat of chats) { - if (chatsRepository.find((cr) => cr.id === chat.id)) { - continue; - } - - chatsRaw.push({ id: chat.id, owner: this.instance.wuid }); - } - - await this.sendDataWebhook(Events.CHATS_UPSERT, chatsRaw); - await this.repository.chat.insert(chatsRaw, database.SAVE_DATA.CHATS); - }, - - 'chats.update': async ( - chats: Partial< - proto.IConversation & { - lastMessageRecvTimestamp?: number; - } & { - conditional: (bufferedData: BufferedEventData) => boolean; - } - >[], - ) => { - const chatsRaw: ChatRaw[] = chats.map((chat) => { - return { id: chat.id, owner: this.instance.wuid }; - }); - await this.sendDataWebhook(Events.CHATS_UPDATE, chatsRaw); - }, - - 'chats.delete': async (chats: string[]) => { - chats.forEach( - async (chat) => - await this.repository.chat.delete({ - where: { owner: this.instance.wuid, id: chat }, - }), - ); - await this.sendDataWebhook(Events.CHATS_DELETE, [...chats]); - }, - }; - - private readonly contactHandle = { - 'contacts.upsert': async (contacts: Contact[], database: Database) => { - const contactsRepository = await this.repository.contact.find({ - where: { owner: this.instance.wuid }, - }); - - 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, - owner: this.instance.wuid, - }); - } - await this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw); - await this.repository.contact.insert(contactsRaw, database.SAVE_DATA.CONTACTS); - }, - - 'contacts.update': async (contacts: Partial[]) => { - 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, - owner: this.instance.wuid, - }); - } - await this.sendDataWebhook(Events.CONTACTS_UPDATE, contactsRaw); - }, - }; - - private readonly messageHandle = { - 'messaging-history.set': async ( - { - messages, - chats, - isLatest, - }: { - chats: Chat[]; - contacts: Contact[]; - messages: proto.IWebMessageInfo[]; - isLatest: boolean; - }, - database: Database, - ) => { - if (isLatest) { - const chatsRaw: ChatRaw[] = chats.map((chat) => { - return { - id: chat.id, - owner: this.instance.wuid, - }; - }); - await this.sendDataWebhook(Events.CHATS_SET, chatsRaw); - await this.repository.chat.insert(chatsRaw, database.SAVE_DATA.CHATS); - } - - const messagesRaw: MessageRaw[] = []; - const messagesRepository = await this.repository.message.find({ - where: { owner: this.instance.wuid }, - }); - for await (const [, m] of Object.entries(messages)) { - if ( - m.message?.protocolMessage || - m.message?.senderKeyDistributionMessage || - !m.message - ) { - continue; - } - if ( - messagesRepository.find( - (mr) => mr.owner === this.instance.wuid && mr.key.id === m.key.id, - ) - ) { - continue; - } - - if (Long.isLong(m?.messageTimestamp)) { - m.messageTimestamp = m.messageTimestamp?.toNumber(); - } - - messagesRaw.push({ - key: m.key, - pushName: m.pushName, - participant: m.participant, - message: { ...m.message }, - messageTimestamp: m.messageTimestamp as number, - owner: this.instance.wuid, - }); - } - - await this.repository.message.insert( - [...messagesRaw], - database.SAVE_DATA.OLD_MESSAGE, - ); - this.sendDataWebhook(Events.MESSAGES_SET, [...messagesRaw]); - messages = undefined; - }, - - 'messages.upsert': async ( - { - messages, - type, - }: { - messages: proto.IWebMessageInfo[]; - type: MessageUpsertType; - }, - database: Database, - ) => { - const received = messages[0]; - if ( - type !== 'notify' || - !received?.message || - received.message?.protocolMessage || - received.message.senderKeyDistributionMessage - ) { - return; - } - - if (Long.isLong(received.messageTimestamp)) { - received.messageTimestamp = received.messageTimestamp?.toNumber(); - } - - const messageRaw: MessageRaw = { - key: received.key, - pushName: received.pushName, - message: { ...received.message }, - messageTimestamp: received.messageTimestamp as number, - owner: this.instance.wuid, - 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); - }, - - 'messages.update': async (args: WAMessageUpdate[], database: Database) => { - 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(), - owner: this.instance.wuid, - }; - await this.sendDataWebhook(Events.MESSAGES_UPDATE, message); - await this.repository.messageUpdate.insert( - [message], - database.SAVE_DATA.MESSAGE_UPDATE, - ); - } - } - }, - }; - - private readonly groupHandler = { - 'groups.upsert': (groupMetadata: GroupMetadata[]) => { - this.sendDataWebhook(Events.GROUPS_UPSERT, groupMetadata); - }, - - 'groups.update': (groupMetadataUpdate: Partial[]) => { - this.sendDataWebhook(Events.GROUPS_UPDATE, groupMetadataUpdate); - }, - - 'group-participants.update': (participantsUpdate: { - id: string; - participants: string[]; - action: ParticipantAction; - }) => { - this.sendDataWebhook(Events.GROUP_PARTICIPANTS_UPDATE, participantsUpdate); - }, - }; - - private eventHandler() { - this.client.ev.process((events) => { - if (!this.endSession) { - const database = this.configService.get('DATABASE'); - - if (events['connection.update']) { - this.connectionUpdate(events['connection.update']); - } - - if (events['creds.update']) { - this.instance.authState.saveCreds(); - } - - if (events['messaging-history.set']) { - const payload = events['messaging-history.set']; - this.messageHandle['messaging-history.set'](payload, database); - } - - if (events['messages.upsert']) { - const payload = events['messages.upsert']; - this.messageHandle['messages.upsert'](payload, database); - } - - if (events['messages.update']) { - const payload = events['messages.update']; - this.messageHandle['messages.update'](payload, database); - } - - if (events['presence.update']) { - const payload = events['presence.update']; - this.sendDataWebhook(Events.PRESENCE_UPDATE, payload); - } - - if (events['groups.upsert']) { - const payload = events['groups.upsert']; - this.groupHandler['groups.upsert'](payload); - } - - if (events['groups.update']) { - const payload = events['groups.update']; - this.groupHandler['groups.update'](payload); - } - - if (events['group-participants.update']) { - const payload = events['group-participants.update']; - this.groupHandler['group-participants.update'](payload); - } - - if (events['chats.upsert']) { - const payload = events['chats.upsert']; - this.chatHandle['chats.upsert'](payload, database); - } - - if (events['chats.update']) { - const payload = events['chats.update']; - this.chatHandle['chats.update'](payload); - } - - if (events['chats.delete']) { - const payload = events['chats.delete']; - this.chatHandle['chats.delete'](payload); - } - - if (events['contacts.upsert']) { - const payload = events['contacts.upsert']; - this.contactHandle['contacts.upsert'](payload, database); - } - - if (events['contacts.update']) { - const payload = events['contacts.update']; - this.contactHandle['contacts.update'](payload); - } - } - }); - } - - private createJid(number: string) { - if (number.includes('@g.us') || number.includes('@s.whatsapp.net')) { - return number; - } - 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') { - const joker = Number.parseInt(match[3][0]); - const ddd = Number.parseInt(match[2]); - if (joker < 7 || ddd < 31) { - return match[0]; - } - 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: T, - options?: Options, - ) { - const jid = this.createJid(number); - const isWA = (await this.whatsappNumber({ numbers: [jid] }))[0]; - if (!isWA.exists && !isJidGroup(isWA.jid)) { - throw new BadRequestException(isWA); - } - - const sender = isJidGroup(jid) ? jid : isWA.jid; - - if (isJidGroup(sender)) { - try { - await this.client.groupMetadata(sender); - } catch (error) { - throw new NotFoundException('Group not found'); - } - } - - try { - if (options?.delay) { - await this.client.presenceSubscribe(sender); - await this.client.sendPresenceUpdate(options?.presence ?? 'composing', jid); - await delay(options.delay); - await this.client.sendPresenceUpdate('paused', sender); - } - - const messageSent = await (async () => { - if (!message['audio']) { - return await this.client.sendMessage(sender, { - forward: { - key: { remoteJid: this.instance.wuid, fromMe: true }, - message, - }, - }); - } - - return await this.client.sendMessage( - sender, - message as unknown as AnyMessageContent, - ); - })(); - - this.sendDataWebhook(Events.SEND_MESSAGE, messageSent).catch((error) => - this.logger.error(error), - ); - this.repository.message - .insert( - [{ ...messageSent, owner: this.instance.wuid }], - this.configService.get('DATABASE').SAVE_DATA.NEW_MESSAGE, - ) - .catch((error) => this.logger.error(error)); - - return messageSent; - } catch (error) { - this.logger.error(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) { - try { - const prepareMedia = await prepareWAMessageMedia( - { - [mediaMessage.mediatype]: isURL(mediaMessage.media) - ? { url: mediaMessage.media } - : Buffer.from(mediaMessage.media, 'base64'), - } as any, - { upload: this.client.waUploadToServer }, - ); - - const mediaType = mediaMessage.mediatype + 'Message'; - - if (mediaMessage.mediatype === 'document' && !mediaMessage.fileName) { - const regex = new RegExp(/.*\/(.+?)\./); - const arrayMatch = regex.exec(mediaMessage.media); - mediaMessage.fileName = arrayMatch[1]; - } - - let mimetype: string; - - if (isURL(mediaMessage.media)) { - mimetype = getMIMEType(mediaMessage.media); - } else { - mimetype = getMIMEType(mediaMessage.fileName); - } - - prepareMedia[mediaType].caption = mediaMessage?.caption; - prepareMedia[mediaType].mimetype = mimetype; - prepareMedia[mediaType].fileName = mediaMessage.fileName; - - if (mediaMessage.mediatype === 'video') { - prepareMedia[mediaType].jpegThumbnail = Uint8Array.from( - readFileSync(join(process.cwd(), 'public', 'images', 'video-cover.png')), - ); - prepareMedia[mediaType].gifPlayback = false; - } - - return generateWAMessageFromContent( - '', - { [mediaType]: { ...prepareMedia[mediaType] } }, - { userJid: this.instance.wuid }, - ); - } catch (error) { - this.logger.error(error); - throw new InternalServerErrorException(error?.toString() || error); - } - } - - public async mediaMessage(data: SendMediaDto) { - const generate = await this.prepareMediaMessage(data.mediaMessage); - - return await this.sendMessageWithTyping( - data.number, - { ...generate.message }, - data?.options, - ); - } - - public async audioWhatsapp(data: SendAudioDto) { - return this.sendMessageWithTyping( - data.number, - { - audio: isURL(data.audioMessage.audio) - ? { url: data.audioMessage.audio } - : Buffer.from(data.audioMessage.audio, 'base64'), - ptt: true, - mimetype: 'audio/ogg; codecs=opus', - }, - { presence: 'recording', delay: data?.options?.delay }, - ); - } - - public async buttonMessage(data: SendButtonDto) { - const embeddedMedia: any = {}; - let mediatype = 'TEXT'; - - if (data.buttonMessage?.mediaMessage) { - mediatype = data.buttonMessage.mediaMessage?.mediatype.toUpperCase() ?? 'TEXT'; - embeddedMedia.mediaKey = mediatype.toLowerCase() + 'Message'; - const generate = await this.prepareMediaMessage(data.buttonMessage.mediaMessage); - embeddedMedia.message = generate.message[embeddedMedia.mediaKey]; - embeddedMedia.contentText = `*${data.buttonMessage.title}*\n\n${data.buttonMessage.description}`; - } - - const btnItems = { - text: data.buttonMessage.buttons.map((btn) => btn.buttonText), - ids: data.buttonMessage.buttons.map((btn) => btn.buttonId), - }; - - if (!arrayUnique(btnItems.text) || !arrayUnique(btnItems.ids)) { - throw new BadRequestException( - 'Button texts cannot be repeated', - 'Button IDs cannot be repeated.', - ); - } - - return await this.sendMessageWithTyping( - data.number, - { - buttonsMessage: { - text: !embeddedMedia?.mediaKey ? data.buttonMessage.title : undefined, - contentText: embeddedMedia?.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], - [embeddedMedia?.mediaKey]: embeddedMedia?.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 message: proto.IMessage = {}; - - const vcard = (contact: ContactMessage) => { - return ( - 'BEGIN:VCARD\n' + - 'VERSION:3.0\n' + - 'FN:' + - contact.fullName + - '\n' + - 'item1.TEL;waid=' + - contact.wuid + - ':' + - contact.phoneNumber + - '\n' + - 'item1.X-ABLabel:Celular\n' + - 'END:VCARD' - ); - }; - - if (data.contactMessage.length === 1) { - message.contactMessage = { - displayName: data.contactMessage[0].fullName, - vcard: vcard(data.contactMessage[0]), - }; - } else { - message.contactsArrayMessage = { - displayName: `${data.contactMessage.length} contacts`, - contacts: data.contactMessage.map((contact) => { - return { - displayName: contact.fullName, - vcard: vcard(contact), - }; - }), - }; - } - - return await this.sendMessageWithTyping(data.number, { ...message }, 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); - if (isJidGroup(jid)) { - const group = await this.findGroup({ groupJid: jid }, 'inner'); - onWhatsapp.push(new OnWhatsAppDto(group.id, !!group?.id, group?.subject)); - } else { - try { - const result = (await this.client.onWhatsApp(jid))[0]; - onWhatsapp.push(new OnWhatsAppDto(result.jid, result.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 deleteMessage(del: DeleteMessage) { - try { - return await this.client.sendMessage(del.remoteJid, { delete: del }); - } catch (error) { - throw new InternalServerErrorException( - 'Error while deleting message for everyone', - error?.toString(), - ); - } - } - - public async getBase64FromMediaMessage(m: proto.IWebMessageInfo) { - try { - const msg = m?.message - ? m - : ((await this.getMessage(m.key, true)) as proto.IWebMessageInfo); - - for (const subtype of MessageSubtype) { - if (msg.message[subtype]) { - msg.message = msg.message[subtype].message; - } - } - - let mediaMessage: any; - let mediaType: string; - - for (const type of TypeMediaMessage) { - mediaMessage = msg.message[type]; - if (mediaMessage) { - mediaType = type; - break; - } - } - - if (!mediaMessage) { - throw 'The message is not of the media type'; - } - - if (typeof mediaMessage['mediaKey'] === 'object') { - msg.message = JSON.parse(JSON.stringify(msg.message)); - } - - const buffer = await downloadMediaMessage( - { key: msg?.key, message: msg?.message }, - 'buffer', - {}, - { - logger: P({ level: 'error' }), - reuploadRequest: this.client.updateMediaMessage, - }, - ); - - return { - mediaType, - fileName: mediaMessage['fileName'], - caption: mediaMessage['caption'], - size: { - fileLength: mediaMessage['fileLength'], - height: mediaMessage['height'], - width: mediaMessage['width'], - }, - mimetype: mediaMessage['mimetype'], - base64: buffer.toString('base64'), - }; - } catch (error) { - this.logger.error(error); - throw new BadRequestException(error.toString()); - } - } - - public async fetchContacts(query: ContactQuery) { - if (query?.where) { - query.where.owner = this.instance.wuid; - } else { - query = { - where: { - owner: this.instance.wuid, - }, - }; - } - return await this.repository.contact.find(query); - } - - public async fetchMessages(query: MessageQuery) { - if (query?.where) { - query.where.owner = this.instance.wuid; - } else { - query = { - where: { - owner: this.instance.wuid, - }, - limit: query?.limit, - }; - } - return await this.repository.message.find(query); - } - - public async fetchStatusMessage(query: MessageUpQuery) { - if (query?.where) { - query.where.owner = this.instance.wuid; - } else { - query = { - where: { - owner: this.instance.wuid, - }, - limit: query?.limit, - }; - } - return await this.repository.messageUpdate.find(query); - } - - public async fetchChats() { - return await this.repository.chat.find({ where: { owner: this.instance.wuid } }); - } - - // 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); - } - - const group = await this.client.groupMetadata(id); - - return { groupMetadata: group }; - } catch (error) { - this.logger.error(error); - throw new InternalServerErrorException('Error creating group', error.toString()); - } - } - - public async updateGroupPicture(picture: GroupPictureDto) { - try { - let pic: WAMediaUpload; - if (isURL(picture.image)) { - pic = (await axios.get(picture.image, { responseType: 'arraybuffer' })).data; - } else if (isBase64(picture.image)) { - pic = Buffer.from(picture.image, 'base64'); - } else { - throw new BadRequestException('"profilePicture" must be a url or a base64'); - } - await this.client.updateProfilePicture(picture.groupJid, pic); - - return { update: 'success' }; - } catch (error) { - throw new InternalServerErrorException('Error creating group', error.toString()); - } - } - - public async findGroup(id: GroupJid, reply: 'inner' | 'out' = 'out') { - try { - return await this.client.groupMetadata(id.groupJid); - } catch (error) { - if (reply === 'inner') { - return; - } - throw new NotFoundException('Error fetching group', error.toString()); - } - } - - public async inviteCode(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.participants.map((p) => this.createJid(p)); - const updateParticipants = await this.client.groupParticipantsUpdate( - update.groupJid, - participants, - update.action, - ); - return { updateParticipants: updateParticipants }; - } 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/codechat/src/whatsapp/types/wa.types.ts b/codechat/src/whatsapp/types/wa.types.ts deleted file mode 100644 index e277bb9..0000000 --- a/codechat/src/whatsapp/types/wa.types.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* eslint-disable @typescript-eslint/no-namespace */ -import { AuthenticationState, WAConnectionState } from '@adiwajshing/baileys'; - -export enum Events { - QRCODE_UPDATED = 'qrcode.updated', - CONNECTION_UPDATE = 'connection.update', - STATUS_INSTANCE = 'status.instance', - MESSAGES_SET = 'messages.set', - MESSAGES_UPSERT = 'messages.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', - CHATS_DELETE = 'chats.delete', - GROUPS_UPSERT = 'groups.upsert', - GROUPS_UPDATE = 'groups.update', - GROUP_PARTICIPANTS_UPDATE = 'group-participants.update', -} - -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; - profileName?: string; - profilePictureUrl?: 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'; -} - -export const TypeMediaMessage = [ - 'imageMessage', - 'documentMessage', - 'audioMessage', - 'videoMessage', - 'stickerMessage', -]; - -export const MessageSubtype = [ - 'ephemeralMessage', - 'documentWithCaptionMessage', - 'viewOnceMessage', - 'viewOnceMessageV2', -]; diff --git a/codechat/src/whatsapp/whatsapp.module.ts b/codechat/src/whatsapp/whatsapp.module.ts deleted file mode 100644 index c11a801..0000000 --- a/codechat/src/whatsapp/whatsapp.module.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { configService } from '../config/env.config'; -import { Logger } from '../config/logger.config'; -import { eventEmitter } from '../config/event.config'; -import { MessageRepository } from './repository/message.repository'; -import { WAMonitoringService } from './services/monitor.service'; -import { ChatRepository } from './repository/chat.repository'; -import { ContactRepository } from './repository/contact.repository'; -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'; -import { RepositoryBroker } from './repository/repository.manager'; -import { - AuthModel, - ChatModel, - ContactModel, - MessageModel, - MessageUpModel, -} from './models'; -import { dbserver } from '../db/db.connect'; -import { WebhookRepository } from './repository/webhook.repository'; -import { WebhookModel } from './models/webhook.model'; -import { AuthRepository } from './repository/auth.repository'; - -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, configService); -const webhookRepository = new WebhookRepository(WebhookModel, configService); -const authRepository = new AuthRepository(AuthModel, configService); - -export const repository = new RepositoryBroker( - messageRepository, - chatRepository, - contactRepository, - messageUpdateRepository, - webhookRepository, - authRepository, - dbserver?.getClient(), -); - -export const waMonitor = new WAMonitoringService(eventEmitter, configService, repository); - -const authService = new AuthService(configService, waMonitor, repository); - -const webhookService = new WebhookService(waMonitor); - -export const webhookController = new WebhookController(webhookService); - -export const instanceController = new InstanceController( - waMonitor, - configService, - repository, - eventEmitter, - 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/codechat/start.sh b/codechat/start.sh deleted file mode 100644 index e756b70..0000000 --- a/codechat/start.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh - -if [ "$DOCKER_ENV" = "true" ]; -then - echo "Enabling environment variables for Docker" - echo "DOCKER_ENV=$DOCKER_ENV" - echo -else - cp ./src/env.yml ./dist/src -fi -echo "> removing dist" -rm -rf ./dist -echo -echo "> transpiling..." -npm run build - -echo -echo "> Successfully build " - -echo -echo "> Starting application..." -echo - -node ./dist/src/main.js diff --git a/codechat/tsconfig.json b/codechat/tsconfig.json deleted file mode 100644 index 4183b6f..0000000 --- a/codechat/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "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/codechat/views/qrcode.hbs b/codechat/views/qrcode.hbs deleted file mode 100644 index 2c8289a..0000000 --- a/codechat/views/qrcode.hbs +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - Generate QRCode - - - - -
- -
-

Connect to whatsapp

-
- - - -
-
-
- -
- -
- - - - - - \ No newline at end of file diff --git a/conector/.env b/conector/.env index 6524db4..2d74851 100644 --- a/conector/.env +++ b/conector/.env @@ -3,7 +3,7 @@ CHATWOOT_ACCOUNT_ID = 1 CHATWOOT_TOKEN = djM2mKUyBquXtDWGfo65Kjup CHATWOOT_BASE_URL = https://chatwoot.kabaweb.in -CODECHAT_BASE_URL = http://172.19.0.4:8083 +CODECHAT_BASE_URL = http://172.19.0.103:8083 CODECHAT_API_KEY = t8OOEeISKzpmc3jjcMqBWYSaJsafdefer # SE DESEJA ASSINAR O A MENSAGEM COM O NOME DO USUÁRIO MUDE PARA true diff --git a/conector/docker-compose.yaml b/conector/docker-compose.yaml index 2634eb8..7401e8d 100644 --- a/conector/docker-compose.yaml +++ b/conector/docker-compose.yaml @@ -14,7 +14,7 @@ services: container_name: conector networks: kabacorp: - ipv4_address: 172.19.0.2 + ipv4_address: 172.19.0.103 tty: true ports: - "1234:1234" \ No newline at end of file diff --git a/mongodb/docker-compose.yaml b/mongodb/docker-compose.yaml index 837c234..f388f67 100644 --- a/mongodb/docker-compose.yaml +++ b/mongodb/docker-compose.yaml @@ -16,12 +16,12 @@ services: # sudo mkdir -p /data/mongodb - ./data/mongodb:/data/db ports: - - 27017:27017 + - 27027:27017 environment: MONGO_INITDB_ROOT_USERNAME: root # Set a password to access the bank MONGO_INITDB_ROOT_PASSWORD: kabacorp networks: kabacorp: - ipv4_address: 172.19.0.5 + ipv4_address: 172.19.0.100 tty: true \ No newline at end of file diff --git a/mongoexpress/docker-compose.yaml b/mongoexpress/docker-compose.yaml index 919ffa5..a85fb55 100644 --- a/mongoexpress/docker-compose.yaml +++ b/mongoexpress/docker-compose.yaml @@ -12,12 +12,12 @@ services: container_name: mongo-express networks: kabacorp: - ipv4_address: 172.19.0.6 + ipv4_address: 172.19.0.101 tty: true ports: - - 8081:8081 + - 8091:8081 environment: ME_CONFIG_BASICAUTH_USERNAME: "edcarlos@kabaweb.in" ME_CONFIG_BASICAUTH_PASSWORD: "Senha" ME_CONFIG_MONGODB_PORT: "27017" - ME_CONFIG_MONGODB_URL: "mongodb://root:kabacorp@172.19.0.5:27017" \ No newline at end of file + ME_CONFIG_MONGODB_URL: "mongodb://root:kabacorp@172.19.0.100:27017" \ No newline at end of file diff --git a/codechat/src/.env b/util/codechat.env similarity index 90% rename from codechat/src/.env rename to util/codechat.env index eab42ee..23e322f 100644 --- a/codechat/src/.env +++ b/util/codechat.env @@ -19,7 +19,7 @@ STORE_CHATS=false # Permanent data storage DATABASE_ENABLED=true -DATABASE_CONNECTION_URI='mongodb://root:kabacorp@172.19.0.5:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true' +DATABASE_CONNECTION_URI='mongodb://root:kabacorp@172.19.0.100:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true' DATABASE_CONNECTION_DB_PREFIX_NAME='codechat' DATABASE_SAVE_DATA_INSTANCE=false DATABASE_SAVE_DATA_OLD_MESSAGE=false @@ -33,7 +33,7 @@ REDIS_URI='' # Webhook Settings ## Define a global webhook that will listen for enabled events from all instances -WEBHOOK_GLOBAL_URL=http://172.19.0.2:1234/webhook/codechat +WEBHOOK_GLOBAL_URL=http://172.19.0.103:1234/webhook/codechat WEBHOOK_GLOBAL_ENABLED=true ## Set the events you want to hear WEBHOOK_EVENTS_STATUS_INSTANCE=true @@ -69,4 +69,4 @@ AUTHENTICATION_TYPE='jwt' # or 'apikey' AUTHENTICATION_API_KEY='t8OOEeISKzpmc3jjcMqBWYSaJsafdefer' ## Set the secret key to encrypt and decrypt your token and its expiration time AUTHENTICATION_JWT_EXPIRIN_IN=0 # seconds - 3600s ===1h | zero (0) - never expires -AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG' +AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG' \ No newline at end of file diff --git a/codechat/docker-compose.yml b/util/docker-compose-codechat.yml similarity index 92% rename from codechat/docker-compose.yml rename to util/docker-compose-codechat.yml index be17239..64191fe 100644 --- a/codechat/docker-compose.yml +++ b/util/docker-compose-codechat.yml @@ -15,7 +15,7 @@ services: container_name: codechat networks: kabacorp: - ipv4_address: 172.19.0.4 + ipv4_address: 172.19.0.102 tty: true env_file: - ./src/.env