├── .env ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .idx └── dev.nix ├── .replit ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── config.js ├── index.js ├── lib ├── database.js ├── function.js ├── logs.js ├── schema.js ├── scraper.js ├── serialize.js ├── sticker.js └── system │ ├── event.js │ ├── group-participants.js │ ├── group-update.js │ └── message.js ├── main.js ├── package.json └── replit.nix /.env: -------------------------------------------------------------------------------- 1 | ## Apikey https://api.alyachan.dev 2 | API_ENDPOINT = 'https://api.alyachan.dev/' 3 | API_KEY = '' -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v3 27 | 28 | # Runs a single command using the runners shell 29 | - name: Run a one-line script 30 | run: echo Hello, world! 31 | 32 | # Runs a set of commands using the runners shell 33 | - name: Run a multi-line script 34 | run: | 35 | echo Add other actions to build, 36 | echo test, and deploy your project. 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | session/ 2 | database.json 3 | package-lock.json 4 | yarn.lock 5 | node_modules 6 | temp 7 | database.json -------------------------------------------------------------------------------- /.idx/dev.nix: -------------------------------------------------------------------------------- 1 | # To learn more about how to use Nix to configure your environment 2 | # see: https://developers.google.com/idx/guides/customize-idx-env 3 | { pkgs, ... }: { 4 | # Which nixpkgs channel to use. 5 | channel = "stable-24.11"; # or "unstable" 6 | 7 | # Use https://search.nixos.org/packages to find packages 8 | packages = [ 9 | pkgs.nodejs_20 10 | pkgs.neofetch 11 | pkgs.jellyfin-ffmpeg 12 | pkgs.imagemagick 13 | pkgs.libwebp 14 | pkgs.yarn 15 | pkgs.libuuid 16 | ]; 17 | 18 | # Sets environment variables in the workspace 19 | env = { 20 | LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ 21 | pkgs.libuuid 22 | ]; 23 | }; 24 | idx = { 25 | # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" 26 | extensions = [ 27 | # "vscodevim.vim" 28 | ]; 29 | 30 | # Enable previews 31 | previews = { 32 | enable = true; 33 | previews = { 34 | # web = { 35 | # # Example: run "npm run dev" with PORT set to IDX's defined port for previews, 36 | # # and show it in IDX's web preview panel 37 | # command = ["npm" "run" "dev"]; 38 | # manager = "web"; 39 | # env = { 40 | # # Environment variables to set for your server 41 | # PORT = "$PORT"; 42 | # }; 43 | # }; 44 | }; 45 | }; 46 | 47 | # Workspace lifecycle hooks 48 | workspace = { 49 | # Runs when a workspace is first created 50 | onCreate = { 51 | # Example: install JS dependencies from NPM 52 | # npm-install = "npm install"; 53 | }; 54 | # Runs when the workspace is (re)started 55 | onStart = { 56 | # Example: start a background task to watch and re-build backend code 57 | # watch-backend = "npm run watch-backend"; 58 | }; 59 | }; 60 | }; 61 | } -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | run = "clear && node ." 2 | # run = "clear && pm2 kill && pm2 start index.js && pm2 save && pm2 logs" 3 | 4 | [nix] 5 | channel = "stable-24_11" 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IDX.aI.enableInlineCompletion": true, 3 | "IDX.aI.enableCodebaseIndexing": true 4 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | Based WhatsApp Bot that uses the whatsapp-web.js Library 635 | Copyright (C) 2023 Dika Ardnt. 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | hisoka-baileys Copyright (C) 2023 Dika Ardnt. 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 | [![Telegram](https://img.shields.io/badge/Telegram-2CA5E0)](https://t.me/znanx) 7 | [![WhatsApp](https://img.shields.io/badge/WhatsApp-25D366)](https://wa.me/6281252848955) 8 | [![Instagram](https://img.shields.io/badge/Instagram-%23E4405F.svg)](https://instagram.com/naando.io) 9 | [![License](https://img.shields.io/badge/license-GPL--3.0-orange)](./LICENSE) 10 | [![Support](https://img.shields.io/badge/Buy%20me%20coffe-sawer-blue)](https://saweria.co/Nando35) 11 | 12 |
13 | 14 | ## MOON CASE VERSION 15 | 16 | This code is an implementation of [Baileys](https://github.com/WhiskeySockets/Baileys). 17 | Use this script wisely, use your head in utilizing existing technology
18 | 19 | 20 |
Windows 21 | Requirements: 22 | * Git [`Click here`](https://git-scm.com/downloads) 23 | * NodeJS [`Click here`](https://nodejs.org/en/download) 24 | * FFmpeg [`Click here`](https://ffmpeg.org/download.html) 25 | * Speedtest by Okla 26 | 27 | ```bash 28 | Add to PATH environment variable 29 | ``` 30 |
31 | 32 |
Termux 33 | ```sh 34 | apt update && apt upgrade -y 35 | ``` 36 | ```sh 37 | apt install nodejs git ffmpeg wget curl zip -y 38 | ``` 39 | 40 | ### Speedtest Install 41 | ```sh 42 | curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash && apt install speedtest -y 43 | ``` 44 | 45 | ### Nvm installation 46 | 47 | ```sh 48 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash 49 | ``` 50 | 51 | ```sh 52 | wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash 53 | ``` 54 | 55 | ```sh 56 | source ~/.bashrc 57 | ``` 58 |
59 | 60 |
Ubuntu 61 | ```sh 62 | sudo apt update -y && sudo apt upgrade -y 63 | ``` 64 | ```sh 65 | sudo apt install nodejs git ffmpeg wget curl zip -y 66 | ``` 67 | 68 | ### Speedtest Install 69 | ```sh 70 | curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | sudo bash && apt install speedtest -y 71 | ``` 72 | 73 | ### Nvm installation 74 | 75 | ```sh 76 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash 77 | ``` 78 | 79 | ```sh 80 | wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash 81 | ``` 82 | 83 | ```sh 84 | source ~/.bashrc 85 | ``` 86 | 87 | ### Chromium Installation 88 | 89 | ```sh 90 | wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 91 | ``` 92 | ```sh 93 | sudo dpkg -i google-chrome-stable_current_amd64.deb 94 | ``` 95 | ```sh 96 | sudo apt --fix-broken install -y 97 | ``` 98 | 99 | > 100 | > After finishing, restart the terminal to load the new information. 101 | > 102 | 103 | ### Nodejs installation 104 | 105 | ```sh 106 | nvm install node 107 | ``` 108 |
109 | 110 | 111 | ### pm2 installation 112 | ```sh 113 | npm i -g pm2 114 | ``` 115 | 116 | ```sh 117 | node --version 118 | ``` 119 | 120 | ## Start Script 121 | 122 | Cloning the Repository 123 | ``` 124 | git clone https://github.com/rifnd/moon-case.git 125 | ``` 126 | 127 | Go to the project directory and install all dependencies.
128 | ```sh 129 | cd moon-case 130 | 131 | npm i 132 | ``` 133 | 134 | Finally, run the command below to start : 135 | ```sh 136 | # non pm2 137 | npm start 138 | 139 | # with pm2 140 | npm run dev 141 | ``` 142 | 143 | ## Login With Pairing Code 144 | or type your number in [`here`](https://github.com/rifnd/moon-case/blob/master/config.js#L50) 145 | ```sh 146 | node index.js --pairing 147 | ``` 148 | 149 | ### Note 150 | 151 | This code is in no way affiliated with WhatsApp. Use at your own discretion. Don't spam this. 152 | 153 | This code was produced based on the baileys library and it is still under development. 154 | 155 | # Donate to the project. 156 | * [`Saweria`](https://saweria.co/Nando35) 157 | 158 |
-------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | import Func from './lib/function.js' 2 | import { 3 | fileURLToPath 4 | } from 'url' 5 | 6 | /** write down your menu here */ 7 | const menu = { 8 | admin: ['hidetag', 'add', 'welcome', 'leaving', 'setpp', 'setname', 'tagall', 'kick', 'promote', 'demote'], 9 | converter: ['sticker', 'toimg', 'togif', 'qc', 'ttp', 'attp', 'emojimix'], 10 | downloader: ['tiktok', 'tikwm', 'tikmp3', 'facebook', 'instagram', 'igstory', 'twitter', 'threads', 'play', 'ytmp3', 'ytmp4', 'capcut', 'capcutwm', 'cocofun', 'douyin', 'douyinwm', 'douyinmp3', 'likee', 'likeewm', 'pindl'], 11 | effect: ['paretro', 'retrolga', 'plumy', 'hdr', 'sepia', 'duotone', 'blackwhite', 'sketch', 'sketchrill', 'oils', 'esragan', 'watercolor', 'galaxy', 'freplace', 'rainbow', 'solarize', 'pinkbir'], 12 | fun: ['apakah', 'siapakah', 'kapankah', 'rate', 'benarkah', 'bisakah'], 13 | group: ['afk', 'linkgroup', 'delete', 'ava', 'quoted', 'rvo'], 14 | internet: ['ytsearch', 'ai', 'aiimg', 'aiarticle', 'bard', 'bing', 'bingimg', 'blackbox', 'aicode', 'gemini', 'waifudiff', 'brainly', 'pinterest', 'google', 'gimage', 'kbbg'], 15 | miscs: ['speed', 'owner', 'sc', 'ping', 'checkapi'], 16 | owner: ['eval', 'exec', 'mute', 'public', 'setpp', 'setname', 'unblock', 'block', 'setcover', 'autoread', 'setlink'], 17 | 'text maker': ['comicbox', 'gradientshadow', 'lava', 'thunder', 'neondevil', 'sumertimes', 'matrix', 'firework', 'neonlight', 'greenneon', 'pokemon', 'dragonball', 'naruto', 'blackpink', 'onglass', 'greenbrush', 'amongus', 'naruto2', 'flaming', 'woodblock'], 18 | tools: ['remini', 'recolor', 'ocr', 'calc', 'cekresi', 'ss', 'ssweb', 'shortlink', 'translate', 'tts', 'text2img', 'transcibe', 'nulis', 'removebg', 'toanime', 'tozombie', 'turnme', 'gta5style'], 19 | 'voice changer': ['bass', 'blown', 'deep', 'earrape', 'fast', 'fat', 'nightcore', 'reverse', 'robot', 'slow', 'smooth', 'tupai'] 20 | } 21 | 22 | const limit = { 23 | free: 15, 24 | premium: 150, 25 | VIP: 'Infinity', 26 | download: { 27 | free: 50000000, // use byte 28 | premium: 350000000, // use byte 29 | VIP: 1130000000, // use byte 30 | } 31 | } 32 | 33 | export default { 34 | menu, 35 | limit, 36 | /** change config here */ 37 | options: { 38 | database: 'database.json', /** End .json when using JSON database or use Mongo URI */ 39 | owner: ['6285179886349'], 40 | evaluate_chars: ['=>', '>', '$', '~>', '!', '+', '/', '#', '.'], 41 | sessionName: 'session', 42 | prefix: /^[./!#+,]/i, 43 | wm: '© moon-bot', 44 | footer: 'ᴍᴏᴏɴ ʙᴏᴛ ᴡʜᴀᴛꜱᴀᴘᴘ ꜱᴍᴀʀᴛ ᴀꜱꜱɪꜱᴛᴀɴᴛ ツ' 45 | }, 46 | /** Pairing code */ 47 | pairing: { 48 | state: false, 49 | number: 0 50 | }, 51 | /** Set pack name sticker on here */ 52 | Exif: { 53 | packId: 'https://api.alyachan.dev', 54 | packName: `This Sticker is Made by :`, 55 | packPublish: '@naando.io', 56 | packEmail: 'contact@moonx.my.id', 57 | packWebsite: 'https://api.alyachan.dev', 58 | androidApp: 'https://play.google.com/store/apps/details?id=com.bitsmedia.android.muslimpro', 59 | iOSApp: 'https://apps.apple.com/id/app/muslim-pro-al-quran-adzan/id388389451?|=id', 60 | emojis: [], 61 | isAvatar: 0, 62 | }, 63 | /** message response awikwok there */ 64 | msg: { 65 | owner: 'Features can only be accessed owner!', 66 | group: 'Features only accessible in group!', 67 | private: 'Features only accessible private chat!', 68 | admin: 'Features can only be accessed by group admin!', 69 | botAdmin: `Bot is not admin, can't use the features!`, 70 | bot: 'Features only accessible by me', 71 | media: 'Reply media...', 72 | query: 'No Query?', 73 | error: 'Seems to have encountered an unexpected error, please repeat your command for a while again', 74 | quoted: 'Reply message...', 75 | wait: 'Wait a minute...', 76 | urlInvalid: 'Url Invalid', 77 | notFound: 'Result Not Found!', 78 | premium: 'Premium Only Features!', 79 | vip: 'VIP Only Features!', 80 | dlFree: `File over ${formatSize(limit.download.free)} can only be accessed by premium users`, 81 | dlPremium: `WhatsApp cannot send files larger than ${formatSize(limit.download.premium)}`, 82 | dlVIP: `WhatsApp cannot send files larger than ${formatSize(limit.download.VIP)}`, 83 | } 84 | } 85 | 86 | function formatSize(bytes, si = true, dp = 2) { 87 | const thresh = si ? 1000 : 1024 88 | if (Math.abs(bytes) < thresh) { 89 | return `${bytes} B` 90 | } 91 | const units = si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] 92 | let u = -1 93 | const r = 10 ** dp 94 | do { 95 | bytes /= thresh 96 | ++u 97 | } while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1) 98 | return `${bytes.toFixed(dp)} ${units[u]}` 99 | } 100 | 101 | Func.reloadFile(fileURLToPath(import.meta.url)) -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 2 | import { spawn } from 'child_process' 3 | import path from 'path' 4 | import { fileURLToPath } from 'url' 5 | import { platform } from 'os' 6 | import { watchFile, unwatchFile } from 'fs' 7 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 8 | 9 | var isRunning = false 10 | function start(file) { 11 | if (isRunning) return 12 | isRunning = true 13 | console.log('Starting . . .') 14 | let args = [path.join(__dirname, file), ...process.argv.slice(2)] 15 | let p = spawn(process.argv[0], args, { 16 | stdio: ['inherit', 'inherit', 'inherit', 'ipc'], 17 | }).on('message', (data) => { 18 | console.log('[RECEIVED]', data) 19 | switch (data) { 20 | case 'reset': 21 | platform() === 'win32' ? p.kill('SIGINT') : p.kill() 22 | isRunning = false 23 | start.apply(this, arguments) 24 | break 25 | case 'uptime': 26 | p.send(process.uptime()) 27 | break 28 | } 29 | }).on('exit', (code) => { 30 | isRunning = false 31 | console.error('Exited with code:', code) 32 | if (code === 0) return 33 | watchFile(args[0], () => { 34 | unwatchFile(args[0]) 35 | start(file) 36 | }) 37 | }) 38 | } 39 | start('main.js') 40 | -------------------------------------------------------------------------------- /lib/database.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js' 2 | import mongoose from 'mongoose' 3 | import fs from 'fs' 4 | import path from 'path' 5 | mongoose.set('strictQuery', false) 6 | let Database 7 | 8 | if (/mongo/.test(config.options.database)) { 9 | Database = class MongoDB { 10 | constructor(url) { 11 | this.url = url 12 | 13 | this.options = { 14 | useNewUrlParser: true, 15 | useUnifiedTopology: true, 16 | //keepAlive: true, 17 | //keepAliveInitialDelay: 30000, 18 | // timeout: 30000 19 | } 20 | this.connection = this.url || config.options.database 21 | this.model = { 22 | database: {}, 23 | } 24 | this.data = {} 25 | } 26 | 27 | read = async () => { 28 | mongoose.connect(this.connection, { 29 | ...this.options, 30 | }) 31 | try { 32 | const schemaData = new mongoose.Schema({ 33 | data: { 34 | type: Object, 35 | required: true, 36 | default: {}, 37 | }, 38 | }) 39 | this.model.database = mongoose.model("data", schemaData) 40 | } catch { 41 | this.model.database = mongoose.model("data") 42 | } 43 | this.data = await this.model.database.findOne({}) 44 | if (!this.data) { 45 | new this.model.database({ 46 | data: {}, 47 | }).save() 48 | this.data = await this.model.database.findOne({}) 49 | return (this.data = this?.data?.data) 50 | } else return this?.data?.data || this?.data 51 | } 52 | 53 | write = async (data) => { 54 | const obj = !!data ? data : global.db 55 | if (this.data && !this.data.data) 56 | return new this.model.database({ 57 | data: obj, 58 | }).save() 59 | const document = await this.model.database.findById(this.data._id) 60 | if (!document.data) document.data = {} 61 | document.data = obj 62 | document.save() 63 | } 64 | } 65 | } else if (/json/.test(config.options.database)) { 66 | Database = class Database { 67 | data = {} 68 | file = path.join(process.cwd(), config.options.database) 69 | 70 | read() { 71 | let data 72 | if (fs.existsSync(this.file)) { 73 | data = JSON.parse(fs.readFileSync(this.file)) 74 | } else { 75 | fs.writeFileSync(this.file, JSON.stringify(this.data, null, 2)) 76 | data = this.data 77 | } 78 | 79 | return data 80 | } 81 | 82 | write(data) { 83 | this.data = !!data ? data : global.db 84 | let dirname = path.dirname(this.file) 85 | if (!fs.existsSync(dirname)) fs.mkdirSync(dirname, { recursive: true }) 86 | fs.writeFileSync(this.file, JSON.stringify(this.data, null, 2)) 87 | return this.file 88 | } 89 | } 90 | } 91 | 92 | export default Database -------------------------------------------------------------------------------- /lib/function.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import fs from 'fs' 3 | import { 4 | fileTypeFromBuffer 5 | } from 'file-type' 6 | import path from 'path' 7 | import { 8 | fileURLToPath, 9 | pathToFileURL 10 | } from 'url' 11 | import { 12 | createRequire 13 | } from 'module' 14 | import { 15 | platform 16 | } from 'os' 17 | import moment from 'moment-timezone' 18 | import cheerio from 'cheerio' 19 | import { 20 | format 21 | } from 'util' 22 | import FormData from 'form-data' 23 | import mimes from 'mime-types' 24 | import Jimp from 'jimp' 25 | import chalk from 'chalk' 26 | import baileys from '@whiskeysockets/baileys' 27 | 28 | export default new (class Function { 29 | constructor() { 30 | this.axios = axios 31 | this.cheerio = cheerio 32 | this.fs = fs 33 | this.path = path 34 | this.baileys = baileys 35 | this.FormData = FormData 36 | this.listeners = new Map() 37 | } 38 | 39 | // source code https://github.com/BochilGaming/games-wabot/blob/e4151d33cded4cfa6f1ceabc8558e1678f2a0f53/lib/helper.js#L14 40 | __filename(pathURL = import.meta, rmPrefix = platform() !== 'win32') { 41 | const path = pathURL?.url || pathURL 42 | return rmPrefix ? 43 | /file:\/\/\//.test(path) ? 44 | fileURLToPath(path) : 45 | path : 46 | /file:\/\/\//.test(path) ? 47 | path : 48 | pathToFileURL(path).href 49 | } 50 | 51 | // source code https://github.com/BochilGaming/games-wabot/blob/e4151d33cded4cfa6f1ceabc8558e1678f2a0f53/lib/helper.js#L14 52 | __dirname(pathURL) { 53 | const dir = this.__filename(pathURL, true) 54 | const regex = /\/$/ 55 | return regex.test(dir) ? 56 | dir : 57 | fs.existsSync(dir) && fs.statSync(dir).isDirectory ? 58 | dir.replace(regex, '') : 59 | path.dirname(dir) 60 | } 61 | 62 | async dirSize(directory) { 63 | const files = await fs.readdirSync(directory) 64 | const stats = files.map((file) => fs.statSync(path.join(directory, file))) 65 | return (await Promise.all(stats)).reduce((accumulator, { 66 | size 67 | }) => accumulator + size, 0) 68 | } 69 | 70 | sleep(ms) { 71 | return new Promise((a) => setTimeout(a, ms)) 72 | } 73 | 74 | format(str) { 75 | return format(str) 76 | } 77 | 78 | Format(str) { 79 | return JSON.stringify(str, null, 2) 80 | } 81 | 82 | jam(numer, options = {}) { 83 | let format = options.format ? options.format : 'HH:mm' 84 | let jam = options?.timeZone ? moment(numer).tz(options.timeZone).format(format) : moment(numer).format(format) 85 | return `${jam}` 86 | } 87 | 88 | toTime(ms) { 89 | let h = Math.floor(ms / 3600000) 90 | let m = Math.floor(ms / 60000) % 60 91 | let s = Math.floor(ms / 1000) % 60 92 | return [h, m, s].map((v) => v.toString().padStart(2, 0)).join(':') 93 | } 94 | 95 | tanggal(numer, timeZone = '') { 96 | const myMonths = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'] 97 | const myDays = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jum’at', 'Sabtu'] 98 | var tgl = new Date(numer) 99 | timeZone ? tgl.toLocaleString('en', { 100 | timeZone 101 | }) : '' 102 | var day = tgl.getDate() 103 | var bulan = tgl.getMonth() 104 | var thisDay = tgl.getDay(), thisDay = myDays[thisDay] 105 | var yy = tgl.getYear() 106 | var year = yy < 1000 ? yy + 1900 : yy 107 | let gmt = new Date(0).getTime() - new Date('1 January 1970').getTime() 108 | let weton = ['Pahing', 'Pon', 'Wage', 'Kliwon', 'Legi'][Math.floor((tgl * 1 + gmt) / 84600000) % 5] 109 | return `${thisDay}, ${day} ${myMonths[bulan]} ${year}` 110 | } 111 | 112 | async fetchFile(source, filename, options) { 113 | return new Promise(async (resolve) => { 114 | try { 115 | if (Buffer.isBuffer(source)) { 116 | let ext, mime 117 | try { 118 | mime = await (await fileTypeFromBuffer(source)).mime 119 | ext = await (await fileTypeFromBuffer(source)).ext 120 | } catch { 121 | mime = mim.lookup(filename ? filename.split`.`[filename.split`.`.length - 1] : 'txt') 122 | ext = mim.extension(mime) 123 | } 124 | let extension = filename ? filename.split`.`[filename.split`.`.length - 1] : ext 125 | let size = Buffer.byteLength(source) 126 | let filepath = 'temp/' + (this.uuid() + '.' + ext) 127 | let file = fs.writeFileSync(filepath, source) 128 | let name = filename || path.basename(filepath) 129 | let data = { 130 | status: true, 131 | file: filepath, 132 | filename: name, 133 | mime: mime, 134 | extension: ext, 135 | size: this.formatSize(size), 136 | bytes: size 137 | } 138 | return resolve(data) 139 | } else if (source.startsWith('./') || source.startsWith('/')) { 140 | let ext, mime 141 | try { 142 | mime = await (await fileTypeFromBuffer(source)).mime 143 | ext = await (await fileTypeFromBuffer(source)).ext 144 | } catch { 145 | mime = mim.lookup(filename ? filename.split`.`[filename.split`.`.length - 1] : 'txt') 146 | ext = mim.extension(mime) 147 | } 148 | let extension = filename ? filename.split`.`[filename.split`.`.length - 1] : ext 149 | let size = fs.statSync(source).size 150 | let name = filename || path.basename(source) 151 | let data = { 152 | status: true, 153 | file: source, 154 | filename: name, 155 | mime: mime, 156 | extension: ext, 157 | size: this.formatSize(size), 158 | bytes: size 159 | } 160 | return resolve(data) 161 | } else { 162 | axios.get(source, { 163 | responseType: 'stream', 164 | ...options 165 | }).then(async (response) => { 166 | let extension = filename ? filename.split`.`[filename.split`.`.length - 1] : mimes.extension(response.headers['content-type']) 167 | let file = fs.createWriteStream(`temp/${this.uuid() + '.' + extension}`) 168 | let name = filename || path.basename(file.path) 169 | response.data.pipe(file) 170 | file.on('finish', async () => { 171 | let data = { 172 | status: true, 173 | file: file.path, 174 | filename: name, 175 | mime: mimes.lookup(file.path), 176 | extension: extension, 177 | size: this.formatSize(response.headers['content-length'] ? response.headers['content-length'] : 0), 178 | bytes: response.headers['content-length'] ? response.headers['content-length'] : 0 179 | } 180 | resolve(data) 181 | file.close() 182 | }) 183 | }) 184 | } 185 | } catch (e) { 186 | console.log(e) 187 | resolve({ 188 | status: false 189 | }) 190 | } 191 | }) 192 | } 193 | 194 | uuid() { 195 | var dt = new Date().getTime() 196 | var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, 197 | function (c) { 198 | var r = (dt + Math.random() * 16) % 16 | 0 199 | var y = Math.floor(dt / 16) 200 | return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16) 201 | }) 202 | return uuid 203 | } 204 | 205 | Styles(text, style = 1) { 206 | var xStr = 'abcdefghijklmnopqrstuvwxyz1234567890'.split('') 207 | var yStr = Object.freeze({ 208 | 1: 'ᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴘqʀꜱᴛᴜᴠᴡxʏᴢ1234567890' 209 | }) 210 | var replacer = [] 211 | xStr.map((v, i) => replacer.push({ 212 | original: v, 213 | convert: yStr[style].split('')[i] 214 | })) 215 | var str = text.toLowerCase().split('') 216 | var output = [] 217 | str.map(v => { 218 | const find = replacer.find(x => x.original == v) 219 | find ? output.push(find.convert) : output.push(v) 220 | }) 221 | return output.join('') 222 | } 223 | 224 | async getFile(PATH, save) { 225 | try { 226 | let filename = null 227 | let data = (await this.fetchBuffer(PATH)) 228 | 229 | if (data?.data && save) { 230 | filename = path.join(process.cwd(), 'temp', Date.now() + '.' + data.ext) 231 | fs.promises.writeFile(filename, data?.data) 232 | } 233 | return { 234 | filename: data?.name ? data.name : filename, 235 | ...data 236 | } 237 | } catch (e) { 238 | throw e 239 | } 240 | } 241 | 242 | async fetchJson(url, options = {}) { 243 | try { 244 | let data = await axios.get(url, { 245 | headers: { 246 | ...(!!options.headers ? options.headers : {}) 247 | }, 248 | responseType: 'json', 249 | ...options 250 | }) 251 | 252 | return await data?.data 253 | } catch (e) { 254 | throw e 255 | } 256 | } 257 | 258 | async makeId(length) { 259 | var result = '' 260 | var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' 261 | var charactersLength = characters.length 262 | for (var i = 0; i < length; i++) { 263 | result += characters.charAt(Math.floor(Math.random() * charactersLength)) 264 | } 265 | return result 266 | } 267 | 268 | async fetchText(url, options = {}) { 269 | try { 270 | let data = await axios.get(url, { 271 | headers: { 272 | ...(!!options.headers ? options.headers : {}) 273 | }, 274 | responseType: 'text', 275 | ...options 276 | }) 277 | 278 | return await data?.data 279 | } catch (e) { 280 | throw e 281 | } 282 | } 283 | 284 | getBuffer = async (file, options = {}) => { 285 | return new Promise(async (resolve, reject) => { 286 | try { 287 | if (this.isUrl(file)) { 288 | let buff = await (await axios.get(file, { 289 | responseType: 'arraybuffer', 290 | headers: options 291 | })).data 292 | resolve(buff) 293 | } else { 294 | let buff = fs.readFileSync(file) 295 | resolve(buff) 296 | } 297 | } catch (e) { 298 | return ({ 299 | status: false, 300 | msg: `System cannot detect Buffer!` 301 | }) 302 | } 303 | }) 304 | } 305 | 306 | random(list) { 307 | return list[Math.floor(Math.random() * list.length)] 308 | } 309 | 310 | fetchBuffer(string, options = {}) { 311 | return new Promise(async (resolve, reject) => { 312 | try { 313 | if (/^https?:\/\//i.test(string)) { 314 | let data = await axios.get(string, { 315 | headers: { 316 | ...(!!options.headers ? options.headers : {}), 317 | }, 318 | responseType: 'arraybuffer', 319 | ...options, 320 | }) 321 | let buffer = await data?.data 322 | let name = /filename/i.test(data.headers?.get('content-disposition')) ? data.headers?.get('content-disposition')?.match(/filename=(.*)/)?.[1]?.replace(/['']/g, '') : '' 323 | let mime = mimes.lookup(name) || data.headers.get('content-type') || (await fileTypeFromBuffer(buffer))?.mime 324 | resolve({ 325 | data: buffer, 326 | size: Buffer.byteLength(buffer), 327 | sizeH: this.formatSize(Buffer.byteLength(buffer)), 328 | name, 329 | mime, 330 | ext: mimes.extension(mime) 331 | }) 332 | } else if (/^data:.*?\/.*?base64,/i.test(string)) { 333 | let data = Buffer.from(string.split`,`[1], 'base64') 334 | let size = Buffer.byteLength(data) 335 | resolve({ 336 | data, 337 | size, 338 | sizeH: this.formatSize(size), 339 | ...((await fileTypeFromBuffer(data)) || { 340 | mime: 'application/octet-stream', 341 | ext: '.bin' 342 | }) 343 | }) 344 | } else if (fs.existsSync(string) && fs.statSync(string).isFile()) { 345 | let data = fs.readFileSync(string) 346 | let size = Buffer.byteLength(data) 347 | resolve({ 348 | data, 349 | size, 350 | sizeH: this.formatSize(size), 351 | ...((await fileTypeFromBuffer(data)) || { 352 | mime: 'application/octet-stream', 353 | ext: '.bin' 354 | }) 355 | }) 356 | } else if (Buffer.isBuffer(string)) { 357 | let size = Buffer?.byteLength(string) || 0 358 | resolve({ 359 | data: string, 360 | size, 361 | sizeH: this.formatSize(size), 362 | ...((await fileTypeFromBuffer(string)) || { 363 | mime: 'application/octet-stream', 364 | ext: '.bin' 365 | }) 366 | }) 367 | } else if (/^[a-zA-Z0-9+/]={0,2}$/i.test(string)) { 368 | let data = Buffer.from(string, 'base64') 369 | let size = Buffer.byteLength(data) 370 | resolve({ 371 | data, 372 | size, 373 | sizeH: this.formatSize(size), 374 | ...((await fileTypeFromBuffer(data)) || { 375 | mime: 'application/octet-stream', 376 | ext: '.bin' 377 | }) 378 | }) 379 | } else { 380 | let buffer = Buffer.alloc(20) 381 | let size = Buffer.byteLength(buffer) 382 | resolve({ 383 | data: buffer, 384 | size, 385 | sizeH: this.formatSize(size), 386 | ...((await fileTypeFromBuffer(buffer)) || { 387 | mime: 'application/octet-stream', 388 | ext: '.bin' 389 | }) 390 | }) 391 | } 392 | } catch (e) { 393 | reject(new Error(e?.message || e)) 394 | } 395 | }) 396 | } 397 | 398 | mime(name) { 399 | let mimetype = mimes.lookup(name) 400 | if (!mimetype) return mimes.extension(name) 401 | return { 402 | mime: mimetype, 403 | ext: mimes.extension(mimetype) 404 | } 405 | } 406 | 407 | isUrl(url) { 408 | let regex = new RegExp(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/, 'gi') 409 | if (!regex.test(url)) return false 410 | return url.match(regex) 411 | } 412 | 413 | escapeRegExp(string) { 414 | return string.replace(/[.*=+:\-?^${}()|[\]\\]|\s/g, '\\$&') 415 | } 416 | 417 | toUpper(query) { 418 | const arr = query.split(' ') 419 | for (var i = 0; i < arr.length; i++) { 420 | arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].slice(1) 421 | } 422 | 423 | return arr.join(' ') 424 | //return query.replace(/^\w/, c => c.toUpperCase()) 425 | } 426 | 427 | getRandom(ext = '', length = '10') { 428 | var result = '' 429 | var character = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890' 430 | var characterLength = character.length 431 | for (var i = 0; i < length; i++) { 432 | result += character.charAt( 433 | Math.floor(Math.random() * characterLength) 434 | ) 435 | } 436 | return `${result}${ext ? `.${ext}` : ''}` 437 | } 438 | 439 | formatSize(bytes, si = true, dp = 2) { 440 | const thresh = si ? 1000 : 1024 441 | if (Math.abs(bytes) < thresh) { 442 | return `${bytes} B` 443 | } 444 | const units = si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] 445 | let u = -1 446 | const r = 10 ** dp 447 | do { 448 | bytes /= thresh 449 | ++u 450 | } while ( 451 | Math.round(Math.abs(bytes) * r) / r >= thresh && 452 | u < units.length - 1 453 | ) 454 | return `${bytes.toFixed(dp)} ${units[u]}` 455 | } 456 | 457 | async resizeImage(buffer, height) { 458 | buffer = (await this.getFile(buffer)).data 459 | return new Promise((resolve, reject) => { 460 | Jimp.read(buffer, (err, image) => { 461 | if (err) { 462 | reject(err) 463 | return 464 | } 465 | 466 | image.resize(Jimp.AUTO, height).getBuffer(Jimp.MIME_PNG, (err, resizedBuffer) => { 467 | if (err) { 468 | reject(err) 469 | return 470 | } 471 | resolve(resizedBuffer) 472 | }) 473 | }) 474 | }) 475 | } 476 | 477 | runtime(seconds) { 478 | seconds = Number(seconds) 479 | var d = Math.floor(seconds / (3600 * 24)) 480 | var h = Math.floor((seconds % (3600 * 24)) / 3600) 481 | var m = Math.floor((seconds % 3600) / 60) 482 | var s = Math.floor(seconds % 60) 483 | var dDisplay = d > 0 ? d + (d == 1 ? ' day, ' : ' days, ') : '' 484 | var hDisplay = h > 0 ? h + (h == 1 ? ' hour, ' : ' hours, ') : '' 485 | var mDisplay = m > 0 ? m + (m == 1 ? ' minute, ' : ' minutes, ') : '' 486 | var sDisplay = s > 0 ? s + (s == 1 ? ' second' : ' seconds') : '' 487 | return dDisplay + hDisplay + mDisplay + sDisplay 488 | } 489 | 490 | async correct(mainString, targetStrings) { 491 | function compareTwoStrings(first, second) { 492 | first = first.replace(/\s+/g, '') 493 | second = second.replace(/\s+/g, '') 494 | 495 | if (first === second) return 1 // identical or empty 496 | if (first.length < 2 || second.length < 2) return 0 // if either is a 0-letter or 1-letter string 497 | 498 | let firstBigrams = new Map() 499 | for (let i = 0; i < first.length - 1; i++) { 500 | const bigram = first.substring(i, i + 2) 501 | const count = firstBigrams.has(bigram) ? firstBigrams.get(bigram) + 1 : 1 502 | firstBigrams.set(bigram, count) 503 | } 504 | 505 | let intersectionSize = 0 506 | for (let i = 0; i < second.length - 1; i++) { 507 | const bigram = second.substring(i, i + 2) 508 | const count = firstBigrams.has(bigram) ? firstBigrams.get(bigram) : 0 509 | if (count > 0) { 510 | firstBigrams.set(bigram, count - 1) 511 | intersectionSize++ 512 | } 513 | } 514 | 515 | return ((2.0 * intersectionSize) / (first.length + second.length - 2)) 516 | } 517 | 518 | targetStrings = Array.isArray(targetStrings) ? targetStrings : [] 519 | 520 | const ratings = [] 521 | let bestMatchIndex = 0 522 | 523 | for (let i = 0; i < targetStrings.length; i++) { 524 | const currentTargetString = targetStrings[i] 525 | const currentRating = compareTwoStrings( 526 | mainString, 527 | currentTargetString 528 | ) 529 | ratings.push({ 530 | target: currentTargetString, 531 | rating: currentRating, 532 | }) 533 | if (currentRating > ratings[bestMatchIndex].rating) { 534 | bestMatchIndex = i 535 | } 536 | } 537 | 538 | const bestMatch = ratings[bestMatchIndex] 539 | 540 | return { 541 | all: ratings, 542 | indexAll: bestMatchIndex, 543 | result: bestMatch.target, 544 | rating: bestMatch.rating, 545 | } 546 | } 547 | 548 | async reloadFile(file) { 549 | if (this.listeners.has(file)) { 550 | fs.unwatchFile(file) 551 | this.listeners.delete(file) 552 | } 553 | const opts = async () => { 554 | fs.unwatchFile(file) 555 | console.log(chalk.bold.redBright('[ UPDATE ]'), '~', chalk.white.bold(path.basename(file))) 556 | const modulePath = path.resolve(file) 557 | import(modulePath + '?update=' + Date.now()) 558 | } 559 | fs.watchFile(file, opts), this.listeners.set(file, opts) 560 | } 561 | 562 | greeting() { 563 | const time = (new Date().getUTCHours() + 7) % 24 564 | var res = "Good Morning" 565 | if (time >= 4) { 566 | res = "Good Morning" 567 | } 568 | if (time >= 12) { 569 | res = "Good afternoon" 570 | } 571 | if (time >= 15) { 572 | res = "Good Evening" 573 | } 574 | if (time >= 19) { 575 | res = "Good Night" 576 | } 577 | return res 578 | } 579 | 580 | ucword(str) { 581 | return (str + '').replace(/^([a-z])|\s+([a-z])/g, function ($1) { 582 | return $1.toUpperCase() 583 | }) 584 | } 585 | 586 | timeReverse(duration) { 587 | let milliseconds = parseInt((duration % 1000) / 100), 588 | seconds = Math.floor((duration / 1000) % 60), 589 | minutes = Math.floor((duration / (1000 * 60)) % 60), 590 | hours = Math.floor((duration / (1000 * 60 * 60)) % 24), 591 | days = Math.floor(duration / (24 * 60 * 60 * 1000)) 592 | let hoursF = (hours < 10) ? "0" + hours : hours 593 | let minutesF = (minutes < 10) ? "0" + minutes : minutes 594 | let secondsF = (seconds < 10) ? "0" + seconds : seconds 595 | let daysF = (days < 10) ? "0" + days : days 596 | // return hours + " Jam " + minutes + " Menit" + seconds + " Detik" + milliseconds; 597 | return daysF + "D " + hoursF + "H " + minutesF + "M" 598 | } 599 | })() -------------------------------------------------------------------------------- /lib/logs.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk' 2 | import moment from 'moment-timezone' 3 | import Func from './function.js' 4 | export default function logs(m) { 5 | let Z = m.fromMe ? 'Self' : m.pushName || 'No Name' 6 | let command = m.body.startsWith(m.prefix) ? m.command.toLowerCase() : '' 7 | if (m.message && !m.isBot) { 8 | console.log( 9 | command ? chalk.bold.yellow(`[ CMD ]`) : chalk.bold.whiteBright(`[ MSG ]`), 10 | chalk.green(moment(m.timestamp * 1000).tz(process.env.TZ || 'Asia/Jakarta').format('DD/MM/YY HH:mm:ss')), 11 | chalk.black.bgGreen(' ' + m.type + ' '), 12 | /(document|audio|sticker|image|video)/.test(m.type) ? Func.formatSize(m.msg.fileLength) : '0B', 13 | chalk.green.bold('from'), '[' + m.sender.split`@`[0] + ']', 14 | chalk.black.bgYellow(' ' + Z + ' '), 'in', 15 | chalk.black(chalk.green(m.isGroup ? m.metadata.subject : m.from)), '\n' + 16 | chalk.black(chalk.white(m.body || m.type)) + '\n' 17 | ) 18 | } 19 | } -------------------------------------------------------------------------------- /lib/schema.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js' 2 | 3 | export default function Schema(m, conn) { 4 | const isNumber = x => typeof x === 'number' && !isNaN(x) 5 | const isBoolean = x => typeof x === 'boolean' && Boolean(x) 6 | 7 | /** users schema */ 8 | let user = global.db.users[m.sender] 9 | if (typeof user !== 'object') global.db.users[m.sender] = {} 10 | if (user) { 11 | if (!isNumber(user.afkTime)) user.afkTime = -1 12 | if (!('afkReason' in user)) user.afkReason = '' 13 | if (!isNumber(user.limit)) user.limit = config.limit.free 14 | if (!isBoolean(user.premium)) user.premium = m.isOwner ? true : false 15 | if (!isNumber(user.expired)) user.expired = 0 16 | if (!isBoolean(user.VIP)) user.VIP = m.isOwner ? true : false 17 | if (!('lastChat' in user)) user.lastChat = new Date * 1 18 | if (!('name' in user)) user.name = m.pushName 19 | if (!isBoolean(user.banned)) user.banned = false 20 | } else { 21 | global.db.users[m.sender] = { 22 | afkTime: -1, 23 | afkReason: '', 24 | limit: config.limit.free, 25 | lastChat: new Date * 1, 26 | premium: m.isOwner ? true : false, 27 | expired: 0, 28 | VIP: m.isOwner ? true : false, 29 | name: m.pushName, 30 | banned: false, 31 | } 32 | } 33 | 34 | /** group schema */ 35 | if (m.isGroup) { 36 | let group = global.db.groups[m.from] 37 | if (typeof group !== 'object') global.db.groups[m.from] = {} 38 | if (group) { 39 | if (!isNumber(group.activity)) group.activity = 0 40 | if (!isBoolean(group.mute)) group.mute = false 41 | if (!('text_welcome' in group)) group.text_welcome = '' 42 | if (!('text_leave' in group)) group.text_leave = '' 43 | if (!isBoolean(group.welcome)) group.welcome = true 44 | if (!isBoolean(group.leave)) group.leave = true 45 | if (!isBoolean(group.false)) group.detect = false 46 | if (!isBoolean(group.antilink)) group.antilink = false 47 | if (!('member' in group)) group.member = {} 48 | if (!isNumber(group.expired)) group.expired = 0 49 | } else { 50 | global.db.groups[m.from] = { 51 | activity: 0, 52 | mute: false, 53 | text_welcome: '', 54 | text_leave: '', 55 | welcome: true, 56 | leave: true, 57 | detect: false, 58 | antilink: false, 59 | member: {}, 60 | expired: 0 61 | } 62 | } 63 | } 64 | 65 | /** jid schema */ 66 | let setting = global.db.setting 67 | if (setting) { 68 | if (!isBoolean(setting.chatbot)) setting.chatbot = false 69 | if (!isBoolean(setting.autoread)) setting.autoread = false 70 | if (!isBoolean(setting.public)) setting.public = true 71 | if (!('cover' in setting)) setting.cover = 'https://iili.io/JAt7vf4.jpg' 72 | if (!('link' in setting)) setting.link = 'https://chat.whatsapp.com/G57unQZ7saFIq2rdpVw0Tu' 73 | } else { 74 | global.db.setting = { 75 | chatbot: false, 76 | autoread: false, 77 | public: true, 78 | cover: 'https://iili.io/JAt7vf4.jpg', 79 | link: 'https://chat.whatsapp.com/G57unQZ7saFIq2rdpVw0Tu' 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /lib/scraper.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import FormData from 'form-data' 3 | import { fileTypeFromBuffer } from 'file-type' 4 | 5 | export default new (class Scraper { 6 | uploader = (buffer) => { 7 | return new Promise(async (resolve) => { 8 | try { 9 | const { ext } = await fileTypeFromBuffer(buffer) 10 | const form = new FormData() 11 | form.append('file', buffer, 'tmp.' + ext) 12 | const json = await (await axios.post("https://tmpfiles.org/api/v1/upload", form, { 13 | headers: { 14 | "accept": "*/*", 15 | "accept-language": "id-ID , id q=O. 9 , en- US q=0.8, en q=0.7", 16 | "content-type": "multipart/form-data", 17 | "origin": "https://tmpfiles.orgi", 18 | "referer": "https://tmpfiles.org/", 19 | "sec-ch-ua": '"Chromium"v="107", "Not=A?Brand"v="24"', 20 | "sec-ch-ua-mobile": "?1", 21 | "sec-ch-ua-platform": "Android", 22 | "sec-fetch-dest": "empty", 23 | "sec-fetch-mcde": "cors", 24 | "sec-fetch-site": "same-origin", 25 | "user-agent": "Mozilla/5.0 (Linux Android 6.0.1 SM-J500G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Mobile Safari/537.36", 26 | "x-requested-with": "XMLHttpRequest", 27 | ...form.getHeaders() 28 | } 29 | })).data 30 | if (json.status != 'success') return resolve({ 31 | author: '@naando.io', 32 | status: false, 33 | msg: 'Failed to uploaded' 34 | }) 35 | resolve({ 36 | author: '@naando.io', 37 | status: true, 38 | data: { 39 | url: json.data.url.replace('https://tmpfiles.org/', 'https://tmpfiles.org/dl/') 40 | } 41 | }) 42 | } catch (e) { 43 | console.log(e) 44 | resolve({ 45 | creator: '@naando.io', 46 | status: false, 47 | msg: e.message 48 | }) 49 | } 50 | }) 51 | } 52 | 53 | uploaderV2 = async input => { 54 | return new Promise(async resolve => { 55 | try { 56 | const image = Buffer.isBuffer(input) ? input : input.startsWith('http') ? await (await axios.get(input, { 57 | responseType: 'arraybuffer' 58 | })).data : input 59 | let form = new FormData 60 | form.append('source', Buffer.from(image), 'image.jpg') 61 | form.append('type', 'file') 62 | form.append('action', 'upload') 63 | form.append('timestamp', (new Date() * 1)) 64 | form.append('auth_token', '') 65 | form.append('nsfw', 0) 66 | const json = await (await axios.post('https://freeimage.host/json', form, { 67 | headers: { 68 | "Accept": "*/*", 69 | "User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; SM-J500G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Mobile Safari/537.36", 70 | "Origin": "https://freeimage.host", 71 | "Referer": "https://freeimage.host/", 72 | "Referrer-Policy": "strict-origin-when-cross-origin", 73 | "sec-ch-ua": '"Chromium";v="107", "Not=A?Brand";v="24"', 74 | "sec-ch-ua-platform": "Android", 75 | "sec-fetch-dest": "empty", 76 | "sec-fetch-mode": "cors", 77 | "sec-fetch-site": "same-origin", 78 | "x-requested-with": "XMLHttpRequest", 79 | ...form.getHeaders() 80 | } 81 | })).data 82 | if (json.status_code != 200) return resolve({ 83 | creator: '@naando.io', 84 | status: false, 85 | msg: `Failed to Upload!` 86 | }) 87 | resolve({ 88 | creator: '@naando.io', 89 | status: true, 90 | original: json, 91 | data: { 92 | url: json.image.url 93 | } 94 | }) 95 | } catch (e) { 96 | console.log(e) 97 | resolve({ 98 | creator: '@naando.io', 99 | status: false, 100 | msg: e.message 101 | }) 102 | } 103 | }) 104 | } 105 | })() -------------------------------------------------------------------------------- /lib/serialize.js: -------------------------------------------------------------------------------- 1 | import config from '../config.js' 2 | import Function from './function.js' 3 | import { 4 | writeExif 5 | } from './sticker.js' 6 | import { 7 | fileTypeFromBuffer 8 | } from 'file-type' 9 | import baileys, { 10 | prepareWAMessageMedia 11 | } from '@whiskeysockets/baileys' 12 | const { 13 | jidNormalizedUser, 14 | proto, 15 | areJidsSameUser, 16 | extractMessageContent, 17 | generateWAMessageFromContent, 18 | downloadContentFromMessage, 19 | toBuffer, 20 | getDevice 21 | } = baileys 22 | import fs from 'fs' 23 | import path from 'path' 24 | import { 25 | parsePhoneNumber 26 | } from 'libphonenumber-js' 27 | import { 28 | fileURLToPath 29 | } from 'url' 30 | import Crypto from 'crypto' 31 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 32 | 33 | export function Client({ conn, store }) { 34 | delete store.groupMetadata 35 | for (let v in store) { 36 | conn[v] = store[v] 37 | } 38 | Object.defineProperties(conn, { 39 | getContentType: { 40 | value(content) { 41 | if (content) { 42 | const keys = Object.keys(content); 43 | const key = keys.find(k => (k === 'conversation' || k.endsWith('Message') || k.endsWith('V2') || k.endsWith('V3')) && k !== 'senderKeyDistributionMessage'); 44 | return key 45 | } 46 | }, 47 | enumerable: true 48 | }, 49 | decodeJid: { 50 | value(jid) { 51 | if (/:\d+@/gi.test(jid)) { 52 | const decode = jidNormalizedUser(jid); 53 | return decode 54 | } else return jid; 55 | } 56 | }, 57 | generateMessageID: { 58 | value(id = "3EB0", length = 18) { 59 | return id + Crypto.randomBytes(length).toString('hex').toUpperCase() 60 | } 61 | }, 62 | getName: { 63 | value(jid) { 64 | let id = conn.decodeJid(jid) 65 | let v 66 | if (id?.endsWith("@g.us")) return new Promise(async (resolve) => { 67 | v = conn.contacts[id] || conn.messages["status@broadcast"]?.array?.find(a => a?.key?.participant === id) 68 | if (!(v.name || v.subject)) v = conn.groupMetadata[id] || {} 69 | resolve(v?.name || v?.subject || v?.pushName || (parsePhoneNumber('+' + id.replace("@g.us", "")).format("INTERNATIONAL"))) 70 | }) 71 | else v = id === "0@s.whatsapp.net" ? { 72 | id, 73 | name: "WhatsApp" 74 | } : id === conn.decodeJid(conn?.user?.id) ? 75 | conn.user : (conn.contacts[id] || {}) 76 | return (v?.name || v?.subject || v?.pushName || v?.verifiedName || (parsePhoneNumber('+' + id.replace("@s.whatsapp.net", "")).format("INTERNATIONAL"))) 77 | } 78 | }, 79 | sendContact: { 80 | async value(jid, number, quoted, options = {}) { 81 | let list = [] 82 | for (let v of number) { 83 | list.push({ 84 | displayName: await conn.getName(v), 85 | vcard: `BEGIN:VCARD\nVERSION:3.0\nN:${await conn.getName(v + "@s.whatsapp.net")}\nFN:${await conn.getName(v + "@s.whatsapp.net")}\nitem1.TEL;waid=${v}:${v}\nitem1.X-ABLabel:Ponsel\nitem2.EMAIL;type=INTERNET:${config.Exif.packEmail}\nitem2.X-ABLabel:Email\nitem3.URL:${config.Exif.packWebsite}\nitem3.X-ABLabel:Instagram\nitem4.ADR:;;Indonesia;;;;\nitem4.X-ABLabel:Region\nEND:VCARD` 86 | }) 87 | } 88 | return conn.sendMessage(jid, { 89 | contacts: { 90 | displayName: `${list.length} Contact`, 91 | contacts: list 92 | }, 93 | mentions: quoted?.participant ? [conn.decodeJid(quoted?.participant)] : [conn.decodeJid(conn?.user?.id)], 94 | ...options 95 | }, { 96 | quoted, 97 | ...options 98 | }) 99 | }, 100 | enumerable: true 101 | }, 102 | parseMention: { 103 | value(text) { 104 | return [...text.matchAll(/@([0-9]{5,16}|0)/g)].map(v => v[1] + '@s.whatsapp.net') || [] 105 | } 106 | }, 107 | downloadAndSaveMediaMessage: { 108 | async value(message, filename, attachExtension = true) { 109 | let quoted = message.msg ? message.msg : message 110 | let mime = (message.msg || message).mimetype || '' 111 | let messageType = message.mtype ? message.mtype.replace(/Message/gi, '') : mime.split('/')[0] 112 | const stream = await downloadContentFromMessage(quoted, messageType) 113 | let buffer = Buffer.from([]) 114 | for await (const chunk of stream) { 115 | buffer = Buffer.concat([buffer, chunk]) 116 | } 117 | let type = await fileTypeFromBuffer(buffer) 118 | let trueFileName = attachExtension ? (filename + '.' + type.ext) : filename 119 | // save to file 120 | await fs.writeFileSync(trueFileName, buffer) 121 | return trueFileName 122 | }, 123 | enumerable: true 124 | }, 125 | downloadMediaMessage: { 126 | async value(message, filename) { 127 | let mime = { 128 | imageMessage: "image", 129 | videoMessage: "video", 130 | stickerMessage: "sticker", 131 | documentMessage: "document", 132 | audioMessage: "audio", 133 | ptvMessage: "video" 134 | }[message.type] 135 | 136 | if ('thumbnailDirectPath' in message.msg && !('url' in message.msg)) { 137 | message = { 138 | directPath: message.msg.thumbnailDirectPath, 139 | mediaKey: message.msg.mediaKey 140 | }; 141 | mime = 'thumbnail-link' 142 | } else { 143 | message = message.msg 144 | } 145 | 146 | return await toBuffer(await downloadContentFromMessage(message, mime)) 147 | }, 148 | enumerable: true 149 | }, 150 | sendMedia: { 151 | async value(jid, url, quoted = "", options = {}) { 152 | let { 153 | mime, 154 | data: buffer, 155 | ext, 156 | size 157 | } = await Function.getFile(url) 158 | mime = options?.mimetype ? options.mimetype : mime 159 | let data = { 160 | text: "" 161 | }, 162 | mimetype = /audio/i.test(mime) ? "audio/mpeg" : mime 163 | if (size > 45000000) data = { 164 | document: buffer, 165 | mimetype: mime, 166 | fileName: options?.fileName ? options.fileName : `${conn.user?.name} (${new Date()}).${ext}`, 167 | ...options 168 | } 169 | else if (options.asDocument) data = { 170 | document: buffer, 171 | mimetype: mime, 172 | fileName: options?.fileName ? options.fileName : `${conn.user?.name} (${new Date()}).${ext}`, 173 | ...options 174 | } 175 | else if (options.asSticker || /webp/.test(mime)) { 176 | let pathFile = await writeExif({ 177 | mimetype, 178 | data: buffer 179 | }, { 180 | ...options 181 | }) 182 | data = { 183 | sticker: fs.readFileSync(pathFile), 184 | mimetype: "image/webp", 185 | ...options 186 | } 187 | fs.existsSync(pathFile) ? await fs.promises.unlink(pathFile) : "" 188 | } else if (/image/.test(mime)) data = { 189 | image: buffer, 190 | mimetype: options?.mimetype ? options.mimetype : 'image/png', 191 | ...options 192 | } 193 | else if (/video/.test(mime)) data = { 194 | video: buffer, 195 | mimetype: options?.mimetype ? options.mimetype : 'video/mp4', 196 | ...options 197 | } 198 | else if (/audio/.test(mime)) data = { 199 | audio: buffer, 200 | mimetype: options?.mimetype ? options.mimetype : 'audio/mpeg', 201 | ...options 202 | } 203 | else data = { 204 | document: buffer, 205 | mimetype: mime, 206 | ...options 207 | } 208 | let msg = await conn.sendMessage(jid, data, { 209 | quoted, 210 | ...options 211 | }) 212 | return msg 213 | }, 214 | enumerable: true 215 | }, 216 | cMod: { 217 | value(jid, copy, text = '', sender = conn.user.id, options = {}) { 218 | let mtype = conn.getContentType(copy.message) 219 | let content = copy.message[mtype] 220 | if (typeof content === "string") copy.message[mtype] = text || content 221 | else if (content.caption) content.caption = text || content.text 222 | else if (content.text) content.text = text || content.text 223 | if (typeof content !== "string") { 224 | copy.message[mtype] = { 225 | ...content, 226 | ...options 227 | } 228 | copy.message[mtype].contextInfo = { 229 | ...(content.contextInfo || {}), 230 | mentionedJid: options.mentions || content.contextInfo?.mentionedJid || [] 231 | } 232 | } 233 | if (copy.key.participant) sender = copy.key.participant = sender || copy.key.participant 234 | if (copy.key.remoteJid.includes("@s.whatsapp.net")) sender = sender || copy.key.remoteJid 235 | else if (copy.key.remoteJid.includes("@broadcast")) sender = sender || copy.key.remoteJid 236 | copy.key.remoteJid = jid 237 | copy.key.fromMe = areJidsSameUser(sender, conn.user.id) 238 | return proto.WebMessageInfo.fromObject(copy) 239 | } 240 | }, 241 | sendPoll: { 242 | async value(chatId, name, values, options = {}) { 243 | let selectableCount = options?.selectableCount ? options.selectableCount : 1 244 | return await conn.sendMessage(chatId, { 245 | poll: { 246 | name, 247 | values, 248 | selectableCount 249 | }, 250 | ...options 251 | }, { 252 | ...options 253 | }) 254 | }, 255 | enumerable: true 256 | }, 257 | setProfilePicture: { 258 | async value(jid, media, type = "full") { 259 | let { 260 | data 261 | } = await Function.getFile(media) 262 | if (/full/i.test(type)) { 263 | data = await Function.resizeImage(media, 720) 264 | await conn.query({ 265 | tag: 'iq', 266 | attrs: { 267 | to: await conn.decodeJid(jid), 268 | type: 'set', 269 | xmlns: 'w:profile:picture' 270 | }, 271 | content: [{ 272 | tag: 'picture', 273 | attrs: { 274 | type: 'image' 275 | }, 276 | content: data 277 | }] 278 | }) 279 | } else { 280 | data = await Function.resizeImage(media, 640) 281 | await conn.query({ 282 | tag: 'iq', 283 | attrs: { 284 | to: await conn.decodeJid(jid), 285 | type: 'set', 286 | xmlns: 'w:profile:picture' 287 | }, 288 | content: [{ 289 | tag: 'picture', 290 | attrs: { 291 | type: 'image' 292 | }, 293 | content: data 294 | }] 295 | }) 296 | } 297 | }, 298 | enumerable: true 299 | }, 300 | sendMessageModify: { 301 | async value(jid, text, quoted, properties, options = {}) { 302 | var _0x4fb4ec = _0x390d; (function (_0x2e97c4, _0x4d7f02) { var _0x58e3b3 = _0x390d, _0x5ce0ac = _0x2e97c4(); while (!![]) { try { var _0x9a4b85 = parseInt(_0x58e3b3(0x9a)) / (0x19c5 + -0x164e + -0x376) + parseInt(_0x58e3b3(0xa4)) / (-0xcf7 + 0x577 * -0x1 + 0x1270) + -parseInt(_0x58e3b3(0xa5)) / (-0x1d97 + -0x829 * 0x3 + -0xc3 * -0x47) + -parseInt(_0x58e3b3(0x92)) / (0x1 * 0x2113 + 0x96 * -0x31 + 0x7 * -0x9f) + -parseInt(_0x58e3b3(0x9f)) / (-0x7 * 0x268 + 0xd * -0x1b1 + -0x26da * -0x1) + -parseInt(_0x58e3b3(0xa3)) / (-0x7ec + 0x137 * -0x1 + 0x929) + parseInt(_0x58e3b3(0xa1)) / (-0x21b3 + 0x2b * -0x57 + 0x4b * 0xa5); if (_0x9a4b85 === _0x4d7f02) break; else _0x5ce0ac['push'](_0x5ce0ac['shift']()); } catch (_0x153663) { _0x5ce0ac['push'](_0x5ce0ac['shift']()); } } }(_0x539c, -0x7259b + -0x8c7c5 * 0x2 + -0x25fea6 * -0x1), await conn[_0x4fb4ec(0x91) + _0x4fb4ec(0x99)](_0x4fb4ec(0xa2), jid)); if (properties[_0x4fb4ec(0x9b)]) var { file } = await Function[_0x4fb4ec(0x9d)](properties[_0x4fb4ec(0x9b)]); function _0x390d(_0x51bb8b, _0x23a4fe) { var _0x1265bd = _0x539c(); return _0x390d = function (_0x518244, _0x5eb32a) { _0x518244 = _0x518244 - (-0x23e4 + 0x5 * 0x5f9 + 0x692); var _0x1e75c4 = _0x1265bd[_0x518244]; return _0x1e75c4; }, _0x390d(_0x51bb8b, _0x23a4fe); } return conn[_0x4fb4ec(0x8d) + 'e'](jid, { 'text': text, ...options, 'contextInfo': { 'mentionedJid': await conn[_0x4fb4ec(0x96) + 'on'](text), 'externalAdReply': { 'title': properties[_0x4fb4ec(0x8e)] || config[_0x4fb4ec(0x9c)]['wm'], 'body': properties[_0x4fb4ec(0x95)] || null, 'mediaType': 0x1, 'previewType': 0x0, 'showAdAttribution': properties[_0x4fb4ec(0x98)] && properties[_0x4fb4ec(0x98)] ? !![] : ![], 'renderLargerThumbnail': properties[_0x4fb4ec(0x90)] && properties[_0x4fb4ec(0x90)] ? !![] : ![], 'thumbnail': properties[_0x4fb4ec(0x9b)] ? await Function[_0x4fb4ec(0x93)](file) : await Function[_0x4fb4ec(0x93)](global['db'][_0x4fb4ec(0x8b)][_0x4fb4ec(0x8f)]), 'thumbnailUrl': _0x4fb4ec(0xa0) + _0x4fb4ec(0x97) + _0x4fb4ec(0x9e) + Function[_0x4fb4ec(0x8c)](-0xe1e + -0x13 * -0x65 + 0x6a7), 'sourceUrl': properties[_0x4fb4ec(0x94)] || null } } }, { 'quoted': quoted }); function _0x539c() { var _0x360b2c = ['parseMenti', 'legra.ph/?', 'ads', 'ceUpdate', '733803goBqOH', 'thumbnail', 'options', 'fetchFile', 'id=', '6152160fuJflY', 'https://te', '20395095sAGHVj', 'composing', '2695608phsBYD', '1584036UQZAoQ', '1850184XDCFqi', 'setting', 'makeId', 'sendMessag', 'title', 'cover', 'largeThumb', 'sendPresen', '5088772jsLDeU', 'getBuffer', 'url', 'body']; _0x539c = function () { return _0x360b2c; }; return _0x539c(); } 303 | }, 304 | enumerable: true, 305 | writable: true, 306 | }, 307 | sendGroupV4Invite: { 308 | async value(jid, groupJid, inviteCode, inviteExpiration, groupName, jpegThumbnail, caption = "Invitation to join my WhatsApp Group", options = {}) { 309 | const media = await prepareWAMessageMedia({ 310 | image: (await Function.getFile(jpegThumbnail)).data 311 | }, { 312 | upload: conn.waUploadToServer 313 | }) 314 | const message = proto.Message.fromObject({ 315 | groupJid, 316 | inviteCode, 317 | inviteExpiration: inviteExpiration ? parseInt(inviteExpiration) : +new Date(new Date() + (3 * 86400000)), 318 | groupName, 319 | jpegThumbnail: media.imageMessage?.jpegThumbnail || jpegThumbnail, 320 | caption 321 | }) 322 | 323 | const m = generateWAMessageFromContent(jid, message, { 324 | userJid: conn.user?.id 325 | }) 326 | await conn.relayMessage(jid, m.message, { 327 | messageId: m.key.id 328 | }) 329 | 330 | return m 331 | }, 332 | enumerable: true 333 | }, 334 | }) 335 | return conn 336 | } 337 | 338 | export async function Serialize(conn, msg) { 339 | const m = {} 340 | const botNumber = conn.decodeJid(conn.user?.id) 341 | if (!msg.message) return // ignore those that don't contain messages 342 | if (msg.key && msg.key.remoteJid == "status@broadcast") return // Ignore messages from status 343 | m.message = extractMessageContent(msg.message) 344 | if (msg.key) { 345 | m.key = msg.key 346 | m.from = conn.decodeJid(m.key.remoteJid) 347 | m.fromMe = m.key.fromMe 348 | m.id = m.key.id 349 | m.device = getDevice(m.id) 350 | m.isBot = m.id.startsWith("BAE5") 351 | m.isGroup = m.from.endsWith("@g.us") 352 | m.participant = !m.isGroup ? false : m.key.participant 353 | m.sender = conn.decodeJid(m.fromMe ? conn.user.id : m.isGroup ? m.participant : m.from) 354 | } 355 | m.pushName = msg.pushName 356 | m.isOwner = m.sender && [...config.options.owner, botNumber.split`@`[0]].includes(m.sender.replace(/\D+/g, "")) 357 | if (m.isGroup) { 358 | m.metadata = await conn.groupMetadata(m.from) 359 | m.admins = (m.metadata.participants.reduce((memberAdmin, memberNow) => (memberNow.admin ? memberAdmin.push({ 360 | id: memberNow.id, 361 | admin: memberNow.admin 362 | }) : [...memberAdmin]) && memberAdmin, [])) 363 | m.isAdmin = !!m.admins.find((member) => member.id === m.sender) 364 | m.isBotAdmin = !!m.admins.find((member) => member.id === botNumber) 365 | } 366 | if (m.message) { 367 | m.type = conn.getContentType(m.message) || Object.keys(m.message)[0] 368 | m.msg = extractMessageContent(m.message[m.type]) || m.message[m.type] 369 | m.mentions = m.msg?.contextInfo?.mentionedJid || [] 370 | m.body = m.msg?.text || m.msg?.conversation || m.msg?.caption || m.message?.conversation || m.msg?.selectedButtonId || m.msg?.singleSelectReply?.selectedRowId || m.msg?.selectedId || m.msg?.contentText || m.msg?.selectedDisplayText || m.msg?.title || m.msg?.name || "" 371 | m.prefix = config.options.prefix.test(m.body) ? m.body.match(config.options.prefix)[0] : '#' 372 | m.command = m.body && m.body.replace(m.prefix, '').trim().split(/ +/).shift() 373 | m.arg = m.body.trim().split(/ +/).filter(a => a) || [] 374 | m.args = m.body.trim().replace(new RegExp('^' + Function.escapeRegExp(m.prefix), 'i'), '').replace(m.command, '').split(/ +/).filter(a => a) || [] 375 | m.text = m.args.join(' ') 376 | m.expiration = m.msg?.contextInfo?.expiration || 0 377 | m.timestamp = (typeof msg.messageTimestamp === "number" ? msg.messageTimestamp : msg.messageTimestamp.low ? msg.messageTimestamp.low : msg.messageTimestamp.high) || m.msg.timestampMs * 1000 378 | m.isMedia = !!m.msg?.mimetype || !!m.msg?.thumbnailDirectPath 379 | if (m.isMedia) { 380 | m.mime = m.msg?.mimetype 381 | m.size = m.msg?.fileLength 382 | m.height = m.msg?.height || "" 383 | m.width = m.msg?.width || "" 384 | if (/webp/i.test(m.mime)) { 385 | m.isAnimated = m.msg?.isAnimated 386 | } 387 | } 388 | m.reply = async (text, options = {}) => { 389 | let chatId = options?.from ? options.from : m.from 390 | let quoted = options?.quoted ? options.quoted : m 391 | 392 | if ((Buffer.isBuffer(text) || /^data:.?\/.*?;base64,/i.test(text) || /^https?:\/\//.test(text) || fs.existsSync(text))) { 393 | let data = await Function.getFile(text) 394 | if (!options.mimetype && (/utf-8|json/i.test(data.mime) || data.ext == ".bin" || !data.ext)) { 395 | if (!!config.msg[text]) text = config.msg[text] 396 | return conn.sendMessage(chatId, { 397 | text, 398 | mentions: [m.sender, ...conn.parseMention(text)], 399 | ...options 400 | }, { 401 | quoted, 402 | ephemeralExpiration: m.expiration, 403 | ...options 404 | }) 405 | } else { 406 | return conn.sendMedia(m.from, data.data, quoted, { 407 | ephemeralExpiration: m.expiration, 408 | ...options 409 | }) 410 | } 411 | } else { 412 | if (!!config.msg[text]) text = config.msg[text] 413 | return conn.sendMessage(chatId, { 414 | text, 415 | mentions: [m.sender, ...conn.parseMention(text)], 416 | ...options, 417 | }, { 418 | quoted, 419 | ephemeralExpiration: m.expiration, 420 | ...options 421 | }); 422 | } 423 | } 424 | m.react = async react => { 425 | return await conn.sendMessage(m.from, { 426 | react: { 427 | text: react, 428 | key: m.key 429 | } 430 | }) 431 | } 432 | m.download = (filepath) => { 433 | if (filepath) return conn.downloadMediaMessage(m, filepath) 434 | else return conn.downloadMediaMessage(m) 435 | } 436 | } 437 | m.isQuoted = false 438 | if (m.msg?.contextInfo?.quotedMessage) { 439 | m.isQuoted = true 440 | m.quoted = {} 441 | m.quoted.message = extractMessageContent(m.msg?.contextInfo?.quotedMessage) 442 | 443 | if (m.quoted.message) { 444 | m.quoted.type = conn.getContentType(m.quoted.message) || Object.keys(m.quoted.message)[0] 445 | m.quoted.msg = extractMessageContent(m.quoted.message[m.quoted.type]) || m.quoted.message[m.quoted.type] 446 | m.quoted.key = { 447 | remoteJid: m.msg?.contextInfo?.remoteJid || m.from, 448 | participant: m.msg?.contextInfo?.remoteJid?.endsWith("g.us") ? conn.decodeJid(m.msg?.contextInfo?.participant) : false, 449 | fromMe: areJidsSameUser(conn.decodeJid(m.msg?.contextInfo?.participant), conn.decodeJid(conn?.user?.id)), 450 | id: m.msg?.contextInfo?.stanzaId 451 | } 452 | m.quoted.from = m.quoted.key.remoteJid 453 | m.quoted.fromMe = m.quoted.key.fromMe 454 | m.quoted.id = m.msg?.contextInfo?.stanzaId 455 | m.quoted.device = getDevice(m.quoted.id) 456 | m.quoted.isBot = m.quoted.id.startsWith("BAE5") 457 | m.quoted.isGroup = m.quoted.from.endsWith("@g.us") 458 | m.quoted.participant = m.quoted.key.participant 459 | m.quoted.sender = conn.decodeJid(m.msg?.contextInfo?.participant) 460 | 461 | m.quoted.isOwner = m.quoted.sender && [...config.options.owner, botNumber.split`@`[0]].includes(m.quoted.sender.replace(/\D+/g, "")) 462 | if (m.quoted.isGroup) { 463 | m.quoted.metadata = await conn.groupMetadata(m.quoted.from) 464 | m.quoted.admins = (m.quoted.metadata.participants.reduce((memberAdmin, memberNow) => (memberNow.admin ? memberAdmin.push({ 465 | id: memberNow.id, 466 | admin: memberNow.admin 467 | }) : [...memberAdmin]) && memberAdmin, [])) 468 | m.quoted.isAdmin = !!m.quoted.admins.find((member) => member.id === m.quoted.sender) 469 | m.quoted.isBotAdmin = !!m.quoted.admins.find((member) => member.id === botNumber) 470 | } 471 | 472 | m.quoted.mentions = m.quoted.msg?.contextInfo?.mentionedJid || [] 473 | m.quoted.body = m.quoted.msg?.text || m.quoted.msg?.caption || m.quoted?.message?.conversation || m.quoted.msg?.selectedButtonId || m.quoted.msg?.singleSelectReply?.selectedRowId || m.quoted.msg?.selectedId || m.quoted.msg?.contentText || m.quoted.msg?.selectedDisplayText || m.quoted.msg?.title || m.quoted?.msg?.name || "" 474 | m.quoted.prefix = config.options.prefix.test(m.quoted.body) ? m.quoted.body.match(config.options.prefix)[0] : "#" 475 | m.quoted.command = m.quoted.body && m.quoted.body.replace(m.quoted.prefix, '').trim().split(/ +/).shift() 476 | m.quoted.arg = m.quoted.body.trim().split(/ +/).filter(a => a) || [] 477 | m.quoted.args = m.quoted.body.trim().replace(new RegExp("^" + Function.escapeRegExp(m.quoted.prefix), 'i'), '').replace(m.quoted.command, '').split(/ +/).filter(a => a) || [] 478 | m.quoted.text = m.quoted.args.join(" ") 479 | m.quoted.isMedia = !!m.quoted.msg?.mimetype || !!m.quoted.msg?.thumbnailDirectPath 480 | if (m.quoted.isMedia) { 481 | m.quoted.mime = m.quoted.msg?.mimetype 482 | m.quoted.size = m.quoted.msg?.fileLength 483 | m.quoted.height = m.quoted.msg?.height || '' 484 | m.quoted.width = m.quoted.msg?.width || '' 485 | if (/webp/i.test(m.quoted.mime)) { 486 | m.quoted.isAnimated = m?.quoted?.msg?.isAnimated || false 487 | } 488 | } 489 | m.quoted.reply = (text, options = {}) => { 490 | return m.reply(text, { 491 | quoted: m.quoted, 492 | ...options 493 | }) 494 | } 495 | m.quoted.download = (filepath) => { 496 | if (filepath) return conn.downloadMediaMessage(m.quoted, filepath) 497 | else return conn.downloadMediaMessage(m.quoted) 498 | } 499 | } 500 | } 501 | return m 502 | } -------------------------------------------------------------------------------- /lib/sticker.js: -------------------------------------------------------------------------------- 1 | import config from "../config.js" 2 | import Func from "./function.js" 3 | import fs from "fs" 4 | import Crypto from "crypto" 5 | import ff from "fluent-ffmpeg" 6 | import webp from "node-webpmux" 7 | import path from "path" 8 | import { spawn } from "child_process" 9 | import { fileURLToPath } from "url" 10 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 11 | 12 | function ffmpeg(buffer, args = [], ext = '', ext2 = '') { 13 | return new Promise(async (resolve, reject) => { 14 | try { 15 | let tmp = path.join(__dirname, '../temp', + new Date + '.' + ext) 16 | let out = tmp + '.' + ext2 17 | await fs.promises.writeFile(tmp, buffer) 18 | spawn('ffmpeg', [ 19 | '-y', 20 | '-i', tmp, 21 | ...args, 22 | out 23 | ]).on('error', reject).on('close', async (code) => { 24 | try { 25 | await fs.promises.unlink(tmp) 26 | if (code !== 0) return reject(code) 27 | resolve({ 28 | data: await fs.promises.readFile(out), 29 | filename: out, 30 | delete() { 31 | return fs.promises.unlink(out) 32 | } 33 | }) 34 | } catch (e) { 35 | reject(e) 36 | } 37 | }) 38 | } catch (e) { 39 | reject(e) 40 | } 41 | }) 42 | } 43 | 44 | function toPTT(buffer, ext) { 45 | return ffmpeg(buffer, [ 46 | '-vn', 47 | '-c:a', 'libopus', 48 | '-b:a', '128k', 49 | '-vbr', 'on', 50 | ], ext, 'ogg') 51 | } 52 | 53 | function toAudio(buffer, ext) { 54 | return ffmpeg(buffer, [ 55 | '-vn', 56 | '-c:a', 'libopus', 57 | '-b:a', '128k', 58 | '-vbr', 'on', 59 | '-compression_level', '10' 60 | ], ext, 'opus') 61 | } 62 | 63 | async function imageToWebp(media) { 64 | const tmpFileOut = path.join(process.cwd(), "temp", `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`) 65 | const tmpFileIn = path.join(process.cwd(), "temp", `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.jpg`) 66 | 67 | fs.writeFileSync(tmpFileIn, media) 68 | 69 | await new Promise((resolve, reject) => { 70 | ff(tmpFileIn) 71 | .on("error", reject) 72 | .on("end", () => resolve(true)) 73 | //.addOutputOptions([`-vcodec`,`libwebp`,`-vf`,`scale=512:512:force_original_aspect_ratio=increase,fps=15,crop=512:512`]).toFormat('webp').save(tmpFileOut) 74 | .addOutputOptions([ 75 | "-vcodec", 76 | "libwebp", 77 | "-vf", 78 | "scale='min(320,iw)':min'(320,ih)':force_original_aspect_ratio=decrease,fps=15, pad=320:320:-1:-1:color=white@0.0, split [a][b]; [a] palettegen=reserve_transparent=on:transparency_color=ffffff [p]; [b][p] paletteuse" 79 | ]) 80 | .toFormat("webp") 81 | .save(tmpFileOut) 82 | }) 83 | 84 | const buff = fs.readFileSync(tmpFileOut) 85 | fs.promises.unlink(tmpFileOut) 86 | fs.promises.unlink(tmpFileIn) 87 | return buff 88 | } 89 | 90 | async function videoToWebp(media) { 91 | const tmpFileOut = path.join(process.cwd(), "temp", `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`) 92 | const tmpFileIn = path.join(process.cwd(), "temp", `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.mp4`) 93 | 94 | fs.writeFileSync(tmpFileIn, media) 95 | 96 | await new Promise((resolve, reject) => { 97 | ff(tmpFileIn) 98 | .on("error", reject) 99 | .on("end", () => resolve(true)) 100 | //.addOutputOptions([`-vcodec`,`libwebp`,`-vf`,`scale=512:512:force_original_aspect_ratio=increase,fps=15,crop=512:512`]).toFormat('webp').save(tmpFileOut) 101 | .addOutputOptions([ 102 | '-vcodec', 103 | 'libwebp', 104 | '-vf', 105 | // eslint-disable-next-line no-useless-escape 106 | "scale='min(320,iw)':min'(320,ih)':force_original_aspect_ratio=decrease,fps=15, pad=320:320:-1:-1:color=white@0.0, split [a][b]; [a] palettegen=reserve_transparent=on:transparency_color=ffffff [p]; [b][p] paletteuse", 107 | '-loop', 108 | '0', 109 | '-ss', 110 | '00:00:00.0', 111 | '-t', 112 | '00:00:05.0', 113 | '-preset', 114 | 'default', 115 | '-an', 116 | '-vsync', 117 | '0' 118 | ]) 119 | .toFormat("webp") 120 | .save(tmpFileOut) 121 | }) 122 | 123 | const buff = fs.readFileSync(tmpFileOut) 124 | fs.promises.unlink(tmpFileOut) 125 | fs.promises.unlink(tmpFileIn) 126 | return buff 127 | } 128 | 129 | async function writeExif(media, metadata) { 130 | let wMedia = /webp/.test(media.mimetype) ? media.data : /image/.test(media.mimetype) ? await imageToWebp(media.data) : /video/.test(media.mimetype) ? await videoToWebp(media.data) : "" 131 | const tmpFileOut = path.join(process.cwd(), "temp", `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`) 132 | const tmpFileIn = path.join(process.cwd(), "temp", `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(30)}.webp`) 133 | fs.writeFileSync(tmpFileIn, wMedia) 134 | if (Object.keys(metadata).length != 0 || Object.keys(config?.Exif).length != 0) { 135 | const img = new webp.Image() 136 | let opt = { packId: metadata?.packId ? metadata.packId : config?.Exif?.packId, packName: metadata?.packName ? metadata.packName : '', packPublish: metadata?.packPublish ? metadata.packPublish : '', packEmail: metadata?.packEmail ? metadata.packEmail : config?.Exif?.packEmail, packWebsite: metadata?.packWebsite ? metadata.packWebsite : config?.Exif?.packWebsite, androidApp: metadata?.androidApp ? metadata.androidApp : config?.Exif?.androidApp, iOSApp: metadata?.iOSApp ? metadata.iOSApp : config?.Exif?.iOSApp, emojis: metadata?.emojis ? metadata.emojis : config?.Exif?.emojis, isAvatar: metadata?.isAvatar ? metadata.isAvatar : config?.Exif?.isAvatar } 137 | const json = { "sticker-pack-id": opt.packId, "sticker-pack-name": opt.packName, "sticker-pack-publisher": opt.packPublish, "sticker-pack-publisher-email": opt.packEmail, "sticker-pack-publisher-website": opt.packWebsite, "android-app-store-link": opt.androidApp, "ios-app-store-link": opt.iOSApp, "emojis": opt.emojis, "is-avatar-sticker": opt.isAvatar } 138 | const exifAttr = Buffer.from([0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00]) 139 | const jsonBuff = Buffer.from(JSON.stringify(json), "utf-8") 140 | const exif = Buffer.concat([exifAttr, jsonBuff]) 141 | exif.writeUIntLE(jsonBuff.length, 14, 4) 142 | await img.load(tmpFileIn) 143 | fs.promises.unlink(tmpFileIn) 144 | img.exif = exif 145 | await img.save(tmpFileOut) 146 | return tmpFileOut 147 | } 148 | } 149 | 150 | function webp2mp4File(source) { 151 | return new Promise((resolve, reject) => { 152 | const form = new Func.FormData() 153 | let isUrl = typeof source === 'string' && /https?:\/\//.test(source) 154 | form.append('new-image-url', isUrl ? source : "") 155 | form.append('new-image', isUrl ? "" : source, Date.now() + "-image.webp") 156 | Func.axios({ 157 | method: 'post', 158 | url: 'https://s6.ezgif.com/webp-to-mp4', 159 | data: form, 160 | headers: { 161 | 'Content-Type': `multipart/form-data; boundary=${form._boundary}` 162 | } 163 | }).then(({ data }) => { 164 | const bodyFormThen = new Func.FormData() 165 | const $ = Func.cheerio.load(data) 166 | const file = $('input[name="file"]').attr('value') 167 | const token = $('input[name="token"]').attr('value') 168 | const convert = $('input[name="file"]').attr('value') 169 | 170 | bodyFormThen.append('file', file) 171 | bodyFormThen.append('convert', "Convert WebP to MP4!") 172 | Func.axios({ 173 | method: 'post', 174 | url: 'https://ezgif.com/webp-to-mp4/' + file, 175 | data: bodyFormThen, 176 | headers: { 177 | 'Content-Type': `multipart/form-data; boundary=${bodyFormThen._boundary}` 178 | } 179 | }).then(({ data }) => { 180 | const $ = Func.cheerio.load(data) 181 | const result = 'https:' + $('div#output > p.outfile > video > source').attr('src') 182 | resolve(result) 183 | }).catch(reject) 184 | }).catch(reject) 185 | }) 186 | } 187 | 188 | export { imageToWebp, videoToWebp, writeExif, webp2mp4File, ffmpeg, toAudio, toPTT } -------------------------------------------------------------------------------- /lib/system/event.js: -------------------------------------------------------------------------------- 1 | import Func from '../function.js' 2 | import config from '../../config.js' 3 | import AlyaApi from '@moonr/api' 4 | const Api = new AlyaApi() 5 | 6 | export default async function Event(conn, m) { 7 | /** autoread */ 8 | if (global.db.setting.autoread) { 9 | conn.sendPresenceUpdate('available', m.chat) 10 | conn.readMessages([m.key]) 11 | } 12 | /** chat bot */ 13 | if (!m.isGroup) { 14 | try { 15 | if (global.db.setting.chatbot && m.body && !config.options.evaluate_chars.some(v => m.body.startsWith(v))) { 16 | const json = await Api.post('api/completions', { 17 | model: 'cognitivecomputations/dolphin-2.9.1-llama-3-70b', 18 | messages: JSON.stringify([{ role: 'system', content: 'Be a helpful assistant' }, { role: 'user', content: `${m.body}` }]) 19 | }) 20 | if (!json.status) return console.log(json) 21 | if (!m.fromMe && !m.isGroup && json.status) return m.reply(json.data.choices[0].message.content) 22 | } 23 | } catch (e) { 24 | console.log(e) 25 | } 26 | } 27 | /** afk */ 28 | if (m.isGroup) { 29 | let jids = [...new Set([...(m.mentions || []), ...(m.quoted ? [m.quoted.sender] : [])])] 30 | for (let jid of jids) { 31 | let user = global.db.users[jid] 32 | if (!user) continue 33 | let afkTime = user.afkTime 34 | if (!afkTime || afkTime < 0) continue 35 | let reason = user.afkReason || '' 36 | m.reply(`Jangan tag dia!\nDia sedang AFK ${reason ? 'dengan alasan ' + reason : 'tanpa alasan'} Selama ${Func.toTime(new Date - afkTime)}`) 37 | } 38 | if (global.db.users[m.sender].afkTime > -1) { 39 | m.reply(`Kamu berhenti AFK${global.db.users[m.sender].afkReason ? ' setelah ' + global.db.users[m.sender].afkReason : ''}\n\nSelama ${Func.toTime(new Date() - global.db.users[m.sender].afkTime)}`) 40 | global.db.users[m.sender].afkTime = -1 41 | global.db.users[m.sender].afkReason = '' 42 | } 43 | } 44 | /** expired premium */ 45 | if (global.db.users[m.sender] && (new Date * 1) >= global.db.users[m.sender].expired && global.db.users[m.sender].expired != 0) { 46 | return m.reply('Your premium package has expired, thank you for buying and using our service.').then(async () => { 47 | global.db.users[m.sender].premium = false 48 | global.db.users[m.sender].expired = 0 49 | global.db.users[m.sender].limit = config.limit.free 50 | }) 51 | } 52 | /** expired group */ 53 | if (m.isGroup && (new Date * 1) >= global.db.groups[m.from].expired && global.db.groups[m.from].expired != 0) { 54 | return m.reply('Bot time has expired and will leave from this group, thank you.').then(async () => { 55 | global.db.groups[m.from].expired = 0 56 | await Func.sleep(2000).then(() => conn.groupLeave(m.from)) 57 | }) 58 | } 59 | } -------------------------------------------------------------------------------- /lib/system/group-participants.js: -------------------------------------------------------------------------------- 1 | import config from "../../config.js" 2 | 3 | export default async function GroupParticipants(conn, { id, participants, action }) { 4 | try { 5 | const metadata = await conn.groupMetadata(id) 6 | 7 | // participants 8 | for (const jid of participants) { 9 | // get profile picture user 10 | let profile 11 | try { 12 | profile = await conn.profilePictureUrl(jid, "image") 13 | } catch { 14 | profile = "https://lh3.googleusercontent.com/proxy/esjjzRYoXlhgNYXqU8Gf_3lu6V-eONTnymkLzdwQ6F6z0MWAqIwIpqgq_lk4caRIZF_0Uqb5U8NWNrJcaeTuCjp7xZlpL48JDx-qzAXSTh00AVVqBoT7MJ0259pik9mnQ1LldFLfHZUGDGY=w1200-h630-p-k-no-nu" 15 | } 16 | 17 | // action 18 | if (action == "add") { 19 | if (!db.groups[id]?.welcome) return 20 | conn.sendMessageModify(id, `Welcome @${jid.split("@")[0]} to "${metadata.subject}"`, null, { 21 | largeThumb: true, 22 | thumbnail: profile, 23 | url: db.setting.link 24 | }) 25 | } else if (action == "remove") { 26 | if (!db.groups[id]?.leave) return 27 | conn.sendMessageModify(id, `@${jid.split("@")[0]} Leaving From "${metadata.subject}"`, null, { 28 | largeThumb: true, 29 | thumbnail: profile, 30 | url: db.setting.link 31 | }) 32 | } 33 | } 34 | } catch (e) { 35 | throw e 36 | } 37 | } -------------------------------------------------------------------------------- /lib/system/group-update.js: -------------------------------------------------------------------------------- 1 | import config from "../../config.js" 2 | 3 | export default async function GroupUpdate(conn, update) { 4 | try { 5 | for (const action of update) { 6 | // get profile picture group 7 | let profile 8 | try { 9 | profile = await conn.profilePictureUrl(action.id, "image") 10 | } catch { 11 | profile = "https://lh3.googleusercontent.com/proxy/esjjzRYoXlhgNYXqU8Gf_3lu6V-eONTnymkLzdwQ6F6z0MWAqIwIpqgq_lk4caRIZF_0Uqb5U8NWNrJcaeTuCjp7xZlpL48JDx-qzAXSTh00AVVqBoT7MJ0259pik9mnQ1LldFLfHZUGDGY=w1200-h630-p-k-no-nu" 12 | } 13 | 14 | // action 15 | if (!db.groups[id]?.detect) return 16 | if (action.announce) { 17 | conn.sendMessageModify(action.id, `Group has been Closed`, null, { 18 | largeThumb: true, 19 | thumbnail: profile, 20 | url: db.setting.link 21 | }) 22 | } else if (!action.announce) { 23 | conn.sendMessageModify(action.id, `Group is opened`, null, { 24 | largeThumb: true, 25 | thumbnail: profile, 26 | url: db.setting.link 27 | }) 28 | } 29 | } 30 | } catch (e) { 31 | throw e 32 | } 33 | } -------------------------------------------------------------------------------- /lib/system/message.js: -------------------------------------------------------------------------------- 1 | import 'dotenv/config' 2 | import AlyaApi from '@moonr/api' 3 | import config from '../../config.js' 4 | import Func from '../function.js' 5 | import Scraper from '../scraper.js' 6 | import fs from 'fs' 7 | import axios from 'axios' 8 | import path from 'path' 9 | import moment from 'moment-timezone' 10 | import { 11 | getBinaryNodeChildren, 12 | generateWAMessage 13 | } from '@whiskeysockets/baileys' 14 | import { 15 | exec 16 | } from 'child_process' 17 | import { 18 | format, 19 | promisify 20 | } from 'util' 21 | import { 22 | fileURLToPath 23 | } from 'url' 24 | import { 25 | createRequire 26 | } from 'module' 27 | import yts from 'yt-search' 28 | 29 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 30 | const __filename = Func.__filename(import.meta.url) 31 | const require = createRequire(import.meta.url) 32 | const Api = new AlyaApi() 33 | 34 | export default async function Message(conn, m, chatUpdate, database) { 35 | try { 36 | if (!m) return 37 | if (!config.options.public && !m.isOwner) return 38 | if (m.from && db.groups[m.from]?.mute && !m.isOwner) return 39 | if (m.isBot) return 40 | 41 | const prefix = m.prefix 42 | const isCmd = m.body.startsWith(prefix) 43 | const command = isCmd ? m.command.toLowerCase() : '' 44 | const quoted = m.isQuoted ? m.quoted : m 45 | const mime = (quoted.msg || quoted).mimetype || '' 46 | const isMedia = /(document|audio|sticker|image|video)/.test(mime) 47 | 48 | await (await import('./event.js')).default(conn, m) 49 | await (await import('../logs.js')).default(m) 50 | await (await import('../schema.js')).default(m) 51 | 52 | switch (command) { 53 | /** menu */ 54 | case 'menu': 55 | case 'help': { 56 | let d = new Date(new Date + 3600000) 57 | let locale = 'id' 58 | let weton = ['Pahing', 'Pon', 'Wage', 'Kliwon', 'Legi'][Math.floor(d / 84600000) % 5] 59 | let week = d.toLocaleDateString(locale, { weekday: 'long' }) 60 | let date = d.toLocaleDateString(locale, { 61 | day: 'numeric', 62 | month: 'long', 63 | year: 'numeric' 64 | }) 65 | let dateIslamic = Intl.DateTimeFormat(locale + '-TN-u-ca-islamic', { 66 | day: 'numeric', 67 | month: 'long', 68 | year: 'numeric' 69 | }).format(d) 70 | let time = d.toLocaleTimeString(locale, { 71 | hour: 'numeric', 72 | minute: 'numeric', 73 | second: 'numeric' 74 | }) 75 | let text = `${Func.greeting()} @${m.sender.split`@`[0]}👋\n` 76 | text += `\n` 77 | text += `⎔ Date : ${week} ${weton}, ${date}\n` 78 | text += `⎔ Islamic Date : ${dateIslamic}\n` 79 | text += `⎔ Time : ${time}\n` 80 | text += `\n` 81 | text += `⎔ Uptime : ${Func.toTime(process.uptime() * 1000)}\n` 82 | text += `⎔ API : https://api.alyachan.dev\n` 83 | text += `⎔ Github : https://github.com/rifnd/moon-case\n` 84 | text += `\n` 85 | Object.entries(config.menu).sort((a, b) => a[0].localeCompare(b[0])).forEach(([type, commands]) => { 86 | text += `⎔ ${Func.toUpper(type)}\n\n` 87 | if (commands.length > 0) { 88 | text += `┌ ${prefix}${commands[0]}\n` 89 | for (let i = 1; i < commands.length - 1; i++) { 90 | text += `│ ${prefix}${commands[i]}\n` 91 | } 92 | if (commands.length > 1) { 93 | text += `└ ${prefix}${commands[commands.length - 1]}\n` 94 | } 95 | } 96 | text += `\n` 97 | }) 98 | return conn.sendMessageModify(m.from, text + config.options.footer, m, { 99 | largeThumb: true, 100 | thumbnail: global.db.setting.cover, 101 | url: global.db.setting.link 102 | }) 103 | } 104 | break 105 | 106 | /** misc menu */ 107 | case 'owner': 108 | case 'creator': { 109 | conn.sendContact(m.from, config.options.owner, m) 110 | } 111 | break 112 | case 'sc': 113 | case 'sourcecode': { 114 | m.reply('https://github.com/rifnd/moon-case') 115 | } 116 | break 117 | case 'ping': { 118 | const moment = (await import('moment-timezone')).default 119 | const calculatePing = function (timestamp, now) { 120 | return moment.duration(now - moment(timestamp * 1000)).asSeconds() 121 | } 122 | m.reply(`*Ping :* *_${calculatePing(m.timestamp, Date.now())} second(s)_*`) 123 | } 124 | break 125 | case 'run': 126 | case 'runtime': { 127 | m.reply(`${Func.toTime(process.uptime() * 1000)}`) 128 | } 129 | break 130 | case 'apikey': 131 | case 'checkapi': { 132 | m.reply('Checking API Key...') 133 | const json = await Api.get('v1/check-key') 134 | m.reply(Func.Format(json)) 135 | } 136 | break 137 | case 'groups': { 138 | try { 139 | let groups = Object.entries(await conn.groupFetchAllParticipating()).map(entry => entry[1]).filter(group => !group.isCommunity) 140 | if (groups.length === 0) return m.reply('Bot does not join any groups') 141 | let capt = 'List Group :\n\n' 142 | groups.map((x, i) => { 143 | let v = global.db.groups[x.id] 144 | if (!v) { 145 | global.db.groups[x.id] = { 146 | activity: 0, 147 | mute: false, 148 | text_welcome: '', 149 | text_leave: '', 150 | welcome: true, 151 | leave: true, 152 | detect: false, 153 | antilink: false, 154 | member: {}, 155 | expired: 0 156 | } 157 | } 158 | capt += `*${(i + 1)}.* ${x.subject}\n` 159 | capt += `│ Expired : ${v.expired == 0 ? 'NOT SET' : Func.timeReverse(v.expired - new Date() * 1)}\n` 160 | capt += `│ Member : ${x.participants.length}\n` 161 | capt += `│ Status : ${(v.mute ? 'OFF' : 'ON')}\n` 162 | capt += `└ Last Activity : ${moment(v.activity).format('DD/MM/YY HH:mm:ss')}\n\n` 163 | }) 164 | capt += config.options.wm 165 | m.reply(capt) 166 | } catch (e) { 167 | return m.reply(format(e)) 168 | } 169 | } 170 | break 171 | 172 | /** AI menu */ 173 | case 'ai': 174 | case 'openai': 175 | case 'chatgpt': { 176 | try { 177 | if (!m.text) return m.reply(`Example : ${prefix + command} moon bot`) 178 | m.react('🕒') 179 | var json = await Api.get('api/openai', { 180 | prompt: m.text 181 | }) 182 | if (!json.status) return m.reply(Func.Format(json)) 183 | m.reply(json.data.content) 184 | } catch (e) { 185 | m.reply(format(e)) 186 | } 187 | } 188 | break 189 | case 'article': { 190 | try { 191 | if (!m.text) return m.reply(`Example : ${prefix + command} hujan | Indonesia`) 192 | let [teks, iso] = m.text.split` | ` 193 | m.react('🕒') 194 | let json = await Api.get('api/ai-article', { 195 | text: teks, lang: iso 196 | }) 197 | if (!json.status) return m.reply(format(json)) 198 | m.reply(json.data.content) 199 | } catch (e) { 200 | return m.reply(format(e)) 201 | } 202 | } 203 | break 204 | case 'gemini': 205 | case 'bard': { 206 | try { 207 | if (!m.text) return m.reply(`Example : ${prefix + command} moon bot`) 208 | m.react('🕒') 209 | if (/image\/(jpe?g|png)/.test(quoted.mime)) { 210 | let res = await Scraper.uploader(await quoted.download()) 211 | var json = await Api.get('api/func-chat', { 212 | model: 'gemini', 213 | system: m.text, 214 | image: res.data.url 215 | }) 216 | if (!respon.status) return m.reply(format(respon)) 217 | m.reply(json.data.content) 218 | 219 | } else if (m.text) { 220 | var json = await Api.get('api/ai-gemini', { 221 | q: m.text 222 | }) 223 | if (!json.status) return m.reply(format(json)) 224 | m.reply(json.data.content) 225 | } 226 | } catch (e) { 227 | return m.reply(format(e)) 228 | } 229 | } 230 | break 231 | case 'blackbox': { 232 | try { 233 | if (!m.text) return m.reply(`Example : ${prefix + command} moon bot`) 234 | m.react('🕒') 235 | if (/image\/(jpe?g|png)/.test(quoted.mime)) { 236 | let res = await Scraper.uploader(await quoted.download()) 237 | var json = await Api.get('api/func-chat', { 238 | model: 'blackbox', 239 | system: m.text, 240 | image: res.data.url 241 | }) 242 | if (!respon.status) return m.reply(format(respon)) 243 | m.reply(json.data.content) 244 | 245 | } else if (m.text) { 246 | var json = await Api.post('api/ai-blackbox', { 247 | messages: JSON.stringify([{ id: "6D0t86e", role: "system", content: "Be a helpful assistant" }, { id: "6D0t86e", role: "user", content: `${m.text}` }]) 248 | }) 249 | if (!json.status) return m.reply(format(json)) 250 | m.reply(json.data.content) 251 | } 252 | } catch (e) { 253 | return m.reply(format(e)) 254 | } 255 | } 256 | break 257 | case 'claude': { 258 | try { 259 | if (!m.text) return m.reply(`Example : ${prefix + command} moon bot`) 260 | m.react('🕒') 261 | var result = await Api.get('api/duckduckgo', { 262 | msg: text, 263 | model: 'claude-3-haiku-20240307' 264 | }) 265 | if (!result.status) return m.reply(format(json)) 266 | m.reply(result.data.content) 267 | } catch (e) { 268 | return m.reply(format(e)) 269 | } 270 | } 271 | break 272 | case 'code': { 273 | try { 274 | if (!m.text) return m.reply(`Example : ${prefix + command} How to create delay function | js`) 275 | let [code, act] = m.text.split` | ` 276 | m.react('🕒') 277 | const json = await Api.get('api/ai-code', { 278 | text: code, action: act 279 | }) 280 | if (!json.status) return m.reply(format(json)) 281 | m.reply(json.data.code) 282 | } catch (e) { 283 | return m.reply(format(e)) 284 | } 285 | } 286 | break 287 | case 'copilot': { 288 | try { 289 | if (!m.text) return m.reply(`Example : ${prefix + command} kucing`) 290 | m.react('🕒') 291 | const json = await Api.get('api/ai-copilot', { 292 | q: m.text 293 | }) 294 | if (!json.status) return m.reply(format(json)) 295 | m.reply(json.data.content) 296 | } catch (e) { 297 | return m.reply(format(e)) 298 | } 299 | } 300 | break 301 | case 'stablediffusion': 302 | case 'stablediff': 303 | case 'diffusion': { 304 | try { 305 | if (!m.text) return m.reply(`Example : ${prefix + command} anime girl`) 306 | m.react('🕒') 307 | let json = await Api.get('api/diffusion', { 308 | prompt: m.text 309 | }) 310 | if (!json.status) return m.reply(format(json)) 311 | for (let i = 0; i < 3; i++) { 312 | let rand = Math.floor(json.data.length * Math.random()) 313 | m.reply(json.data[rand].cover, { 314 | caption: `${json.data[rand].prompt}`, 315 | fileName: Func.uuid(10) + '.jpg', mimetype: 'image/jpeg' 316 | }) 317 | } 318 | } catch (e) { 319 | return m.reply(format(e)) 320 | } 321 | } 322 | break 323 | case 'dokter': { 324 | try { 325 | if (!m.text) return m.reply(`Example : ${prefix + command} sakit kepala`) 326 | m.react('🕒') 327 | let json = await Api.get('api/ai-dokter', { 328 | text: m.text 329 | }) 330 | if (!json.status) return m.reply(format(json)) 331 | m.reply(json.data.content) 332 | } catch (e) { 333 | return m.reply(format(e)) 334 | } 335 | } 336 | break 337 | case 'mathsolver': { 338 | try { 339 | if (!m.text) return m.reply(`Example : ${prefix + command} 1 + 1`) 340 | m.react('🕒') 341 | let json = await Api.get('api/ai-mathsolver', { 342 | text: m.text 343 | }) 344 | if (!json.status) return m.reply(format(json)) 345 | m.reply(json.data.answe) 346 | } catch (e) { 347 | return m.reply(format(e)) 348 | } 349 | } 350 | break 351 | case 'meta': { 352 | try { 353 | if (!m.text) return m.reply(`Example : ${prefix + command} 1 + 1`) 354 | m.react('🕒') 355 | let json = await Api.get('api/ai-meta', { 356 | prompt: m.text 357 | }) 358 | if (!json.status) return m.reply(format(json)) 359 | if (json.data.imagine_media.length != 0) { 360 | json.data.imagine_media.map(async v => { 361 | await m.reply(v.uri, { 362 | caption: config.options.wm 363 | }) 364 | await Func.sleep(1500) 365 | }) 366 | } else { 367 | m.reply(json.data.content) 368 | } 369 | } catch (e) { 370 | return m.reply(format(e)) 371 | } 372 | } 373 | break 374 | case 'qwen': { 375 | try { 376 | if (!m.text) return m.reply(`Example : ${prefix + command} mark itu orang atau alien`) 377 | m.react('🕒') 378 | let json = await Api.get('api/qwen', { 379 | msg: m.text, 380 | model: 'qwen-max-latest', 381 | realtime: false 382 | }) 383 | if (!json.status) return m.reply(format(json)) 384 | m.reply(result.data.choices[0].message.content) 385 | } catch (e) { 386 | return m.reply(format(e)) 387 | } 388 | } 389 | break 390 | 391 | /** voice menu */ 392 | case 'bass': case 'blown': case 'deep': case 'earrape': case 'fast': case 'fat': case 'nightcore': case 'reverse': case 'robot': case 'slow': case 'smooth': case 'tupai': { 393 | try { 394 | let set 395 | if (/bass/.test(command)) set = '-af equalizer=f=54:width_type=o:width=2:g=20' 396 | if (/blown/.test(command)) set = '-af acrusher=.1:1:64:0:log' 397 | if (/deep/.test(command)) set = '-af atempo=4/4,asetrate=44500*2/3' 398 | if (/earrape/.test(command)) set = '-af volume=12' 399 | if (/fast/.test(command)) set = '-filter:a "atempo=1.63,asetrate=44100"' 400 | if (/fat/.test(command)) set = '-filter:a "atempo=1.6,asetrate=22100"' 401 | if (/nightcore/.test(command)) set = '-filter:a atempo=1.06,asetrate=44100*1.25' 402 | if (/reverse/.test(command)) set = '-filter_complex "areverse"' 403 | if (/robot/.test(command)) set = '-filter_complex "afftfilt=real=\'hypot(re,im)*sin(0)\':imag=\'hypot(re,im)*cos(0)\':win_size=512:overlap=0.75"' 404 | if (/slow/.test(command)) set = '-filter:a "atempo=0.7,asetrate=44100"' 405 | if (/smooth/.test(command)) set = '-filter:v "minterpolate=\'mi_mode=mci:mc_mode=aobmc:vsbmc=1:fps=120\'"' 406 | if (/tupai/.test(command)) set = '-filter:a "atempo=0.5,asetrate=65100"' 407 | if (/audio/.test(mime)) { 408 | m.react('🕒') 409 | let media = await conn.downloadAndSaveMediaMessage(quoted) 410 | let ran = Func.getRandom('.mp3') 411 | exec(`ffmpeg -i ${media} ${set} ${ran}`, (err, stderr, stdout) => { 412 | fs.unlinkSync(media) 413 | if (err) return m.reply(err) 414 | let buff = fs.readFileSync(ran) 415 | m.reply(buff, { fileName: 'vn.mp3', mimetype: 'audio/mpeg' }) 416 | fs.unlinkSync(ran) 417 | }) 418 | } else { 419 | m.reply(`Balas audio yang ingin diubah dengan caption *${prefix + command}*`) 420 | } 421 | } catch (e) { 422 | console.log(e) 423 | } 424 | } 425 | break 426 | 427 | /** fun menu */ 428 | case 'apakah': { 429 | if (!m.text) return m.reply('Apa yang ingin kamu tanyakan?') 430 | let jawab = ['Ya', 'Mungkin iya', 'Mungkin', 'Mungkin tidak', 'Tidak', 'Tidak mungkin', 'Kurang tau', 'kayaknya iya', 'Mungkin sih', 'Sepertinya iya', 'Sepertinya tidak', 'mustahil', 'hooh', 'iyoooo', 'gak tau saya'] 431 | let json = Func.random(jawab) 432 | m.reply(json) 433 | } 434 | break 435 | case 'kapankah': { 436 | if (!m.text) return m.reply('Apa yang ingin kamu tanyakan?') 437 | let jawab = ['detik', 'menit', 'jam', 'hari', 'minggu', 'bulan', 'tahun', 'dekade', 'abad'] 438 | let json = Func.random(jawab) 439 | m.reply(`${Math.floor(Math.random() * 10)} ${json} lagi ...`) 440 | } 441 | break 442 | case 'rate': { 443 | if (!m.text) return m.reply('Apa yang ingin kamu rate?') 444 | const ra = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '100'] 445 | const json = Func.random(ra) 446 | m.reply(`${text} ${json}%`) 447 | } 448 | break 449 | case 'siapakah': { 450 | if (!m.text) return m.reply('Apa yang ingin kamu tanyakan?') 451 | if (!m.isGroup) return m.reply('group') 452 | let who 453 | if (!m.isGroup) { 454 | let member = [m.sender, conn.user.jid] 455 | who = member[Math.floor(Math.random() * member.length)] 456 | } else { 457 | let member = participants.map((u) => u.id) 458 | who = member[Math.floor(Math.random() * member.length)] 459 | } 460 | m.reply(`@${who.split`@`[0]}`) 461 | } 462 | break 463 | case 'benarkah': { 464 | if (!m.text) return m.reply('Apanya yang benar?') 465 | let jawab = ['Iya', 'Sudah pasti', 'Sudah pasti benar', 'Tidak', 'Tentu tidak', 'Sudah pasti tidak'] 466 | const json = Func.random(jawab) 467 | m.reply(json) 468 | } 469 | break 470 | case 'bisakah': { 471 | if (!m.text) return m.reply('Apanya yang bisa?') 472 | let jawab = ['Iya', 'Bisa', 'Tentu saja bisa', 'Tentu bisa', 'Sudah pasti', 'Sudah pasti bisa', 'Tidak', 'Tidak bisa', 'Tentu tidak', 'tentu tidak bisa', 'Sudah pasti tidak'] 473 | const json = Func.random(jawab) 474 | m.reply(json) 475 | } 476 | break 477 | 478 | /** downloader menu */ 479 | case 'tiktok': 480 | case 'tikwm': 481 | case 'tikmp3': 482 | case 'tt': { 483 | if (!/https?:\/\/(www\.|v(t|m|vt)\.|t\.)?tiktok\.com/i.test(m.text)) return m.reply(`Example : ${prefix + command} https://vt.tiktok.com/ZSwWCk5o/`) 484 | await m.react('🕒') 485 | var json = await Api.get('api/tiktok', { 486 | url: Func.isUrl(m.text)[0] 487 | }) 488 | if (!json.status) return m.reply(Func.Format(json)) 489 | if (command == 'tiktok' || command == 'tt') { 490 | let result = json.data.find(v => v.type == 'nowatermark') 491 | if (!result) { 492 | json.data.map(x => { 493 | m.reply(x.url, { 494 | caption: `${json.author.nickname}\n\n${json.title}` 495 | }) 496 | }) 497 | } else { 498 | m.reply(result.url, { 499 | caption: `${json.author.nickname}\n\n${json.title}` 500 | }) 501 | } 502 | } else if (command == 'tikwm') return m.reply(json.data.find(v => v.type == 'watermark').url, { 503 | caption: `${json.author.nickname}\n\n${json.title}` 504 | }) 505 | else if (command == 'tikmp3') return m.reply(json.music_info.url) 506 | } 507 | break 508 | case 'fb': 509 | case 'fbdl': 510 | case 'facebook': { 511 | if (!/https?:\/\/(fb\.watch|(www\.|web\.|m\.)?facebook\.com)/i.test(m.text)) return m.reply(`Example : ${prefix + command} https://www.facebook.com/watch/?v=2018727118289093`) 512 | await m.react('🕒') 513 | var json = await Api.get('api/fb', { 514 | url: Func.isUrl(m.text)[0] 515 | }) 516 | if (!json.status) return m.reply(Func.Format(json)) 517 | let result = json.data.find(v => v.quality == 'HD') || json.data.find(v => v.quality == 'SD') 518 | m.reply(result.url, { caption: `${result.quality}` }) 519 | } 520 | break 521 | case 'ig': 522 | case 'igdl': 523 | case 'instagram': { 524 | if (!/https?:\/\/(www\.)?instagram\.com\/(p|reel|tv)/i.test(m.text)) return m.reply(`Example : ${prefix + command} https://www.instagram.com/p/CITVsRYnE9h/`) 525 | await m.react('🕒') 526 | var json = await Api.get('api/ig', { 527 | url: Func.isUrl(m.text)[0] 528 | }) 529 | if (!json.status) return m.reply(Func.Format(json)) 530 | for (let v of json.data) { 531 | m.reply(v.url, { caption: config.options.wm }) 532 | } 533 | } 534 | break 535 | case 'twit': 536 | case 'twt': 537 | case 'twitter': { 538 | if (!/https?:\/\/(www\.)?(twitter|X)\.com\/.*\/status/i.test(m.text)) return m.reply(`Example : ${prefix + command} https://twitter.com/jokowi/status/1687008875864846336?s=20`) 539 | await m.react('🕒') 540 | var json = await Api.get('api/twitter', { 541 | url: Func.isUrl(m.text)[0] 542 | }) 543 | if (!json.status) return m.reply(Func.Format(json)) 544 | for (let v of json.data) { 545 | m.reply(v.url, { caption: config.options.wm }) 546 | } 547 | } 548 | break 549 | case 'threads': 550 | case 'thread': 551 | case 'threadsdl': { 552 | if (!/https?:\/\/(www\.)?(threads)\.net/i.test(m.text)) return m.reply(`Example : ${prefix + command} https://www.threads.net/t/CuiXbGvPyJz/?igshid=NTc4MTIwNjQ2YQ==`) 553 | await m.react('🕒') 554 | var json = await Api.get('api/threads', { 555 | url: Func.isUrl(m.text)[0] 556 | }) 557 | if (!json.status) return m.reply(Func.Format(json)) 558 | for (let v of json.data) { 559 | m.reply(v.url, { caption: config.options.wm }) 560 | } 561 | } 562 | break 563 | case 'igstory': 564 | case 'igs': 565 | case 'instagramstory': { 566 | if (!m.text) return m.reply(`Example : ${prefix + command} bulansutena`) 567 | await m.react('🕒') 568 | let old = new Date() 569 | var json = await Api.get('api/igs', { 570 | q: m.text 571 | }) 572 | if (!json.status) return m.reply(Func.Format(json)) 573 | json.data.map(async (v, i) => { 574 | m.reply(v.url, { caption: config.options.wm }) 575 | await Func.delay(1500) 576 | }) 577 | } 578 | break 579 | case 'play': 580 | case 'lagu': 581 | case 'music': { 582 | if (!m.text) return m.reply(`Example : ${prefix + command} hapus aku`) 583 | await m.react('🕒') 584 | let ys = await (await yts(m.text)).all 585 | var yt = ys.filter(p => p.type == 'video') 586 | var json = await Api.get('api/yta', { 587 | url: yt[0].url 588 | }) 589 | if (!json.status) return m.reply(Func.Format(json)) 590 | let cap = '*Youtube Play*\n\n' 591 | cap += `⎔ *Title* : ${json.title}\n` 592 | cap += `⎔ *Size* : ${json.data.size}\n` 593 | cap += `⎔ *Duration* : ${json.duration}\n` 594 | cap += `⎔ *Quality* : ${json.data.quality}\n\n` 595 | cap += config.options.wm 596 | conn.sendMessageModify(m.from, cap, m, { 597 | largeThumb: true, 598 | thumbnail: json.thumbnail 599 | }).then(() => { 600 | m.reply(json.data.url, { fileName: json.data.filename, mimetype: 'audio/mpeg' }) 601 | }) 602 | } 603 | break 604 | case 'yta': 605 | case 'ytmp3': { 606 | if (!/(?:https?:\/\/)?(?:youtu\.be\/|(?:www\.|m\.)?(?:music\.)?youtube\.com\/(?:watch|v|embed|shorts))/i.test(m.text)) return m.reply(`Example : ${prefix + command} https://youtu.be/_EYbfKMTpRs`) 607 | await m.react('🕒') 608 | var json = await Api.get('api/yta', { 609 | url: Func.isUrl(m.text) 610 | }) 611 | if (!json.status) return m.reply(Func.Format(json)) 612 | let cap = `◦ Title : ${json.title}\n` 613 | cap += `◦ Size : ${json.data.size}\n` 614 | cap += `◦ Duration : ${json.duration}\n` 615 | cap += `◦ Quality : ${json.data.quality}` 616 | conn.sendMessageModify(m.from, cap, m, { 617 | largeThumb: true, 618 | thumbnail: json.thumbnail 619 | }).then(() => { 620 | m.reply(json.data.url, { fileName: json.data.filename, mimetype: 'audio/mpeg' }) 621 | }) 622 | } 623 | break 624 | case 'ytv': 625 | case 'ytmp4': { 626 | if (!/(?:https?:\/\/)?(?:youtu\.be\/|(?:www\.|m\.)?(?:music\.)?youtube\.com\/(?:watch|v|embed|shorts))/i.test(m.text)) return m.reply(`Example : ${prefix + command} https://youtu.be/_EYbfKMTpRs`) 627 | await m.react('🕒') 628 | var json = await Api.get('api/yta', { 629 | url: Func.isUrl(m.text) 630 | }) 631 | if (!json.status) return m.reply(Func.Format(json)) 632 | let cap = `◦ Title : ${json.title}\n` 633 | cap += `◦ Size : ${json.data.size}\n` 634 | cap += `◦ Duration : ${json.duration}\n` 635 | cap += `◦ Quality : ${json.data.quality}` 636 | m.reply(json.data.url, { caption: cap, fileName: json.data.filename, mimetype: 'video/mp4' }) 637 | } 638 | break 639 | 640 | /** internet menu */ 641 | case 'ytsearch': 642 | case 'yts': { 643 | if (!m.text) return m.reply(`Example : ${prefix + command} Tasya Rosmala`) 644 | m.react('🕒') 645 | let yt = await (await yts(m.text)).all 646 | if (yt.length == 0) return m.reply(Func.Format(yt)) 647 | let cap = 'Youtube Search\n\n' 648 | yt.map((v, i) => { 649 | if (1 < 15) { 650 | cap += `*` + (i + 1) + `*. ` + v.title + `\n` 651 | cap += `∘ Duration : ` + v.timestamp + `\n` 652 | cap += `∘ Views : ` + v.views + `\n` 653 | cap += `∘ Upload : ` + v.ago + `\n` 654 | cap += `∘ Url : ` + v.url + `\n\n` 655 | } 656 | }) 657 | m.reply(cap) 658 | } 659 | break 660 | case 'brainly': { 661 | try { 662 | if (!m.text) return m.reply(`Example : ${prefix + command} perang dunia ke 2 terjadi kapan`) 663 | m.react('🕒') 664 | const json = await Api.get('api/brainly', { 665 | q: m.text, lang: 'id' 666 | }) 667 | if (!json.status) return m.reply(format(json)) 668 | let teks = `Brainly\n\n` 669 | json.data.map((v, i) => { 670 | teks += `*${(i + 1)}*. ${v.question}\n` 671 | teks += `› *Answer* : \n${v.answers}\n\n` 672 | }) 673 | m.reply(teks) 674 | } catch (e) { 675 | return m.reply(format(e)) 676 | } 677 | } 678 | break 679 | case 'pinterest': { 680 | try { 681 | if (!m.text) return m.reply(`Example : ${prefix + command} moon`) 682 | m.react('🕒') 683 | let json = await Api.get('api/pinterest', { 684 | q: m.text 685 | }) 686 | if (!json.status) return m.reply(format(e)) 687 | for (let i = 0; i < 5; i++) { 688 | var rand = Math.floor(json.data.length * Math.random()) 689 | m.reply(json.data[rand].url, { 690 | caption: config.options.wm 691 | }) 692 | await Func.sleep(2500) 693 | } 694 | } catch (e) { 695 | return m.reply(format(e)) 696 | } 697 | } 698 | break 699 | case 'gimage': 700 | case 'google': { 701 | if (!m.text) return m.reply(`Example : ${prefix + command} red moon`) 702 | m.react('🕒') 703 | try { 704 | if (command == 'google') { 705 | let json = await Api.get('api/google', { 706 | q: m.text 707 | }) 708 | let teks = `Google\n\n` 709 | json.data.map((v, i) => { 710 | teks += `*` + (i + 1) + `.* ` + v.title + `\n` 711 | teks += `• Snippet : ` + v.snippet + `\n` 712 | teks += `• Link : ` + v.url + `\n\n` 713 | }) 714 | m.reply(teks) 715 | } 716 | if (command == 'gimage') { 717 | let json = await Api.get('api/google-image', { 718 | q: m.text 719 | }) 720 | for (let i = 0; i < 5; i++) { 721 | let random = Math.floor(json.data.length * Math.random()) 722 | let caption = `• Title : ${json.data[random].origin.title}\n` 723 | caption += `• Dimensions : ${json.data[random].width} × ${json.data[random].height}` 724 | m.reply(json.data[random].url, { 725 | caption: caption 726 | }) 727 | await Func.sleep(2500) 728 | } 729 | } 730 | } catch (e) { 731 | return m.reply(format(e)) 732 | } 733 | } 734 | break 735 | 736 | /** owner menu */ 737 | case 'backup': { 738 | try { 739 | m.react('🕒') 740 | await database.read(global.db) 741 | fs.writeFileSync('database.json', JSON.stringify(global.db, null, 3), 'utf-8') 742 | await m.reply(fs.readFileSync('./database.json'), { 743 | fileName: 'database.json', 744 | mimetype: 'application/json' 745 | }) 746 | } catch (e) { 747 | return m.reply(format(e)) 748 | } 749 | } 750 | break 751 | case 'block': { 752 | if (!m.isOwner) return m.reply('owner') 753 | let users = m.mentions.length !== 0 ? m.mentions.slice(0, 2) : m.isQuoted ? [m.quoted.sender] : m.text.split(',').map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').slice(0, 2) 754 | if (users.length == 0) return m.reply('Fuck You 🖕') 755 | await conn.updateBlockStatus(users, 'block').then((res) => m.reply(Func.Format(res))).catch((err) => m.reply(Func.Format(err))) 756 | } 757 | break 758 | case 'unblock': { 759 | if (!m.isOwner) return m.reply('owner') 760 | let users = m.mentions.length !== 0 ? m.mentions.slice(0, 2) : m.isQuoted ? [m.quoted.sender] : m.text.split(',').map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').slice(0, 2) 761 | if (users.length == 0) return m.reply('Fuck You 🖕') 762 | await conn.updateBlockStatus(users, 'unblock').then((res) => m.reply(Func.Format(res))).catch((err) => m.reply(Func.Format(err))) 763 | } 764 | break 765 | case 'setpp': { 766 | const media = await quoted.download() 767 | if (m.isOwner && !m.isGroup) { 768 | if (/full/i.test(m.text)) await conn.setProfilePicture(conn?.user?.id, media, "full") 769 | else if (/(de(l)?(ete)?|remove)/i.test(m.text)) await conn.removeProfilePicture(conn.decodeJid(conn?.user?.id)) 770 | else await conn.setProfilePicture(conn?.user?.id, media, 'normal') 771 | } else if (m.isGroup && m.isAdmin && m.isBotAdmin) { 772 | if (/full/i.test(m.text)) await conn.setProfilePicture(m.from, media, 'full') 773 | else if (/(de(l)?(ete)?|remove)/i.test(m.text)) await conn.removeProfilePicture(m.from) 774 | else await conn.setProfilePicture(m.from, media, 'normal') 775 | } 776 | } 777 | break 778 | case 'setname': { 779 | if (m.isOwner && !m.isGroup) { 780 | await conn.updateProfileName(m.isQuoted ? quoted.body : quoted.text) 781 | } else if (m.isGroup && m.isAdmin && m.isBotAdmin) { 782 | await conn.groupUpdateSubject(m.from, m.isQuoted ? quoted.body : quoted.text) 783 | } 784 | } 785 | break 786 | case 'public': 787 | case 'autoread': 788 | case 'chatbot': { 789 | if (!m.isOwner) return m.reply('owner') 790 | if (!m.text) return m.reply(`Current status : [ ${global.db.setting[command.toLowerCase()] ? 'ON' : 'OFF'} ] (Enter *On* or *Off*)`) 791 | let option = m.text.toLowerCase() 792 | let optionList = ['on', 'off'] 793 | if (!optionList.includes(option)) return m.reply(`Current status : [ ${global.db.setting[command.toLowerCase()] ? 'ON' : 'OFF'} ] (Enter *On* or *Off*)`) 794 | let status = option != 'on' ? false : true 795 | if (global.db.setting[command.toLowerCase()] == status) return m.reply(`${Func.ucword(command)} has been ${option == 'on' ? 'activated' : 'inactivated'} previously.`) 796 | global.db.setting[command.toLowerCase()] = status 797 | m.reply(`${Func.ucword(command)} has been ${option == 'on' ? 'activated' : 'inactivated'} successfully.`) 798 | } 799 | break 800 | case 'setcover': { 801 | if (!/image\/(jpe?g|png)/.test(quoted.mime)) return m.reply(`Send or reply to images with commands ${prefix + command}`) 802 | m.react('🕒') 803 | let media = await quoted.download() 804 | let res = await Scraper.uploaderV2(media) 805 | if (!res.status) return m.reply(Func.Format(res)) 806 | db.setting.cover = res.data.url 807 | m.reply('Cover successfully changed') 808 | } 809 | break 810 | case 'setlink': { 811 | if (!/^https:\/\//i.test(m.text)) return m.reply(`Example : ${prefix + command} https://wa`) 812 | m.react('🕒') 813 | db.setting.link = Func.isUrl(m.text) 814 | m.reply('Link successfully changed') 815 | } 816 | break 817 | case 'debounce': 818 | case 'restart': { 819 | if (!m.isOwner) return m.reply('owner') 820 | await m.reply('Restarting . . .').then(async () => { 821 | await database.write(global.db) 822 | process.send('reset') 823 | }) 824 | } 825 | break 826 | case 'join': { 827 | try { 828 | if (!m.isOwner) return m.reply('owner') 829 | if (!m.text) return m.reply(`Example : ${prefix + command} https://chat.whatsapp.com/codeInvite`) 830 | let link = /chat.whatsapp.com\/([0-9A-Za-z]{20,24})/i 831 | let [_, code] = m.text.match(link) || [] 832 | if (!code) return m.reply('Invalid URL') 833 | let id = await conn.groupAcceptInvite(code) 834 | if (!id.endsWith('g.us')) return m.reply(`Sorry i can't join to this group :(`) 835 | let member = await (await conn.groupMetadata(id)).participants.map(v => v.id) 836 | return m.reply(`Joined!`) 837 | } catch { 838 | return m.reply(`Sorry i can't join to this group :(`) 839 | } 840 | } 841 | break 842 | case '-expired': 843 | case '+expired': { 844 | try { 845 | if (!m.isOwner) return m.reply('owner') 846 | if (!m.isGroup) return m.reply('group') 847 | if (command == '+expired') { 848 | if (!m.args[0] || isNaN(m.args[0])) return m.reply(`Example : ${prefix + command} 30`) 849 | let who 850 | if (m.isGroup) who = m.args[1] ? m.args[1] : m.from 851 | else who = m.args[1] 852 | var jumlahHari = 86400000 * m.args[0] 853 | var now = new Date() * 1 854 | if (now < global.db.groups[who].expired) 855 | global.db.groups[who].expired += jumlahHari 856 | else global.db.groups[who].expired = now + jumlahHari 857 | m.reply(`Successfully set expiration days for group ${m.metadata.subject}, for ${m.args[0]} days`) 858 | } else if (command == '-expired') { 859 | let who 860 | if (m.isGroup) who = m.args[1] ? m.args[1] : m.from 861 | else who = m.args[1] 862 | if (new Date() * 1 < global.db.groups[who].expired) 863 | global.db.groups[who].expired = undefined 864 | else global.db.groups[who].expired = undefined 865 | m.reply(`Successfully removed the expiration day for this Group`) 866 | } 867 | } catch (e) { 868 | return m.reply(format(e)) 869 | } 870 | } 871 | break 872 | case '+premium': { 873 | if (m.quoted) { 874 | if (m.quoted.isBot) return m.reply(`🚩 Can't make the bot a premium user.`) 875 | if (m.args && isNaN(m.args[0])) return m.reply(`🚩 Day must be a number.`) 876 | let days = m.args[0] ? parseInt(m.args[0]) : 30 877 | let jid = conn.decodeJid(m.quoted.sender) 878 | let users = global.db.users[jid] 879 | users.expired += users.premium ? (86400000 * days) : ((new Date() * 1) + (86400000 * days)) 880 | users.limit += 2000 881 | m.reply(users.premium ? `Succesfully added ${days} days premium access for @${jid.replace(/@.+/, '')}.` : `Successfully added @${jid.replace(/@.+/, '')} to premium user.`).then(() => users.premium = true) 882 | } else if (m.mentions.length != 0) { 883 | if (m.args && m.args[1] && isNaN(m.args[1])) return m.reply(`Day must be a number.`) 884 | let days = m.args[1] ? parseInt(m.args[1]) : 30 885 | let jid = conn.decodeJid(m.mentions[0]) 886 | const users = global.db.users[jid] 887 | users.expired += users.premium ? (86400000 * days) : ((new Date() * 1) + (86400000 * days)) 888 | users.limit += 2000 889 | conn.reply(m.chat, users.premium ? `Succesfully added ${days} days premium access for @${jid.replace(/@.+/, '')}.` : `Successfully added @${jid.replace(/@.+/, '')} to premium user.`).then(() => users.premium = true) 890 | } else if (m.text && /|/.test(m.text)) { 891 | let [number, day] = text.split`|` 892 | let p = (await conn.onWhatsApp(number))[0] || {} 893 | if (!p.exists) return m.reply('Number not registered on WhatsApp.') 894 | if (isNaN(day)) return m.reply(`Day must be a number.`) 895 | let days = day ? parseInt(day) : 30 896 | let jid = conn.decodeJid(p.jid) 897 | const users = global.db.users[jid] 898 | if (!users) return m.reply(`Can't find user data.`) 899 | users.expired += users.premium ? (86400000 * days) : ((new Date() * 1) + (86400000 * days)) 900 | users.limit += 2000 901 | conn.reply(m.chat, users.premium ? `Succesfully added ${days} days premium access for @${jid.replace(/@.+/, '')}.` : `Successfully added @${jid.replace(/@.+/, '')} to premium user.`).then(() => users.premium = true) 902 | } else { 903 | let teks = `• *Example* :\n\n` 904 | teks += `${prefix + command} 6285xxxxx | 7\n` 905 | teks += `${prefix + command} @0 7\n` 906 | teks += `${prefix + command} 7 (reply chat target)` 907 | m.reply(teks) 908 | } 909 | } 910 | break 911 | case '-premium': { 912 | let input = m.text ? m.text : m.quoted ? m.quoted.sender : m.mentionedJid.length > 0 ? m.mentioneJid[0] : false 913 | if (!input) return m.reply(`Mention or reply chat target.`) 914 | let p = await conn.onWhatsApp(input.trim()) 915 | if (p.length == 0) return m.reply(`Invalid number.`) 916 | let jid = conn.decodeJid(p[0].jid) 917 | let number = jid.replace(/@.+/, '') 918 | let data = global.db.users[jid] 919 | if (typeof data == 'undefined') return m.reply(`Can't find user data.`) 920 | if (!data.premium) return m.reply(`Not a premium account.`) 921 | data.premium = false 922 | data.expired = 0 923 | m.reply(`@${jid.replace(/@.+/, '')}'s premium status has been successfully deleted.`) 924 | } 925 | break 926 | 927 | /** converter menu */ 928 | case 'sticker': 929 | case 's': 930 | case 'stiker': { 931 | if (/image|video|webp/i.test(quoted.mime)) { 932 | m.react('🕒') 933 | const buffer = await quoted.download() 934 | if (quoted?.msg?.seconds > 10) return m.reply(`Max video 9 second`) 935 | let exif 936 | if (m.text) { 937 | let [packname, author] = m.text.split('|') 938 | exif = { 939 | packName: packname ? packname : '', 940 | packPublish: author ? author : '' 941 | } 942 | } else { 943 | exif = { 944 | ...config.Exif 945 | } 946 | } 947 | m.reply(buffer, { 948 | asSticker: true, 949 | ...exif 950 | }) 951 | } else if (m.mentions[0]) { 952 | m.react('🕒') 953 | let url = await conn.profilePictureUrl(m.mentions[0], 'image'); 954 | m.reply(url, { 955 | asSticker: true, 956 | ...config.Exif 957 | }) 958 | } else if (/(https?:\/\/.*\.(?:png|jpg|jpeg|webp|mov|mp4|webm|gif))/i.test(m.text)) { 959 | m.react('🕒') 960 | m.reply(Func.isUrl(m.text)[0], { 961 | asSticker: true, 962 | ...config.Exif 963 | }) 964 | } else { 965 | m.reply(`Method Not Support`) 966 | } 967 | } 968 | break 969 | case 'brat': { 970 | try { 971 | const args = m.text.trim().split(' ') 972 | const mode = m.args[0] === 'gif' ? 'gif' : 'text' 973 | const content = mode === 'gif' ? args.slice(1).join(' ') : m.text.trim() 974 | if (!content) return m.reply(`Example : ${prefix + command} moon`) 975 | if (content.length > 100) return m.reply('Text is too long, max 100 characters.') 976 | m.react('🕒') 977 | if (mode === 'gif') { 978 | let json = await Api.get('api/bratgif', { 979 | text: content 980 | }) 981 | m.reply(json.data.url, { 982 | asSticker: true, 983 | ...config.Exif 984 | }) 985 | } else { 986 | let json = await Api.get('api/brat', { 987 | text: content 988 | }) 989 | m.reply(json.data.url, { 990 | asSticker: true, 991 | ...config.Exif 992 | }) 993 | } 994 | } catch (e) { 995 | return conn.reply(m.chat, Func.jsonFormat(e), m) 996 | } 997 | } 998 | break 999 | case 'ttp': { 1000 | if (!m.text) return m.reply(`Example : ${prefix + command} moon`) 1001 | if (m.text.length > 10) return m.reply(`Max 10 letters`) 1002 | m.react('🕒') 1003 | let json = await Api.get('api/ttp', { 1004 | text: m.text 1005 | }) 1006 | if (!json.status) return m.reply(format(json)) 1007 | m.reply(json.data.url, { 1008 | asSticker: true, 1009 | ...config.Exif 1010 | }) 1011 | } 1012 | break 1013 | case 'attp': { 1014 | if (!m.text) return m.reply(`Example : ${prefix + command} moon`) 1015 | if (m.text.length > 10) return m.reply(`Max 10 letters`) 1016 | m.react('🕒') 1017 | let json = await Api.get('api/attp', { 1018 | text: m.text 1019 | }) 1020 | if (!json.status) return m.reply(format(json)) 1021 | m.reply(json.data.url, { 1022 | asSticker: true, 1023 | ...config.Exif 1024 | }) 1025 | } 1026 | break 1027 | case 'emojimix': { 1028 | if (!m.text) return m.reply(`Example : *${prefix + command} 🥵 + 🥶*`) 1029 | m.react('🕒') 1030 | var [emo1, emo2] = m.text.split` + ` 1031 | let json = await Api.get('api/emojimix', { 1032 | emo1, emo2 1033 | }) 1034 | m.reply(json.data.url, { 1035 | asSticker: true, 1036 | ...config.Exif 1037 | }) 1038 | } 1039 | break 1040 | case 'qc': 1041 | case 'quickchat': { 1042 | let text = m.text 1043 | if (!text) return m.reply(`Example : ${prefix + command} moon-bot`) 1044 | if (text.length > 30) return m.reply(`Max 30 character.`) 1045 | m.react('🕒') 1046 | let pic 1047 | try { 1048 | pic = await conn.profilePictureUrl(m.quoted ? m.quoted.sender : m.sender, 'image') 1049 | } catch { 1050 | pic = 'https://telegra.ph/file/32ffb10285e5482b19d89.jpg' 1051 | } 1052 | const obj = { 1053 | "type": "quote", 1054 | "format": "png", 1055 | "backgroundColor": "#FFFFFF", 1056 | "width": 512, 1057 | "height": 768, 1058 | "scale": 2, 1059 | "messages": [{ 1060 | "entities": [], 1061 | "avatar": true, 1062 | "from": { 1063 | "id": 1, 1064 | "name": m.quoted ? db.users[m.quoted.sender].name : m.pushName, 1065 | "photo": { 1066 | "url": pic 1067 | } 1068 | }, 1069 | "text": text, 1070 | "replyMessage": {} 1071 | }] 1072 | } 1073 | const json = await axios.post('https://bot.lyo.su/quote/generate', obj, { 1074 | headers: { 1075 | 'Content-Type': 'application/json' 1076 | } 1077 | }) 1078 | const buffer = Buffer.from(json.data.result.image, 'base64') 1079 | m.reply(buffer, { 1080 | asSticker: true, 1081 | ...config.Exif 1082 | }) 1083 | } 1084 | break 1085 | case 'toimg': 1086 | case 'togif': 1087 | case 'tovideo': 1088 | case 'toimage': { 1089 | let { 1090 | webp2mp4File 1091 | } = (await import('../sticker.js')) 1092 | if (!/webp/i.test(quoted.mime)) return m.reply(`Reply Sticker with command ${prefix + command}`) 1093 | if (quoted.isAnimated) { 1094 | let media = await webp2mp4File((await quoted.download())) 1095 | await m.reply(media) 1096 | } 1097 | let media = await quoted.download() 1098 | await m.reply(media, { 1099 | mimetype: 'image/png' 1100 | }) 1101 | } 1102 | break 1103 | 1104 | /** group menu */ 1105 | case 'linkgroup': 1106 | case 'linkgrup': 1107 | case 'linkgc': { 1108 | if (!m.isGroup) return m.reply('group') 1109 | //if (!m.isAdmin) return m.reply('admin') 1110 | if (!m.isBotAdmin) return m.reply('botAdmin') 1111 | await m.reply('https://chat.whatsapp.com/' + (await conn.groupInviteCode(m.from))) 1112 | } 1113 | break 1114 | case 'afk': { 1115 | let user = db.users[m.sender] 1116 | user.afkTime = + new Date 1117 | user.afkReason = m.text 1118 | m.reply(`@${m.sender.split`@`[0]} is now AFK\n\nReason : ${user.afkReason ? user.afkReason : '-'}`) 1119 | } 1120 | break 1121 | case 'del': 1122 | case 'delete': { 1123 | if (!quoted) return 1124 | conn.sendMessage(m.from, { 1125 | delete: { 1126 | remoteJid: m.from, 1127 | fromMe: m.isBotAdmin ? false : true, 1128 | id: quoted.id, 1129 | participant: quoted.sender 1130 | } 1131 | }) 1132 | } 1133 | break 1134 | case 'ava': { 1135 | let text = m.text 1136 | let number = isNaN(text) ? (text.startsWith('+') ? text.replace(/[()+\s-]/g, '') : (text).split`@`[1]) : text 1137 | if (!text && !quoted) return m.reply(`Mention or reply chat target.`) 1138 | if (isNaN(number)) return m.reply(`Invalid number.`) 1139 | if (number.length > 15) return m.reply(`Invalid format.`) 1140 | try { 1141 | if (text) { 1142 | var user = number + '@s.whatsapp.net' 1143 | } else if (m.quoted.sender) { 1144 | var user = m.quoted.sender 1145 | } else if (m.mentionedJid) { 1146 | var user = number + '@s.whatsapp.net' 1147 | } 1148 | } catch (e) { } finally { 1149 | var pic = false 1150 | try { 1151 | var pic = await conn.profilePictureUrl(user, 'image') 1152 | } catch { } finally { 1153 | if (!pic) return m.reply(`He/She didn't put a profile picture.`) 1154 | m.reply(pic) 1155 | } 1156 | } 1157 | } 1158 | break 1159 | case 'quoted': 1160 | case 'q': { 1161 | const { Serialize } = (await import('../serialize.js')) 1162 | if (!m.isQuoted) m.reply('quoted') 1163 | try { 1164 | const message = await Serialize(conn, (await conn.loadMessage(m.from, m.quoted.id))) 1165 | if (!message.isQuoted) return m.reply('Quoted Not Found') 1166 | conn.sendMessage(m.from, { 1167 | forward: message.quoted 1168 | }) 1169 | } catch { 1170 | m.reply('Quoted Not Found') 1171 | } 1172 | } 1173 | break 1174 | case 'rvo': { 1175 | if (!m.quoted) return m.reply(`Reply view once message to use this command.`) 1176 | if (m.quoted.message) { 1177 | let type = Object.keys(m.quoted.message)[0] 1178 | let q = m.quoted.message[type] 1179 | let media = await conn.downloadAndSaveMediaMessage(q) 1180 | if (/video/.test(type)) { 1181 | return await m.reply(media, { caption: q.caption || '' }) 1182 | } else if (/image/.test(type)) { 1183 | return await m.reply(media, { caption: q.caption || '' }) 1184 | } 1185 | } else m.reply(`Koplak`) 1186 | } 1187 | break 1188 | 1189 | /** admin menu */ 1190 | case 'hidetag': 1191 | case 'ht': 1192 | case 'h': { 1193 | if (!m.isGroup) return m.reply('group') 1194 | if (!m.isAdmin) return m.reply('admin') 1195 | let mentions = m.metadata.participants.map(a => a.id) 1196 | let mod = await conn.cMod(m.from, quoted, /hidetag|tag|ht|h|totag/i.test(quoted.body.toLowerCase()) ? quoted.body.toLowerCase().replace(prefix + command, "") : quoted.body) 1197 | conn.sendMessage(m.from, { 1198 | forward: mod, 1199 | mentions 1200 | }) 1201 | } 1202 | break 1203 | case 'tagall': { 1204 | if (!m.isGroup) return m.reply('group') 1205 | if (!m.isAdmin) return m.reply('admin') 1206 | let teks = `Tagall\n\n"${m.text ? m.text : 'Hi admin mention you'}"\n\n` 1207 | for (let a of m.metadata.participants) { 1208 | teks += `◦ @${a.id.split('@')[0]}\n` 1209 | } 1210 | m.reply(teks) 1211 | } 1212 | break 1213 | case 'add': { 1214 | if (!m.isGroup) return m.reply('group') 1215 | if (!m.isAdmin) return m.reply('admin') 1216 | if (!m.isBotAdmin) return m.reply('botAdmin') 1217 | let users = m.mentions.length !== 0 ? m.mentions.slice(0, 2) : m.isQuoted ? [m.quoted.sender] : m.text.split(',').map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').slice(0, 2) 1218 | if (users.length == 0) return m.reply('Fuck You 🖕') 1219 | await conn.groupParticipantsUpdate(m.from, users, 'add').then(async (res) => { 1220 | for (let i of res) { 1221 | if (i.status == 403) { 1222 | let node = getBinaryNodeChildren(i.content, 'add_request') 1223 | await m.reply(`Can't add @${i.jid.split('@')[0]}, send invitation...`) 1224 | let url = await conn.profilePictureUrl(m.from, 'image').catch(_ => 'https://lh3.googleusercontent.com/proxy/esjjzRYoXlhgNYXqU8Gf_3lu6V-eONTnymkLzdwQ6F6z0MWAqIwIpqgq_lk4caRIZF_0Uqb5U8NWNrJcaeTuCjp7xZlpL48JDx-qzAXSTh00AVVqBoT7MJ0259pik9mnQ1LldFLfHZUGDGY=w1200-h630-p-k-no-nu') 1225 | await conn.sendGroupV4Invite(i.jid, m.from, node[0]?.attrs?.code || node.attrs.code, node[0]?.attrs?.expiration || node.attrs.expiration, m.metadata.subject, url, 'Invitation to join my WhatsApp Group') 1226 | } else if (i.status == 409) return m.reply(`@${i.jid?.split('@')[0]} already in this group`) 1227 | else m.reply(Func.Format(i)) 1228 | } 1229 | }) 1230 | } 1231 | break 1232 | case 'kick': { 1233 | if (!m.isGroup) return m.reply('group') 1234 | if (!m.isAdmin) return m.reply('admin') 1235 | if (!m.isBotAdmin) return m.reply('botAdmin') 1236 | let users = m.mentions.length !== 0 ? m.mentions.slice(0, 2) : m.isQuoted ? [m.quoted.sender] : m.text.split(',').map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').slice(0, 2) 1237 | if (users.length == 0) return m.reply('Fuck You 🖕') 1238 | await conn.groupParticipantsUpdate(m.from, users, 'remove').then((res) => m.reply(Func.Format(res))).catch((err) => m.reply(Func.Format(err))) 1239 | } 1240 | break 1241 | case 'promote': { 1242 | if (!m.isGroup) return m.reply('group') 1243 | if (!m.isAdmin) return m.reply('admin') 1244 | if (!m.isBotAdmin) return m.reply('botAdmin') 1245 | let users = m.mentions.length !== 0 ? m.mentions.slice(0, 2) : m.isQuoted ? [m.quoted.sender] : m.text.split(',').map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').slice(0, 2) 1246 | if (users.length == 0) return m.reply('Fuck You 🖕') 1247 | await conn.groupParticipantsUpdate(m.from, users, 'promote').then((res) => m.reply(Func.Format(res))).catch((err) => m.reply(Func.Format(err))) 1248 | } 1249 | break 1250 | case 'demote': { 1251 | if (!m.isGroup) return m.reply('group') 1252 | if (!m.isAdmin) return m.reply('admin') 1253 | if (!m.isBotAdmin) return m.reply('botAdmin') 1254 | let users = m.mentions.length !== 0 ? m.mentions.slice(0, 2) : m.isQuoted ? [m.quoted.sender] : m.text.split(',').map(v => v.replace(/[^0-9]/g, '') + '@s.whatsapp.net').slice(0, 2) 1255 | if (users.length == 0) return m.reply('Fuck You 🖕') 1256 | await conn.groupParticipantsUpdate(m.from, users, 'demote').then((res) => m.reply(Func.Format(res))).catch((err) => m.reply(Func.Format(err))) 1257 | } 1258 | break 1259 | case 'welcome': 1260 | case 'leaving': 1261 | case 'detect': { 1262 | if (!m.isAdmin) return m.reply('admin') 1263 | if (!m.isBotAdmin && /antilink/.test(command.toLowerCase())) return m.reply('botAdmin') 1264 | if (!m.text) return m.reply(`Current status : [ ${global.db.groups[command.toLowerCase()] ? 'ON' : 'OFF'} ] (Enter *On* or *Off*)`) 1265 | let option = m.text.toLowerCase() 1266 | let optionList = ['on', 'off'] 1267 | if (!optionList.includes(option)) return m.reply(`Current status : [ ${global.db.groups[command.toLowerCase()] ? 'ON' : 'OFF'} ] (Enter *On* or *Off*)`) 1268 | let status = option != 'on' ? false : true 1269 | if (global.db.groups[command.toLowerCase()] == status) return m.reply(`${Func.ucword(command)} has been ${option == 'on' ? 'activated' : 'inactivated'} previously.`) 1270 | global.db.groups[command.toLowerCase()] = status 1271 | m.reply(`${Func.ucword(command)} has been ${option == 'on' ? 'activated' : 'inactivated'} successfully.`) 1272 | } 1273 | break 1274 | 1275 | /** image effect */ 1276 | case 'paretro': case 'retrolga': case 'plumy': case 'hdr': case 'sepia': case 'duotone': case 'blackwhite': case 'sketch': case 'sketchril': case 'oils': case 'esragan': case 'watercolor': case 'galaxy': case 'freplace': case 'rainbow': case 'solarize': case 'pinkbir': { 1277 | try { 1278 | if (!/image\/(jpe?g|png)/.test(quoted.mime)) return m.reply(`Reply or send photo use this command`) 1279 | m.react('🕒') 1280 | let result = await Scraper.uploader(await quoted.download()) 1281 | let json = await Api.get('api/effect', { 1282 | image: result.data.url, style: command.toLowerCase() 1283 | }) 1284 | if (!json.status) return m.reply(Func.Format(json)) 1285 | m.reply(json.data.url, { 1286 | caption: config.options.wm 1287 | }) 1288 | } catch (e) { 1289 | return m.reply(format(e)) 1290 | } 1291 | } 1292 | break 1293 | 1294 | /** text maker */ 1295 | case 'comicbox': case 'gradientshadow': case 'lava': case 'thunder': case 'neondevil': case 'sumertimes': case 'matrix': case 'firework': case 'neonlight': case 'greenneon': case 'pokemon': case 'dragonball': case 'naruto': case 'blackpink': case 'onglass': case 'greenbrush': case 'amongus': case 'naruto2': case 'flaming': case 'woodblock': { 1296 | try { 1297 | if (!m.text) return m.reply(`Example : ${prefix + command} moon-bot`) 1298 | if (m.text.length > 10) return m.reply(`Max 10 character`) 1299 | m.react('🕒') 1300 | var link 1301 | if (/comicbox/.test(command)) link = 'https://textpro.me/create-online-3d-comic-book-style-text-effects-1156.html' 1302 | if (/gradientshadow/.test(command)) link = 'https://textpro.me/create-a-gradient-text-shadow-effect-online-1141.html' 1303 | if (/lava/.test(command)) link = 'https://textpro.me/lava-text-effect-online-914.html' 1304 | if (/thunder/.test(command)) link = 'https://textpro.me/create-thunder-text-effect-online-881.html' 1305 | if (/neondevil/.test(command)) link = 'https://textpro.me/create-neon-devil-wings-text-effect-online-free-1014.html' 1306 | if (/sumertimes/.test(command)) link = 'https://textpro.me/create-a-summer-neon-light-text-effect-online-1076.html' 1307 | if (/matrix/.test(command)) link = 'https://textpro.me/matrix-style-text-effect-online-884.html' 1308 | if (/firework/.test(command)) link = 'https://textpro.me/firework-sparkle-text-effect-930.html' 1309 | if (/neonlight/.test(command)) link = 'https://textpro.me/neon-light-text-effect-with-galaxy-style-981.html' 1310 | if (/greenneon/.test(command)) link = 'https://textpro.me/green-neon-text-effect-874.html' 1311 | if (/pokemon/.test(command)) link = 'https://textpro.me/create-pokemon-logo-style-text-effect-online-1134.html' 1312 | /** ephoto360 */ 1313 | if (/dragonball/.test(command)) link = 'https://en.ephoto360.com/create-dragon-ball-style-text-effects-online-809.html' 1314 | if (/naruto/.test(command)) link = 'https://en.ephoto360.com/naruto-shippuden-logo-style-text-effect-online-808.html' 1315 | if (/blackpink/.test(command)) link = 'https://en.ephoto360.com/create-blackpink-logo-online-free-607.html' 1316 | if (/onglass/.test(command)) link = 'https://en.ephoto360.com/write-text-on-wet-glass-online-589.html' 1317 | if (/greenbrush/.test(command)) link = 'https://en.ephoto360.com/green-brush-text-effect-typography-maker-online-153.html' 1318 | if (/amongus/.test(command)) link = 'https://en.ephoto360.com/create-a-cover-image-for-the-game-among-us-online-762.html' 1319 | /** photooxy */ 1320 | if (/naruto2/.test(command)) link = 'https://photooxy.com/manga-and-anime/make-naruto-banner-online-free-378.html' 1321 | if (/flaming/.test(command)) link = 'https://photooxy.com/logo-and-text-effects/realistic-flaming-text-effect-online-197.html' 1322 | if (/woodblock/.test(command)) link = 'https://photooxy.com/logo-and-text-effects/carved-wood-effect-online-171.html' 1323 | let json = await Api.get('api/textmaker', { 1324 | url: link, text: m.text 1325 | }) 1326 | if (!json.status) return m.reply(Func.Format(json)) 1327 | m.reply(json.data.url_file, { 1328 | caption: config.options.wm 1329 | }) 1330 | } catch (e) { 1331 | return m.reply(format(e)) 1332 | } 1333 | } 1334 | break 1335 | 1336 | /** text maker 2 */ 1337 | case 'pornhub': case 'marvelstudio': case 'marvelstudio2': case 'glitchtiktok': case 'deadpool': case '8bittext': case 'thorlogo': case 'captainamerica': case 'amongus2': { 1338 | try { 1339 | if (!m.text) return m.reply(`Example : ${prefix + command} moon | bot`) 1340 | let [text1, text2] = m.text.split('|') 1341 | if (text1.length > 10 || text2.length > 10) return m.reply('Max 10 character') 1342 | m.react('🕒'), link 1343 | /** text pro */ 1344 | if (/pornhub/.test(command)) link = 'https://textpro.me/generate-a-free-logo-in-pornhub-style-online-977.html' 1345 | if (/marvelstudio/.test(command)) link = 'https://textpro.me/create-logo-style-marvel-studios-ver-metal-972.html' 1346 | if (/marvelstudio2/.test(command)) link = 'https://textpro.me/create-logo-style-marvel-studios-online-971.html' 1347 | if (/glitchtiktok/.test(command)) link = 'https://textpro.me/create-glitch-text-effect-style-tik-tok-983.html' 1348 | if (/deadpool/.test(command)) link = 'https://textpro.me/create-deadpool-logo-style-text-effect-online-1159.html' 1349 | if (/8bittext/.test(command)) link = 'https://textpro.me/video-game-classic-8-bit-text-effect-1037.html' 1350 | /** ephoto360 */ 1351 | if (/thorlogo/.test(command)) link = 'https://en.ephoto360.com/create-thor-logo-style-text-effects-online-for-free-796.html' 1352 | if (/captainamerica/.test(command)) link = 'https://en.ephoto360.com/create-a-cinematic-captain-america-text-effect-online-715.html' 1353 | if (/amongus2/.test(command)) link = 'https://en.ephoto360.com/create-a-banner-game-among-us-with-your-name-763.html' 1354 | if (/latestspace/.test(command)) link = 'https://en.ephoto360.com/latest-space-3d-text-effect-online-559.html' 1355 | let json = await Api.get('api/textmaker2', { 1356 | url: link, text1, text2 1357 | }) 1358 | if (!json.status) return m.reply(format(json)) 1359 | m.reply(json.data.url_file, { 1360 | caption: config.options.wm 1361 | }) 1362 | } catch (e) { 1363 | return m.reply(format(e)) 1364 | } 1365 | } 1366 | break 1367 | 1368 | /** tools menu */ 1369 | case 'fetch': 1370 | case 'get': { 1371 | if (!/^https:\/\//i.test(m.text)) return m.reply(`Example : ${prefix + command} https://api.alyachan.pro`) 1372 | m.react('🕒') 1373 | let mime = (await import('mime-types')) 1374 | const res = await axios.get(Func.isUrl(m.text)[0], { 1375 | responseType: 'arraybuffer' 1376 | }) 1377 | if (!/utf-8|json|html|plain/.test(res?.headers?.get('content-type'))) { 1378 | let fileName = /filename/i.test(res.headers?.get("content-disposition")) ? res.headers?.get("content-disposition")?.match(/filename=(.*)/)?.[1]?.replace(/["';]/g, '') : '' 1379 | return m.reply(res.data, { 1380 | fileName, 1381 | mimetype: mime.lookup(fileName) 1382 | }) 1383 | } 1384 | let text = res?.data?.toString() || res?.data 1385 | text = format(text) 1386 | try { 1387 | m.reply(text.slice(0, 65536) + '') 1388 | } catch (e) { 1389 | m.reply(format(e)) 1390 | } 1391 | } 1392 | break 1393 | case 'totext': 1394 | case 'ocr': { 1395 | if (/image/i.test(quoted.mime)) { 1396 | m.react('🕒') 1397 | const url = await Scraper.uploader(await quoted.download()) 1398 | const json = await Api.get('api/ocr', { 1399 | image: url.data.url 1400 | }) 1401 | if (!json.status) return m.reply(format(json)) 1402 | m.reply(json.data.text) 1403 | } else if (/(https?:\/\/.*\.(?:png|jpg|jpeg))/i.test(m.text)) { 1404 | const json = await Api.get('api/ocr', { 1405 | image: Func.isUrl(m.text)[0] 1406 | }) 1407 | if (!json.status) return m.reply(format(json)) 1408 | m.reply(json.data.text) 1409 | } else { 1410 | m.reply(`Send or reply your photo...`) 1411 | } 1412 | } 1413 | break 1414 | case 'hd': 1415 | case 'remini': { 1416 | if (/image/i.test(quoted.mime)) { 1417 | m.react('🕒') 1418 | m.react('🕒') 1419 | const url = await Scraper.uploader(await quoted.download()) 1420 | const json = await Api.get('api/remini', { 1421 | image: url.data.url 1422 | }) 1423 | if (!json.status) return m.reply(format(json)) 1424 | m.reply(json.data.url, { 1425 | caption: config.options.wm 1426 | }) 1427 | } else if (/(https?:\/\/.*\.(?:png|jpg|jpeg))/i.test(m.text)) { 1428 | const json = await Api.get('api/remini', { 1429 | image: Func.isUrl(m.text)[0] 1430 | }) 1431 | if (!json.status) return m.reply(format(json)) 1432 | m.reply(json.data.url, { 1433 | caption: config.options.wm 1434 | }) 1435 | } else { 1436 | m.reply(`Send or reply your photo...`) 1437 | } 1438 | } 1439 | break 1440 | case 'nobg': 1441 | case 'removebg': { 1442 | if (/image/i.test(quoted.mime)) { 1443 | m.react('🕒') 1444 | const url = await Scraper.uploader(await quoted.download()) 1445 | const json = await Api.get('api/removebg', { 1446 | image: url.data.url 1447 | }) 1448 | if (!json.status) return m.reply(format(json)) 1449 | m.reply(json.data.url, { 1450 | caption: config.options.wm 1451 | }) 1452 | } else if (/(https?:\/\/.*\.(?:png|jpg|jpeg))/i.test(m.text)) { 1453 | const json = await Api.get('api/removebg', { 1454 | image: Func.isUrl(m.text)[0] 1455 | }) 1456 | if (!json.status) return m.reply(format(json)) 1457 | m.reply(json.data.url, { 1458 | caption: config.options.wm 1459 | }) 1460 | } else { 1461 | m.reply(`Send or reply your photo...`) 1462 | } 1463 | } 1464 | break 1465 | case 'toanime': 1466 | case 'jadianime': { 1467 | if (/image/i.test(quoted.mime)) { 1468 | m.react('🕒') 1469 | const url = await Scraper.uploader(await quoted.download()) 1470 | const json = await Api.get('api/toanime', { 1471 | image: url.data.url, style: 'anime' 1472 | }) 1473 | if (!json.status) return m.reply(format(json)) 1474 | m.reply(json.data.url, { 1475 | caption: config.options.wm 1476 | }) 1477 | } else if (/(https?:\/\/.*\.(?:png|jpg|jpeg))/i.test(m.text)) { 1478 | const json = await Api.get('api/toanime', { 1479 | image: Func.isUrl(m.text)[0], style: 'anime' 1480 | }) 1481 | if (!json.status) return m.reply(format(json)) 1482 | m.reply(json.data.url, { 1483 | caption: config.options.wm 1484 | }) 1485 | } else { 1486 | m.reply(`Send or reply your photo...`) 1487 | } 1488 | } 1489 | break 1490 | case 'tozombie': 1491 | case 'jadizombie': { 1492 | if (/image/i.test(quoted.mime)) { 1493 | m.react('🕒') 1494 | const url = await Scraper.uploader(await quoted.download()) 1495 | const json = await Api.get('api/tozombie', { 1496 | image: url.data.url 1497 | }) 1498 | if (!json.status) return m.reply(format(json)) 1499 | m.reply(json.data.url, { 1500 | caption: config.options.wm 1501 | }) 1502 | } else if (/(https?:\/\/.*\.(?:png|jpg|jpeg))/i.test(m.text)) { 1503 | var json = await Api.get('api/tozombie', { 1504 | image: Func.isUrl(m.text)[0] 1505 | }) 1506 | if (!json.status) return m.reply(format(json)) 1507 | m.reply(json.data.url, { 1508 | caption: config.options.wm 1509 | }) 1510 | } else { 1511 | m.reply(`Send or reply your photo...`) 1512 | } 1513 | } 1514 | break 1515 | case 'gta5style': { 1516 | if (/image/i.test(quoted.mime)) { 1517 | m.react('🕒') 1518 | const url = await Scraper.uploader(await quoted.download()) 1519 | let json = await Api.get('api/ai-photo-editor', { 1520 | image: url.data.url, 1521 | style: 'gta_5' 1522 | }) 1523 | if (!json.status) return m.reply(format(json)) 1524 | m.reply(json.data.url, { 1525 | caption: config.options.wm 1526 | }) 1527 | } else if (/(https?:\/\/.*\.(?:png|jpg|jpeg))/i.test(m.text)) { 1528 | let json = await Api.get('api/ai-photo-editor', { 1529 | image: Func.isUrl(m.text)[0], 1530 | style: 'gta_5' 1531 | }) 1532 | if (!json.status) return m.reply(format(json)) 1533 | m.reply(json.data.url, { 1534 | caption: config.options.wm 1535 | }) 1536 | } else { 1537 | m.reply(`Send or reply your photo...`) 1538 | } 1539 | } 1540 | break 1541 | case 'recolor': { 1542 | if (/image/i.test(quoted.mime)) { 1543 | m.react('🕒') 1544 | const url = await Scraper.uploader(await quoted.download()) 1545 | var json = await Api.get('api/recolor', { 1546 | image: url.data.url 1547 | }) 1548 | if (!json.status) return m.reply(format(json)) 1549 | m.reply(json.data.url, { 1550 | caption: config.options.wm 1551 | }) 1552 | } else if (/(https?:\/\/.*\.(?:png|jpg|jpeg))/i.test(m.text)) { 1553 | var json = await Api.get('api/recolor', { 1554 | image: Func.isUrl(m.text)[0] 1555 | }) 1556 | if (!json.status) return m.reply(format(json)) 1557 | m.reply(json.data.url, { 1558 | caption: config.options.wm 1559 | }) 1560 | } else { 1561 | m.reply(`Send or reply your photo...`) 1562 | } 1563 | } 1564 | break 1565 | case 'screenshot': 1566 | case 'ss': { 1567 | try { 1568 | if (!m.args[1]) return m.reply(`Example : ${prefix + command} mobile https://api.alyachan.dev`) 1569 | if (!/^https?:\/\//.test(m.args[1])) return m.reply('Prefix the link with https:// or http://') 1570 | let mode = m.args[0].toLowerCase(), url = m.args[1] 1571 | if (!['mobile', 'desktop'].includes(mode)) return m.reply('Use mobile or desktop mode.') 1572 | m.react('🕒') 1573 | const json = await Api.get('api/ssweb', { 1574 | url: url, mode: mode 1575 | }) 1576 | if (!json.status) return m.reply(format(json)) 1577 | m.reply(json.data.url, { 1578 | caption: config.options.wm 1579 | }) 1580 | } catch (e) { 1581 | return m.reply(format(e)) 1582 | } 1583 | } 1584 | break 1585 | case 'shortlink': 1586 | case 'expand': { 1587 | if (!/^https:\/\//i.test(m.text)) return m.reply(`Example : ${prefix + command} https://api.alyachan.dev`) 1588 | m.react('🕒') 1589 | const json = await Api.get('api/shorten', { url: Func.isUrl(m.text)[0] }) 1590 | if (!json.status) return m.reply(Func.Format(json)) 1591 | await m.reply(Func.Format(json.data.url)) 1592 | } 1593 | break 1594 | case 'tr': 1595 | case 'translate': { 1596 | let text = m.text 1597 | if (!text) return m.reply(`No Query?\n\nExample : ${prefix + command} id What your name`) 1598 | m.react('🕒') 1599 | if (text && m.quoted && m.quoted.text) { 1600 | let lang = text.slice(0, 2) 1601 | try { 1602 | let data = m.quoted.text 1603 | let json = await Api.get('api/translate', { text: data, iso: lang }) 1604 | m.reply(json.data.text) 1605 | } catch (e) { 1606 | console.log(e) 1607 | m.reply(`Language code is not supported`) 1608 | } 1609 | } else if (text) { 1610 | let lang = text.slice(0, 2) 1611 | try { 1612 | let data = text.substring(2).trim() 1613 | let json = await Api.get('api/translate', { text: data, iso: lang }) 1614 | m.reply(json.data.text) 1615 | } catch (e) { 1616 | console.log(e) 1617 | m.reply(`Language code is not supported`) 1618 | } 1619 | } 1620 | } 1621 | break 1622 | case 'tts': { 1623 | let lang 1624 | if (!m.args[0]) return m.reply(`Example : ${prefix + command} id What your name`) 1625 | m.react('🕒') 1626 | try { 1627 | let text = m.args.slice(1).join('') 1628 | if ((m.args[0] || '').length !== 2) { 1629 | lang = 'id' 1630 | text = m.args.join(' ') 1631 | } 1632 | if (!text && m.quoted && m.quoted.text) text = m.quoted.text 1633 | conn.sendPresenceUpdate('recording', m.chat) 1634 | let json = await Api.get('api/tts', { 1635 | text: text, iso: m.args[0] 1636 | }) 1637 | conn.sendMedia(m.from, json.data.url, m, { 1638 | ptt: true 1639 | }) 1640 | } catch (e) { 1641 | console.log(e) 1642 | return m.reply(`enter language code`) 1643 | } 1644 | } 1645 | break 1646 | case 'magernulis': 1647 | case 'nulis': { 1648 | if (!m.text) return m.reply(`Example : ${prefix + command} moon-bot wehwehweh`) 1649 | m.react('🕒') 1650 | let old = new Date() 1651 | let json = await Api.get('api/nulis', { 1652 | text: m.text 1653 | }) 1654 | if (!json.status) return m.reply(Func.Format(json)) 1655 | m.reply(json.data.url, { 1656 | caption: `Proccess : ${((new Date - old) * 1)} ms` 1657 | }) 1658 | } 1659 | break 1660 | case 'calc': 1661 | case 'kalk': 1662 | case 'calculator': { 1663 | let val = m.text 1664 | .replace(/[^0-9\-\/+*×÷πEe()piPI/]/g, '') 1665 | .replace(/×/g, '*') 1666 | .replace(/÷/g, '/') 1667 | .replace(/π|pi/gi, 'Math.PI') 1668 | .replace(/e/gi, 'Math.E') 1669 | .replace(/\/+/g, '/') 1670 | .replace(/\++/g, '+') 1671 | .replace(/-+/g, '-') 1672 | let format = val 1673 | .replace(/Math\.PI/g, 'π') 1674 | .replace(/Math\.E/g, 'e') 1675 | .replace(/\//g, '÷') 1676 | .replace(/\*×/g, '×') 1677 | try { 1678 | console.log(val) 1679 | let result = (new Function('return ' + val))() 1680 | if (!result) throw result 1681 | m.reply(`*${format}* = _${result}_`) 1682 | } catch (e) { 1683 | if (e == undefined) return m.reply('Isinya?') 1684 | m.reply('Format salah, hanya 0-9 dan Simbol -, +, *, /, ×, ÷, π, e, (, ) yang disupport') 1685 | } 1686 | } 1687 | break 1688 | 1689 | /** end command */ 1690 | default: 1691 | /** eval */ 1692 | if (['>', 'eval', '=>'].some(a => m.body?.toLowerCase()?.startsWith(a))) { 1693 | if (!m.isOwner) return m.reply('owner') 1694 | let evalCmd = '' 1695 | try { 1696 | evalCmd = /await/i.test(m.text) ? eval('(async() => { ' + m.text + ' })()') : eval(m.text) 1697 | } catch (e) { 1698 | evalCmd = e 1699 | } 1700 | new Promise(async (resolve, reject) => { 1701 | try { 1702 | resolve(evalCmd) 1703 | } catch (err) { 1704 | reject(err) 1705 | } 1706 | }) 1707 | ?.then((res) => m.reply(format(res))) 1708 | ?.catch((err) => m.reply(format(err))) 1709 | } 1710 | 1711 | /** exec */ 1712 | if (['$', 'exec'].some(a => m.body?.toLowerCase()?.startsWith(a))) { 1713 | if (!m.isOwner) return m.reply('owner') 1714 | try { 1715 | exec(m.text, async (err, stdout) => { 1716 | if (err) return m.reply(format(err)) 1717 | if (stdout) return m.reply(format(stdout)) 1718 | }) 1719 | } catch (e) { 1720 | m.reply(format(e)) 1721 | } 1722 | } 1723 | 1724 | /** test */ 1725 | if (/^bot/i.test(m.body) && m.isBot) { 1726 | m.reply(`Bot Activated "${m.pushName}"`) 1727 | } 1728 | } 1729 | } catch (e) { 1730 | console.log(e) 1731 | m.reply(format(e)) 1732 | } 1733 | Func.reloadFile(fileURLToPath(import.meta.url)) 1734 | } -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import config from './config.js' 2 | import { Client, Serialize } from './lib/serialize.js' 3 | import baileys from '@whiskeysockets/baileys' 4 | import { Boom } from '@hapi/boom' 5 | import Pino from 'pino' 6 | import NodeCache from 'node-cache' 7 | import chalk from 'chalk' 8 | import readline from 'readline' 9 | import fs from 'fs' 10 | 11 | const { useMultiFileAuthState, DisconnectReason, makeInMemoryStore, jidNormalizedUser, makeCacheableSignalKeyStore, PHONENUMBER_MCC, Browsers, fetchLatestBaileysVersion } = baileys 12 | const database = (new (await import('./lib/database.js')).default()) 13 | const store = makeInMemoryStore({ logger: Pino({ level: 'silent' }).child({ level: 'silent' }) }) 14 | const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) 15 | const question = (text) => new Promise((resolve) => rl.question(text, resolve)) 16 | 17 | // start connect to client 18 | async function start() { 19 | process.on('unhandledRejection', (err) => console.error(err)) 20 | 21 | const content = await database.read() 22 | if (content && Object.keys(content).length === 0) { 23 | global.db = { users: {}, groups: {}, setting: {}, ...(content || {}) } 24 | await database.write(global.db) 25 | } else { 26 | global.db = content 27 | } 28 | 29 | const { version, isLatest } = await fetchLatestBaileysVersion() 30 | const { state, saveCreds } = await useMultiFileAuthState(`./${config.options.sessionName}`) 31 | const msgRetryCounterCache = new NodeCache() // for retry message, "waiting message" 32 | 33 | const conn = baileys.default({ 34 | version, 35 | logger: Pino({ level: 'fatal' }).child({ level: 'fatal' }), // hide log 36 | printQRInTerminal: config.pairing.state ? false : true, // popping up QR in terminal log 37 | auth: { 38 | creds: state.creds, 39 | keys: makeCacheableSignalKeyStore(state.keys, Pino({ level: 'fatal' }).child({ level: 'fatal' })), 40 | }, 41 | browser: Browsers.ubuntu('Chrome'), 42 | markOnlineOnConnect: true, // set false for offline 43 | generateHighQualityLinkPreview: true, // make high preview link 44 | getMessage: async (key) => { 45 | let jid = jidNormalizedUser(key.remoteJid) 46 | let msg = await store.loadMessage(jid, key.id) 47 | return msg?.message || '' 48 | }, 49 | msgRetryCounterCache, // Resolve waiting messages 50 | defaultQueryTimeoutMs: undefined, // for this issues https://github.com/WhiskeySockets/Baileys/issues/276 51 | }) 52 | 53 | // bind store, write store maybe 54 | store.bind(conn.ev) 55 | 56 | // push update name to store.contacts 57 | conn.ev.on('contacts.update', (update) => { 58 | for (let contact of update) { 59 | let id = jidNormalizedUser(contact.id) 60 | if (store && store.contacts) store.contacts[id] = { id, name: contact.notify } 61 | } 62 | }) 63 | 64 | // bind extra client 65 | await Client({ conn, store }) 66 | 67 | // login use pairing code 68 | if (config.pairing.state && !conn.authState.creds.registered) { 69 | let phoneNumber 70 | if (!!config.pairing.number) { 71 | phoneNumber = config.pairing.number.toString().replace(/[^0-9]/g, '') 72 | if (!Object.keys(PHONENUMBER_MCC).some(v => phoneNumber.startsWith(v))) { 73 | console.log(chalk.bgBlack(chalk.redBright("Start with your country's WhatsApp code, Example : 62xxx"))) 74 | process.exit(0) 75 | } 76 | } else { 77 | phoneNumber = await question(chalk.bgBlack(chalk.greenBright(`Please type your WhatsApp number : `))) 78 | phoneNumber = phoneNumber.replace(/[^0-9]/g, '') 79 | if (!Object.keys(PHONENUMBER_MCC).some(v => phoneNumber.startsWith(v))) { 80 | console.log(chalk.bgBlack(chalk.redBright("Start with your country's WhatsApp code, Example : 62xxx"))) 81 | phoneNumber = await question(chalk.bgBlack(chalk.greenBright(`Please type your WhatsApp number : `))) 82 | phoneNumber = phoneNumber.replace(/[^0-9]/g, '') 83 | rl.close() 84 | } 85 | } 86 | setTimeout(async () => { 87 | let code = await conn.requestPairingCode(phoneNumber) 88 | code = code?.match(/.{1,4}/g)?.join("-") || code 89 | console.log(chalk.black(chalk.bgGreen(`Your Pairing Code : `)), chalk.black(chalk.white(code))) 90 | }, 3000) 91 | } 92 | 93 | // for auto restart when error client 94 | conn.ev.on('connection.update', async (update) => { 95 | const { lastDisconnect, connection, qr } = update 96 | if (connection === 'close') { 97 | let reason = new Boom(lastDisconnect?.error)?.output.statusCode 98 | if (reason === DisconnectReason.badSession) { 99 | console.log(`Bad Session File, Please Delete Session and Scan Again`) 100 | process.send('reset') 101 | } else if (reason === DisconnectReason.connectionClosed) { 102 | console.log('Connection closed, reconnecting....') 103 | await start() 104 | } else if (reason === DisconnectReason.connectionLost) { 105 | console.log('Connection Lost from Server, reconnecting...') 106 | await start() 107 | } else if (reason === DisconnectReason.connectionReplaced) { 108 | console.log('Connection Replaced, Another New Session Opened, Please Close Current Session First') 109 | process.exit(1) 110 | } else if (reason === DisconnectReason.loggedOut) { 111 | console.log(`Device Logged Out, Please Scan Again And Run.`) 112 | process.exit(1) 113 | } else if (reason === DisconnectReason.restartRequired) { 114 | console.log('Restart Required, Restarting...') 115 | await start() 116 | } else if (reason === DisconnectReason.timedOut) { 117 | console.log('Connection TimedOut, Reconnecting...') 118 | process.send('reset') 119 | } else if (reason === DisconnectReason.multideviceMismatch) { 120 | console.log('Multi device mismatch, please scan again') 121 | process.exit(0) 122 | } else { 123 | console.log(reason) 124 | process.send('reset') 125 | } 126 | } 127 | if (connection === 'open') { 128 | console.log('Connected, you login as : ' + '[ ' + conn.user.name + ' ]') 129 | } 130 | }) 131 | 132 | // write session 133 | conn.ev.on('creds.update', saveCreds) 134 | 135 | // messages 136 | conn.ev.on('messages.upsert', async (message) => { 137 | if (!message.messages) return 138 | const m = await Serialize(conn, message.messages[0]) 139 | await (await import(`./lib/system/message.js?v=${Date.now()}`)).default(conn, m, message, database) 140 | }) 141 | 142 | // group participants update 143 | conn.ev.on('group-participants.update', async (message) => { 144 | await (await import(`./lib/system/group-participants.js?v=${Date.now()}`)).default(conn, message) 145 | }) 146 | 147 | // group update 148 | conn.ev.on('groups.update', async (update) => { 149 | await (await import(`./lib/system/group-update.js?v=${Date.now()}`)).default(conn, update) 150 | }) 151 | 152 | // auto reject call when user call 153 | conn.ev.on('call', async (json) => { 154 | if (config.options.antiCall) { 155 | for (const id of json) { 156 | if (id.status === 'offer') { 157 | let msg = await conn.sendMessage(id.from, { 158 | text: `Maaf untuk saat ini, Kami tidak dapat menerima panggilan, entah dalam group atau pribadi\n\nJika Membutuhkan bantuan ataupun request fitur silahkan chat owner :p`, 159 | mentions: [id.from], 160 | }) 161 | conn.sendContact(id.from, config.options.owner, msg) 162 | await conn.rejectCall(id.id, id.from) 163 | } 164 | } 165 | } 166 | }) 167 | 168 | // rewrite database every 30 seconds 169 | setInterval(async () => { 170 | if (global.db) await database.write(global.db) 171 | }, 30000) // write database every 30 seconds 172 | 173 | if (!fs.existsSync('./temp')) fs.mkdirSync('./temp') 174 | 175 | /** auto clear tmp */ 176 | setInterval(() => { 177 | try { 178 | const tmpFiles = fs.readdirSync('./temp') 179 | if (tmpFiles.length > 0) { 180 | tmpFiles.filter(v => !v.endsWith('.file')).map(v => fs.unlinkSync('./temp/' + v)) 181 | } 182 | } catch { } 183 | }, 60 * 1000 * 10) // every 10 minute 184 | 185 | return conn 186 | } 187 | start().catch(() => start()) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "moon-case", 3 | "version": "1.5.0", 4 | "description": "A whatsApp bot using library baileys", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node index.js --max-old-space-size=8192", 9 | "dev": "pm2 start index.js --name moon-bot --max-memory-restart 8G -- --max-old-space-size=8192" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/rifnd/moon-case.git" 14 | }, 15 | "keywords": [ 16 | "weabot", 17 | "wa-bot", 18 | "whatsapp-bot" 19 | ], 20 | "author": "rifnd", 21 | "license": "GPL-3.0-or-later", 22 | "bugs": { 23 | "url": "https://github.com/rifnd/moon-case/issues" 24 | }, 25 | "homepage": "https://github.com/rifnd/moon-case#readme", 26 | "dependencies": { 27 | "@moonr/api": "^0.0.1", 28 | "@whiskeysockets/baileys": "npm:@neoxr/baileys", 29 | "chalk": "^5.3.0", 30 | "child_process": "^1.0.2", 31 | "dotenv": "^16.4.7", 32 | "file-type": "^18.5.0", 33 | "fluent-ffmpeg": "^2.1.2", 34 | "moment-timezone": "^0.5.43", 35 | "mongoose": "^6.3.3", 36 | "node-cache": "^5.1.2", 37 | "node-fetch": "^3.3.2", 38 | "node-id3": "^0.2.6", 39 | "node-webpmux": "^3.1.7", 40 | "yt-search": "^2.10.4" 41 | }, 42 | "devDependencies": { 43 | "@adiwajshing/keyed-db": "^0.2.4", 44 | "axios": "^1.4.0", 45 | "jimp": "^0.16.13", 46 | "link-preview-js": "^3.0.4", 47 | "open": "^8.4.2", 48 | "qrcode-terminal": "0.12.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /replit.nix: -------------------------------------------------------------------------------- 1 | { pkgs } : { 2 | deps = [ 3 | pkgs.nodejs_20 4 | pkgs.neofetch 5 | pkgs.jellyfin-ffmpeg 6 | pkgs.imagemagick 7 | pkgs.libwebp 8 | pkgs.yarn 9 | pkgs.libuuid 10 | ]; 11 | env = { 12 | LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ 13 | pkgs.libuuid 14 | ]; 15 | }; 16 | } --------------------------------------------------------------------------------