dfstorm 1 year ago
parent
commit
923c18e507
  1. 48
      README.md
  2. 454
      src/main.py

48
README.md

@ -1,3 +1,49 @@
# python-telegram
A small python CLI telegram client
A small python CLI telegram client. Please note that this application was a
"test" and is not intended to be used other than testing purpose.
The app only retrieve message recived while active and keep them in logs files (./logs/).
## Installation
### libtdjson.so (1.6.6)
The script will look for the telegram library at "./libtdjson.so". You may either
download it from my website (https://ggenois.dev/pub/libtdjson.so (1.6.6, 64bit Fedora Machine))
or build it by folowing those instructions: https://github.com/tdlib/td#building
### API Keys
I do not provide API credentials; you must provide your own.
To get one, create a Telegram App via https://my.telegram.org/apps and then fill "iApiID" (line 14) with your "App api_id" and "sApiHash" (Line 15) with "App api_hash:".
## Usage
While no user is logged in, the app is in "Authentification mode". Once logged in, you get into an interactive console with those commands:
### exit
exit the application without loging out.
### updateChatsList
Actualise Chats List.
### updateMe
Update Personnals informartions
### listChatRoom
List Available chat room(s)
### getKnowUsers
List Known users of this instance.
### join
Join (set active) a chat room by it's index. Use "listChatRoom" to get rooms indexes.
### send
Send a message into the active room.
### logout
Logout then quit the app.

454
src/main.py

@ -0,0 +1,454 @@
#!/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)
Loading…
Cancel
Save