-
Notifications
You must be signed in to change notification settings - Fork 14
/
telebridge.py
3283 lines (3100 loc) · 158 KB
/
telebridge.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import simplebot
import deltachat
from simplebot.bot import DeltaBot, Replies
from deltachat import Chat, Contact, Message
from deltachat import account_hookimpl
from typing import Optional
import sys
import os
import io
from os.path import expanduser
import psutil
from telethon.sessions import StringSession
from telethon import TelegramClient as TC
from telethon import functions, types
from telethon.tl.functions.users import GetFullUserRequest
from telethon.tl.functions.messages import GetDialogsRequest
from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.functions.channels import JoinChannelRequest
from telethon.tl.functions.messages import ImportChatInviteRequest, SendMessageRequest
from telethon.tl.functions.contacts import ResolveUsernameRequest
from telethon.tl.types import ChannelParticipantsSearch
from telethon.tl.types import InputPeerEmpty, WebDocument, WebDocumentNoProxy, InputWebFileLocation
from telethon.tl.types import PeerUser, PeerChat, PeerChannel
from telethon import utils, errors
from telethon.errors import SessionPasswordNeededError
from telethon import helpers
import asyncio
import re
import time
import json
import urllib.parse
from datetime import datetime
from threading import Event, Thread
import copy
#For telegram sticker stuff
import lottie
from lottie.importers import importers
from lottie.exporters import exporters
from lottie.utils.stripper import float_strip, heavy_strip
#For secure cloud storage
import dropbox
from dropbox.files import WriteMode
from dropbox.exceptions import ApiError, AuthError
from dropbox import DropboxOAuth2FlowNoRedirect
import zipfile
import base64
import psycopg2
import html
import markdown
import random
import string
version = "0.2.18"
api_id = os.getenv('API_ID')
api_hash = os.getenv('API_HASH')
login_hash = os.getenv('LOGIN_HASH')
admin_addr = os.getenv('ADMIN')
DATABASE_URL = os.getenv('DATABASE_URL')
TGTOKEN = os.getenv('TGTOKEN')
ADDR = os.getenv('ADDR')
bot_home = expanduser("~")
MAX_BUBBLE_SIZE = 1000
MAX_BUBBLE_LINES = 38
global phonedb
phonedb = {}
global smsdb
smsdb = {}
global hashdb
hashdb = {}
global clientdb
clientdb = {}
global logindb
logindb = {}
global messagedb
#{contac_addr:{dc_id:{dc_msg:tg_msg}}}
messagedb = {}
global last_messagedb
#{contac_addr:{dc_id:{dc_msg:tg_msg}}}
last_messagedb = {}
global unreaddb
#{'dc_id:dc_msg':[contact,tg_id,tg_msg]}
unreaddb = {}
global autochatsdb
#{contact_addr:{dc_id:tg_id}}
autochatsdb = {}
global chatdb
chatdb = {}
global resultsdb
#{contact_addr:results}
resultsdb = {}
global prealiasdb
prealiasdb = {}
global aliasdb
aliasdb = {}
global auto_load_task
auto_load_task = None
global encode_bot_addr
encode_bot_addr = ''
global SYNC_ENABLED
SYNC_ENABLED = 0
global UPDATE_DELAY
UPDATE_DELAY = 16
global authorize_url
authorize_url = None
dark_html = """<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-size: 18px;
color: white;
background-color: black;}
a:link {
color: #aaaaff;}
</style>
</head>
<body>"""
loop = asyncio.new_event_loop()
#Secure save storage to use in non persistent storage
DBXTOKEN = os.getenv('DBXTOKEN')
APP_KEY = os.getenv('APP_KEY')
if APP_KEY:
auth_flow = DropboxOAuth2FlowNoRedirect(APP_KEY, use_pkce=True, token_access_type='offline')
if DBXTOKEN:
if APP_KEY:
dbx = dropbox.Dropbox(oauth2_refresh_token=DBXTOKEN, app_key=APP_KEY)
else:
dbx = dropbox.Dropbox(DBXTOKEN)
# Check that the access token is valid
try:
dbx.users_get_current_account()
except AuthError:
print("ERROR: Invalid access token; try re-generating an "
"access token from the app console on the web.")
def save_bot_db():
if TGTOKEN:
async_cloud_db()
elif DBXTOKEN:
backup_db()
elif DATABASE_URL:
db_save()
def backup(backup_path):
with open(backup_path, 'rb') as f:
print("Uploading " + backup_path + " to Dropbox...")
if backup_path.startswith('.'):
dbx_backup_path = backup_path.replace('.','',1)
else:
dbx_backup_path =backup_path
try:
dbx.files_upload(f.read(), dbx_backup_path, mode=WriteMode('overwrite'))
except ApiError as err:
# This checks for the specific error where a user doesn't have
# enough Dropbox space quota to upload this file
if (err.error.is_path() and
err.error.get_path().reason.is_insufficient_space()):
#sys.exit("ERROR: Cannot back up; insufficient space.")
print("ERROR: Cannot back up; insufficient space.", err)
elif err.user_message_text:
print(err.user_message_text)
sys.exit()
else:
print(err)
sys.exit()
def db_init():
try:
con = psycopg2.connect(DATABASE_URL)
cur = con.cursor()
cur.execute("SELECT * FROM information_schema.tables WHERE table_name=%s", ('simplebot_db',))
if bool(cur.rowcount):
print("La tabla existe!")
else:
print("La tabla no existe, creando...")
cur.execute("CREATE TABLE simplebot_db (id bigint PRIMARY KEY, name TEXT, data BYTEA)")
con.commit()
cur.execute("ALTER TABLE simplebot_db ALTER COLUMN data SET STORAGE EXTERNAL")
con.commit()
cur.close()
except Exception as error:
print('Cause: {}'.format(error))
finally:
if con is not None:
con.close()
print('Database connection closed.')
def db_save():
try:
con = psycopg2.connect(DATABASE_URL)
cur = con.cursor()
print("Salvando a postgres...")
zipfile = zipdir(bot_home+'/.simplebot/', encode_bot_addr+'.zip')
bin = open(zipfile, 'rb').read()
#cur.execute("TRUNCATE simplebot_db")
#con.commit()
cur.execute("INSERT INTO simplebot_db(id,name,data) VALUES(%s,%s,%s) ON CONFLICT (id) DO UPDATE SET name = excluded.name, data = excluded.data", (0,encode_bot_addr, psycopg2.Binary(bin)))
con.commit()
cur.close()
except Exception as error:
print('Cause: {}'.format(error))
finally:
if con is not None:
con.close()
print('Database connection closed.')
async def cloud_db(tfile):
try:
client = TC(StringSession(TGTOKEN), api_id, api_hash)
await client.connect()
await client.get_dialogs()
storage_msg = await client.get_messages('me', search='simplebot_tg_db\n'+ADDR)
if storage_msg.total>0:
await client.edit_message('me', storage_msg[-1].id, 'simplebot_tg_db\n'+ADDR+'\n'+str(datetime.now()), file=tfile)
else:
await client.send_message('me', 'simplebot_tg_db\n'+ADDR+'\n'+str(datetime.now()), file=tfile)
await client.disconnect()
os.remove('./'+tfile)
except Exception as e:
estr = str('Error on line {}'.format(sys.exc_info()[-1].tb_lineno)+'\n'+str(type(e).__name__)+'\n'+str(e))
print(estr)
def async_cloud_db():
zipfile = zipdir(bot_home+'/.simplebot/', encode_bot_addr+'.zip')
loop.run_until_complete(cloud_db(zipfile))
def zipdir(dir_path,file_path):
zf = zipfile.ZipFile(file_path, "w", compression=zipfile.ZIP_LZMA)
for dirname, subdirs, files in os.walk(dir_path):
if dirname.endswith('account.db-blobs'):
continue
zf.write(dirname)
print(dirname)
for filename in files:
#if filename=='bot.db-journal':
#continue
print(filename)
zf.write(os.path.join(dirname, filename))
zf.close()
return file_path
def savelogin(bot):
bot.set('LOGINDB',json.dumps(logindb))
save_bot_db()
def saveautochats(bot):
bot.set('AUTOCHATSDB',json.dumps(autochatsdb))
save_bot_db()
def savealias(bot):
bot.set('ALIASDB',json.dumps(aliasdb))
save_bot_db()
def fixautochats(bot):
cids = []
dchats = bot.account.get_chats()
for c in dchats:
cids.append(str(c.id))
#print('Chats guardados: '+str(cids))
tmpdict = copy.deepcopy(autochatsdb)
for (key, value) in tmpdict.items():
for (inkey, invalue) in value.items():
if str(inkey) not in cids:
print('El chat '+str(inkey)+' no existe en el bot')
del autochatsdb[key][inkey]
def backup_db():
#bot.account.stop_io()
print('Backup...')
zipfile = zipdir(bot_home+'/.simplebot/', encode_bot_addr+'.zip')
#bot.account.start_io()
if os.path.getsize('./'+zipfile)>22:
backup('./'+zipfile)
else:
print('Invalid zip file!')
os.remove('./'+zipfile)
#end secure save storage
def extract_text_block(block):
text_block = ""
if hasattr(block,'text') and block.text:
if isinstance(block.text,types.TextBold):
if hasattr(block,'url') and block.url:
text_block += "<b><a href='"+block.url+"'>"+extract_text_block(block.text.text)+"</a></b>"
else:
text_block += "<b>"+extract_text_block(block.text.text)+"</b>"
elif isinstance(block.text,types.TextItalic):
if hasattr(block,'url') and block.url:
text_block += "<i><a href='"+block.url+"'>"+extract_text_block(block.text.text)+"</a></i>"
else:
text_block += "<i>"+extract_text_block(block.text.text)+"</i>"
elif isinstance(block.text,types.TextPlain):
if hasattr(block,'url') and block.url:
text_block += "<a href='"+block.url+"'>"+extract_text_block(block.text)+"</a>"
else:
text_block += extract_text_block(block.text)
elif isinstance(block.text,types.TextUrl):
text_block += extract_text_block(block.text)
elif isinstance(block.text,types.TextFixed):
text_block += "<i>"+extract_text_block(block.text)+"</i>"
elif isinstance(block.text,types.TextAnchor):
if hasattr(block.text, 'name'):
text_block += "<anchor name='"+block.text.name+"'>"+extract_text_block(block.text)+"</anchor>"
else:
text_block += "<anchor>"+extract_text_block(block.text)+"</anchor>"
elif isinstance(block.text,types.TextConcat):
for tc in block.text.texts:
text_block += extract_text_block(tc)
elif isinstance(block.text,types.TextEmpty):
text_block += ""
else:
text_block += str(block.text)
return text_block
def remove_attach(filename):
head, tail = os.path.split(filename)
bot_attach = bot_home+'/.simplebot/accounts/'+encode_bot_addr+'/account.db-blobs/'+str(tail)
if os.path.exists(bot_attach):
print("Eliminando adjunto "+filename)
os.remove(bot_attach)
class AccountPlugin:
@account_hookimpl
def ac_chat_modified(self, chat):
print('Chat modificado/creado: '+chat.get_name())
if chat.is_multiuser():
save_bot_db()
"""
@account_hookimpl
def ac_process_ffi_event(self, ffi_event):
if ffi_event.name=="DC_EVENT_WEBXDC_STATUS_UPDATE":
print(ffi_event)
if ffi_event.name == "DC_EVENT_WARNING":
#print('Evento warning detectado!', ffi_event)
if ffi_event.data2 and ffi_event.data2.find("Daily user sending quota exceeded")>=0:
print('Limite diario de mensajes alcanzado!')
if ffi_event.name == "DC_EVENT_MSG_READ":
msg = str(ffi_event.data1)+':'+str(ffi_event.data2)
print(msg)
if msg in unreaddb:
async_read_unread(unreaddb[msg][0], unreaddb[msg][1], unreaddb[msg][2])
del unreaddb[msg]
"""
@simplebot.hookimpl(tryfirst=True)
def deltabot_incoming_message(message, replies) -> Optional[bool]:
"""Check that the sender is not in the black or white list."""
sender_addr = message.get_sender_contact().addr
if white_list and sender_addr!=admin_addr and sender_addr not in white_list:
if message.text.lower().startswith('/pdown') or message.text.lower().startswith('/alias'):
return None
print('Usuario '+str(sender_addr)+' no esta en la lista blanca')
return True
if black_list and sender_addr!=admin_addr and sender_addr in black_list:
print('Usuario '+str(sender_addr)+' esta en la lista negra')
return True
#print(message)
"""
if message.chat.is_multiuser():
if get_tg_id(message.chat, bot):
contactos = message.chat.get_contacts()
if len(contactos)>2:
if contactos.index(message.get_sender_contact())>1:
print('Mensaje de en un usuario no propietario del grupo')
return True
else:
print('Bot en un grupo que no es de telegram!')
return True
"""
return None
"""
@simplebot.hookimpl
def deltabot_member_added(chat, contact, actor, message, replies, bot) -> None:
if actor:
print('Miembro '+str(contact.addr)+' agregado por '+str(actor.addr)+' chat: '+str(chat.get_name()))
else:
print('My self!')
"""
@simplebot.hookimpl
def deltabot_init(bot: DeltaBot) -> None:
#bot.account.add_account_plugin(AccountPlugin())
bot.account.set_config("displayname","Telegram Bridge")
bot.account.set_avatar("telegram.jpeg")
#bot.account.set_config("delete_device_after","21600")
global MAX_MSG_LOAD
global MAX_MSG_LOAD_AUTO
global MAX_AUTO_CHATS
global MAX_SIZE_DOWN
global MIN_SIZE_DOWN
global CAN_IMP
global SYNC_ENABLED
global UPDATE_DELAY
global white_list
global black_list
MAX_MSG_LOAD = bot.get('MAX_MSG_LOAD') or 5
MAX_MSG_LOAD = int(MAX_MSG_LOAD)
MAX_MSG_LOAD_AUTO = bot.get('MAX_MSG_LOAD_AUTO') or 5
MAX_MSG_LOAD_AUTO = int(MAX_MSG_LOAD_AUTO)
MAX_AUTO_CHATS = bot.get('MAX_AUTO_CHATS') or 10
MAX_AUTO_CHATS = int(MAX_AUTO_CHATS)
MAX_SIZE_DOWN = bot.get('MAX_SIZE_DOWN') or 20485760
MAX_SIZE_DOWN = int(MAX_SIZE_DOWN)
MIN_SIZE_DOWN = bot.get('MIN_SIZE_DOWN') or 655360
MIN_SIZE_DOWN = int(MIN_SIZE_DOWN)
CAN_IMP = bot.get('CAN_IMP') or 0
CAN_IMP = int(CAN_IMP)
UPDATE_DELAY = bot.get('UPDATE_DELAY') or 16
UPDATE_DELAY = int(UPDATE_DELAY)
SYNC_ENABLED = bot.get('SYNC_ENABLED') or 0
SYNC_ENABLED = int(SYNC_ENABLED)
if SYNC_ENABLED:
bot.account.set_config("mdns_enabled","1")
#use env to add to the lists like "user1@domine.com user2@domine.com" with out ""
white_list = os.getenv('WHITE_LIST') or bot.get('WHITE_LIST')
black_list = os.getenv('BLACK_LIST') or bot.get('BLACK_LIST')
if white_list:
white_list = white_list.split()
if black_list:
black_list = black_list.split()
bot.commands.register(name = "/eval" ,func = eval_func, admin = True)
bot.commands.register(name = "/start" ,func = start_updater, admin = True)
bot.commands.register(name = "/stop" ,func = stop_updater, admin = True)
bot.commands.register(name = "/more" ,func = async_load_chat_messages)
bot.commands.register(name = "/load" ,func = async_updater)
bot.commands.register(name = "/exec" ,func = async_run, admin = True)
bot.commands.register(name = "/login" ,func = async_login_num)
bot.commands.register(name = "/sms" ,func = async_login_code)
bot.commands.register(name = "/pass" ,func = async_login_2fa)
bot.commands.register(name = "/token" ,func = async_login_session)
bot.commands.register(name = "/logout" ,func = logout_tg)
bot.commands.register(name = "/remove" ,func = remove_chat)
bot.commands.register(name = "/down" ,func = async_down_chat_messages)
bot.commands.register(name = "/pdown" ,func = async_private_down_chat_messages)
bot.commands.register(name = "/comment" ,func = async_comment_chat_messages)
bot.commands.register(name = "/c" ,func = async_click_button)
bot.commands.register(name = "/b" ,func = async_send_cmd)
bot.commands.register(name = "/search" ,func = async_search_chats)
bot.commands.register(name = "/join" ,func = async_join_chats)
bot.commands.register(name = "/preview" ,func = async_preview_chats)
bot.commands.register(name = "/auto" ,func = async_add_auto_chats)
bot.commands.register(name = "/inline" ,func = async_inline_cmd)
bot.commands.register(name = "/inmore" ,func = async_inmore_cmd)
bot.commands.register(name = "/inclick" ,func = async_inclick_cmd)
bot.commands.register(name = "/indown" ,func = async_indown_cmd)
bot.commands.register(name = "/list" ,func = list_chats)
bot.commands.register(name = "/forward" ,func = async_forward_message)
bot.commands.register(name = "/pin" ,func = async_pin_messages)
bot.commands.register(name = "/news" ,func = async_chat_news)
bot.commands.register(name = "/info" ,func = async_chat_info)
bot.commands.register(name = "/setting" ,func = bot_settings, admin = True)
bot.commands.register(name = "/react" ,func = async_react_button)
bot.commands.register(name = "/link2" ,func = link_to)
bot.commands.register(name = "/dbxtoken" ,func = get_dbxtoken, admin = True)
bot.commands.register(name = "/chat" ,func = create_comment_chat)
bot.commands.register(name = "/alias" ,func = create_alias)
@simplebot.hookimpl
def deltabot_start(bot: DeltaBot) -> None:
bridge_init = Event()
Thread(
target=start_background_loop,
args=(bridge_init,),
daemon=True,
).start()
bridge_init.wait()
global auto_load_task
auto_load_task = asyncio.run_coroutine_threadsafe(auto_load(bot=bot, message = Message, replies = Replies),tloop)
global bot_addr
bot_addr = bot.account.get_config('addr')
global encode_bot_addr
encode_bot_addr = urllib.parse.quote(bot_addr, safe='')
global logindb
logindb = json.loads(bot.get('LOGINDB') or '{}')
global autochatsdb
autochatsdb = json.loads(bot.get('AUTOCHATSDB') or '{}')
global aliasdb
aliasdb = json.loads(bot.get('ALIASDB') or '{}')
#fixautochats(bot)
for (key,_) in logindb.items():
loop.run_until_complete(load_delta_chats(contacto=key))
time.sleep(5)
if admin_addr:
bot.get_chat(admin_addr).send_text('El bot '+bot_addr+' se ha iniciado correctamente')
def create_alias(bot, replies, message, payload):
"""Configure your alias for anonimous Super Groups,
send /alias first in private"""
global prealiasdb
global aliasdb
parametros = payload.split()
addr = message.get_sender_contact().addr
if len(parametros)==0 and not message.chat.is_multiuser():
calias = ''.join(random.choice(string.ascii_lowercase) for i in range(6))
prealiasdb[calias] = addr
replies.add('Envie /alias_'+calias+' en el super grupo anónimo donde esté el bot (mantenga presionado /alias... para copiarlo)')
if len(parametros)==1:
if parametros[0] in prealiasdb:
aliasdb[addr]=prealiasdb[parametros[0]]
savealias(bot)
bot.get_chat(prealiasdb[parametros[0]]).send_text('Alias '+addr+' confirmado para '+prealiasdb[parametros[0]])
del prealiasdb[parametros[0]]
def get_dbxtoken(bot, replies, message, payload):
"Get fresh Dropbox Token (DBXTOKEN)"
global authorize_url
if APP_KEY:
try:
authorize_url = auth_flow.start()
replies.add(text='Por favor acceda a la siguiente URI, presione [Allow o Permitir], copie el codigo de autorizacion y envielo aqui:\n\n'+str(authorize_url))
except:
code = str(sys.exc_info())
replies.add(text=code)
else:
replies.add(text='Debe colocar su APP_KEY de Dropbox en las variables de entorno, para poder generar nuevos codigos de acceso.')
def parse_entiti(r_text, s_text,offset,tlen):
if r_text == '▚':
h_text = helpers.add_surrogate(r_text*tlen)
else:
spaces = " "*tlen
h_text = helpers.add_surrogate(r_text+spaces)
mystring = h_text.join([s_text[:offset],s_text[offset+tlen:]])
return mystring
def broadcast_message(bot, msg):
for (user,_) in logindb.items():
try:
bot.get_chat(user).send_text(msg)
except:
print('Error sending broadcast to '+user)
def register_msg(contacto, dc_id, dc_msg, tg_msg):
global messagedb
#{contac_addr:{dc_id:{dc_msg:tg_msg}}}
if contacto not in messagedb:
messagedb[contacto] = {}
if dc_id not in messagedb[contacto]:
messagedb[contacto][dc_id] = {}
messagedb[contacto][dc_id][dc_msg] = tg_msg
def register_last_msg(contacto, dc_id, dc_msg, tg_msg):
global last_messagedb
#{contac_addr:{dc_id:{dc_msg:tg_msg}}}
if contacto not in last_messagedb:
last_messagedb[contacto] = {}
if dc_id not in last_messagedb[contacto]:
last_messagedb[contacto][dc_id] = {}
else:
last_messagedb[contacto][dc_id].clear()
last_messagedb[contacto][dc_id][dc_msg] = tg_msg
def is_register_msg(contacto, dc_id, dc_msg):
if contacto in messagedb:
if dc_id in messagedb[contacto]:
if dc_msg in messagedb[contacto][dc_id]:
t_reply = messagedb[contacto][dc_id][dc_msg]
return t_reply
else:
return
else:
return
else:
return
def find_register_msg(contacto, dc_id, tg_msg):
if contacto in messagedb:
if dc_id in messagedb[contacto]:
if tg_msg in messagedb[contacto][dc_id].values():
for (key, value) in messagedb[contacto][dc_id].items():
if value == tg_msg:
d_reply = key
return d_reply
else:
return
else:
return
else:
return
def last_register_msg(contacto, dc_id):
if contacto in last_messagedb:
if dc_id in last_messagedb[contacto]:
for (_, value) in last_messagedb[contacto][dc_id].items():
last_id = value
return last_id
else:
return
else:
return
def get_tg_id(chat, bot):
f_id = bot.get(str(chat.id))
tg_ids = []
if f_id:
tg_ids = [f_id]
elif not DBXTOKEN and not DATABASE_URL and not TGTOKEN:
dchat = chat.get_name()
tg_ids = re.findall(r"\[([\-A-Za-z0-9_]+)\]", dchat)
if len(tg_ids)>0:
bot.set(str(chat.id),tg_ids[-1])
if len(tg_ids)>0:
if tg_ids[-1].lstrip('-').isnumeric():
f_id = int(tg_ids[-1])
else:
f_id = tg_ids[-1]
return f_id
else:
return None
def get_tg_reply(chat, bot):
f_id = bot.get("rp2_"+str(chat.id))
tg_ids = []
if f_id and (DBXTOKEN or DATABASE_URL or TGTOKEN):
tg_ids = [f_id]
elif not DBXTOKEN and not DATABASE_URL and not TGTOKEN:
dchat = chat.get_name()
tg_ids = re.findall(r"\(([0-9]+)\)", dchat)
if len(tg_ids)>0:
bot.set("rp2_"+str(chat.id),tg_ids[-1])
if len(tg_ids)>0:
if tg_ids[-1].lstrip('-').isnumeric():
f_id = int(tg_ids[-1])
else:
f_id = tg_ids[-1]
return f_id
else:
return None
def print_dep_message(loader):
if not loader.failed_modules:
return
sys.stderr.write("Make sure you have the correct dependencies installed\n")
for failed, dep in loader.failed_modules.items():
sys.stderr.write("For %s install %s\n" % (failed, dep))
async def convertsticker(infilepath,outfilepath):
importer = None
suf = os.path.splitext(infilepath)[1][1:]
print(suf)
for p in importers:
if suf in p.extensions:
importer = p
break
exporter = exporters.get(os.path.splitext(outfilepath)[1][1:])
if not exporter:
print_dep_message(exporters)
an = importer.process(infilepath)
an.scale(128,128)
exporter.process(an, outfilepath, lossless=False, method=3, quality=50, skip_frames=10)
async def read_unread(contacto,target,tg_id):
try:
client = TC(StringSession(logindb[contacto]), api_id, api_hash)
await client.connect()
await client.get_dialogs()
mensajes = await client.get_messages(target, ids=[int(tg_id)])
if len(mensajes)>0 and mensajes[0]:
await mensajes[0].mark_read()
await client.disconnect()
except:
code = str(sys.exc_info())
print('Error marcando mensaje como leido\n'+code)
def async_read_unread(contacto, target, tg_id):
loop.run_until_complete(read_unread(contacto, target, tg_id))
def safe_html(hcode):
scode = html.escape(hcode)
return scode
async def chat_news(bot, payload, replies, message):
addr = message.get_sender_contact().addr
if addr not in logindb:
replies.add(text = 'Debe iniciar sesión para ver sus chats!')
return
if addr not in chatdb:
chatdb[addr] = {}
try:
if not os.path.exists(addr):
os.mkdir(addr)
client = TC(StringSession(logindb[addr]), api_id, api_hash)
await client.connect()
me = await client.get_me()
my_id = me.id
all_chats = await client.get_dialogs(ignore_migrated = True)
chat_list = ""
chat_count = 0
is_full = (payload.lower().find('full')>=0)
is_img = (payload.lower().find('img')>=0)
is_private = (payload.lower().find('private')>=0)
global bot_addr
for d in all_chats:
if d.unread_count>0:
no_leidos = str(d.unread_count)
no_leidos = '<a style="color:white;background:red;border-radius:15px;padding-left:3px;padding-top:3px;padding-right:3px;padding-bottom:3px">'+no_leidos+'</a>'
else:
no_leidos = ''
if not is_full:
continue
if hasattr(d.entity,'username') and d.entity.username:
uname = safe_html(str(d.entity.username))
else:
uname = 'None'
if is_private:
tchat = await client(functions.messages.GetPeerDialogsRequest(peers=[d.id]))
if hasattr(tchat,'chats') and tchat.chats:
continue
ttitle = "Unknown"
last_message = ""
send_by = ""
profile_photo = ""
if hasattr(d,'title'):
ttitle = d.title
tid = str(d.id)
if True:
titulo = safe_html(str(ttitle))
if my_id == d.id:
titulo = 'Mensajes guardados'
if len(titulo)<1:
titulo = '?'
profile_letter = '<div style="font-size:50px;color:white;background:#7777ff;border-radius:25px;width:50px;height:50px"><center>'+str(titulo[0])+'</center></div>'
if str(d.id) in chatdb[addr]:
comando = '<br><a href="mailto:'+bot_addr+'?body=/remove_'+str(d.id)+'">❌ Desvilcular</a> <a href="mailto:?body=/link2_'+str(d.id)+'">↔️ Vincular con...</a>'
else:
comando = '<br><a href="mailto:'+bot_addr+'?body=/load_'+str(d.id)+'">✅ Cargar</a> <a href="mailto:?body=/link2_'+str(d.id)+'">↔️ Vincular con...</a>'
try:
if is_img:
profile_photo = '<img src="data:image/jpeg;base64,{}" alt="{}" style="width:50px;height:50px;border-radius:25px"/>'.format(base64.b64encode(await client.download_profile_photo(d.id,bytes,download_big=False)).decode(), str(titulo[0]))
else:
profile_photo = profile_letter
except:
profile_photo = profile_letter
if hasattr(d,'message') and d.message:
if hasattr(d.message,'from_id') and d.message.from_id:
if hasattr(d.message.from_id,'user_id') and d.message.from_id.user_id:
try:
full_pchat = await client(functions.users.GetFullUserRequest(id = d.message.from_id.user_id))
if hasattr(full_pchat,'users') and full_pchat.users:
send_by = '<br><b>'+safe_html(str(full_pchat.users[0].first_name))+'</b>'
except:
print('Error obteniendo entidad '+str(d.message.from_id.user_id))
try:
pchat = await client.get_entity(d.message.from_id.user_id)
if hasattr(pchat, 'first_name') and pchat.first_name:
send_by = '<br><b>'+safe_html(str(pchat.first_name))+'</b>'
except:
continue
if hasattr(d.message,'message') and d.message.message:
#last_message += send_by
last_message += d.message.message.replace('\n',' ')
if len(last_message)>50:
last_message = last_message[0:50]+'...'
else:
last_message = last_message
else:
last_message = '[imagen/archivo]'
chat_count +=1
chat_list += '<table><tr><td>'+str(profile_photo)+'</td><td><b>'+str(titulo)+'</b> '+str(no_leidos)+str(send_by)+'<br>'+str(safe_html(last_message))+str(comando)+'</td></tr></table><hr>'
#bubbles
#chat_list += '<br><div style="border-radius:3px;color:white;background:#7777ff">'+profile_photo+'<b>'+titulo+'</b> <a style="color:white;background:red;border-radius:15px">'+no_leidos+'</a>'+send_by+'<br>'+last_message+comando+'</div>'
#img = await client.download_profile_photo(d.entity, message.get_sender_contact().addr)
chat_list += "</body></html>"
await client.disconnect()
replies.add(text=str(chat_count)+' chats',html=chat_list)
except Exception as e:
estr = str('Error on line {}'.format(sys.exc_info()[-1].tb_lineno)+'\n'+str(type(e).__name__)+'\n'+str(e))
print(estr)
replies.add(text=estr)
def async_chat_news(bot, payload, replies, message):
"""See a list of all your chats status/unread from telegram. Example: /news
you can pass the parameter private to see only the private chat like: /news private
you can pass the parameter full to see a full chat list like: /news full
pass the img parameter to see the chats profile photos like: /news img"""
loop.run_until_complete(chat_news(bot, payload, replies, message))
def link_to(bot, payload, replies, message):
"""Link chat with a Telegram chat"""
if message.get_sender_contact().addr not in logindb:
replies.add(text = 'Debe iniciar sesión para vincular chats!')
return
if not bot.is_admin(message.get_sender_contact()) and (len(message.chat.get_contacts())>2 or message.chat.is_mailinglist()):
replies.add(text = 'Debe ser administrador para vincular un chat de mas de 2 miembros o un super grupo!')
return
if payload:
tchat = payload.replace('@','')
tchat = tchat.replace(' ','_')
bot.set(str(message.chat.id), tchat)
replies.add(text='Se ha asociado el chat de Telegram '+payload+' con este chat')
else:
bot.set(str(message.chat.id),None)
bot.set("rp2_"+str(message.chat.id),None)
replies.add(text='Este chat se desvinculó de Telegram!')
save_bot_db()
async def chat_info(bot, payload, replies, message):
f_id = get_tg_id(message.chat, bot)
c_id = get_tg_reply(message.chat, bot)
addr = message.get_sender_contact().addr
if not f_id:
replies.add(text = 'Este no es un chat de telegram!')
return
if addr not in logindb:
replies.add(text = 'Debe iniciar sesión para ver información del chat!')
return
if addr not in chatdb:
chatdb[addr] = {}
try:
if not os.path.exists(addr):
os.mkdir(addr)
if f_id:
#TODO show more chat information
myreplies = Replies(bot, logger=bot.logger)
client = TC(StringSession(logindb[addr]), api_id, api_hash)
await client.connect()
await client.get_dialogs()
tinfo =""
img = None
if message.quote:
t_reply = is_register_msg(addr, message.chat.id, message.quote.id)
if not t_reply:
replies.add(text='No se encontró la referencia de este mensaje con el de Telegram', quote=message)
else:
if c_id:
mensaje = []
async for m in client.iter_messages(f_id, reply_to = c_id):
if m.id == t_reply:
mensaje.append(m)
break
else:
mensaje = await client.get_messages(f_id, ids=[t_reply])
if mensaje and mensaje[0]:
if mensaje[0].from_id:
if isinstance(mensaje[0].from_id, types.PeerUser):
full = await client(GetFullUserRequest(mensaje[0].from_id))
tinfo += "Por usuario:"
if full.users[0].username:
tinfo += "\n@👤: @"+str(full.users[0].username)
if full.users[0].first_name:
tinfo += "\nNombre: "+str(full.users[0].first_name)
if full.users[0].last_name:
tinfo += "\nApellidos: "+str(full.users[0].last_name)
tinfo += "\n🆔️: "+str(mensaje[0].from_id.user_id)
img = await client.download_profile_photo(mensaje[0].from_id.user_id)
elif isinstance(mensaje[0].from_id, types.PeerChannel):
full = await client(functions.channels.GetFullChannelRequest(channel = mensaje[0].from_id))
tinfo += "Por grupo/canal:"
if hasattr(full,'post_author') and full.post_author:
tinfo += "\nAutor: "+full.post_author
img = await client.download_profile_photo(mensaje[0].from_id.channel_id)
elif isinstance(mensaje[0].from_id, types.PeerChat):
full = await client(functions.messages.GetFullChatRequest(chat_id = mensaje[0].from_id))
tinfo += "Por chat:"
if hasattr(full,'about') and full.about:
tinfo += "\nBiografia: "+str(full.about)
tinfo += "\n\nMensaje:"
tinfo += "\nTelegram mensaje id: "+str(t_reply)
tinfo += "\nDeltaChat mensaje id: "+str(message.quote.id)
tinfo += "\nFecha de envio (UTC): "+str(mensaje[0].date)
myreplies.add(text=tinfo, html=mensaje[0].stringify(), filename=img, quote=message, chat=message.chat)
myreplies.send_reply_messages()
if img and img!='' and os.path.exists(img):
os.remove(img)
remove_attach(img)
else:
replies.add(text="El mensaje fue eliminado?")
await client.disconnect()
return
pchat = await client.get_input_entity(f_id)
if isinstance(pchat, types.InputPeerChannel):
full_pchat = await client(functions.channels.GetFullChannelRequest(channel = pchat))
if hasattr(full_pchat,'chats') and full_pchat.chats and len(full_pchat.chats)>0:
tinfo += "\nTitulo: "+full_pchat.chats[0].title
if hasattr(full_pchat.full_chat,'participants_count') and full_pchat.full_chat.participants_count:
tinfo += "\nParticipantes: "+str(full_pchat.full_chat.participants_count)
elif isinstance(pchat, types.InputPeerUser) or isinstance(pchat, types.InputPeerSelf):
full_pchat = await client(functions.users.GetFullUserRequest(id = pchat))
if hasattr(full_pchat,'users') and full_pchat.users:
tinfo += "\nNombre: "+full_pchat.users[0].first_name
if full_pchat.users[0].last_name:
tinfo += "\nApellidos: "+full_pchat.users[0].last_name
if hasattr(full_pchat.users[0],"username") and full_pchat.users[0].username:
tinfo+="\n@: "+full_pchat.users[0].username
elif isinstance(pchat, types.InputPeerChat):
print('Hemos encontrado un InputPeerChat: '+str(f_id))
full_pchat = await client(functions.messages.GetFullChatRequest(chat_id=pchat.id))
if hasattr(full_pchat,'chats') and full_pchat.chats and len(full_pchat.chats)>0:
tinfo = full_pchat.chats[0].title
if hasattr(full_pchat,'user') and full_pchat.user:
tinfo = full_pchat.user.first_name
try:
img = await client.download_profile_photo(f_id, addr)
except:
img = None
print("Error descargando foto de perfil de "+str(f_id))
await client.disconnect()
myreplies.add(text=tinfo, html = "<code>"+str(full_pchat)+"</code>", filename = img, quote=message, chat=message.chat)
myreplies.send_reply_messages()
if img and img!='' and os.path.exists(img):
os.remove(img)
remove_attach(img)
except Exception as e:
estr = str('Error on line {}'.format(sys.exc_info()[-1].tb_lineno)+'\n'+str(type(e).__name__)+'\n'+str(e))
print(estr)
replies.add(text=estr)
def async_chat_info(bot, payload, replies, message):
"""Show message information from telegram. Example: reply a message with /info"""
loop.run_until_complete(chat_info(bot, payload, replies, message))
async def pin_messages(bot, message, replies):
if not message.quote:
replies.add(text = "Debe responder a un mensaje para fijarlo")
return
f_id = get_tg_id(message.chat, bot)
if not f_id:
replies.add(text = "Este no es un chat de telegram!")
return
addr = message.get_sender_contact().addr
try:
client = TC(StringSession(logindb[addr]), api_id, api_hash)
await client.connect()
t_reply = is_register_msg(addr, message.chat.id, message.quote.id)
if t_reply:
await client.pin_message(f_id, t_reply)
replies.add(text = 'Mensaje fijado!')
else:
replies.add(text = 'No se puede fijar el mensaje porque no esta asociado a un mensaje de Telegram!')
await client.disconnect()
except:
code = str(sys.exc_info())
print(code)
if replies:
replies.add(text=code)
def async_pin_messages(bot, message, replies):
"""Pin message in chats with right permission repling it, example:
/pin
"""
loop.run_until_complete(pin_messages(bot, message, replies))
async def forward_message(bot, message, replies, payload):
addr = message.get_sender_contact().addr
if addr not in logindb:
replies.add(text = 'Debe iniciar sesión para reenviar mensajes!')
return
f_id = get_tg_id(message.chat, bot)
if not f_id:
replies.add(text = 'Este no es un chat de telegram!')
return
parametros = payload.split()
m_id = None
d_id = None
if len(parametros)>1:
if parametros[0].isnumeric():
m_id = int(parametros[0])
s = payload.replace(parametros[0]+' ','',1)
s = s.replace(' ','_')
if s.isnumeric():
d_id = int(s)
else: