├── .github └── workflows │ └── stale.yml ├── .gitignore ├── .npmignore ├── asset ├── 2gram banner small.jpg ├── 2gram banner.jpg └── 2gram logo.jpg ├── core ├── botapi.js ├── duacommand.js ├── duaevent.js ├── duagram.js ├── duamessage.js ├── duastore.js ├── flow.md └── telegram.js ├── docs ├── helper.md ├── middleware.md ├── onSignal.md ├── release.md ├── terminal.md └── todo.md ├── examples ├── aboutme.js ├── botLogin.js ├── download.js ├── example.env ├── localSession.js ├── middleware.js ├── onConnected.js ├── ping.js ├── sendURLMediaFile.js ├── terminal.js └── userLogin.js ├── index.js ├── package-lock.json ├── package.json ├── readme.md ├── test ├── config.js ├── error.js ├── noStart.js └── start.js ├── utils ├── date.js ├── index.js ├── log.js └── peer.js └── version.js /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | on: 3 | schedule: 4 | - cron: "20 0 * * *" 5 | workflow_dispatch: 6 | inputs: 7 | debug-only: 8 | description: 'In debug mod' 9 | required: false 10 | default: 'false' 11 | jobs: 12 | stale: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/stale@v3 16 | with: 17 | repo-token: ${{ secrets.TOKEN_GITHUB }} 18 | stale-issue-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.' 19 | stale-pr-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.' 20 | stale-issue-label: 'lifecycle/stale' 21 | exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned' 22 | stale-pr-label: 'lifecycle/stale' 23 | exempt-pr-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned' 24 | days-before-stale: 30 25 | days-before-close: 7 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .test 3 | .env* 4 | .data* 5 | .dg* 6 | bak 7 | debug 8 | ref 9 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .test 3 | test 4 | .env* 5 | .dg* 6 | .data 7 | asset 8 | bak 9 | debug 10 | docs 11 | ref 12 | examples 13 | node_modules -------------------------------------------------------------------------------- /asset/2gram banner small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubotindonesia/duagram/e01920e041050e47c496d0e751de6157fc68a704/asset/2gram banner small.jpg -------------------------------------------------------------------------------- /asset/2gram banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubotindonesia/duagram/e01920e041050e47c496d0e751de6157fc68a704/asset/2gram banner.jpg -------------------------------------------------------------------------------- /asset/2gram logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubotindonesia/duagram/e01920e041050e47c496d0e751de6157fc68a704/asset/2gram logo.jpg -------------------------------------------------------------------------------- /core/botapi.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const RequestPromise = require('request-promise'); 3 | const Stream = require('stream'); 4 | const Path = require('path'); 5 | 6 | class botApi { 7 | constructor(token) { 8 | if (token === undefined) { 9 | throw new Error('Please provide a Telegram bot token when instantiating'); 10 | } 11 | this._token = token; 12 | } 13 | 14 | request(method, params, formData) { 15 | if (arguments.length === 0 || typeof arguments[0] !== 'string') { 16 | throw new Error('Please provide method as a string'); 17 | } 18 | 19 | // the 2nd, 3rd or 4th argument could be a callback 20 | let callback; 21 | if (typeof arguments[3] == 'function') { 22 | callback = arguments[3]; 23 | } else if (typeof arguments[2] == 'function') { 24 | callback = arguments[2]; 25 | formData = null; 26 | } else if (typeof arguments[1] == 'function') { 27 | callback = arguments[1]; 28 | params = null; 29 | } 30 | 31 | let options = { 32 | uri: 'https://api.telegram.org/bot' + this._token + '/' + method, 33 | qs: params, 34 | formData: formData, 35 | simple: false, 36 | resolveWithFullResponse: true, 37 | forever: true 38 | }; 39 | 40 | return RequestPromise(options) 41 | .then(resp => { 42 | if (resp.statusCode !== 200) { 43 | throw new Error(resp.statusCode + ':\n'+ resp.body); 44 | } 45 | 46 | let updates = JSON.parse(resp.body); 47 | 48 | if (updates.ok) { 49 | if (callback) { 50 | callback(null, updates); 51 | } 52 | 53 | return updates; 54 | } 55 | return null; 56 | }) 57 | .catch(error => { 58 | if (callback) { 59 | callback(error); 60 | } 61 | else { 62 | throw error; 63 | } 64 | }); 65 | } 66 | 67 | getMe(callback) { 68 | return this.request('getMe', callback); 69 | } 70 | 71 | getUpdates(offset, callback) { 72 | let params = { 73 | offset: offset, 74 | timeout: 10 75 | }; 76 | 77 | return this.request('getUpdates', params, callback); 78 | } 79 | 80 | setWebhook(url, more, callback) { 81 | let params = { 82 | url: url 83 | } 84 | 85 | if (typeof more == 'function') { 86 | callback = more; 87 | } else { 88 | Object.assign(params, more); 89 | } 90 | 91 | return this.request('setWebhook', url, params, callback); 92 | } 93 | 94 | deleteWebhook(more, callback) { 95 | return this.request('deleteWebhook', more, callback); 96 | } 97 | 98 | getWebhookInfo(callback) { 99 | return this.request('getWebhookInfo', callback); 100 | } 101 | 102 | logOut(callback) { 103 | return this.request('logOut', callback); 104 | } 105 | 106 | close(callback) { 107 | return this.request('close', callback); 108 | } 109 | 110 | sendMessage(chatId, text, more, callback) { 111 | let params = { 112 | chat_id: chatId, 113 | text: text 114 | }; 115 | 116 | if (typeof more == 'function') { 117 | callback = more; 118 | } else { 119 | Object.assign(params, more, callback); 120 | } 121 | 122 | return this.request('sendMessage', params, callback); 123 | } 124 | 125 | answerCallbackQuery(callbackQueryId, more, callback) { 126 | let params = { 127 | callback_query_id: callbackQueryId 128 | }; 129 | 130 | if (typeof more == 'function') { 131 | callback = more; 132 | } else { 133 | Object.assign(params, more); 134 | } 135 | 136 | return this.request('answerCallbackQuery', params, callback); 137 | } 138 | 139 | setMyCommands(commands, callback) { 140 | return this.request('setMyCommands', commands, callback); 141 | } 142 | 143 | getMyCommands() { 144 | return this.request('getMyCommands', callback); 145 | } 146 | 147 | editMessageText(chatId, messageId, text, more, callback) { 148 | let params = { 149 | chat_id: chatId, 150 | message_id: messageId, 151 | text: text 152 | }; 153 | 154 | if (typeof more == 'function') { 155 | callback = more; 156 | } else { 157 | Object.assign(params, more); 158 | } 159 | 160 | return this.request('editMessageText', params, callback); 161 | } 162 | 163 | editInlineMessageText(inlineMessageId, text, more, callback) { 164 | let params = { 165 | inline_message_id: inlineMessageId, 166 | text: text 167 | }; 168 | 169 | if (typeof more == 'function') { 170 | callback = more; 171 | } else { 172 | Object.assign(params, more); 173 | } 174 | 175 | return this.request('editMessageText', params, callback); 176 | } 177 | 178 | editMessageCaption(chatId, messageId, caption, more, callback) { 179 | let params = { 180 | chat_id: chatId, 181 | message_id: messageId, 182 | caption: caption 183 | }; 184 | 185 | if (typeof more == 'function') { 186 | callback = more; 187 | } else { 188 | Object.assign(params, more); 189 | } 190 | 191 | return this.request('editMessageCaption', params, callback); 192 | } 193 | 194 | editInlineMessageCaption(inlineMessagId, caption, more, callback) { 195 | let params = { 196 | inline_message_id: inlineMessageId, 197 | caption: caption 198 | }; 199 | 200 | if (typeof more == 'function') { 201 | callback = more; 202 | } else { 203 | Object.assign(params, more); 204 | } 205 | 206 | return this.request('editMessageCaption', params, callback); 207 | } 208 | 209 | editMessageMedia(chatId, messageId, media, more, callback) { 210 | let params = { 211 | chat_id: chatId, 212 | message_id: messageId, 213 | media: media 214 | }; 215 | 216 | if (typeof more == 'function') { 217 | callback = more; 218 | } else { 219 | Object.assign(params, more); 220 | } 221 | 222 | return this.request('editMessageMedia', params, callback); 223 | } 224 | 225 | editInlineMessageMedia(inlineMessageId, media, more, callback) { 226 | let params = { 227 | inline_message_id: inlineMessageId, 228 | media: media 229 | }; 230 | 231 | if (typeof more == 'function') { 232 | callback = more; 233 | } else { 234 | Object.assign(params, more); 235 | } 236 | 237 | return this.request('editMessageMedia', params, callback); 238 | } 239 | 240 | editMessageReplyMarkup(chatId, messageId, replyMarkup, callback) { 241 | let params = { 242 | chat_id: chatId, 243 | message_id: messageId, 244 | reply_markup: replyMarkup 245 | } 246 | 247 | return this.request('editMessageReplyMarkup', params, callback); 248 | } 249 | 250 | editInlineMessageReplyMarkup(inlineMessageId, replyMarkup, callback) { 251 | let params = { 252 | inline_message_id: inlineMessageId, 253 | reply_markup: replyMarkup 254 | } 255 | 256 | return this.request('editMessageReplyMarkup', params, callback); 257 | } 258 | 259 | deleteMessage(chatId, messageId, callback) { 260 | let params = { 261 | chat_id: chatId, 262 | message_id: messageId 263 | } 264 | 265 | return this.request('deleteMessage', params, callback); 266 | } 267 | 268 | answerInlineQuery(inlineQueryId, results, more, callback) { 269 | let params = { 270 | inline_query_id: inlineQueryId, 271 | results: results 272 | }; 273 | 274 | if (typeof more == 'function') { 275 | callback = more; 276 | } else { 277 | Object.assign(params, more); 278 | } 279 | 280 | return this.request('answerInlineQuery', params, callback); 281 | } 282 | 283 | forwardMessage(chatId, fromChatId, messageId, more, callback) { 284 | let params = { 285 | chat_id: chatId, 286 | from_chat_id: fromChatId, 287 | message_id: messageId 288 | } 289 | 290 | if (typeof more == 'function') { 291 | callback = more; 292 | } else { 293 | Object.assign(params, more); 294 | } 295 | 296 | return this.request('forwardMessage', params, callback); 297 | } 298 | 299 | copyMessage(chatId, fromChatId, messageId, more, callback) { 300 | let params = { 301 | chat_id: chatId, 302 | from_chat_id: fromChatId, 303 | message_id: messageId 304 | } 305 | 306 | if (typeof more == 'function') { 307 | callback = more; 308 | } else { 309 | Object.assign(params, more); 310 | } 311 | 312 | return this.request('copyMessage', params, callback); 313 | } 314 | 315 | sendPhoto(chatId, photo, more, callback) { 316 | let params; 317 | let formData; 318 | 319 | if (isReadableStream(photo)) { 320 | params = { 321 | chat_id: chatId 322 | }; 323 | formData = { 324 | photo: photo 325 | }; 326 | } else { 327 | params = { 328 | chat_id: chatId, 329 | photo: photo 330 | }; 331 | } 332 | 333 | if (typeof more == 'function') { 334 | callback = more; 335 | } else { 336 | Object.assign(params, more); 337 | } 338 | 339 | return this.request('sendPhoto', params, formData, callback); 340 | } 341 | 342 | sendAudio(chatId, audio, more, callback) { 343 | let params; 344 | let formData; 345 | 346 | if (isReadableStream(audio)) { 347 | params = { 348 | chat_id: chatId 349 | }; 350 | formData = { 351 | audio: audio 352 | }; 353 | } else { 354 | params = { 355 | chat_id: chatId, 356 | audio: audio 357 | }; 358 | } 359 | 360 | if (typeof more == 'function') { 361 | callback = more; 362 | } else { 363 | Object.assign(params, more); 364 | } 365 | 366 | return this.request('sendAudio', params, formData, callback); 367 | } 368 | 369 | sendDocument(chatId, document, more, callback) { 370 | let params; 371 | let formData; 372 | 373 | if (isReadableStream(document)) { 374 | params = { 375 | chat_id: chatId 376 | }; 377 | formData = { 378 | document: document 379 | }; 380 | } else { 381 | params = { 382 | chat_id: chatId, 383 | document: document 384 | }; 385 | } 386 | 387 | if (typeof more == 'function') { 388 | callback = more; 389 | } else { 390 | Object.assign(params, more); 391 | } 392 | 393 | return this.request('sendDocument', params, formData, callback); 394 | } 395 | 396 | sendAnimation(chatId, animation, more, callback) { 397 | let params; 398 | let formData; 399 | 400 | if (isReadableStream(animation)) { 401 | params = { 402 | chat_id: chatId 403 | }; 404 | formData = { 405 | animation: animation 406 | }; 407 | } else { 408 | params = { 409 | chat_id: chatId, 410 | animation: animation 411 | }; 412 | } 413 | 414 | if (typeof more == 'function') { 415 | callback = more; 416 | } else { 417 | Object.assign(params, more); 418 | } 419 | 420 | return this.request('sendAnimation', params, formData, callback); 421 | } 422 | 423 | // Polls 424 | 425 | sendPoll(chatId, question, pollOptions, more, callback) { 426 | let params = { 427 | chat_id: chatId, 428 | question: question, 429 | options: pollOptions 430 | }; 431 | 432 | if (typeof more == 'function') { 433 | callback = more; 434 | } else { 435 | Object.assign(params, more, callback); 436 | } 437 | 438 | return this.request('sendPoll', params, callback); 439 | } 440 | 441 | stopPoll(chatId, messageId, more, callback) { 442 | let params = { 443 | chat_id: chatId, 444 | message_id: messageId 445 | }; 446 | 447 | if (typeof more == 'function') { 448 | callback = more; 449 | } else { 450 | Object.assign(params, more, callback); 451 | } 452 | 453 | return this.request('stopPoll', params, callback); 454 | } 455 | 456 | // Stickers 457 | 458 | sendSticker(chatId, sticker, more, callback) { 459 | let params; 460 | let formData; 461 | 462 | if (isReadableStream(sticker)) { 463 | params = { 464 | chat_id: chatId 465 | }; 466 | formData = { 467 | sticker: sticker 468 | }; 469 | } else { 470 | params = { 471 | chat_id: chatId, 472 | sticker: sticker 473 | }; 474 | } 475 | 476 | if (typeof more == 'function') { 477 | callback = more; 478 | } else { 479 | Object.assign(params, more); 480 | } 481 | 482 | return this.request('sendSticker', params, formData, callback); 483 | } 484 | 485 | getStickerSet(name, callback) { 486 | let params = { 487 | name: name 488 | } 489 | 490 | return this.request('getStickerSet', params, callback); 491 | } 492 | 493 | uploadStickerFile(userId, pngFile, callback) { 494 | let params = { 495 | user_id: userId, 496 | }; 497 | 498 | let formData = { 499 | png_sticker: pngFile 500 | }; 501 | 502 | return this.request('uploadStickerFile', params, formData, callback); 503 | } 504 | 505 | createNewStickerSet(userId, name, title, stickerFile, emojis, more, callback) { 506 | let params; 507 | let formData; 508 | 509 | if (isReadableStream(stickerFile)) { 510 | // stickerFile is a readableStream, check extension 511 | let ext = Path.extname(stickerFile.path); 512 | 513 | if (ext === '.png') { 514 | params = { 515 | user_id: userId, 516 | name: name, 517 | title: title, 518 | emojis: emojis 519 | }; 520 | formData = { 521 | png_sticker: stickerFile 522 | }; 523 | } else if (ext === '.tgs') { 524 | params = { 525 | user_id: userId, 526 | name: name, 527 | title: title, 528 | emojis: emojis 529 | }; 530 | formData = { 531 | tgs_sticker: stickerFile 532 | }; 533 | } 534 | } else { 535 | // stickerFile is a string, either a file_id or HTTP URL 536 | params = { 537 | user_id: userId, 538 | name: name, 539 | title: title, 540 | png_sticker: stickerFile, 541 | emojis: emojis 542 | }; 543 | } 544 | 545 | if (typeof more == 'function') { 546 | callback = more; 547 | } else { 548 | Object.assign(params, more); 549 | } 550 | 551 | return this.request('createNewStickerSet', params, formData, callback); 552 | } 553 | 554 | addStickerToSet(userId, name, stickerFile, emojis, more, callback) { 555 | let params; 556 | let formData; 557 | 558 | if (isReadableStream(stickerFile)) { 559 | // stickerFile is a readableStream, check extension 560 | let ext = Path.extname(stickerFile.path); 561 | 562 | if (ext === '.png') { 563 | params = { 564 | user_id: userId, 565 | name: name, 566 | emojis: emojis 567 | }; 568 | formData = { 569 | png_sticker: stickerFile 570 | }; 571 | } else if (ext === '.tgs') { 572 | params = { 573 | user_id: userId, 574 | name: name, 575 | emojis: emojis 576 | }; 577 | formData = { 578 | tgs_sticker: stickerFile 579 | }; 580 | } 581 | } else { 582 | // stickerFile is a string, either a file_id or HTTP URL 583 | params = { 584 | user_id: userId, 585 | name: name, 586 | png_sticker: stickerFile, 587 | emojis: emojis 588 | }; 589 | } 590 | 591 | if (typeof more == 'function') { 592 | callback = more; 593 | } else { 594 | Object.assign(params, more); 595 | } 596 | 597 | return this.request('addStickerToSet', params, formData, callback); 598 | } 599 | 600 | setStickerPositionInSet(sticker, position, callback) { 601 | let params = { 602 | sticker: sticker, 603 | position: position 604 | } 605 | 606 | return this.request('setStickerPositionInSet', params, callback); 607 | } 608 | 609 | deleteStickerFromSet(sticker, callback) { 610 | let params = { 611 | sticker: sticker 612 | } 613 | 614 | return this.request('deleteStickerFromSet', params, callback); 615 | } 616 | 617 | setStickerSetThumb(name, userId, thumbnailFile, callback) { 618 | let params; 619 | let formData; 620 | 621 | if (isReadableStream(thumbnailFile)) { 622 | // thumbnailFile is a readableStream, check extension 623 | let ext = Path.extname(thumbnailFile.path); 624 | 625 | if (ext === '.png') { 626 | params = { 627 | user_id: userId, 628 | name: name 629 | }; 630 | formData = { 631 | thumb: thumbnailFile 632 | }; 633 | } else if (ext === '.tgs') { 634 | params = { 635 | user_id: userId, 636 | name: name 637 | }; 638 | formData = { 639 | thumb: thumbnailFile 640 | }; 641 | } 642 | } else { 643 | // thumbnailFile is a string, either a file_id or HTTP URL 644 | params = { 645 | user_id: userId, 646 | name: name, 647 | thumb: thumbnailFile 648 | }; 649 | } 650 | 651 | return this.request('setStickerSetThumb', params, formData, callback); 652 | } 653 | 654 | sendVideo(chatId, video, more, callback) { 655 | let params; 656 | let formData; 657 | 658 | if (isReadableStream(video)) { 659 | params = { 660 | chat_id: chatId 661 | }; 662 | formData = { 663 | video: video 664 | }; 665 | } else { 666 | params = { 667 | chat_id: chatId, 668 | video: video 669 | }; 670 | } 671 | 672 | if (typeof more == 'function') { 673 | callback = more; 674 | } else { 675 | Object.assign(params, more); 676 | } 677 | 678 | return this.request('sendVideo', params, formData, callback); 679 | } 680 | 681 | sendVoice(chatId, voice, more, callback) { 682 | let params; 683 | let formData; 684 | 685 | if (isReadableStream(voice)) { 686 | params = { 687 | chat_id: chatId 688 | }; 689 | formData = { 690 | voice: voice 691 | }; 692 | } else { 693 | params = { 694 | chat_id: chatId, 695 | voice: voice 696 | }; 697 | } 698 | 699 | if (typeof more == 'function') { 700 | callback = more; 701 | } else { 702 | Object.assign(params, more); 703 | } 704 | 705 | return this.request('sendVoice', params, formData, callback); 706 | } 707 | 708 | sendVideoNote(chatId, videoNote, more, callback) { 709 | let params; 710 | let formData; 711 | 712 | if (isReadableStream(videoNote)) { 713 | params = { 714 | chat_id: chatId 715 | }; 716 | formData = { 717 | video_note: videoNote 718 | }; 719 | } else { 720 | params = { 721 | chat_id: chatId, 722 | video_note: videoNote 723 | }; 724 | } 725 | 726 | if (typeof more == 'function') { 727 | callback = more; 728 | } else { 729 | Object.assign(params, more); 730 | } 731 | 732 | return this.request('sendVideoNote', params, formData, callback); 733 | } 734 | 735 | sendMediaGroup(chatId, media, more, callback) { 736 | let params = { 737 | chat_id: chatId, 738 | media: media 739 | }; 740 | 741 | if (typeof more == 'function') { 742 | callback = more; 743 | } else { 744 | Object.assign(params, more); 745 | } 746 | 747 | return this.request('sendMediaGroup', params, callback); 748 | } 749 | 750 | sendLocation(chatId, lat, lon, more, callback) { 751 | let params = { 752 | chat_id: chatId, 753 | latitude: lat, 754 | longitude: lon 755 | }; 756 | 757 | if (typeof more == 'function') { 758 | callback = more; 759 | } else { 760 | Object.assign(params, more); 761 | } 762 | 763 | return this.request('sendLocation', params, callback); 764 | } 765 | 766 | editMessageLiveLocation(chatId, messageId, lat, lon, more, callback) { 767 | let params = { 768 | chat_id: chatId, 769 | message_id: messageId, 770 | latitude: lat, 771 | longitude: lon 772 | } 773 | 774 | if (typeof more == 'function') { 775 | callback = more; 776 | } else { 777 | Object.assign(params, more); 778 | } 779 | 780 | return this.request('editMessageLiveLocation', params, callback); 781 | } 782 | 783 | editInlineMessageLiveLocation(inlineMessageId, lat, lon, more, callback) { 784 | let params = { 785 | inline_message_id: inlineMessageId, 786 | latitude: lat, 787 | longitude: lon 788 | } 789 | 790 | if (typeof more == 'function') { 791 | callback = more; 792 | } else { 793 | Object.assign(params, more); 794 | } 795 | 796 | return this.request('editMessageLiveLocation', params, callback); 797 | } 798 | 799 | stopMessageLiveLocation(chatId, messageId, more, callback) { 800 | let params = { 801 | chat_id: chatId, 802 | message_id: messageId 803 | } 804 | 805 | if (typeof more == 'function') { 806 | callback = more; 807 | } else { 808 | Object.assign(params, more); 809 | } 810 | 811 | return this.request('stopMessageLiveLocation', params, callback); 812 | } 813 | 814 | stopInlineMessageLiveLocation(inlineMessageId, more, callback) { 815 | let params = { 816 | inline_message_id: inlineMessageId 817 | } 818 | 819 | if (typeof more == 'function') { 820 | callback = more; 821 | } else { 822 | Object.assign(params, more); 823 | } 824 | 825 | return this.request('stopMessageLiveLocation', params, callback); 826 | } 827 | 828 | sendVenue(chatId, lat, lon, title, address, more, callback) { 829 | let params = { 830 | chat_id: chatId, 831 | latitude: lat, 832 | longitude: lon, 833 | title: title, 834 | address: address 835 | } 836 | 837 | if (typeof more == 'function') { 838 | callback = more; 839 | } else { 840 | Object.assign(params, more); 841 | } 842 | 843 | return this.request('sendVenue', params, callback); 844 | } 845 | 846 | sendContact(chatId, phoneNumber, firstName, more, callback) { 847 | let params = { 848 | chat_id: chatId, 849 | phone_number: phoneNumber, 850 | first_name: firstName 851 | } 852 | 853 | if (typeof more == 'function') { 854 | callback = more; 855 | } else { 856 | Object.assign(params, more); 857 | } 858 | 859 | return this.request('sendContact', params, callback); 860 | } 861 | 862 | sendDice(chatId, more, callback) { 863 | let params = { 864 | chat_id: chatId 865 | } 866 | 867 | if (typeof more == 'function') { 868 | callback = more; 869 | } else { 870 | Object.assign(params, more); 871 | } 872 | 873 | return this.request('sendDice', params, callback); 874 | } 875 | 876 | sendChatAction(chatId, action, callback) { 877 | if (typeof action !== 'string') { 878 | throw new Error('sendChatAction method needs a string input'); 879 | } 880 | 881 | let params = { 882 | chat_id: chatId, 883 | action: action 884 | }; 885 | 886 | return this.request('sendChatAction', params, callback); 887 | } 888 | 889 | getUserProfilePhotos(userId, more, callback) { 890 | let params = { 891 | user_id: userId, 892 | }; 893 | 894 | if (typeof more == 'function') { 895 | callback = more; 896 | } else { 897 | Object.assign(params, more); 898 | } 899 | 900 | return this.request('getUserProfilePhotos', params, callback); 901 | } 902 | 903 | getFile(fileId, callback) { 904 | let params = { 905 | file_id: fileId 906 | }; 907 | 908 | return this.request('getFile', params, callback); 909 | } 910 | 911 | kickChatMember(chatId, userId, more, callback) { 912 | let params = { 913 | chat_id: chatId, 914 | user_id: userId 915 | }; 916 | 917 | if (typeof more == 'function') { 918 | callback = more; 919 | } else { 920 | Object.assign(params, more); 921 | } 922 | 923 | return this.request('kickChatMember', params, callback); 924 | } 925 | 926 | unbanChatMember(chatId, userId, more, callback) { 927 | let params = { 928 | chat_id: chatId, 929 | user_id: userId 930 | }; 931 | 932 | if (typeof more == 'function') { 933 | callback = more; 934 | } else { 935 | Object.assign(params, more); 936 | } 937 | 938 | return this.request('unbanChatMember', params, callback); 939 | } 940 | 941 | restrictChatMember(chatId, userId, permissions, more, callback) { 942 | let params = { 943 | chat_id: chatId, 944 | user_id: userId, 945 | permissions: permissions 946 | } 947 | 948 | if (typeof more == 'function') { 949 | callback = more; 950 | } else { 951 | Object.assign(params, more); 952 | } 953 | 954 | return this.request('restrictChatMember', params, callback); 955 | } 956 | 957 | promoteChatMember(chatId, userId, more, callback) { 958 | let params = { 959 | chat_id: chatId, 960 | user_id: userId 961 | } 962 | 963 | if (typeof more == 'function') { 964 | callback = more; 965 | } else { 966 | Object.assign(params, more); 967 | } 968 | 969 | return this.request('promoteChatMember', params, callback); 970 | } 971 | 972 | setChatAdministratorCustomTitle(chatId, userId, customTitle, callback) { 973 | let params = { 974 | chat_id: chatId, 975 | user_id: userId, 976 | custom_title: customTitle 977 | } 978 | 979 | return this.request('setChatAdministratorCustomTitle', params, callback); 980 | } 981 | 982 | setChatPermissions(chatId, permissions, callback) { 983 | let params = { 984 | chat_id: chatId, 985 | permissions: permissions 986 | } 987 | 988 | if (typeof more == 'function') { 989 | callback = more; 990 | } else { 991 | Object.assign(params, more); 992 | } 993 | 994 | return this.request('setChatPermissions', params, callback); 995 | } 996 | 997 | exportChatInviteLink(chatId, callback) { 998 | let params = { 999 | chat_id: chatId 1000 | } 1001 | 1002 | return this.request('exportChatInviteLink', params, callback); 1003 | } 1004 | 1005 | createChatInviteLink(chatId, more, callback) { 1006 | let params = { 1007 | chat_id: chatId 1008 | } 1009 | 1010 | if (typeof more == 'function') { 1011 | callback = more; 1012 | } else { 1013 | Object.assign(params, more); 1014 | } 1015 | 1016 | return this.request('createChatInviteLink', params, callback); 1017 | } 1018 | 1019 | editChatInviteLink(chatId, inviteLink, more, callback) { 1020 | let params = { 1021 | chat_id: chatId, 1022 | invite_link: inviteLink 1023 | } 1024 | 1025 | if (typeof more == 'function') { 1026 | callback = more; 1027 | } else { 1028 | Object.assign(params, more); 1029 | } 1030 | 1031 | return this.request('editChatInviteLink', params, callback); 1032 | } 1033 | 1034 | revokeChatInviteLink(chatId, inviteLink, callback) { 1035 | let params = { 1036 | chat_id: chatId, 1037 | invite_link: inviteLink 1038 | } 1039 | 1040 | return this.request('revokeChatInviteLink', params, callback); 1041 | } 1042 | 1043 | setChatPhoto(chatId, photo, callback) { 1044 | let params = { 1045 | chat_id: chatId 1046 | }; 1047 | 1048 | let formData = { 1049 | photo: photo 1050 | }; 1051 | 1052 | return this.request('setChatPhoto', params, formData, callback); 1053 | } 1054 | 1055 | deleteChatPhoto(chatId, callback) { 1056 | let params = { 1057 | chat_id: chatId 1058 | } 1059 | 1060 | return this.request('deleteChatPhoto', params, callback); 1061 | } 1062 | 1063 | setChatTitle(chatId, title, callback) { 1064 | let params = { 1065 | chat_id: chatId, 1066 | title: title 1067 | } 1068 | 1069 | return this.request('setChatTitle', params, callback); 1070 | } 1071 | 1072 | setChatDescription(chatId, description, callback) { 1073 | let params = { 1074 | chat_id: chatId, 1075 | description: description 1076 | } 1077 | 1078 | return this.request('setChatDescription', params, callback); 1079 | } 1080 | 1081 | pinChatMessage(chatId, messageId, more, callback) { 1082 | let params = { 1083 | chat_id: chatId, 1084 | message_id: messageId 1085 | } 1086 | 1087 | if (typeof more == 'function') { 1088 | callback = more; 1089 | } else { 1090 | Object.assign(params, more); 1091 | } 1092 | 1093 | return this.request('pinChatMessage', params, callback); 1094 | } 1095 | 1096 | unpinChatMessage(chatId, more, callback) { 1097 | let params = { 1098 | chat_id: chatId 1099 | } 1100 | 1101 | if (typeof more == 'function') { 1102 | callback = more; 1103 | } else { 1104 | Object.assign(params, more); 1105 | } 1106 | 1107 | return this.request('unpinChatMessage', params, callback); 1108 | } 1109 | 1110 | unpinAllChatMessages(chatId, callback) { 1111 | let params = { 1112 | chat_id: chatId 1113 | } 1114 | 1115 | return this.request('unpinAllChatMessages', params, callback); 1116 | } 1117 | 1118 | leaveChat(chatId, callback) { 1119 | let params = { 1120 | chat_id: chatId 1121 | }; 1122 | 1123 | return this.request('leaveChat', params, callback); 1124 | } 1125 | 1126 | getChat(chatId, callback) { 1127 | let params = { 1128 | chat_id: chatId 1129 | }; 1130 | 1131 | return this.request('getChat', params, callback); 1132 | } 1133 | 1134 | getChatAdministrators(chatId, callback) { 1135 | let params = { 1136 | chat_id: chatId 1137 | }; 1138 | 1139 | return this.request('getChatAdministrators', params, callback); 1140 | } 1141 | 1142 | getChatMembersCount(chatId, callback) { 1143 | let params = { 1144 | chat_id: chatId 1145 | }; 1146 | 1147 | return this.request('getChatMembersCount', params, callback); 1148 | } 1149 | 1150 | getChatMember(chatId, userId, callback) { 1151 | let params = { 1152 | chat_id: chatId, 1153 | user_id: userId 1154 | }; 1155 | 1156 | return this.request('getChatMember', params, callback); 1157 | } 1158 | 1159 | setChatStickerSet(chatId, stickerSetName, callback) { 1160 | let params = { 1161 | chat_id: chatId, 1162 | sticker_set_name: stickerSetName 1163 | } 1164 | 1165 | return this.request('setChatStickerSet', params, callback); 1166 | } 1167 | 1168 | deleteChatStickerSet(chatId, callback) { 1169 | let params = { 1170 | chat_id: chatId 1171 | } 1172 | 1173 | return this.request('deleteChatStickerSet', params, callback); 1174 | } 1175 | 1176 | // Payment 1177 | 1178 | sendInvoice(chatId, title, description, payload, providerToken, startParameter, currency, prices, more, callback) { 1179 | let params = { 1180 | chat_id: chatId, 1181 | title: title, 1182 | description: description, 1183 | payload: payload, 1184 | provider_token: providerToken, 1185 | start_parameter: startParameter, 1186 | currency: currency, 1187 | prices: prices 1188 | }; 1189 | 1190 | if (typeof more == 'function') { 1191 | callback = more; 1192 | } else { 1193 | Object.assign(params, more); 1194 | } 1195 | 1196 | return this.request('sendInvoice', params, callback); 1197 | } 1198 | 1199 | answerShippingQuery(shippingQueryId, ok, callback) { 1200 | let params = { 1201 | shipping_query_id: shippingQueryId, 1202 | ok: ok 1203 | }; 1204 | 1205 | if (typeof more == 'function') { 1206 | callback = more; 1207 | } else { 1208 | Object.assign(params, more); 1209 | } 1210 | 1211 | return this.request('answerShippingQuery', params, callback); 1212 | } 1213 | 1214 | answerPreCheckoutQuery(preCheckoutQueryId, ok, errorMessage) { 1215 | let params = { 1216 | pre_checkout_query_id: preCheckoutQueryId, 1217 | ok: ok, 1218 | error_message: errorMessage 1219 | }; 1220 | 1221 | return this.request('answerShippingQuery', params, callback); 1222 | } 1223 | 1224 | // Games 1225 | 1226 | sendGame(chatId, gameShortName, more, callback) { 1227 | let params = { 1228 | chat_id: chatId, 1229 | game_short_name: gameShortName 1230 | }; 1231 | 1232 | if (typeof more == 'function') { 1233 | callback = more; 1234 | } else { 1235 | Object.assign(params, more); 1236 | } 1237 | 1238 | return this.request('sendGame', params, callback); 1239 | } 1240 | 1241 | setGameScore(userId, score, more, callback) { 1242 | let params = { 1243 | user_id: userId, 1244 | score: score 1245 | } 1246 | 1247 | if (typeof more == 'function') { 1248 | callback = more; 1249 | } else { 1250 | Object.assign(params, more); 1251 | } 1252 | 1253 | return this.request('setGameScore', params, callback); 1254 | } 1255 | 1256 | getGameHighScores(userId, more, callback) { 1257 | let params = { 1258 | user_id: userId 1259 | }; 1260 | 1261 | if (typeof more == 'function') { 1262 | callback = more; 1263 | } else { 1264 | Object.assign(params, more); 1265 | } 1266 | 1267 | return this.request('getGameHighScores', params, callback); 1268 | } 1269 | } 1270 | 1271 | function isReadableStream(object) { 1272 | return object instanceof Stream.Stream && 1273 | typeof object._read === "function" && 1274 | typeof object._readableState === "object" 1275 | } 1276 | 1277 | module.exports = botApi; 1278 | -------------------------------------------------------------------------------- /core/duacommand.js: -------------------------------------------------------------------------------- 1 | const DuaEvent = require('./duaevent'); 2 | 3 | class DuaCommand extends DuaEvent { 4 | constructor() { 5 | super(); 6 | } 7 | 8 | async getMe(peer = false) { 9 | return await this.client.getMe(peer) 10 | } 11 | 12 | // alias 13 | async sendMessage(peer, text, more = {}) { 14 | return await this.telegram.sendMessage(peer, text, more); 15 | } 16 | 17 | async editMessage(peer, id, text, more = {}) { 18 | return await this.telegram.editMessage(peer, id, text, more); 19 | } 20 | 21 | async deleteMessages(peer, ids, revoke = true) { 22 | return await this.telegram.deleteMessages(peer, ids, revoke) 23 | } 24 | 25 | async deleteMessage(peer, ids, revoke = true) { 26 | return await this.telegram.deleteMessages(peer, ids, revoke) 27 | } 28 | 29 | async forwardMessages(peerFrom, peerTo, ids, more = {}) { 30 | return await this.telegram.forwardMessages(peerFrom, peerTo, ids, more); 31 | } 32 | 33 | async getMessages(peer, ids) { 34 | return await this.telegram.getMessages(peer, ids); 35 | } 36 | 37 | async getMessage(peer, ids) { 38 | return await this.telegram.getMessages(peer, ids); 39 | } 40 | 41 | async readMentions(peer) { 42 | return await this.telegram.readMentions(peer); 43 | } 44 | 45 | async readMessageContents(id) { 46 | return await this.telegram.readMessageContents(id); 47 | } 48 | 49 | async deleteHistory(peer, more = {}) { 50 | return await this.telegram.deleteHistory(peer, more); 51 | } 52 | 53 | async deleteUserHistory(channelId, userId) { 54 | return await this.telegram.deleteUserHistory(channelId, userId); 55 | } 56 | 57 | async pinMessage(peer, id, more = {}) { 58 | return await this.telegram.pinMessage(peer, id, more); 59 | } 60 | 61 | async unpinAllMessages(peer) { 62 | return await this.telegram.unpinAllMessages(peer); 63 | } 64 | 65 | async getUserPhotos(peer, more = {}) { 66 | return await this.telegram.getUserPhotos(peer, more); 67 | } 68 | 69 | getPeerId(ctx) { 70 | return this.telegram.getPeerId(ctx); 71 | } 72 | 73 | async invoke(data) { 74 | return await this.telegram.invoke(data); 75 | } 76 | 77 | async editAdmin(peerChatId, peerUserId, more = {}) { 78 | return await this.telegram.editAdmin(peerChatId, peerUserId, more); 79 | } 80 | 81 | async editBanned(peerChatId, peerUserId, more = {}) { 82 | return await this.telegram.editBanned(peerChatId, peerUserId, more); 83 | } 84 | 85 | async getUserInfo(peer) { 86 | return await this.telegram.getUserInfo(peer); 87 | } 88 | 89 | async joinGroup(peer) { 90 | return await this.telegram.joinGroup(peer); 91 | } 92 | 93 | async downloadMedia(media_data, more = {}) { 94 | return await this.telegram.downloadMedia(media_data, more); 95 | } 96 | 97 | 98 | async sendFile(peer, file, more = {}) { 99 | return await this.telegram.sendFile(peer, file, more); 100 | } 101 | 102 | buildOn(update) { 103 | let command = {}; 104 | 105 | command.reply = async (text, more = {}) => { 106 | return await this.sendMessage(update.chat.id, text, more); 107 | }; 108 | 109 | command.replyWithHTML = async (text, more = {}) => { 110 | return await this.sendMessage(update.chat.id, text, { parse_mode: 'html', ...more }); 111 | }; 112 | command.replyWithMarkdown = async (text, more = {}) => { 113 | return await this.sendMessage(update.chat.id, text, { parse_mode: 'markdown', ...more }); 114 | }; 115 | command.replyWithSendFile = async (file, more = {}) => { 116 | return await this.sendFile(update.chat.id, file, more); 117 | }; 118 | command.deleteMessage = async (revoke = true) => { 119 | return await this.deleteMessages(update.id, revoke); 120 | }; 121 | 122 | Object.assign(update, command); 123 | return update; 124 | } 125 | 126 | // end class 127 | } 128 | 129 | module.exports = DuaCommand; -------------------------------------------------------------------------------- /core/duaevent.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events'); 2 | const DuaMessage = require('./duamessage'); 3 | const { terminal } = require('../utils/log'); 4 | const { StringSession, StoreSession } = require("telegram/sessions"); 5 | const { pbkdf2Sync } = require('crypto'); 6 | 7 | const keygen = (str, len = 8) => { 8 | let key = pbkdf2Sync(str, 'dg', 100000, len, 'sha256'); 9 | return '.dg' + key.toString('hex'); 10 | } 11 | 12 | 13 | const removeNull = (obj) => { 14 | Object.keys(obj).forEach(k => 15 | (obj[k] && typeof obj[k] === 'object') && removeNull(obj[k]) 16 | || 17 | !obj[k] && delete obj[k] 18 | ); 19 | return obj; 20 | }; 21 | 22 | class DuaEvent extends EventEmitter { 23 | constructor() { 24 | super(); 25 | this.DuaMessage = DuaMessage; 26 | this.asBotApi; 27 | this.cmdPrefix = "!./"; 28 | this.BotApi = false; 29 | this.client = false; 30 | this.scanners = []; 31 | this.middlewares = []; 32 | this.terminal = terminal; 33 | 34 | this.session_type; 35 | this.keygen = keygen; 36 | } 37 | 38 | async makeSession() { 39 | let options = this.options; 40 | this.session_type = 'memory'; 41 | let session = new StringSession(options.session); 42 | await session.load(); 43 | 44 | if (!options.local) return session; 45 | 46 | let session_name = options.session_name || this.keygen(options.session); 47 | let local = new StoreSession(session_name); 48 | 49 | local.setDC( 50 | session.dcId, 51 | session.serverAddress, 52 | session.port 53 | ) 54 | 55 | local.setAuthKey( 56 | session.authKey 57 | ) 58 | 59 | this.session_type = 'local'; 60 | return local; 61 | } 62 | 63 | init(options) { 64 | try { 65 | if (typeof options !== 'object') throw Error('Please, check documentation to starting bot.'); 66 | if (!options.api_id) throw Error('api_id is required.'); 67 | if (!options.api_hash) throw Error('api_hash is required.'); 68 | 69 | if (options.as_bot_api && !options.bot_token) throw new Error("bot_api required!"); 70 | // if (!options.as_bot_api && !options.session) throw new Error("session required!"); 71 | 72 | if (parseInt(options.api_id) < 5000) throw Error('api_id - mistake, get it from https://my.telegram.org'); 73 | if (options.as_bot_api && options.bot_token.length < 20) throw Error('bot_token - mistake, get it from @botfather') 74 | 75 | if (options.cmdPrefix) { 76 | this.cmdPrefix = options.cmdPrefix; 77 | } 78 | 79 | options.useWSS = options.useWSS || false; 80 | 81 | options.session = options.session || ''; 82 | options.session_name = options.session_name || false; 83 | options.logDetail = options.logDetail || 'debug'; 84 | options.logLevel = options.logLevel || 1; 85 | options.floodSleepThreshold = options.floodSleepThreshold || 120; 86 | 87 | options.connectionRetries = options.connectionRetries || 3; 88 | options.markRead = options.hasOwnProperty('markRead') ? options.markRead : true; 89 | 90 | options.more = options.more || {}; 91 | this.options = options; 92 | 93 | } catch (error) { 94 | this.terminal.error(error.message); 95 | this.terminal.warn('Please, check your options again.'); 96 | process.exit(); 97 | } 98 | } 99 | 100 | escapeRegExp(str) { 101 | if (typeof str === 'string') 102 | return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') 103 | .replace(/-/g, '\\x2d'); 104 | return str; 105 | } 106 | 107 | fieldType(data) { 108 | if (!data) return false; 109 | let type = data.className.replace('Peer', '').toLowerCase(); 110 | return { 111 | type, 112 | id: data[type + 'Id'] 113 | } 114 | } 115 | 116 | async processMessage(ctx) { 117 | const _ctx = ctx; 118 | ctx = ctx.message; 119 | let more = {}; 120 | 121 | /* let eventKey = []; 122 | for (key in _ctx) { 123 | eventKey.push(key); 124 | } */ 125 | 126 | let from = ctx.fromId ? this.fieldType(ctx.fromId) : false; 127 | let peer = ctx.peerId ? this.fieldType(ctx.peerId) : false; 128 | 129 | let forward_from = false; 130 | if (ctx.fwdFrom) { 131 | let type = false; 132 | let id = false; 133 | if (ctx.fwdFrom.fromId) { 134 | type = ctx.fwdFrom.fromId.className == 'PeerUser' ? 'user' : 'channel'; 135 | id = ctx.fwdFrom.fromId[`${type}Id`]; 136 | } 137 | let { date, fromName } = ctx.fwdFrom; 138 | forward_from = { 139 | type, 140 | id, 141 | date, 142 | name: fromName, 143 | } 144 | } 145 | 146 | let ids = [ctx.id]; 147 | 148 | if (ctx.replyTo) { 149 | let id = ctx.replyTo.replyToMsgId; 150 | ids.push(id); 151 | more.reply = id; 152 | }; 153 | 154 | 155 | let update = await this.getMessages(peer.id, ids); 156 | 157 | more = { peer, from, forward: forward_from, ...more }; 158 | let context = new DuaMessage(update, more).context; 159 | 160 | let result = { 161 | peer, 162 | from, 163 | forward_from, 164 | ...context 165 | }; 166 | result = removeNull(result); 167 | this.processMiddleware(result, _ctx); 168 | } 169 | 170 | addMessageFromMe(update) { 171 | // split off media 172 | let media = {}; 173 | if (update.media) { 174 | media.main = update.media; 175 | delete update.media; 176 | } 177 | 178 | if (update.reply_to_message?.media) { 179 | media.reply = update.reply_to_message.media; 180 | delete update.reply_to_message.media; 181 | } 182 | 183 | let result = JSON.stringify(update); 184 | let me = JSON.stringify(this.me.short); 185 | result = result.replace(/"from":"me"/g, `"from": ${me}`); 186 | result = JSON.parse(result); 187 | 188 | // merge media 189 | if (media.main) 190 | result.media = media.main; 191 | if (media.reply) 192 | result.reply_to_message.media = media.reply; 193 | 194 | 195 | result = removeNull(result); 196 | 197 | if (this.options.markRead && !this.options.as_bot_api) { 198 | this.telegram.readHistory(result); 199 | } 200 | 201 | return result; 202 | } 203 | 204 | processMiddleware(update, _ctx) { 205 | let _update = update; 206 | 207 | update = this.addMessageFromMe(update, _ctx); 208 | update = this.buildOn(update); 209 | 210 | // middleware process 211 | if (this.middlewares.length === 0) { 212 | this.emit('message', update, _ctx); 213 | return this.scanningText(update, _ctx); 214 | } 215 | 216 | const nextFunction = (update, index = 1) => { 217 | return () => { 218 | if (!this.middlewares[index]) { 219 | this.emit('message', update, _ctx); 220 | return this.scanningText(update, _ctx); 221 | } 222 | 223 | return this.middlewares[index](update, nextFunction(update, index + 1)); 224 | } 225 | } 226 | return this.middlewares[0](update, nextFunction(update)); 227 | } 228 | 229 | scanningText(update, _ctx) { 230 | // if (update.media) this.emit('media', update, _ctx); 231 | 232 | if (update.event.length > 0) update.event.forEach(type => this.emit(type, update, _ctx)); 233 | 234 | if (!update.text) return; 235 | let text = update.text; 236 | let found = false; let matchPattern = []; 237 | let walk = true; 238 | 239 | if (this.scanners.length <= 0) return false; 240 | 241 | this.scanners.forEach((scanner) => { 242 | let { key, callback, stop } = scanner; 243 | // this.terminal.debug('scanning:', `${key} match with ${text}?`); 244 | if (!walk) return; 245 | if (key instanceof RegExp) { 246 | let match; 247 | if (match = key.exec(text)) { 248 | found = true; walk = stop; 249 | matchPattern.push(key); 250 | update.match = match; 251 | return callback(update, _ctx); 252 | } 253 | } 254 | if (key == text) { 255 | found = true; walk = stop; 256 | matchPattern.push(key); 257 | return callback(update, _ctx); 258 | } 259 | 260 | }); 261 | if (this.logLevel > 0 && found && matchPattern.length > 0) { 262 | matchPattern.forEach(key => this.terminal.debug(`Match [${key}]: ${text}`)); 263 | } 264 | return found; 265 | } 266 | 267 | hear(key, callback, stop = true) { 268 | return this.scanners.push({ key, callback, stop }) 269 | } 270 | 271 | hears(key, callback, stop = true) { 272 | return this.scanners.push({ key, callback, stop }) 273 | } 274 | 275 | cmd(str, callback, stop = true) { 276 | let key = new RegExp(`^[${this.escapeRegExp(this.cmdPrefix)}]${str}$`, "i"); 277 | return this.scanners.push({ key, callback, stop }) 278 | } 279 | 280 | command(str, callback, stop = true) { 281 | let key = new RegExp(`^[${this.escapeRegExp(this.cmdPrefix)}]${str}$`, "i"); 282 | return this.scanners.push({ key, callback, stop }) 283 | } 284 | 285 | middleware(callback) { 286 | this.middlewares.push(callback); 287 | } 288 | 289 | use(callback) { 290 | this.middlewares.push(callback); 291 | } 292 | 293 | } 294 | 295 | module.exports = DuaEvent; -------------------------------------------------------------------------------- /core/duagram.js: -------------------------------------------------------------------------------- 1 | const { TelegramClient } = require("telegram"); 2 | const { NewMessage } = require('telegram/events'); 3 | const { Logger } = require("telegram/extensions"); 4 | const DuaCommand = require("./duacommand"); 5 | const input = require("input"); 6 | 7 | const { Api: ApiTelegram, Telegram } = require('./telegram'); 8 | const botApi = require('./botapi'); 9 | const Helper = require('../utils'); 10 | 11 | 12 | class DuaGram extends DuaCommand { 13 | constructor(options) { 14 | super(); 15 | this.options = options; 16 | this.Api = ApiTelegram; 17 | this.Helper = Helper; 18 | this.init(options); 19 | } 20 | 21 | get tg() { 22 | return this.telegram; 23 | } 24 | 25 | async start() { 26 | this.startBanner(); 27 | let { 28 | as_bot_api, bot_token, 29 | logDetail, logLevel, markRead, 30 | floodSleepThreshold, connectionRetries, useWSS, more 31 | } = this.options; 32 | 33 | Logger.setLevel(logDetail); 34 | 35 | process.once('SIGINT', async () => { 36 | await this.client.disconnect(); 37 | this.terminal.warn("Terminating process.."); 38 | process.exit(0) 39 | }) 40 | process.once('SIGTERM', async () => { 41 | await this.client.disconnect(); 42 | this.terminal.warn("Terminating process.."); 43 | process.exit(0) 44 | }) 45 | 46 | let session = await this.makeSession(); 47 | 48 | const client = new TelegramClient( 49 | session, 50 | this.options.api_id, 51 | this.options.api_hash, 52 | { 53 | connectionRetries, 54 | useWSS, 55 | ...more 56 | } 57 | ); 58 | this.client = client; 59 | client.floodSleepThreshold = floodSleepThreshold; 60 | 61 | let as_bot_api_info; 62 | 63 | if (as_bot_api) { 64 | await client.start({ 65 | botAuthToken: bot_token 66 | }); 67 | this.BotApi = new botApi(bot_token); 68 | this.asBotApi = true; 69 | as_bot_api_info = 'BotAPI'; 70 | } else { 71 | await client.start({ 72 | phoneNumber: async () => await input.text('Phone number (628xxx):'), 73 | password: async () => await input.password('Password:'), 74 | phoneCode: async () => await input.text('OTP code:'), 75 | onError: (err) => this.terminal.error(err.message) 76 | }); 77 | this.asBotApi = false; 78 | as_bot_api_info = 'userbot'; 79 | 80 | this.terminal.log("This session:"); 81 | console.log(client.session.save()); 82 | } 83 | 84 | let tg = new Telegram(client); 85 | this.telegram = tg; 86 | 87 | let aboutMe = await this.getMe(); 88 | this.storeMe(aboutMe); 89 | 90 | this.terminal.warn(`You login as [${as_bot_api_info}]`); 91 | this.terminal.warn(`Store mode: ${this.session_type}`); 92 | this.terminal.info(this.aboutMe); 93 | 94 | this.terminal.info("I'm ready here, waiting for your activity..."); 95 | 96 | this.emit('connected', aboutMe); 97 | 98 | // newMessage 99 | client.addEventHandler(async (ctx) => { 100 | let message = ctx.message; 101 | // if (markRead && !as_bot_api) tg.readHistory(message); 102 | this.processMessage(ctx); 103 | }, new NewMessage({})); 104 | 105 | // raw 106 | client.addEventHandler(async (update) => { 107 | if (logLevel >= 1) { 108 | if (update.className) this.terminal.debug(`Event: ${update.className}`) 109 | } 110 | if (logLevel >= 2) { 111 | console.log(update); 112 | } 113 | this.emit('raw', update); 114 | this.emit(update.className, update); 115 | }); 116 | } 117 | 118 | storeMe(data) { 119 | let aboutMe = new this.DuaMessage({}, { me: data }).getMe; 120 | let { id, bot, self, first_name, last_name, username } = aboutMe; 121 | this.me = { 122 | handle: data, 123 | long: aboutMe, 124 | short: { id, self, bot, first_name, last_name, username } 125 | } 126 | } 127 | 128 | get aboutMe() { 129 | let { first_name, last_name, username, phone } = this.me.long; 130 | let me = 'About me [name: ' + first_name; 131 | if (last_name) me += ' ' + last_name; 132 | me += ']' 133 | if (username) me += '[username: @' + username + '] '; 134 | if (phone) me += '[phone: +' + phone + '] '; 135 | return me; 136 | } 137 | 138 | // end off DuaGram class 139 | } 140 | 141 | module.exports = { 142 | DuaGram 143 | } -------------------------------------------------------------------------------- /core/duamessage.js: -------------------------------------------------------------------------------- 1 | const removeNull = (obj) => { 2 | Object.keys(obj).forEach(k => 3 | (obj[k] && typeof obj[k] === 'object') && removeNull(obj[k]) 4 | || 5 | !obj[k] && delete obj[k] 6 | ); 7 | return obj; 8 | }; 9 | 10 | class DuaMessage { 11 | constructor(update, more = {}) { 12 | this.more = more; 13 | this.update = update; 14 | 15 | this.chat = more.peer || false; 16 | this.from = more.from || false; 17 | 18 | this.BroadcastStore = {}; 19 | 20 | this.Store = { 21 | message: {}, 22 | user: {}, 23 | channel: {}, 24 | broadcast: {} 25 | } 26 | 27 | } 28 | 29 | get EntityList() { 30 | return [ 31 | 'mention', // @username 32 | 'hashtag', // #hashtag) 33 | 'cashtag', // ($USD), 34 | 'bot_command', // /start@jobs_bot) 35 | 'url', // https://telegram.org 36 | 'email', // do-not-reply@telegram.org 37 | 'phone_number', // +1-212-555-0123 38 | 'bold', // bold text 39 | 'italic', // italic text 40 | 'underline', // underlined text 41 | 'strikethrough', // strikethrough text 42 | 'code', // monowidth string 43 | 'pre', // monowidth block 44 | 'text_link', // for clickable text URLs 45 | 'text_mention' // for users without usernames 46 | ] 47 | } 48 | 49 | fieldType(data) { 50 | if (!data) return false; 51 | let type = data.className.replace('Peer', '').toLowerCase(); 52 | return { 53 | type, 54 | id: data[type + 'Id'] 55 | } 56 | } 57 | 58 | getTypeClassName(data, key = '') { 59 | return data.className ? data.className.replace(key, '').toLowerCase() : false; 60 | } 61 | 62 | entitiesMessage(entities) { 63 | this.broadcastStore('entities'); 64 | let result = []; 65 | let typeEntity = (value) => { 66 | let result = this.getTypeClassName(value, 'MessageEntity') 67 | result 68 | .replace(/texturl/i, 'text_link') 69 | .replace(/botcommand/i, 'bot_command') 70 | .replace(/phone/i, 'phone_number') 71 | .replace(/strike/i, 'strikethrough') 72 | .replace(/mentionname/i, 'text_mention') 73 | ; 74 | return result; 75 | } 76 | 77 | 78 | Object.entries(entities).forEach(([key, value]) => { 79 | let type = typeEntity(value); 80 | this.broadcastStore(type); 81 | result.push({ 82 | type, 83 | offset: value.offset, 84 | length: value.length, 85 | url: value.url || false, 86 | user: value.userId ? { id: value.userId } : false, 87 | }); 88 | }); 89 | return result; 90 | } 91 | 92 | get getMe() { 93 | return this.userStore(this.more.me, true); 94 | } 95 | 96 | // ========================================================================================== 97 | 98 | broadcastStore() { 99 | if (arguments.length < 1) return; 100 | if (arguments.length < 1) return; 101 | 102 | Object.entries(arguments).forEach(([key, value]) => { 103 | this.Store.broadcast[value] = true; 104 | }); 105 | } 106 | 107 | messageStore(data) { 108 | let id = data.id; 109 | 110 | let entities = data.entities ? this.entitiesMessage(data.entities) : false; 111 | let reply_to_message = data.replyTo?.replyToTopId ? { id: data.replyTo?.replyToTopId } : false; 112 | 113 | let message = { 114 | id, 115 | in: data.out ? false : true, 116 | out: data.out, 117 | 118 | date: data.date, 119 | edit_date: data.editDate, 120 | 121 | mentioned: data.mentioned, 122 | media: data.media ? this.mediaStore(data.media) : false, 123 | media_unread: data.mediaUnread, 124 | 125 | pinned: data.pinned, 126 | post_author: data.postAuthor, 127 | 128 | via_bot: data.viaBotId ? { id: data.viaBotId } : false, 129 | views: data.views, 130 | silent: data.silent, 131 | 132 | text: data.message, 133 | fromScheduled: data.fromScheduled, 134 | 135 | entities, 136 | reply_to_message, 137 | } 138 | this.Store.message[id] = message; 139 | } 140 | 141 | userStore(data, me = false) { 142 | let id = data.id; 143 | let user = { 144 | id, 145 | self: data.self, 146 | bot: data.bot, 147 | 148 | contact: data.contact, 149 | mutual_contact: data.mutualContact, 150 | verified: data.verified, 151 | restricted: data.restricted, 152 | min: data.min, 153 | 154 | bot_inline_geo: data.botInlineGeo, 155 | bot_chat_history: data.botChatHistory, 156 | bot_no_chats: data.botNochats, 157 | apply_min_photo: data.applyMinPhoto, 158 | 159 | support: data.support, 160 | scam: data.scam, 161 | fake: data.fake, 162 | // hash: fm.accessHash, 163 | 164 | first_name: data.firstName, 165 | last_name: data.lastName, 166 | username: data.username, 167 | phone: data.phone, 168 | }; 169 | if (me) return user; 170 | this.Store.user[id] = user; 171 | }; 172 | 173 | channelStore(data) { 174 | let id = data.id; 175 | let type = data.className.toLowerCase(); 176 | // if (type == 'channel') id = Number('-100' + id); 177 | let channel = { 178 | id: type == 'channel' ? Number('-100' + id) : Number('-' + id), 179 | type, 180 | first_name: data.firstName, 181 | last_name: data.lastName, 182 | username: data.username, 183 | verified: data.verified, 184 | title: data.title, 185 | }; 186 | this.Store.channel[data.id] = channel; 187 | }; 188 | 189 | store() { 190 | // users 191 | let users = this.update.users; 192 | users.forEach(data => this.userStore(data)); 193 | 194 | // messages 195 | let messages = this.update.messages; 196 | messages.forEach(data => this.messageStore(data)); 197 | 198 | // chats 199 | let channel = this.update.chats; 200 | channel.forEach(data => this.channelStore(data)); 201 | } 202 | 203 | mediaStore(data) { 204 | this.broadcastStore('media'); 205 | 206 | let type = data.className.replace('MessageMedia', '').toLowerCase(); 207 | this.broadcastStore(type); 208 | 209 | // console.log(' >>> MEDIA:', data); 210 | if (!data[type]) return { raw: data }; 211 | 212 | let mime = data[type].mimeType; 213 | 214 | let bc = typeof mime == "string" ? mime.split('/') : false; 215 | if (bc) this.broadcastStore(...bc); 216 | 217 | if (/sticker/i.exec(data[type].mimeType)) this.broadcastStore('sticker'); 218 | 219 | let result = { 220 | id: data[type].id?.value, 221 | type, 222 | date: data[type].date, 223 | size: data[type].size, 224 | mime_type: mime, 225 | dc: data[type].dcId 226 | } 227 | 228 | // result[type] = data[type]; 229 | result.raw = data; 230 | 231 | return result; 232 | } 233 | 234 | get mainMessage() { 235 | let message = this.update.messages[0]; 236 | return this.Store.message[message.id]; 237 | } 238 | 239 | 240 | get context() { 241 | this.store(); 242 | 243 | // console.log('==> Messages:', this.update.messages, '\n===END'); 244 | // console.log('==> ISI STORE', this.Store); 245 | 246 | let broadcast = []; 247 | let messages = this.update.messages; 248 | 249 | // main messsage data 250 | let main = this.mainMessage; 251 | 252 | // from data 253 | let from = 'me'; 254 | if (main.in) { 255 | let data = this.from || this.fieldType(messages[0].fromId); 256 | data = data || { type: 'user', id: messages[0]._senderId }; 257 | if (data) { 258 | from = this.Store[data.type][data.id]; 259 | } 260 | } 261 | 262 | // chat data 263 | let data = this.chat || this.fieldType(messages[0].peerId); 264 | 265 | let chat; 266 | if (this.chat?.type == 'chat') { 267 | chat = this.Store.channel[this.chat.id] 268 | } else { 269 | chat = this.Store[data.type][data.id]; 270 | } 271 | 272 | 273 | // reply data 274 | let reply_to_message = false; 275 | if (this.more.reply) { 276 | this.broadcastStore('reply'); 277 | reply_to_message = this.Store.message[this.more.reply]; 278 | if (reply_to_message.out) reply_to_message.from = 'me'; 279 | if (reply_to_message.in) { 280 | let data = this.fieldType(messages[1].fromId); 281 | data = data || { type: 'user', id: messages[1]._senderId }; 282 | if (data) { 283 | reply_to_message.from = this.Store[data.type][data.id]; 284 | } 285 | } 286 | } 287 | 288 | // forward data 289 | let forward_from = {} 290 | if (this.more.forward && this.more.forward?.id) { 291 | this.broadcastStore('forward'); 292 | forward_from = { 293 | forward_from: this.Store[this.more.forward.type][this.more.forward.id] 294 | } 295 | } 296 | 297 | let result = { 298 | ...main, 299 | ...forward_from, 300 | reply_to_message, 301 | from, 302 | chat 303 | } 304 | 305 | result = removeNull(result); 306 | Object.entries(this.Store.broadcast).forEach(([key, value]) => broadcast.push(key)); 307 | 308 | return { 309 | ...result, 310 | event: broadcast 311 | } 312 | } 313 | } 314 | 315 | module.exports = DuaMessage; -------------------------------------------------------------------------------- /core/duastore.js: -------------------------------------------------------------------------------- 1 | class duaStore { 2 | constructor() { 3 | this.UserStore = {}; 4 | } 5 | 6 | userStore(key, more={}) { 7 | this.UserStore[key] = { 8 | ... more 9 | } 10 | } 11 | } 12 | 13 | module.exports = duaStore; -------------------------------------------------------------------------------- /core/flow.md: -------------------------------------------------------------------------------- 1 | ## Dua Flow 2 | 3 | 1. `../index.js` 4 | 2. duagram 5 | 3. duacommand 6 | 4. duaevent 7 | 5. EventEmitter & duamessage -------------------------------------------------------------------------------- /core/telegram.js: -------------------------------------------------------------------------------- 1 | const { Api } = require("telegram"); 2 | const { CustomFile } = require("telegram/client/uploads"); 3 | const { _parseMessageText } = require("telegram/client/messageParse") 4 | const getPeerId = require('../utils/peer'); 5 | const BigInt = require("big-integer"); 6 | 7 | const fs = require('fs'); 8 | const FileType = require('file-type'); 9 | 10 | function Telegram(client) { 11 | this.client = client; 12 | this.getPeerId = getPeerId; 13 | } 14 | 15 | Telegram.prototype = { 16 | 17 | async invoke(data) { 18 | return await this.client.invoke(data); 19 | }, 20 | 21 | async isChannel(peer) { 22 | let peerID = this.getPeerId(peer); 23 | let result; 24 | try { 25 | let type = await this.client.getEntity(peerID); 26 | result = Boolean(type.className == "Channel"); 27 | } catch (error) { 28 | result = false; 29 | } 30 | return result; 31 | }, 32 | 33 | async sendMessage(peer, text, more = {}) { 34 | let params = { 35 | peer: this.getPeerId(peer), 36 | randomId: this.randomId(), 37 | message: text, 38 | ...more 39 | } 40 | 41 | if (more.parse_mode) { 42 | let parse_mode = more.parse_mode.toLowerCase(); 43 | 44 | let [parseText, entities] = await _parseMessageText(this.client, text, parse_mode); 45 | params.message = parseText; 46 | params.entities = entities; 47 | delete more.parse_mode; 48 | } 49 | return await this.invoke(new Api.messages.SendMessage(params)); 50 | }, 51 | 52 | async editMessage(peer, id, text, more) { 53 | let params = { peer: this.getPeerId(peer), id, ...more } 54 | 55 | if (more.parse_mode) { 56 | let parse_mode = more.parse_mode.toLowerCase(); 57 | 58 | [parseText, entities] = await _parseMessageText(this.client, text, parse_mode); 59 | params.message = parseText; 60 | params.entities = entities; 61 | delete more.parse_mode; 62 | } 63 | 64 | return await this.invoke(new Api.messages.EditMessage(params)); 65 | }, 66 | 67 | async deleteMessages(peer, ids, revoke = true) { 68 | let peerID = this.getPeerId(peer); 69 | let id = typeof ids == 'number' ? [ids] : ids; 70 | 71 | let data = await this.isChannel(peer) 72 | ? new Api.channels.DeleteMessages({ channel: peerID, id }) 73 | : new Api.messages.DeleteMessages({ id, revoke }); 74 | 75 | return await this.invoke(data); 76 | }, 77 | 78 | async deleteMessage(peer, ids, revoke = true) { 79 | return await this.deleteMessages(peer, ids, revoke = true); 80 | }, 81 | 82 | async forwardMessages(peerFrom, peerTo, ids, more = {}) { 83 | let id = typeof ids == 'number' ? [ids] : ids; 84 | return await this.invoke( 85 | new Api.messages.ForwardMessages({ 86 | fromPeer: this.getPeerId(peerFrom), 87 | toPeer: this.getPeerId(peerTo), 88 | randomId: this.randomId(), 89 | id, 90 | ...more 91 | }) 92 | ) 93 | }, 94 | 95 | async getMessages(peer, ids) { 96 | let peerID = this.getPeerId(peer); 97 | let id = typeof ids == 'number' ? [ids] : ids; 98 | 99 | let data = await this.isChannel(peer) 100 | ? new Api.channels.GetMessages({ channel: peerID, id }) 101 | : new Api.messages.GetMessages({ id }); 102 | 103 | return await this.invoke(data); 104 | }, 105 | 106 | async pinMessage(peer, id, more = {}) { 107 | return await this.invoke( 108 | new Api.messages.UpdatePinnedMessage({ 109 | userId: this.getPeerId(peer), id, 110 | ...more 111 | }) 112 | ) 113 | }, 114 | 115 | async unpinAllMessages(peer) { 116 | return await this.invoke( 117 | new Api.messages.UnpinAllMessages({ 118 | userId: this.getPeerId(peer), id 119 | }) 120 | ) 121 | }, 122 | 123 | async getUserPhotos(peer, more = {}) { 124 | return await this.invoke( 125 | new Api.photos.GetUserPhotos({ 126 | userId: this.getPeerId(peer), 127 | ...more 128 | }) 129 | ) 130 | }, 131 | 132 | async readHistory(peer, more = {}) { 133 | let peerID = this.getPeerId(peer); 134 | 135 | let data = await this.isChannel(peer) 136 | ? new Api.channels.ReadHistory({ channel: peerID, ...more }) 137 | : new Api.messages.ReadHistory({ peer: peerID, ...more }); 138 | 139 | return await this.invoke(data); 140 | }, 141 | 142 | async deleteHistory(peer, more = {}) { 143 | let peerID = this.getPeerId(peer); 144 | 145 | let data = await this.isChannel(peer) 146 | ? new Api.channels.DeleteHistory({ channel: peerID, ...more }) 147 | : new Api.messages.DeleteHistory({ peer: peerID, ...more }); 148 | 149 | return await this.invoke(data); 150 | }, 151 | 152 | async deleteUserHistory(channelId, userId) { 153 | return await client.invoke( 154 | new this.Api.channels.DeleteUserHistory({ 155 | channel: channelId, 156 | userId: userId 157 | }) 158 | ) 159 | }, 160 | 161 | async readMentions(peer) { 162 | return await this.invoke( 163 | new Api.messages.ReadMentions({ 164 | peer: this.getPeerId(peer) 165 | }) 166 | ) 167 | }, 168 | 169 | async readMessageContents(ids) { 170 | let id = typeof ids == 'number' ? [ids] : ids; 171 | 172 | return await this.invoke( 173 | new Api.messages.ReadMessageContents({ id }) 174 | ) 175 | }, 176 | 177 | async editAdmin(peerChatId, peerUserId, more = {}) { 178 | let permissions = { 179 | changeInfo: more?.changeInfo || true, 180 | postMessages: more?.postMessages || true, 181 | editMessages: more?.editMessages || true, 182 | deleteMessages: more?.deleteMessages || true, 183 | banUsers: more?.banUsers || true, 184 | inviteUsers: more?.inviteUsers || true, 185 | pinMessages: more?.pinMessages || true, 186 | addAdmins: more?.addAdmins || false, 187 | anonymous: more?.anonymous || false, 188 | manageCall: more?.manageCall || true 189 | } 190 | return await this.invoke( 191 | new Api.channels.EditAdmin({ 192 | channel: this.getPeerId(peerChatId), 193 | userId: this.getPeerId(peerUserId), 194 | adminRights: new Api.ChatAdminRights(permissions), 195 | rank: more?.rank || "" 196 | }) 197 | ) 198 | }, 199 | 200 | async editBanned(peerChatId, peerUserId, more = {}) { 201 | let permissions = { 202 | untilDate: more?.untilDate || 0, 203 | viewMessages: more?.viewMessages || true, 204 | sendMessages: more?.sendMessages || true, 205 | sendMedia: more?.sendMedia || true, 206 | sendStickers: more?.sendStickers || true, 207 | sendGifs: more?.sendGifs || true, 208 | sendGames: more?.sendGames || true, 209 | sendInline: more?.sendInline || true, 210 | sendPolls: more?.sendPolls || true, 211 | changeInfo: more?.changeInfo || true, 212 | inviteUsers: more?.inviteUsers || true, 213 | pinMessages: more?.pinMessages || true 214 | } 215 | return await this.invoke( 216 | new Api.channels.EditBanned({ 217 | channel: this.getPeerId(peerChatId), 218 | participant: this.getPeerId(peerUserId), 219 | bannedRights: new Api.ChatBannedRights(permissions) 220 | }) 221 | ) 222 | 223 | }, 224 | 225 | async getUserInfo(peer) { 226 | return await this.invoke(new Api.help.GetUserInfo({ userId: this.getPeerId(peer) })); 227 | }, 228 | 229 | async joinGroup(peer) { 230 | return await this.invoke(new Api.channels.JoinChannel({ channel: this.getPeerId(peer), })) 231 | }, 232 | 233 | // still not final 234 | async downloadMedia(media_data, more = {}) { 235 | let path = more.path || '.'; 236 | 237 | let buffer = await this.client.downloadMedia(media_data, { workers: 1, ...more }) 238 | let fileType = await FileType.fromBuffer(buffer); 239 | 240 | let file_name = more.file_name || Date.now() + '.' + fileType.ext; 241 | 242 | try { 243 | fs.writeFileSync(path + '/' + file_name, buffer); 244 | return { 245 | status: true, 246 | file: file_name, 247 | path 248 | } 249 | } catch (error) { 250 | return { 251 | status: false, 252 | message: error.message 253 | } 254 | } 255 | }, 256 | 257 | randomId() { 258 | return BigInt(-Math.floor(Math.random() * 10000000000000)); 259 | }, 260 | 261 | 262 | async sendFile(peer, file, more = {}) { 263 | return await this.client.sendFile( 264 | this.getPeerId(peer), 265 | { 266 | file, 267 | ...more 268 | } 269 | ); 270 | }, 271 | 272 | // end of prototype 273 | } 274 | 275 | module.exports = { 276 | Api, 277 | Telegram 278 | } -------------------------------------------------------------------------------- /docs/helper.md: -------------------------------------------------------------------------------- 1 | ## Helper 2 | 3 | | method | desc | example | 4 | | ---------------------- | ----------------------------------- | ------------------------------------------------------------------ | 5 | | clearHTML(string) | cleansing HTML tag | `clearHTML(‘<’) `result: `<` | 6 | | clearMarkdown(string) | cleansing Markdown tag | | 7 | | isIN(array, index) | check index in array, true or false | `IsIN(\[1,2,3\], 2)` true | 8 | | forEach(obj, fn) | for each object / array | `forEach({one:1, two:2}, (v,i) => { .. })` | 9 | | allReplace(text, JSON) | multiple replace text | `allReplace('Hasanudin', {'a':4, 's': 5, 'n\|u': 'o'})` H454oodio | 10 | | random(list) | random array item | `random([‘one’,’two’,’three’])` | 11 | | random(min, max) | random number | `random(0,100)` | 12 | | Button | helper button for Bot API | | 13 | | cleanObject | remove JSON from null, false, undefined, '', 0 | `cleanObject({one:1, empty: undefined, nol:0})` { one:1 } | 14 | | dateFormat | any | any | 15 | | chat.to_api | convert userbot channel to bot api | `chat.to_api(123456789)` -100123456789 | 16 | | chat.from_api | convert from userbot channel id to bot api | `chat.from_api(-100123456789)` 123456789 | 17 | 18 | ## dateFormat 19 | 20 | Helper for date format. 21 | 22 | ```javascript 23 | let dateFormat = bot.Helper.dateFormat 24 | 25 | let now = new Date(); 26 | 27 | dateFormat(now); // 'Sat Jul 10 2021 19:25:54' 28 | dateFormat(now, 'isoDateTime'); // '2021-07-10T19:25:54+0700' 29 | 30 | dateFormat(now, "fullDate"); // 'Saturday, July 10, 2021' 31 | dateFormat(now, 'yyyy-mm-dd'); // '2021-32-10' 32 | ``` 33 | 34 | ### Mask 35 | 36 | | Mask | Description | 37 | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 38 | | `d` | Day of the month as digits; no leading zero for single-digit days. | 39 | | `dd` | Day of the month as digits; leading zero for single-digit days. | 40 | | `ddd` | Day of the week as a three-letter abbreviation. | 41 | | `dddd` | Day of the week as its full name. | 42 | | `m` | Month as digits; no leading zero for single-digit months. | 43 | | `mm` | Month as digits; leading zero for single-digit months. | 44 | | `mmm` | Month as a three-letter abbreviation. | 45 | | `mmmm` | Month as its full name. | 46 | | `yy` | Year as last two digits; leading zero for years less than 10. | 47 | | `yyyy` | Year represented by four digits. | 48 | | `h` | Hours; no leading zero for single-digit hours (12-hour clock). | 49 | | `hh` | Hours; leading zero for single-digit hours (12-hour clock). | 50 | | `H` | Hours; no leading zero for single-digit hours (24-hour clock). | 51 | | `HH` | Hours; leading zero for single-digit hours (24-hour clock). | 52 | | `M` | Minutes; no leading zero for single-digit minutes.
Uppercase M unlike CF `timeFormat`'s m to avoid conflict with months. | 53 | | `MM` | Minutes; leading zero for single-digit minutes.
Uppercase MM unlike CF `timeFormat`'s mm to avoid conflict with months. | 54 | | `s` | Seconds; no leading zero for single-digit seconds. | 55 | | `ss` | Seconds; leading zero for single-digit seconds. | 56 | | `l` _or_ `L` | Milliseconds. `l` gives 3 digits. `L` gives 2 digits. | 57 | | `t` | Lowercase, single-character time marker string: _a_ or _p_.
No equivalent in CF. | 58 | | `tt` | Lowercase, two-character time marker string: _am_ or _pm_.
No equivalent in CF. | 59 | | `T` | Uppercase, single-character time marker string: _A_ or _P_.
Uppercase T unlike CF's t to allow for user-specified casing. | 60 | | `TT` | Uppercase, two-character time marker string: _AM_ or _PM_.
Uppercase TT unlike CF's tt to allow for user-specified casing. | 61 | | `Z` | US timezone abbreviation, e.g. _EST_ or _MDT_. With non-US timezones or in the Opera browser, the GMT/UTC offset is returned, e.g. _GMT-0500_
No equivalent in CF. | 62 | | `o` | GMT/UTC timezone offset, e.g. _\-0500_ or _+0230_.
No equivalent in CF. | 63 | | `S` | The date's ordinal suffix (_st_, _nd_, _rd_, or _th_). Works well with `d`.
No equivalent in CF. | 64 | | `'…'` _or_ `"…"` | Literal character sequence. Surrounding quotes are removed.
No equivalent in CF. | 65 | | `UTC:` | Must be the first four characters of the mask. Converts the date from local time to UTC/GMT/Zulu time before applying the mask. The "UTC:" prefix is removed.
No equivalent in CF. | 66 | 67 | ### Mask Name 68 | 69 | | Name | Mask | Example | 70 | | -------------- | ------------------------------ | ------------------------ | 71 | | default | `ddd mmm dd yyyy HH:MM:ss` | Sat Jun 09 2007 17:46:21 | 72 | | **dateTime** | `yyyy-MM-dd HH:mm:ss` | 2007-06-09 22:46:21 | 73 | | shortDate | `m/d/yy` | 6/9/07 | 74 | | mediumDate | `mmm d, yyyy` | Jun 9, 2007 | 75 | | longDate | `mmmm d, yyyy` | June 9, 2007 | 76 | | fullDate | `dddd, mmmm d, yyyy` | Saturday, June 9, 2007 | 77 | | shortTime | `h:MM TT` | 5:46 PM | 78 | | mediumTime | `h:MM:ss TT` | 5:46:21 PM | 79 | | longTime | `h:MM:ss TT Z` | 5:46:21 PM EST | 80 | | isoDate | `yyyy-mm-dd` | 2007-06-09 | 81 | | isoTime | `HH:MM:ss` | 17:46:21 | 82 | | isoDateTime | `yyyy-mm-dd'T'HH:MM:ss` | 2007-06-09T17:46:21 | 83 | | isoUtcDateTime | `UTC:yyyy-mm-dd'T'HH:MM:ss'Z'` | 2007-06-09T22:46:21Z | 84 | 85 | [Ref-detail](https://blog.stevenlevithan.com/archives/date-time-format) 86 | 87 | -------------------------------------------------------------------------------- /docs/middleware.md: -------------------------------------------------------------------------------- 1 | ## Middleware 2 | 3 | Middleware is an essential part of any modern framework. It allows you to modify requests and responses as they pass between the Telegram and your bot. 4 | 5 | You can imagine middleware as a chain of logic connection your bot to the Telegram request. 6 | 7 | Middleware normally takes two parameters `(ctx, next)`, `ctx` is the context for one Telegram update, `next` is a function that is invoked to execute the downstream middleware. It returns a Promise with a then function for running code after completion. 8 | 9 | ```javascript 10 | bot.use((ctx, next) => { 11 | ctx.additional = 'message test from middleware'; 12 | next(); 13 | }); 14 | 15 | bot.cmd('plus', async (ctx) => { 16 | if (!ctx.out) 17 | return bot.sendMessage(ctx, `Hooked: ${ctx.additional}`); 18 | }) 19 | ``` 20 | 21 | ### Middleware List 22 | 23 | - [duaGram rate-limit](https://www.npmjs.com/package/duagram-ratelimit) -------------------------------------------------------------------------------- /docs/onSignal.md: -------------------------------------------------------------------------------- 1 | ## Event 2 | 3 | Check in `ctx.event` 4 | 5 | - connected 6 | 7 | ### Message 8 | 9 | - raw 10 | - message 11 | 12 | and all event class teleggram, depends on logDetail options. Ex: 13 | 14 | - UpdateUserStatus 15 | - UpdateChannelUserTyping 16 | - UpdateNewChannelMessage 17 | - UpdateDraftMessage 18 | - UpdateChannelUserTyping 19 | - ... etc 20 | 21 | ### Mime Type 22 | 23 | - media 24 | - sticker 25 | - document 26 | - photo 27 | - video 28 | - image 29 | - webp 30 | - ... etc 31 | 32 | ### Entities 33 | 34 | - bold 35 | - italic 36 | - underline 37 | - code 38 | - pre 39 | - url 40 | - text_link 41 | - strikethrough 42 | - phone 43 | - email 44 | - mention 45 | - bot_command -------------------------------------------------------------------------------- /docs/release.md: -------------------------------------------------------------------------------- 1 | ## Release Version 2 | 3 | ### 1.3.8 4 | 5 | - telegram `2.3.0` 6 | 7 | ### 1.3.4 8 | 9 | - telegram `1.8.10` 10 | - shorten release info version 11 | 12 | ### 1.3.3 13 | 14 | `2021-08-19` 15 | 16 | - fix: venue message 17 | 18 | 19 | ### 1.3.2 20 | 21 | - add method: `use` for alias `middleware` 22 | - fix readhistory basic groups! 23 | - fix options markRead 24 | 25 | ### 1.3.1 26 | 27 | `2021-08-18` 28 | 29 | - private groups WORKS! 30 | - bug known: private group can't auto readhistory 31 | - upgrade telegram `1.8.6` 32 | - fix download file until 2 GB 33 | 34 | ### 1.3.0 35 | 36 | - fix version number 37 | - upgrade telegram `1.8.3` 38 | 39 | ### 1.2.31 40 | 41 | - shorten name session to 8 unique char (default) 42 | - [x] known bugs for basic chat, this app not work 43 | 44 | ### 1.2.7 45 | 46 | `2021-08-01` 47 | 48 | - new option: `local` and `session_name`, to save to storage session. 49 | - new event: on `connected`. See [examples](https://github.com/ubotindonesia/duagram/blob/dev/examples/). 50 | - upgrade `telegram` to v`1.8.0` 51 | - bugs fix 52 | - session: recognise _seen_ user after launch 53 | - helper 54 | - `random` 55 | - `forEach` 56 | 57 | ### 1.2.6 58 | 59 | `2021-07-23` 60 | 61 | - upgrade telegram client `v1.7.23`, bug fix for downloadMedia 62 | - more example: `download.js` 63 | 64 | 65 | ### 1.2.5 66 | 67 | `2021-07-18` 68 | 69 | - add `telegram` method 70 | - downloadMedia(`media, more `); // not yet final 71 | - more: file_name, path, ..etc 72 | - ex. `bot.tg.downloadMedia(ctx.media.raw, { path: '/tmp'})` 73 | - fix: 74 | - media.id 75 | 76 | 77 | ### 1.2.4 78 | 79 | `2021-07-17` 80 | 81 | - fix: 82 | - hidden forwarder message 83 | - ctx media 84 | - rename `media.data` to `media.raw` 85 | - terminal can be hook 86 | - add broadcast type: `forward`, `reply` 87 | 88 | 89 | ### v1.2.3 90 | 91 | - add **ctx** method: 92 | - `replyWithMarkdown(text, more)` 93 | - `replyWithSendFile(file, more)` 94 | - add `media.data` if `ctx` contains media. 95 | - add **telegram** method: 96 | - `readMentions(peer)` 97 | - `readMessageContents(id)` 98 | - `deleteHistory(peer, more)` 99 | - `deleteUserHistory(channelId, userId)` 100 | - add **Helper** method: 101 | - `chat.to_api(chat_id)` convert from userbot channel id to bot api 102 | - `chat.from_api(chat_id)` convert to userbot channel id 103 | 104 | ### v1.2.2 105 | 106 | - add `ctx` method 107 | - `reply(text, more)` 108 | - `replyWithHTML(text, more)` 109 | - fix middleware (update, _ctx); 110 | - upgrade telegram client (gramjs) v`1.7.15` 111 | 112 | 113 | ### v1.2.1 114 | 115 | - fix `getPeerId` for new json format (`ctx.chat.id`) 116 | 117 | ### v1.2.0 118 | 119 | `2021-07-15` 120 | 121 | - getMe(`peer`) 122 | - Helper: `cleanObject()` 123 | - `terminal.more()` unstopable json view data 124 | - option:`floodSleepThreshold` default set to `90` seconds 125 | - **rewrite ctx message format**, check examples 126 | 127 | 128 | ### v1.1.5 129 | 130 | `2021-07-12` 131 | 132 | - rename `peerGetID` to `getPeerID`; 133 | - `getPeerId` bot API style accept too (`-100xxxx`) 134 | - `middleware` fix to `message` event 135 | - `telegram` class add more methode 136 | - getMessages 137 | - pinMessage 138 | - unpinAllMessages 139 | - getUserPhotos 140 | - getUserInfo 141 | - editAdmin 142 | - editBanned 143 | - sendFile 144 | - joinGroup 145 | - fix bugs: `more` options to all method 146 | - add package `request` dependendcy 147 | - `lessLog` changeto `terminal.less` 148 | 149 | ### v1.1.2 150 | 151 | `2021-07-11` 152 | 153 | - add middleware feature 154 | - add `cmdPrefix` to options client 155 | - fix bugs: session logging info 156 | 157 | ### v1.0.0 158 | 159 | First version 160 | 161 | `2021-07-10` 162 | 163 | Initial release. -------------------------------------------------------------------------------- /docs/terminal.md: -------------------------------------------------------------------------------- 1 | ## Terminal 2 | 3 | as like console command. 4 | 5 | Additional : 6 | 7 | - `terminal.less` output for logging without prefix _ on the root object 8 | - `terminal.more` output for logging more detail. 9 | 10 | Example: 11 | 12 | ```javascript 13 | let data = { 14 | _header: "head", 15 | _etc: { 16 | t:0, 17 | s: "_", 18 | u: 'user', 19 | } 20 | _one: 1, 21 | one: 1, 22 | two: { 23 | t : 2, 24 | three: { 25 | t: 3, 26 | four: { 27 | t: 4, 28 | five: { 29 | t: 5, 30 | six: { 31 | t: 6 32 | } 33 | } 34 | } 35 | } 36 | } 37 | }; 38 | 39 | console.log(data); 40 | terminal.more(data); 41 | terminal.less(data); 42 | ``` -------------------------------------------------------------------------------- /docs/todo.md: -------------------------------------------------------------------------------- 1 | ## To Do 2 | 3 | - [x] store to persistent (storage) memory 4 | - [x] middleware 5 | - [ ] chat.properties (low) 6 | - [ ] Edit event: `UpdateEditChannelMessage` ... 7 | - [x] ctx method: `ctx.reply`, `ctx...` 8 | - [x] download file 9 | - [x] download progress 10 | - [ ] upload file 11 | - [ ] upload progress 12 | - [ ] parsing sendMessage result -------------------------------------------------------------------------------- /examples/aboutme.js: -------------------------------------------------------------------------------- 1 | const { duaGram } = require("duagram"); 2 | 3 | const bot = new duaGram({ 4 | api_id: 1, 5 | api_hash: 'your-api-hash', 6 | 7 | logLevel: 1, 8 | logDetail: "info", // none, error, warn, info, debug 9 | 10 | session: '' 11 | }); 12 | 13 | bot.cmd('me', (ctx) => { 14 | bot.sendMessage(ctx, JSON.stringify(bot.me.long, null, 2)); 15 | }); 16 | 17 | bot.start(); -------------------------------------------------------------------------------- /examples/botLogin.js: -------------------------------------------------------------------------------- 1 | const { duaGram, terminal } = require("duagram"); 2 | 3 | const bot = new duaGram({ 4 | api_id: 1, 5 | api_hash: 'your-api-hash', 6 | as_bot_api: true, 7 | bot_token: 'your-token-bot', 8 | 9 | logLevel: 1, // 0 false, 1 event, 2 detail 10 | logDetail: "info", // none, error, warn, info, debug 11 | }); 12 | 13 | // event all new message 14 | bot.on('message', async (ctx, _ctx) => { 15 | 16 | terminal.debug('Ctx Legacy'); 17 | console.log(_ctx); 18 | 19 | terminal.debug('Ctx Duagram'); 20 | console.log(ctx); 21 | }); 22 | 23 | bot.cmd('ping', async (ctx) => { 24 | bot.sendMessage(ctx, 'Pong!', { replyToMsgId: ctx.id }); 25 | }); 26 | 27 | bot.cmd('upload', async (ctx) => { 28 | terminal.info('Starting upload...'); 29 | let file = './photo.jpg'; 30 | return bot.sendFile(ctx, file); 31 | 32 | }); 33 | 34 | // bot API Telegram 35 | bot.cmd('start', async (ctx) => { 36 | // message in only 37 | if (ctx.out) return false; 38 | 39 | if (!bot.asBotApi) { 40 | return bot.sendMessage(ctx, "I'm not bot api 😅") 41 | } 42 | 43 | let chat_id = ctx.chat.id; 44 | if (ctx.chat.type == 'channel') { 45 | chat_id = bot.Helper.chat.to_api(chat_id); 46 | } 47 | 48 | // if Bot API, send with Bot API can too 49 | let reply_markup = JSON.stringify({ 50 | inline_keyboard: [ 51 | [ 52 | bot.Helper.Button.url('👥 uBotIndonesia', 'https://t.me/ubotindonesia') 53 | ], [ 54 | bot.Helper.Button.text('One', 'cb1'), 55 | bot.Helper.Button.text('Two', 'cb2') 56 | ] 57 | ] 58 | }); 59 | 60 | let more = { 61 | parse_mode: 'html', 62 | reply_markup 63 | } 64 | 65 | return bot.BotApi.sendMessage(chat_id, 'This message from Bot Api', more) 66 | .then(result => { 67 | terminal.log('Result: BotApi sendMessage') 68 | console.log(result); 69 | }) 70 | .catch(error => terminal.error(error.message)); 71 | }); 72 | 73 | bot.cmd('version', (ctx, _ctx) => { 74 | let telegram = 'Telegram Client: v' + _ctx._client.__version__; 75 | return bot.sendMessage(ctx, `${telegram}\n\n${JSON.stringify(bot.version, null, 2)}`, { parse_mode: 'HTML' }); 76 | }); 77 | 78 | bot.start(); 79 | -------------------------------------------------------------------------------- /examples/download.js: -------------------------------------------------------------------------------- 1 | const { duaGram } = require("duagram"); 2 | 3 | const bot = new duaGram({ 4 | api_id: 1, 5 | api_hash: 'your-api-hash', 6 | 7 | logLevel: 1, 8 | logDetail: "info", // none, error, warn, info, debug 9 | 10 | session: '' 11 | }); 12 | 13 | bot.cmd('download', async (ctx, _ctx) => { 14 | if (!ctx.reply_to_message?.media) return ctx.reply('🤷🏽‍♂️ Please, reply to a message containing media.') 15 | let media = ctx.reply_to_message.media; 16 | 17 | let progressMessage = await bot.sendMessage(ctx, '⏳ Wait...', { replyToMsgId: ctx.id }); 18 | let message_id = progressMessage.id || progressMessage.updates[0].id; 19 | 20 | // prevent flooding message 21 | // just update to every second 22 | let timer1 = Date.now(); 23 | 24 | let progressCallback = (num) => { 25 | num = Math.round(num * 10000) / 100; 26 | if (num >= 100) return true; 27 | 28 | let timer2 = Date.now(); 29 | let timer0 = timer2 - timer1; 30 | if (timer0 >= 1000) { 31 | timer1 = timer2; 32 | return bot.editMessage(ctx, message_id, `⏳ Download .. ${num} %`, { parse_mode: 'html' }) 33 | .catch(e => bot.terminal.error(e.message)); 34 | } 35 | }; 36 | 37 | let result = await bot.downloadMedia(media.raw, { 38 | path: '.data', 39 | progressCallback 40 | }); 41 | 42 | return bot.editMessage(ctx, message_id, `✅ Done.\n\n📁 File name: ${result.path}/${result.file}`, { parse_mode: 'html' }) 43 | }); 44 | 45 | bot.start(); -------------------------------------------------------------------------------- /examples/example.env: -------------------------------------------------------------------------------- 1 | # none, error, warn, info, debug 2 | LOG_DETAIL=none 3 | 4 | # An app ID from https://my.telegram.org/apps. 5 | API_ID= 6 | 7 | # An app hash from https://my.telegram.org/apps. 8 | API_HASH= 9 | 10 | #USE BOT API? 0,1 = yes or no 11 | BOT_API=0 12 | 13 | # A bot token from @BotFather. 14 | BOT_TOKEN= 15 | 16 | # Get it from https://telegram.banghasan.com/ubotstring/ 17 | STRING_SESSION= -------------------------------------------------------------------------------- /examples/localSession.js: -------------------------------------------------------------------------------- 1 | const { duaGram } = require("duagram"); 2 | 3 | const bot = new duaGram({ 4 | api_id: 1, 5 | api_hash: 'your-api-hash', 6 | session: '', 7 | local: true, 8 | session_name: 'data_my_bot' // optional 9 | }); 10 | 11 | bot.on('connected', () => { 12 | bot.sendMessage(213567634, 'test pertamax'); 13 | }) 14 | 15 | bot.start(); -------------------------------------------------------------------------------- /examples/middleware.js: -------------------------------------------------------------------------------- 1 | const { duaGram } = require("duagram"); 2 | 3 | const bot = new duaGram({ 4 | api_id: 1, 5 | api_hash: 'your-api-hash', 6 | 7 | logLevel: 1, // 0 false, 1 event, 2 detail 8 | logDetail: "info", // none, error, warn, info, debug 9 | 10 | session: '' // Fill in the session here if you have one, or leave it blank 11 | }); 12 | 13 | // format date Time 14 | bot.middleware((ctx, next) => { 15 | ctx.date_format = bot.Helper.dateFormat(ctx.date*1000, 'yyyy-MM-dd HH:mm:ss'); 16 | next(); 17 | }); 18 | 19 | bot.middleware(async (ctx, next) => { 20 | ctx.hook = 'Something'; 21 | next(); 22 | }); 23 | 24 | bot.cmd('check', async (ctx) => { 25 | console.log(ctx); 26 | return bot.sendMessage(ctx, `Accepted.\n\nHook message: ${ctx.hook}`, { replyToMsgId: ctx.id }); 27 | }) -------------------------------------------------------------------------------- /examples/onConnected.js: -------------------------------------------------------------------------------- 1 | const { duaGram } = require("duagram"); 2 | 3 | const bot = new duaGram({ 4 | api_id: 1, 5 | api_hash: 'your-api-hash', 6 | session: '' 7 | }); 8 | 9 | bot.on('connected', () => { 10 | bot.sendMessage(213567634, 'test pertamax'); 11 | }) 12 | 13 | bot.start(); -------------------------------------------------------------------------------- /examples/ping.js: -------------------------------------------------------------------------------- 1 | const { duaGram, terminal } = require("duagram"); 2 | const { performance } = require('perf_hooks'); 3 | 4 | const bot = new duaGram({ 5 | api_id: 1, 6 | api_hash: 'your-api-hash', 7 | session: '' 8 | }); 9 | 10 | bot.cmd('ping', async (ctx) => { 11 | let t0 = performance.now(); 12 | let result = await bot.sendMessage(ctx, 'Pong!'); 13 | 14 | let t1 = performance.now(); 15 | let diff = ((t1 - t0) / 1000).toLocaleString('id-ID', { maximumFractionDigits: 3 }); 16 | return bot.editMessage(ctx, result.id || result.updates[0].id, `Pong!\nIn ${diff} seconds.`, { parse_mode: 'html' }) 17 | .catch(e => terminal.error(e.message)); 18 | }); 19 | 20 | bot.start(); -------------------------------------------------------------------------------- /examples/sendURLMediaFile.js: -------------------------------------------------------------------------------- 1 | const { terminal, duaGram } = require("duagram"); 2 | const fetch = require('node-fetch'); //you must install node-fetch first 3 | 4 | const bot = new duaGram({ 5 | api_id: 1, 6 | api_hash: 'your-api-hash', 7 | 8 | logLevel: 1, 9 | logDetail: "info", // none, error, warn, info, debug 10 | 11 | session: '' 12 | }); 13 | 14 | bot.cmd("downloadImage", async (ctx) => { 15 | fetch("https://raw.githubusercontent.com/ubotindonesia/duagram/main/asset/2gram%20banner.jpg") 16 | .then(res => res.buffer()) 17 | .then(async buffer => { 18 | let wait = await bot.sendMessage(ctx, 'Downloading...', { replyToMsgId: ctx.id }); 19 | var chat_id, message_id; 20 | if (wait.updates) { 21 | chat_id = wait.chats[0].id; 22 | message_id = wait.updates[0].id 23 | } else { 24 | chat_id = ctx.peer.id; 25 | message_id = wait.id; 26 | } 27 | const downEdit = async (num) => { 28 | num = Math.floor(num * 100); 29 | var log = `\r[${num}%]` 30 | return bot.client.editMessage(chat_id, {message: message_id, text: 'Downloading... '+log}).catch(e => terminal.error(e.message)) 31 | } 32 | bot.client.sendFile(chat_id, {file: buffer, replyTo: ctx.id, progressCallback: downEdit}) 33 | .then(() => bot.deleteMessage(chat_id, message_id)) 34 | }) 35 | }) 36 | 37 | bot.start(); -------------------------------------------------------------------------------- /examples/terminal.js: -------------------------------------------------------------------------------- 1 | const { duaGram } = require("duagram"); 2 | 3 | class duaG extends duaGram { 4 | constructor(options) { 5 | super(options); 6 | this.myInit(); 7 | this.prefixTerminal = '[anu]'; 8 | } 9 | 10 | myInit() { 11 | this.oldTerminal = this.terminal; 12 | let terminal = this.terminal; 13 | 14 | let newTerminal = {} 15 | let prefix = this.prefix.terminal; 16 | 17 | newTerminal = { 18 | log(...args) { return terminal.log(prefix, ...args); }, 19 | info(...args) { return terminal.info(prefix, ...args); }, 20 | warn(...args) { return terminal.warn(prefix, ...args); }, 21 | debug(...args) { return terminal.debug(prefix, ...args); }, 22 | less(...args) { return terminal.less(prefix, ...args); }, 23 | more(...args) { return terminal.more(prefix, ...args); }, 24 | } 25 | this.terminal = newTerminal; 26 | } 27 | } 28 | 29 | const bot = new duaG({ 30 | api_id: 1, api_hash: 'your-api-hash', session: '' 31 | }); 32 | 33 | bot.cmd('ping', (ctx) => { 34 | bot.terminal.log(ctx); 35 | return ctx.reply('Pong!'); 36 | }); 37 | 38 | bot.start(); 39 | 40 | /* 41 | [15:32:56][info] [anu] ===================== 42 | [15:32:56][info] [anu] | The Journey Begins | 43 | [15:32:56][info] [anu] --------------------- 44 | [15:32:58][log] [anu] This session: xxx 45 | [15:32:59][warn] [anu] You login as [userbot] 46 | [15:32:59][info] [anu] About me [name: your name][username: @username] [phone: +12345678901] 47 | [15:32:59][info] [anu] I'm ready here, waiting for your activity... 48 | ... 49 | 50 | */ -------------------------------------------------------------------------------- /examples/userLogin.js: -------------------------------------------------------------------------------- 1 | const { duaGram, terminal } = require("duagram"); 2 | 3 | const bot = new duaGram({ 4 | api_id: 1, 5 | api_hash: 'your-api-hash', 6 | 7 | logLevel: 1, // 0 false, 1 event, 2 detail 8 | logDetail: "info", // none, error, warn, info, debug 9 | 10 | session: '' // Fill in the session here if you have one, or leave it blank 11 | }); 12 | 13 | // event all new message 14 | bot.on('message', async (ctx, _ctx) => { 15 | terminal.debug('Ctx Legacy'); 16 | console.log(_ctx); 17 | 18 | terminal.debug('Ctx Duagram'); 19 | terminal.more(ctx); 20 | }); 21 | 22 | bot.cmd('ping', async (ctx) => { 23 | bot.sendMessage(ctx, 'Pong!', { replyToMsgId: ctx.id }); 24 | }); 25 | 26 | bot.hear(/^(h+i+|h+e+l+o+)/i, (ctx) => { 27 | // message in only 28 | if (ctx.out) return; 29 | bot.sendMessage(ctx, 'Hi, too!', { parse_mode: 'html' }); 30 | }); 31 | 32 | bot.cmd('upload', (ctx) => { 33 | terminal.info('Starting upload...'); 34 | let file = './photo.jpg'; 35 | return bot.sendFile(ctx, file); 36 | }); 37 | 38 | bot.cmd('version', (ctx) => { 39 | return bot.sendMessage(ctx, `${JSON.stringify(bot.version, null, 2)}`, { parse_mode: 'html' }); 40 | }); 41 | 42 | bot.start(); 43 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const AppVersion = require("./version"); 2 | const { DuaGram } = require("./core/duagram"); 3 | let { terminal } = require('./utils/log'); 4 | 5 | class duaGram extends DuaGram { 6 | constructor(options) { 7 | super(options); 8 | this.me = false; 9 | } 10 | 11 | startBanner() { 12 | this.terminal.info('====================='); 13 | this.terminal.info('| The Journey Begins |'); 14 | this.terminal.info('---------------------') 15 | } 16 | 17 | get version() { 18 | return AppVersion; 19 | } 20 | } 21 | 22 | module.exports = { 23 | duaGram, terminal 24 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "duagram", 3 | "version": "1.3.8", 4 | "description": "Telegram Framework for userbot or bot api", 5 | "main": "index.js", 6 | "homepage": "https://t.me/duagram", 7 | "bugs": "https://github.com/ubotindonesia/duagram/issues", 8 | "repository": "https://github.com/ubotindonesia/duagram", 9 | "engineStrict": true, 10 | "scripts": { 11 | "start": "reset && node debug/dmessage.js", 12 | "test": "node test/start.js" 13 | }, 14 | "keywords": [ 15 | "nodejs", 16 | "telegram", 17 | "userbot", 18 | "mtproto", 19 | "telegrambot", 20 | "indonesia" 21 | ], 22 | "author": "Hasanudin H. Syafaat", 23 | "private": false, 24 | "dependencies": { 25 | "better-logging": "^4.5.0", 26 | "big-integer": "^1.6.48", 27 | "file-type": "^16.5.1", 28 | "input": "^1.0.1", 29 | "request": "^2.88.2", 30 | "request-promise": "^4.2.6", 31 | "telegram": "^2.3.0" 32 | }, 33 | "engines": { 34 | "node": ">=14.0.0" 35 | }, 36 | "devDependencies": { 37 | "dotenv": "^10.0.0", 38 | "json-stringify-safe": "^5.0.1", 39 | "log4js": "^6.3.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## duaGram 2 | 3 | Telegram Framework for userbot and or bot api, using nodejs. 4 | 5 | ![GitHub last commit](https://img.shields.io/github/last-commit/ubotindonesia/duagram) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/ubotindonesia/duagram) ![npm](https://img.shields.io/npm/v/duagram) ![node-current](https://img.shields.io/node/v/duagram?color=red) ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/ubotindonesia/duagram?color=fef) ![GitHub repo size](https://img.shields.io/github/repo-size/ubotindonesia/duagram?color=fee) ![Lines of code](https://img.shields.io/tokei/lines/github/ubotindonesia/duagram?color=dee) ![GitHub top language](https://img.shields.io/github/languages/top/ubotindonesia/duagram?color=dee) ![GitHub all releases](https://img.shields.io/github/downloads/ubotindonesia/duagram/total) ![GitHub Discussions](https://img.shields.io/github/discussions/ubotindonesia/duagram) ![npm](https://img.shields.io/npm/dt/duagram?color=blue) ![GitHub pull requests](https://img.shields.io/github/issues-pr/ubotindonesia/duagram) ![GitHub issues](https://img.shields.io/github/issues/ubotindonesia/duagram) 6 | 7 | 8 | ![duagram](https://github.com/ubotindonesia/duagram/raw/main/asset/2gram%20banner%20small.jpg) 9 | 10 | 11 | ![GitHub watchers](https://img.shields.io/github/watchers/ubotindonesia/duagram?style=social) ![GitHub forks](https://img.shields.io/github/forks/ubotindonesia/duagram?style=social) 12 | ![GitHub Repo stars](https://img.shields.io/github/stars/ubotindonesia/duagram?style=social) 13 | 14 | ### WARNING! 15 | 16 | Use at Your Own Risk. 17 | 18 | > I don't take any responsibility from actions made by you or on your account. 19 | 20 | ### History 21 | 22 | - [Release](https://github.com/ubotindonesia/duagram/releases) 23 | 24 | ### Support 25 | 26 | - [Issues](https://github.com/ubotindonesia/duagram/issues) 27 | - Contributor are welcome... 28 | 29 | ## Install 30 | 31 | `npm i duagram` 32 | 33 | or 34 | 35 | `yarn add duagram` 36 | 37 | or 38 | 39 | `pnpm add duagram` 40 | 41 | ## Quick Start 42 | 43 | ```javascript 44 | const { duaGram } = require("duagram"); 45 | 46 | const bot = new duaGram({ 47 | api_id: 1, 48 | api_hash: 'your-api-hash', 49 | // Fill in the session here if you have one, or leave it blank 50 | session: '', 51 | }); 52 | 53 | bot.cmd('ping', (ctx) => { 54 | console.log(ctx); 55 | // bot.sendMessage(ctx, 'pong'); // or: 56 | // bot.sendMessage(ctx.chat.id, 'pong'); // or: 57 | return ctx.reply('pong!'); 58 | }); 59 | 60 | bot.start(); 61 | ``` 62 | 63 | ## API TELEGRAM 64 | 65 | To use the duaGram, you first have to get API ID dan API HASH. 66 | 67 | Get it from [https://my.telegram.org](https://my.telegram.org) 68 | 69 | 70 | ### Token Bot 71 | 72 | If you connect use Bot API, get a bot account by chatting with [BotFather](https://core.telegram.org/bots#6-botfather). 73 | 74 | BotFather will give you a token, something like `123456789:AbCdfGhIJKlmNoQQRsTUVwxyZ`. 75 | 76 | 77 | ## More Example 78 | 79 | > Do you need more example? [Check this page](https://github.com/ubotindonesia/duagram/tree/main/examples). 80 | 81 | ### User Login 82 | 83 |
source example 84 | 85 | ```javascript 86 | const { duaGram, terminal } = require("duagram"); 87 | 88 | const bot = new duaGram({ 89 | api_id: 1, 90 | api_hash: 'your-api-hash', 91 | 92 | logLevel: 1, // 0 false, 1 event, 2 detail 93 | logDetail: "none", // none, error, warn, info, debug 94 | 95 | // Fill in the session here if you have one, or leave it blank 96 | session: '', 97 | 98 | // The most common error is the FloodWait error which is caused by calling a method multiple times in a short period and acts as a spam filter from telegram. So: 99 | floodSleepThreshold: 120, 100 | 101 | // Mark message history as read 102 | markRead: false 103 | }); 104 | 105 | bot.on('message', (ctx, _ctx) => { 106 | terminal.debug('Ctx Duagram'); 107 | console.log(ctx); 108 | }); 109 | 110 | bot.cmd('ping', (ctx) => { 111 | bot.sendMessage(ctx, 'Pong!', { replyToMsgId: ctx.id }); 112 | }); 113 | 114 | bot.cmd('upload', async (ctx) => { 115 | terminal.info('Starting upload...'); 116 | let file = './photo.jpg'; 117 | return bot.sendFile(ctx.chat.id, file); 118 | 119 | }); 120 | 121 | bot.cmd('version', (ctx) => { 122 | return bot.sendMessage(ctx, `${JSON.stringify(bot.version, null, 2)}`, { parse_mode: 'HTML' }); 123 | }); 124 | 125 | bot.start(); 126 | ``` 127 | 128 |
129 | 130 | ### Bot Login 131 | 132 | Login with Bot API Token 133 | 134 |
source example 135 | 136 | ```javascript 137 | const { duaGram, terminal, Helper } = require("duagram"); 138 | 139 | const bot = new duaGram({ 140 | api_id: 1, 141 | api_hash: 'your-api-hash', 142 | as_bot_api: true, 143 | bot_token: 'your-token-bot' 144 | }); 145 | 146 | // event all new message 147 | bot.on('message', async (ctx) => { 148 | terminal.debug('Ctx Duagram'); 149 | console.log(ctx); 150 | }); 151 | 152 | bot.cmd('ping', (ctx) => { 153 | return ctx.reply('Pong!', { replyToMsgId: ctx.id }); 154 | }); 155 | 156 | bot.cmd('upload', async (ctx) => { 157 | terminal.info('Starting upload...'); 158 | let file = './photo.jpg'; 159 | return await bot.sendFile(ctx.chat.id, file); 160 | 161 | }); 162 | 163 | // bot API 164 | bot.cmd('start', async (ctx) => { 165 | // message in only 166 | if (ctx.out) return false; 167 | 168 | if (!bot.asBotApi) { 169 | return bot.sendMessage(ctx, "I'm not bot api 😅") 170 | } 171 | 172 | let chat_id = ctx.chat.id; 173 | if (ctx.chat.type == 'channel') { 174 | chat_id = bot.Helper.chat.to_api(chat_id); 175 | } 176 | 177 | // if Bot API, send with Bot API can too 178 | let reply_markup = JSON.stringify({ 179 | inline_keyboard: [ 180 | [ 181 | bot.Helper.Button.url('👥 uBotIndonesia', 'https://t.me/ubotindonesia') 182 | ], [ 183 | bot.Helper.Button.text('One', 'cb1'), 184 | bot.Helper.Button.text('Two', 'cb2') 185 | ] 186 | ] 187 | }); 188 | 189 | let more = { 190 | parse_mode: 'html', 191 | reply_markup 192 | } 193 | 194 | return bot.BotApi.sendMessage(chat_id, 'This message from Bot Api', more) 195 | .then(result => { 196 | terminal.log('Result: BotApi sendMessage') 197 | console.log(result); 198 | }) 199 | .catch(error => terminal.error(error.message)); 200 | }); 201 | 202 | bot.cmd('version', (ctx) => { 203 | return bot.sendMessage(ctx, `${JSON.stringify(bot.version, null, 2)}`, { parse_mode: 'HTML' }); 204 | }); 205 | 206 | bot.start(); 207 | ``` 208 | 209 |
210 | 211 | ## Documentation 212 | 213 | ## duaGram 214 | 215 | ### Session 216 | 217 | - Generator: [https://telegram.banghasan.com/ubotstring/](https://telegram.banghasan.com/ubotstring/) 218 | 219 | ### Options 220 | 221 | `const bot = new duaGram(options);` 222 | 223 | 224 | | **Item** | **Description** | **Default** | 225 | | --------------------- | ---------------------------------------------------------------- | ------------- | 226 | | api\_id | get it from [https://my.telegram.org](https://my.telegram.org/) | | 227 | | api\_hash | get it from [https://my.telegram.org](https://my.telegram.org/) | | 228 | | session | session string | | 229 | | session_name | Session name | - | 230 | | local | with local database | `false` | 231 | | logLevel | Show log level 0 off, 1 event name, 2 detail | 1 | 232 | | logDetail | Event Detail (none, error, warn, info, debug) | info | 233 | | as\_bot\_api | Login as bot API? 0 false / 1 true | 0 | 234 | | bot\_token | Token Bot API [@botfahter](https://t.me/botfather) | | 235 | | connectionRetries | Connection Retry | 3 | 236 | | floodSleepThreshold | FloodWait error ? Set this | `120` | 237 | | markRead | Mark message history as read | `TRUE` | 238 | | cmdPrefix | prefix for command trigger | `!/.` 239 | 240 | ### Event 241 | 242 | Available : 243 | 244 | - **on**(`updateType, stop=true`) 245 | - **cmd**(`string, callback, stop=true`) 246 | - **hear**(`regex|string, callback, stop=true`) 247 | - hears(`regex|string, callback, stop=true`) alias hear 248 | - **command** alias `cmd` 249 | 250 | Example: 251 | 252 | ```javascript 253 | bot.cmd('ping', callback); 254 | bot.hear('hello', callback); 255 | bot.hear(/^!time$/i, callback); 256 | 257 | bot.hear(/coffee/i, callback, false); // if found stopable? false. So, catch condition anatoher again bellow 258 | bot.hear(/tea/i, callback); 259 | ``` 260 | 261 | ### Update Type 262 | 263 | Example: 264 | 265 | ```javascript 266 | bot.on('message', (ctx, _ctx) => terminal.less(ctx) ); 267 | ``` 268 | 269 | Available: 270 | 271 | - connected 272 | - raw 273 | - message 274 | - media 275 | - all class names in mtproto, according to the log details 276 | 277 | > You can see on `ctx.event` message too. 278 | 279 | Class name event example: 280 | 281 | - UpdateNewMessage 282 | - UpdateShortMessage 283 | - UpdateReadHistoryOutbox 284 | - UpdateDeleteMessages 285 | - UpdateUserTyping 286 | - etc... [schema](https://core.telegram.org/schema) 287 | 288 | Result `object` **raw** without middleware effect. 289 | 290 | ### Properties 291 | 292 | Method or Accessors of duaGram. 293 | 294 | 295 | | method | description | 296 | | ----------- | ------------------------------------------------------------------- | 297 | | telegram | collection function duagram | 298 | | Api | access for [API Telegram](https://core.telegram.org/) | 299 | | client | client connecton | 300 | | BotApi | wrapper for [Bot Api Telegram](https://core.telegram.org/bots/api) | 301 | | terminal | console replacement for colorful and bettermore | 302 | | lessLog | better than`console.log` function, less prefix \_ field | 303 | | asBotApi | `true`/`false` | 304 | | version | duagrams version info | 305 | | cmdPrefix | default is`.!/` | 306 | | Helper | [Go to helper doc](https://github.com/ubotindonesia/duagram/blob/main/docs/helper.md) | 307 | 308 | Example: 309 | 310 | ```javascript 311 | const bot = new duaGram({...}); 312 | 313 | bot.cmdPrefix = '~'; // bot.cmd('ping', ..) => ~ping 314 | console.log(bot.version); 315 | 316 | ``` 317 | 318 | #### Alias 319 | 320 | - tg (alias `telegram`) 321 | - invoke(`params`) 322 | - sendMessage(`peer, text, more`) 323 | - ... etc (like **telegram** method) 324 | 325 | ## Telegram 326 | 327 | ### method 328 | 329 | - invoke(`params`) 330 | - getPeerId(`ctx`) 331 | - sendMessage(`peer, text, more`) 332 | - editMessage(`peer, id, text, more`) 333 | - deleteMessages(`peer, ids, more`) 334 | - forwardMessages(`peerFrom, peerTo, ids, more`) 335 | - getMessages(`peer, ids`) 336 | - pinMessage(`peer, id, more`) 337 | - unpinAllMessages(`peer`) 338 | - readHistory(`peer, more`) 339 | - getUserPhotos(`peer, more`) 340 | - getUserInfo(`peer`) 341 | - editAdmin(`peerChatId, peerUserId, more = {}`) 342 | - editBanned(`peerChatId, peerUserId, more = {}`) 343 | - joinGroup(`peer`) 344 | - readMentions(`peer`) 345 | - readMessageContents(`id`) 346 | - deleteHistory(`peer, more`) 347 | - deleteUserHistory(`channelId, userId`) 348 | 349 | ## Middleware 350 | 351 | Middleware is an essential part of any modern framework. It allows you to modify requests and responses as they pass between the Telegram and your bot. 352 | 353 | You can imagine middleware as a chain of logic connection your bot to the Telegram request. 354 | 355 | Middleware normally takes two parameters `(ctx, next)`, `ctx` is the context for one Telegram update, `next` is a function that is invoked to execute the downstream middleware. It returns a Promise with a then function for running code after completion. 356 | 357 | ```javascript 358 | bot.middleware((ctx, next) => { 359 | ctx.additional = 'message test from middleware'; 360 | next(); 361 | }); 362 | 363 | bot.cmd('plus', async (ctx) => { 364 | if (!ctx.out) 365 | return bot.sendMessage(ctx.chat.id, `Hooked: ${ctx.additional}`); 366 | }) 367 | ``` 368 | 369 | ### Middleware List 370 | 371 | - [duaGram rate-limit](https://www.npmjs.com/package/duagram-ratelimit) 372 | 373 | Middleware more information: [click here](https://github.com/ubotindonesia/duagram/blob/main/docs/middleware.md). 374 | 375 | ## client 376 | 377 |
Client Details 378 | 379 | ### Method 380 | 381 | - start() 382 | - checkAuthorization() 383 | - signInUser() 384 | - signInUserWithQrCode() 385 | - signInWithPassword() 386 | - inlineQuery() 387 | - buildReplyMarkup() 388 | - downloadFile() 389 | - downloadProfilePhoto() 390 | - downloadMedia() 391 | - setParseMode() 392 | - iterMessages() 393 | - getMessages() 394 | - sendMessage() 395 | - forwardMessages() 396 | - editMessage() 397 | - iterDialogs() 398 | - getDialogs() 399 | - iterParticipants() 400 | - getParticipants() 401 | - removeEventHandler() 402 | - listEventHandlers() 403 | - uploadFile() 404 | - sendFile() 405 | - invoke() 406 | - getMe() 407 | - isBot() 408 | - isUserAuthorized() 409 | - getInputEntity() 410 | - getPeerId() 411 | - connect() 412 | - getDC() 413 | - disconnect() 414 | - destroy() 415 | - ... etc 416 | 417 |
418 | 419 | ## Reference 420 | 421 |
List details 422 | 423 | - [API Telegram](https://core.telegram.org/) 424 | - [Schema](https://core.telegram.org/schema) 425 | - [Bot API Telegram](https://core.telegram.org/bots/api) 426 | - [GramJS](https://gram.js.org/) 427 | - [GramJS Beta](https://gram.js.org/beta/classes/client_telegramclient.telegramclient.html) 428 | - [GramJS Gitbook](https://painor.gitbook.io/gramjs/) 429 | - [Bot Api Example](https://core.telegram.org/bots/samples) 430 | - [NodeJS](https://nodejs.org/dist/latest/docs/api/) 431 | - [MDN Mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript) 432 | - [VS Codium](https://vscodium.com/) 433 | 434 |
435 | 436 | ## Last Words 437 | 438 | ### Happy nge-bot! 439 | 440 | If you are Indonesian, let's join the [@ubotindonesia](https://t.me/ubotindonesia) telegram group. 441 | 442 | _see you again_ ^^ 443 | -------------------------------------------------------------------------------- /test/config.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config({ path: '.env' }) 2 | 3 | let options = { 4 | api_id: parseInt(process.env.API_ID) || 0, 5 | api_hash: process.env.API_HASH || '', 6 | session: process.env.STRING_SESSION || '', 7 | 8 | logLevel: 1, // show log for dev: 0 - false, 1 - event, 2 - detail 9 | logDetail: process.env.LOG_DETAIL || "info", // none, error, warn, info, debug 10 | 11 | as_bot_api: parseInt(process.env.AS_BOT_API) || false, // boelan 12 | bot_token: process.env.BOT_TOKEN || '', // boelan 13 | 14 | connectionRetries: 3, 15 | floodSleepThreshold: 180, // The most common error is the FloodWait error which is caused by calling a method multiple times in a short period and acts as a spam filter from telegram. 16 | 17 | markRead: true // Mark message history as read 18 | } 19 | 20 | // console.log(options); 21 | 22 | module.exports = options; -------------------------------------------------------------------------------- /test/error.js: -------------------------------------------------------------------------------- 1 | const { duaGram, terminal } = require(".."); 2 | 3 | const bot = new duaGram({ 4 | api_id: 123454, 5 | api_hash: 'your-api-hash', 6 | as_bot_api: true, 7 | bot_token: 'your-token-bot', 8 | 9 | logLevel: 1, // 0 false, 1 event, 2 detail 10 | logDetail: "info", // none, error, warn, info, debug 11 | }); 12 | 13 | bot.cmd('ping', (ctx) => { 14 | // bot.sendMessage(ctx, 'pong'); 15 | return ctx.reply('pong!'); 16 | }); 17 | 18 | bot.start(); 19 | -------------------------------------------------------------------------------- /test/noStart.js: -------------------------------------------------------------------------------- 1 | const { terminal, duaGram, Helper } = require(".."); 2 | let options = require('./config'); 3 | let result = {}; 4 | 5 | const bot = new duaGram(options); 6 | 7 | bot.middleware(async (ctx, next) => { 8 | ctx.hooked = 'this is new message'; 9 | next(); 10 | 11 | }); 12 | 13 | bot.hear('hi', (ctx) => { 14 | return bot.sendMessage(ctx.chat.id, 'Hi too..', { replyToMsgId: ctx.id }); 15 | }) 16 | 17 | result.scanners = bot.scanners; 18 | result.middleware = bot.middleware; 19 | result.version = bot.version; 20 | 21 | result.random = bot.Helper.random(['satu', 'dua', 'tiga']); 22 | 23 | terminal.debug('Result noStartBot'); 24 | console.log(result); -------------------------------------------------------------------------------- /test/start.js: -------------------------------------------------------------------------------- 1 | const { terminal, duaGram } = require(".."); 2 | 3 | let options = require('./config'); 4 | 5 | let LoginAsBotApi = true; 6 | 7 | if (LoginAsBotApi) { 8 | options.as_bot_api = true; 9 | options.session = ''; 10 | } 11 | 12 | const bot = new duaGram(options); 13 | 14 | bot.cmd('ping', (ctx) => { 15 | bot.sendMessage(ctx.chat.id, 'pong!') 16 | }) 17 | 18 | 19 | bot.cmd('start', async (ctx) => { 20 | // message in only 21 | if (ctx.out) return false; 22 | 23 | if (!bot.asBotApi) { 24 | return bot.sendMessage(ctx, "I'm not bot api 😅") 25 | } 26 | 27 | // if Bot API, send with Bot API can too 28 | let reply_markup = JSON.stringify({ 29 | inline_keyboard: [ 30 | [ 31 | bot.Helper.Button.url('👥 uBotIndonesia', 'https://t.me/ubotindonesia') 32 | ], [ 33 | bot.Helper.Button.text('One', 'cb1'), 34 | bot.Helper.Button.text('Two', 'cb2') 35 | ] 36 | ] 37 | }); 38 | 39 | let more = { 40 | parse_mode: 'html', 41 | reply_markup 42 | } 43 | 44 | return bot.BotApi.sendMessage(ctx.chat.id, 'This message from Bot Api', more) 45 | .then(result => { 46 | terminal.log('Result: BotApi sendMessage') 47 | console.log(result); 48 | }) 49 | .catch(error => terminal.error(error.message)); 50 | 51 | 52 | 53 | }); 54 | 55 | bot.start(); -------------------------------------------------------------------------------- /utils/date.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-present, Evgeny Nadymov 3 | * 4 | * This source code is licensed under the GPL v.3.0 license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | /* 9 | * Date Format 1.2.3 10 | * (c) 2007-2009 Steven Levithan 11 | * MIT license 12 | * 13 | * Includes enhancements by Scott Trenda 14 | * and Kris Kowal 15 | * 16 | * Accepts a date, a mask, or a date and a mask. 17 | * Returns a formatted version of the given date. 18 | * The date defaults to the current date/time. 19 | * The mask defaults to dateFormat.masks.default. 20 | */ 21 | 22 | // Modif Version DuaGram v1.0 23 | 24 | const dateFormat = (function () { 25 | const token = /d{1,4}|M{1,4}|yy(?:yy)?|([HhmsAa])\1?|[LloSZWN]|"[^"]*"|'[^']*'/g; 26 | const timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g; 27 | const timezoneClip = /[^-+\dA-Z]/g; 28 | 29 | // Regexes and supporting functions are cached through closure 30 | return function (date, mask, utc, gmt) { 31 | // You can't provide utc if you skip other args (use the 'UTC:' mask prefix) 32 | if (arguments.length === 1 && kindOf(date) === 'string' && !/\d/.test(date)) { 33 | mask = date; 34 | date = undefined; 35 | } 36 | 37 | date = date || new Date(); 38 | 39 | if (!(date instanceof Date)) { 40 | date = new Date(date); 41 | } 42 | 43 | if (isNaN(date)) { 44 | throw TypeError('Invalid date'); 45 | } 46 | 47 | mask = String(dateFormat.masks[mask] || mask || dateFormat.masks['default']); 48 | 49 | // Allow setting the utc/gmt argument via the mask 50 | var maskSlice = mask.slice(0, 4); 51 | if (maskSlice === 'UTC:' || maskSlice === 'GMT:') { 52 | mask = mask.slice(4); 53 | utc = true; 54 | if (maskSlice === 'GMT:') { 55 | gmt = true; 56 | } 57 | } 58 | 59 | var _ = utc ? 'getUTC' : 'get'; 60 | var d = date[_ + 'Date'](); 61 | var D = date[_ + 'Day'](); 62 | var M = date[_ + 'Month'](); 63 | var y = date[_ + 'FullYear'](); 64 | var H = date[_ + 'Hours'](); 65 | var m = date[_ + 'Minutes'](); 66 | var s = date[_ + 'Seconds'](); 67 | var L = date[_ + 'Milliseconds'](); 68 | var o = utc ? 0 : date.getTimezoneOffset(); 69 | var W = getWeek(date); 70 | var N = getDayOfWeek(date); 71 | var flags = { 72 | d: d, 73 | dd: pad(d), 74 | ddd: dateFormat.i18n.dayNames[D], 75 | dddd: dateFormat.i18n.dayNames[D + 7], 76 | M: M + 1, 77 | MM: pad(M + 1), 78 | MMM: dateFormat.i18n.monthNames[M], 79 | MMMM: dateFormat.i18n.monthNames[M + 12], 80 | yy: String(y).slice(2), 81 | yyyy: y, 82 | h: H % 12 || 12, 83 | hh: pad(H % 12 || 12), 84 | H: H, 85 | HH: pad(H), 86 | m: m, 87 | mm: pad(m), 88 | s: s, 89 | ss: pad(s), 90 | l: pad(L, 3), 91 | L: pad(Math.round(L / 10)), 92 | a: H < 12 ? dateFormat.i18n.timeNames[0] : dateFormat.i18n.timeNames[1], 93 | aa: H < 12 ? dateFormat.i18n.timeNames[2] : dateFormat.i18n.timeNames[3], 94 | A: H < 12 ? dateFormat.i18n.timeNames[4] : dateFormat.i18n.timeNames[5], 95 | AA: H < 12 ? dateFormat.i18n.timeNames[6] : dateFormat.i18n.timeNames[7], 96 | Z: gmt ? 'GMT' : utc ? 'UTC' : (String(date).match(timezone) || ['']).pop().replace(timezoneClip, ''), 97 | o: (o > 0 ? '-' : '+') + pad(Math.floor(Math.abs(o) / 60) * 100 + (Math.abs(o) % 60), 4), 98 | S: ['th', 'st', 'nd', 'rd'][d % 10 > 3 ? 0 : (((d % 100) - (d % 10) != 10) * d) % 10], 99 | W: W, 100 | N: N 101 | }; 102 | 103 | return mask.replace(token, function (match) { 104 | if (match in flags) { 105 | return flags[match]; 106 | } 107 | return match.slice(1, match.length - 1); 108 | }); 109 | }; 110 | })(); 111 | 112 | dateFormat.masks = { 113 | default: 'ddd MMM dd yyyy HH:mm:ss', 114 | dateTime: 'yyyy-MM-dd HH:mm:ss', 115 | shortDate: 'M/d/yy', 116 | mediumDate: 'MMM d, yyyy', 117 | longDate: 'MMMM d, yyyy', 118 | fullDate: 'dddd, MMMM d, yyyy', 119 | shortTime: 'h:mm AA', 120 | mediumTime: 'h:mm:ss AA', 121 | longTime: 'h:mm:ss AA Z', 122 | isoDate: 'yyyy-MM-dd', 123 | isoTime: 'HH:mm:ss', 124 | isoDateTime: "yyyy-MM-dd'T'HH:mm:sso", 125 | isoUtcDateTime: "UTC:yyyy-MM-dd'T'HH:mm:ss'Z'", 126 | expiresHeaderFormat: 'ddd, dd MMM yyyy HH:mm:ss Z' 127 | }; 128 | 129 | // Internationalization strings 130 | dateFormat.i18n = { 131 | dayNames: [ 132 | 'Sun', 133 | 'Mon', 134 | 'Tue', 135 | 'Wed', 136 | 'Thu', 137 | 'Fri', 138 | 'Sat', 139 | 'Sunday', 140 | 'Monday', 141 | 'Tuesday', 142 | 'Wednesday', 143 | 'Thursday', 144 | 'Friday', 145 | 'Saturday' 146 | ], 147 | monthNames: [ 148 | 'Jan', 149 | 'Feb', 150 | 'Mar', 151 | 'Apr', 152 | 'May', 153 | 'Jun', 154 | 'Jul', 155 | 'Aug', 156 | 'Sep', 157 | 'Oct', 158 | 'Nov', 159 | 'Dec', 160 | 'January', 161 | 'February', 162 | 'March', 163 | 'April', 164 | 'May', 165 | 'June', 166 | 'July', 167 | 'August', 168 | 'September', 169 | 'October', 170 | 'November', 171 | 'December' 172 | ], 173 | timeNames: ['AM', 'PM', 'AM', 'PM', 'AM', 'PM', 'AM', 'PM'] 174 | }; 175 | 176 | function pad(val, len) { 177 | val = String(val); 178 | len = len || 2; 179 | while (val.length < len) { 180 | val = '0' + val; 181 | } 182 | return val; 183 | } 184 | 185 | /** 186 | * Get the ISO 8601 week number 187 | * Based on comments from 188 | * http://techblog.procurios.nl/k/n618/news/view/33796/14863/Calculate-ISO-8601-week-and-year-in-javascript.html 189 | * 190 | * @param {Object} `date` 191 | * @return {Number} 192 | */ 193 | function getWeek(date) { 194 | // Remove time components of date 195 | var targetThursday = new Date(date.getFullYear(), date.getMonth(), date.getDate()); 196 | 197 | // Change date to Thursday same week 198 | targetThursday.setDate(targetThursday.getDate() - ((targetThursday.getDay() + 6) % 7) + 3); 199 | 200 | // Take January 4th as it is always in week 1 (see ISO 8601) 201 | var firstThursday = new Date(targetThursday.getFullYear(), 0, 4); 202 | 203 | // Change date to Thursday same week 204 | firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3); 205 | 206 | // Check if daylight-saving-time-switch occurred and correct for it 207 | var ds = targetThursday.getTimezoneOffset() - firstThursday.getTimezoneOffset(); 208 | targetThursday.setHours(targetThursday.getHours() - ds); 209 | 210 | // Number of weeks between target Thursday and first Thursday 211 | var weekDiff = (targetThursday - firstThursday) / (86400000 * 7); 212 | return 1 + Math.floor(weekDiff); 213 | } 214 | 215 | /** 216 | * Get ISO-8601 numeric representation of the day of the week 217 | * 1 (for Monday) through 7 (for Sunday) 218 | * 219 | * @param {Object} `date` 220 | * @return {Number} 221 | */ 222 | function getDayOfWeek(date) { 223 | var dow = date.getDay(); 224 | if (dow === 0) { 225 | dow = 7; 226 | } 227 | return dow; 228 | } 229 | 230 | /** 231 | * kind-of shortcut 232 | * @param {*} val 233 | * @return {String} 234 | */ 235 | function kindOf(val) { 236 | if (val === null) { 237 | return 'null'; 238 | } 239 | 240 | if (val === undefined) { 241 | return 'undefined'; 242 | } 243 | 244 | if (typeof val !== 'object') { 245 | return typeof val; 246 | } 247 | 248 | if (Array.isArray(val)) { 249 | return 'array'; 250 | } 251 | 252 | return {}.toString 253 | .call(val) 254 | .slice(8, -1) 255 | .toLowerCase(); 256 | } 257 | 258 | // export default dateFormat; 259 | module.exports = dateFormat; -------------------------------------------------------------------------------- /utils/index.js: -------------------------------------------------------------------------------- 1 | const Util = { 2 | /** 3 | Membersihkan tag HTML 4 | @param {string} text yang akan dibersihkan 5 | */ 6 | clearHTML: function (s) { 7 | return s 8 | .replace(/&/g, "&") 9 | .replace(//g, ">"); 11 | }, 12 | 13 | /** 14 | Membersihkan tag Markdown 15 | @param {string} text yang akan dibersihkan 16 | */ 17 | clearMarkdown: function (s) { 18 | return s 19 | .replace(/_/g, "\\_") 20 | .replace(/\*/g, "\\*") 21 | .replace(/\[/g, "\\[") 22 | .replace(/`/g, "\\`"); 23 | }, 24 | 25 | // untuk pengecekkan hak akses 26 | /* contoh: 27 | var adminID = [1, 2, 3, 4] 28 | if ( tg.util.punyaAkses(adminID, msg.from.id) ) { .. } 29 | */ 30 | isIN: function (array, index) { 31 | if (array.indexOf(index) > -1) { 32 | return true; 33 | } else { 34 | return false; 35 | } 36 | }, 37 | 38 | forEach: function (obj, fn) { 39 | // Don't bother if no value provided 40 | if (obj === null || typeof obj === 'undefined') { 41 | return; 42 | } 43 | 44 | // Force an array if not already something iterable 45 | if (typeof obj !== 'object') { 46 | /*eslint no-param-reassign:0*/ 47 | obj = [obj]; 48 | } 49 | 50 | if (Array.isArray(obj)) { 51 | // Iterate over array values 52 | for (var i = 0, l = obj.length; i < l; i++) { 53 | fn.call(null, obj[i], i, obj); 54 | } 55 | } else { 56 | // Iterate over object keys 57 | for (var key in obj) { 58 | if (Object.prototype.hasOwnProperty.call(obj, key)) { 59 | fn.call(null, obj[key], key, obj); 60 | } 61 | } 62 | } 63 | }, 64 | 65 | // allReplace('Hasanudin', {'a':4, 's': 5, 'n|u': 'o'}) //> H454oodio 66 | allReplace: function (str, obj) { 67 | var hasil = str; 68 | for (var x in obj) { 69 | hasil = hasil.replace(new RegExp(x, 'gi'), obj[x]); 70 | } 71 | return hasil; 72 | }, 73 | 74 | random: function () { 75 | // random(list) : item 76 | if (arguments.length === 1 && Array.isArray(arguments[0]) ) { 77 | var list = arguments[0]; 78 | return list[Math.floor((Math.random() * list.length))]; 79 | } 80 | 81 | // random(min, max) : integer 82 | if (arguments.length === 2 && typeof (arguments[0]) === 'number' && typeof (arguments[1]) === 'number') { 83 | var min = arguments[0]; 84 | var max = arguments[1]; 85 | if (max < min) { [min, max] = [max, min]; } 86 | return Math.floor(Math.random() * (max - min + 1)) + min; 87 | } 88 | 89 | return false; 90 | }, 91 | 92 | cleanObject(json) { 93 | const removeNull = (obj) => { 94 | Object.keys(obj).forEach(k => 95 | (obj[k] && typeof obj[k] === 'object') && removeNull(obj[k]) 96 | || 97 | !obj[k] && delete obj[k] 98 | ); 99 | return obj; 100 | }; 101 | 102 | let result = removeNull(json); 103 | return result; 104 | }, 105 | 106 | } 107 | 108 | Util.Button = { 109 | text: function (text, data) { 110 | return { 111 | 'text': text, 112 | 'callback_data': data 113 | } 114 | }, 115 | // inline = alias dari text 116 | inline: function (text, data) { 117 | return { 118 | 'text': text, 119 | 'callback_data': data 120 | } 121 | }, 122 | query: function (text, data) { 123 | return { 124 | 'text': text, 125 | 'switch_inline_query': data 126 | } 127 | }, 128 | url: function (text, url) { 129 | return { 130 | 'text': text, 131 | 'url': url 132 | } 133 | } 134 | } 135 | 136 | Util.chat = { 137 | to_api: (chat_id) => { 138 | return parseInt('-100' + String(chat_id)); 139 | }, 140 | from_api: (chat_id) => { 141 | parseInt(String(chat_id).replace('-100', '')); 142 | } 143 | } 144 | 145 | Util.dateFormat = require('./date'); 146 | 147 | module.exports = Util -------------------------------------------------------------------------------- /utils/log.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const util = require('util') 3 | 4 | let terminal = {} 5 | require('better-logging')(terminal, { 6 | logLevels: { 7 | debug: 3, error: 3, info: 1, 8 | line: 1, log: 0, warn: 1, 9 | }, 10 | format: ctx =>`${ctx.time24}${ctx.type} ${ctx.msg}` 11 | /* format: ctx => { 12 | let regType = /(error|log|info|warn|debug)/i; 13 | let match = regType.exec(ctx.type); 14 | let t = ctx.type 15 | .replace(regType, match[1].padEnd(5, ' ')); 16 | return `${ctx.time24}${t} ${ctx.msg}` 17 | } */ 18 | }); 19 | 20 | function lessLog(object) { 21 | var toPrint = {}; 22 | for (var key in object) { 23 | // console.log("key is", key); 24 | // console.log("key starts with _?", key.startsWith("_")); 25 | if (object.hasOwnProperty(key)) { 26 | if (!key.startsWith("_")) { 27 | toPrint[key] = object[key]; 28 | } 29 | } 30 | } 31 | return console.log(toPrint); 32 | } 33 | 34 | function moreLog(object) { 35 | return console.log(util.inspect(object, false, null, true /* enable colors */)); 36 | } 37 | 38 | const decyle = function decycle(object, replacer) { 39 | "use strict"; 40 | 41 | // Make a deep copy of an object or array, assuring that there is at most 42 | // one instance of each object or array in the resulting structure. The 43 | // duplicate references (which might be forming cycles) are replaced with 44 | // an object of the form 45 | 46 | // {"$ref": PATH} 47 | 48 | // where the PATH is a JSONPath string that locates the first occurance. 49 | 50 | // So, 51 | 52 | // var a = []; 53 | // a[0] = a; 54 | // return JSON.stringify(JSON.decycle(a)); 55 | 56 | // produces the string '[{"$ref":"$"}]'. 57 | 58 | // If a replacer function is provided, then it will be called for each value. 59 | // A replacer function receives a value and returns a replacement value. 60 | 61 | // JSONPath is used to locate the unique object. $ indicates the top level of 62 | // the object or array. [NUMBER] or [STRING] indicates a child element or 63 | // property. 64 | 65 | var objects = new WeakMap(); // object to path mappings 66 | 67 | return (function derez(value, path) { 68 | 69 | // The derez function recurses through the object, producing the deep copy. 70 | 71 | var old_path; // The path of an earlier occurance of value 72 | var nu; // The new object or array 73 | 74 | // If a replacer function was provided, then call it to get a replacement value. 75 | 76 | if (replacer !== undefined) { 77 | value = replacer(value); 78 | } 79 | 80 | // typeof null === "object", so go on if this value is really an object but not 81 | // one of the weird builtin objects. 82 | 83 | if ( 84 | typeof value === "object" 85 | && value !== null 86 | && !(value instanceof Boolean) 87 | && !(value instanceof Date) 88 | && !(value instanceof Number) 89 | && !(value instanceof RegExp) 90 | && !(value instanceof String) 91 | ) { 92 | 93 | // If the value is an object or array, look to see if we have already 94 | // encountered it. If so, return a {"$ref":PATH} object. This uses an 95 | // ES6 WeakMap. 96 | 97 | old_path = objects.get(value); 98 | if (old_path !== undefined) { 99 | return { $ref: old_path }; 100 | } 101 | 102 | // Otherwise, accumulate the unique value and its path. 103 | 104 | objects.set(value, path); 105 | 106 | // If it is an array, replicate the array. 107 | 108 | if (Array.isArray(value)) { 109 | nu = []; 110 | value.forEach(function (element, i) { 111 | nu[i] = derez(element, path + "[" + i + "]"); 112 | }); 113 | } else { 114 | 115 | // If it is an object, replicate the object. 116 | 117 | nu = {}; 118 | Object.keys(value).forEach(function (name) { 119 | nu[name] = derez( 120 | value[name], 121 | path + "[" + JSON.stringify(name) + "]" 122 | ); 123 | }); 124 | } 125 | return nu; 126 | } 127 | return value; 128 | }(object, "$")); 129 | }; 130 | 131 | terminal.less = lessLog; 132 | terminal.more = moreLog; 133 | 134 | module.exports = { 135 | terminal, 136 | lessLog, moreLog, 137 | decyle 138 | }; -------------------------------------------------------------------------------- /utils/peer.js: -------------------------------------------------------------------------------- 1 | function typeCheck(value) { 2 | const return_value = Object.prototype.toString.call(value); 3 | // we can also use regex to do this... 4 | const type = return_value.substring( 5 | return_value.indexOf(" ") + 1, 6 | return_value.indexOf("]")); 7 | 8 | return type.toLowerCase(); 9 | } 10 | 11 | function fieldType(data) { 12 | if (!data) return false; 13 | //let type = data.className == 'PeerUser' ? 'user' : 'channel'; 14 | let type = data.className.replace('Peer', '').toLowerCase(); 15 | return { 16 | type, 17 | id: data[type + 'Id'] 18 | } 19 | } 20 | 21 | module.exports = (ctx, event = 'none') => { 22 | 23 | if (!ctx) throw Error('Unknown peer'); 24 | 25 | if (typeCheck(ctx) == 'number' || typeCheck(ctx) == 'string') { 26 | return ctx; 27 | } 28 | 29 | if (ctx.chat && ctx.chat.id) { 30 | return ctx.chat.id; 31 | } 32 | 33 | if (ctx.peerId) { 34 | let peer = fieldType(ctx.peerId); 35 | return peer.id; 36 | } 37 | 38 | if (typeof ctx.peer?.id == 'number') { 39 | return ctx.peer?.id; 40 | } 41 | 42 | if (typeof ctx.chat?.id == 'number') { 43 | return ctx.chat?.id; 44 | } 45 | 46 | if (typeof ctx.message?.message?.peerId?.userId == 'number') { 47 | return ctx.message?.peerId?.userId; 48 | } 49 | 50 | if (typeof ctx.message?.peerId?.userId == 'number') { 51 | return ctx.message?.peerId?.userId; 52 | } 53 | 54 | if (typeof ctx.peerId?.userId == 'number') 55 | return ctx.peerId?.userId; 56 | 57 | if (typeof ctx.message?.message?.peerId?.channelId == 'number') 58 | return ctx.message?.peerId?.channelId; 59 | 60 | if (typeof ctx.message?.peerId?.channelId == 'number') 61 | return ctx.message?.peerId?.channelId; 62 | 63 | if (typeof ctx.peerId?.channelId == 'number') 64 | return ctx.peerId?.channelId; 65 | 66 | if (typeof ctx.peerId?.chatId == 'number') 67 | return ctx.peerId?.chatId; 68 | 69 | // letakkan akhir 70 | if (ctx.userId) 71 | if (typeof ctx.userId == 'number') 72 | return ctx.userId; 73 | 74 | if (ctx.channelId) 75 | if (typeof ctx.channelId == 'number') 76 | return ctx.channelId; 77 | 78 | console.log('==> PEEER TIDAK DIKETAHUI', ctx) 79 | return ctx; 80 | } -------------------------------------------------------------------------------- /version.js: -------------------------------------------------------------------------------- 1 | let package = require('./package.json') 2 | 3 | module.exports = { 4 | name: package.name, 5 | number: package.version, 6 | desc: package.description, 7 | repo: package.repository, 8 | url: 'https://t.me/duagram', 9 | support: '@ubotindonesia', 10 | 11 | release: { 12 | first: 'July 2021', 13 | last: 'September 2021', 14 | }, 15 | 16 | system: `${process.platform} ${process.arch} nodejs ${process.version}` 17 | } --------------------------------------------------------------------------------