#!/usr/bin/env python3 from threading import Thread from cmd import Cmd from queue import Queue from ctypes import * import json import sys import os import time import array # Telegram API ID and API Hash. iApiID = 0 sApiHash = '' # Used to kill all the loops bMasterSwitch = True # Queue Client >>> Server qInputQueue = Queue() # Queue Server >>> Client qOutputQueue = Queue() # State of the authentification bConStatus = False # Personnal Informations Object objMe = None # List of known Users objUsers = [] # List of Known ChatRoom objChatRooms = [] # Index of the active Chatroom iActiveRoom = -1 # td Lib Json 1.6.6 tdjson = CDLL("./libtdjson.so") td_json_client_create = tdjson.td_json_client_create td_json_client_create.restype = c_void_p td_json_client_create.argtypes = [] td_json_client_receive = tdjson.td_json_client_receive td_json_client_receive.restype = c_char_p td_json_client_receive.argtypes = [c_void_p, c_double] td_json_client_send = tdjson.td_json_client_send td_json_client_send.restype = None td_json_client_send.argtypes = [c_void_p, c_char_p] td_json_client_execute = tdjson.td_json_client_execute td_json_client_execute.restype = c_char_p td_json_client_execute.argtypes = [c_void_p, c_char_p] td_json_client_destroy = tdjson.td_json_client_destroy td_json_client_destroy.restype = None td_json_client_destroy.argtypes = [c_void_p] fatal_error_callback_type = CFUNCTYPE(None, c_char_p) td_set_log_fatal_error_callback = tdjson.td_set_log_fatal_error_callback td_set_log_fatal_error_callback.restype = None td_set_log_fatal_error_callback.argtypes = [fatal_error_callback_type] def on_fatal_error_callback(error_message): print('TDLib fatal error: ', error_message) def td_execute(query): query = json.dumps(query).encode('utf-8') result = td_json_client_execute(None, query) if result: result = json.loads(result.decode('utf-8')) return result c_on_fatal_error_callback = fatal_error_callback_type(on_fatal_error_callback) td_set_log_fatal_error_callback(c_on_fatal_error_callback) # setting TDLib log verbosity level to 1 (errors) td_execute({'@type': 'setLogVerbosityLevel', 'new_verbosity_level': 1, '@extra': 1.01234}) client = td_json_client_create() # simple wrappers for client usage def td_send(query): query = json.dumps(query).encode('utf-8') td_json_client_send(client, query) def td_receive(): result = td_json_client_receive(client, 1.0) if result: result = json.loads(result.decode('utf-8')) return result # another test for TDLib execute method td_execute({'@type': 'getTextEntities', 'text': '@telegram /test_command https://telegram.org telegram.me', '@extra': ['5', 7.0]}) # testing TDLib send method td_send({'@type': 'getAuthorizationState', '@extra': 1.01234}) # Personnal informations object class objPersonnal: def __init__(self, ftid, first_name, last_name, username, phone_number): self.id = ftid self.first_name = first_name self.last_name = last_name self.username = username self.phone_number = phone_number def toJSON(self): return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) # Know Users object class createUser: def __init__(self, ftid, first_name, last_name, username, phone_number): self.id = ftid self.first_name = first_name self.last_name = last_name self.username = username self.phone_number = phone_number def toJSON(self): return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) # Known Chat room Object class createChatRoom: def __init__(self, ftid, sType, iUserId, sTitle): self.id = ftid self.sType = sType self.iUserId = iUserId self.sTitle = sTitle self.rMessage = [] # Update user if present. Otherwise create it def updateUsers(ftid, first_name, last_name, username, phone_number): i = 0; bFound = False while i < len(objUsers): if (objUsers[i].id == ftid): bFound = True objUsers[i].first_name = first_name objUsers[i].last_name = last_name objUsers[i].username = username objUsers[i].phone_number = phone_number i = i + 1 if bFound is False: tmpUser = createUser(ftid, first_name, last_name, username, phone_number) objUsers.append(tmpUser) # Update Chatroom if present. Otherwise create it def updateChatRooms(ftid, sType, iUserId, sTitle): i = 0; bFound = False while i < len(objChatRooms): if (objChatRooms[i].id == ftid): bFound = True objChatRooms[i].sType = sType objChatRooms[i].iUserId = iUserId objChatRooms[i].sTitle = sTitle i = i + 1 if bFound is False: if not os.path.exists('./logs/' + str(ftid) + '.txt'): with open('./logs/' + str(ftid) + '.txt', 'w'): pass tmpChatRoom = createChatRoom(ftid, sType, iUserId, sTitle) objChatRooms.append(tmpChatRoom) # Object for recieved Message class createMessages: def __init__(self, message_id, sender_user_id, messageText): self.message_id = message_id self.sender_user_id = sender_user_id self.messageText = messageText # Return Index of chat room (objChatRooms) from ID def getChatIndexById(chat_id): i = 0 while (i < len(objChatRooms)): if objChatRooms[i].id == chat_id: return i i = i + 1 return -1 # Add a new recieved message def addNewMessage(message_id, chat_id, sender_user_id, messageText): global iActiveRoom iIndex = getChatIndexById(chat_id) if (iIndex >= 0): tmpMsg = createMessages(message_id, sender_user_id, messageText) objChatRooms[iIndex].rMessage.append(tmpMsg) f = open("./logs/" + str(chat_id) +".txt", "a+") f.write(formatMessage(tmpMsg) + '\n') f.close() if iActiveRoom == iIndex: print(formatMessage(tmpMsg)) else: print("No chat found for that message") # Format a recieved message def formatMessage(objMessage): i = 0 sUserCaption = "Anomimous" while i < len(objUsers): if objUsers[i].id == objMessage.sender_user_id: sUserCaption = objUsers[i].first_name + " " + objUsers[i].last_name + " [" + objUsers[i].username + "]" i = i + 1 sReturn = sUserCaption + ": " + objMessage.messageText return sReturn # The client; Handle servers prompts and it's message. class ftClient(Thread): def __init__(self): Thread.__init__(self) self.daemon = True self.start() def run(self): global bMasterSwitch global qInputQueue global qOutputQueue global objMe global objUsers; while bMasterSwitch: #print('Waiting instruction...') query = qOutputQueue.get() sResponse = None if query == "askPhoneNumber": sResponse = input('askPhoneNumber: ') elif query == "askAuthCode": sResponse = input('askAuthCode: ') elif query == "askFistName": sResponse = input('askFistName: ') elif query == "askLastName": sResponse = input('askLastName: ') elif query == "askPassword": sResponse = input('askPassword: ') elif query['@type'] is not None: if query['@type'] == 'updateUser': updateUsers(query['user']['id'], query['user']['first_name'], query['user']['last_name'], query['user']['username'], query['user']['phone_number']) elif query['@type'] == 'updateNewChat': tmpId = 0 if query['chat']['type']['@type'] == "chatTypePrivate": tmpId = query['chat']['type']['user_id'] if query['chat']['type']['@type'] == "chatTypeSupergroup": tmpId = query['chat']['type']['supergroup_id'] #print(query['chat']['title']) updateChatRooms(query['chat']['id'],query['chat']['type']['@type'], tmpId,query['chat']['title']) elif query['@type'] == 'updateNewMessage': #print(query) tmpContent = "Unsupported" if query['message']['content']['@type'] == "messageText": tmpContent = query['message']['content']['text']['text'] #print(tmpContent) addNewMessage(query['message']['id'], query['message']['chat_id'], query['message']['sender_user_id'], tmpContent) elif query['@type'] == 'updateChatPosition': # == dummy = True elif query['@type'] == 'updateChatLastMessage': # == dummy = True elif query['@type'] == 'updateChatReadOutbox': dummy = True elif query['@type'] == 'updateUnreadMessageCount': dummy = True elif query['@type'] == 'updateUserStatus': dummy = True elif query['@type'] == 'updateChatReadInbox': dummy = True elif query['@type'] == 'updateMessageContent': dummy = True elif query['@type'] == 'updateUserChatAction': dummy = True elif query['@type'] == 'updateUnreadChatCount': dummy = True elif query['@type'] == 'updateDeleteMessages': dummy = True elif query['@type'] == 'updateHavePendingNotifications': dummy = True elif query['@type'] == 'updateSupergroupFullInfo': dummy = True elif query['@type'] == 'message': dummy = True elif query['@type'] == 'updateMessageSendSucceeded': dummy = True elif query['@type'] == 'updateSupergroup': dummy = True elif query['@type'] == 'chats': dummy = True elif query['@type'] == 'updateUserFullInfo': dummy = True elif query['@type'] == 'user': dummy = True #else: #print(query) #else: #print(query) if sResponse is not None: qInputQueue.put(sResponse) # The Server class ftServer(Thread): def __init__(self): Thread.__init__(self) self.daemon = True self.start() def run(self): global bMasterSwitch global qInputQueue global qOutputQueue global bConStatus global objMe global iApiID global sApiHash while bMasterSwitch: event = td_receive() if event: if event['@type'] == 'updateAuthorizationState': auth_state = event['authorization_state'] #if auth_state['@type'] == 'authorizationStateClosed': #break if auth_state['@type'] == 'authorizationStateWaitTdlibParameters': print("authorizationStateWaitTdlibParameters") td_send({'@type': 'setTdlibParameters', 'parameters': { 'database_directory': 'tdlib', 'use_message_database': True, 'use_secret_chats': True, 'api_id': iApiID, 'api_hash': sApiHash, 'system_language_code': 'en', 'device_model': 'Desktop', 'system_version': 'Linux', 'application_version': '1.0', 'enable_storage_optimizer': True}}) if auth_state['@type'] == 'authorizationStateWaitEncryptionKey': print("authorizationStateWaitEncryptionKey") td_send({'@type': 'checkDatabaseEncryptionKey', 'encryption_key': ''}) if auth_state['@type'] == 'authorizationStateWaitPhoneNumber': print("authorizationStateWaitPhoneNumber") qOutputQueue.put("askPhoneNumber") print("authorizationStateWaitPhoneNumber: Waiting input") phone_number = qInputQueue.get() #phone_number = input('Please enter your phone number: ') td_send({'@type': 'setAuthenticationPhoneNumber', 'phone_number': phone_number}) if auth_state['@type'] == 'authorizationStateWaitCode': print("authorizationStateWaitCode") #code = input('Please enter the authentication code you received: ') qOutputQueue.put("askAuthCode") code = qInputQueue.get() td_send({'@type': 'checkAuthenticationCode', 'code': code}) if auth_state['@type'] == 'authorizationStateWaitRegistration': print("authorizationStateWaitRegistration") #first_name = input('Please enter your first name: ') qOutputQueue.put("askFistName") first_name = qInputQueue.get() #last_name = input('Please enter your last name: ') qOutputQueue.put("askLastName") last_name = qInputQueue.get() td_send({'@type': 'registerUser', 'first_name': first_name, 'last_name': last_name}) if auth_state['@type'] == 'authorizationStateWaitPassword': print("authorizationStateWaitPassword") #password = input('Please enter your password: ') qOutputQueue.put("askPassword") password = qInputQueue.get() td_send({'@type': 'checkAuthenticationPassword', 'password': password}) if auth_state['@type'] == 'authorizationStateReady': bConStatus = True else: if (event['@type'] == 'user' and objMe is None): objMe = objPersonnal(event['id'], event['first_name'], event['last_name'], event['username'], event['phone_number']) #if event['@type'] == 'updateConnectionState': #if event['state']['@type'] == 'connectionStateReady': #bConStatus = True qOutputQueue.put(event) sys.stdout.flush() # The interactive console class ftConsole(Cmd): def do_exit(self, inp): '''exit the application without loging out.''' global bMasterSwitch bMasterSwitch = False exit() def do_updateChatsList(self, inp): '''Actualise Chats List.''' td_send({'@type': 'getChats', 'limit': 2}) def do_updateMe(self, inp): '''Update Personnals informartions.''' td_send({'@type': 'getMe'}) def do_listChatRoom(self, inp): '''List Available chat room(s)''' print("Chat Room(s)\nTo join a room, use `join ` + The Room index ([x]).") global objChatRooms i = 0 while (i < len(objChatRooms)): #print("[" + str(i) + "]" + str(objChatRooms[i].id) + " " + objChatRooms[i].sType + "[" + str(objChatRooms[i].iUserId) + "]" + objChatRooms[i].sTitle ) print("[" + str(i) + "] " + objChatRooms[i].sTitle ) i = i + 1; def do_getKnowUsers(self, inp): '''List Known users of this instance.''' global objUsers i = 0 print(len(objUsers)); while (i < len(objUsers)): print("[" + str(i) + "]" + objUsers[i].first_name + " " + objUsers[i].last_name + "[" +objUsers[i].username + "]") i = i + 1; def do_join(self, inp): '''Join (set active) a chat room by it's index. use "listChatRoom" to get rooms indexes.''' global objChatRooms global iActiveRoom if objChatRooms[int(inp)] is not None: print("Joining " + objChatRooms[int(inp)].sTitle) iActiveRoom = int(inp) f = open( "./logs/" + str(objChatRooms[int(inp)].id) + ".txt", "r+") tmpTxt = f.read() if tmpTxt: print(tmpTxt) else: print("No room with that ID") def do_send (self, inp): '''Send a message into the active room.''' global objChatRooms global iActiveRoom if objChatRooms[iActiveRoom] is not None: td_send({'@type': 'sendMessage','chat_id': objChatRooms[iActiveRoom].id,'input_message_content': {'@type': 'inputMessageText','text': {'@type': 'formattedText','text': inp}}}) def do_logout(self, inp): '''Logout then quit the app.''' global bMasterSwitch print("td_send logout") td_send({'@type': 'logOut'}) bMasterSwitch = False exit() ftClient() ftServer() while bMasterSwitch: if bConStatus: print("Connected. Launching interactive console...") td_send({'@type': 'getMe'}) td_send({'@type': 'getChats', 'limit': 2}) ftConsole().cmdloop() break time.sleep(1) td_json_client_destroy(client)