master
parent
8eec2c002d
commit
923c18e507
@ -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.
|
||||
|
@ -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…
Reference in New Issue