├── .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 |             
6 |
7 |
8 | 
9 |
10 |
11 |  
12 | 
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 | }
--------------------------------------------------------------------------------