begin message handling rewrite
This commit is contained in:
412
xmpp_tg/xmpp.py
412
xmpp_tg/xmpp.py
@@ -1,5 +1,6 @@
|
||||
import re, sys, os, io, sqlite3, hashlib, time, datetime
|
||||
import xml.etree.ElementTree as ET
|
||||
import logging, traceback
|
||||
|
||||
from sleekxmpp.componentxmpp import ComponentXMPP
|
||||
from sleekxmpp import Presence, Message
|
||||
@@ -261,17 +262,277 @@ class XMPPTelegram(ComponentXMPP):
|
||||
self.send_presence(pto=jid, pfrom=self.boundjid.bare, ptype='unavailable')
|
||||
sys.exit(0)
|
||||
|
||||
def process_command(self, iq):
|
||||
class MessageHandler():
|
||||
_unknown_command_handler = lambda self: "Unknown command, for a list send '!help'"
|
||||
_on_connect = lambda: None
|
||||
|
||||
def __init__(self, msg):
|
||||
self._command = msg["body"].split(" ")[0][1:]
|
||||
self._handler = getattr(self, self._command, self._unknown_command_handler)
|
||||
self.type = "groupchat" if msg["type"] == "groupchat" else "chat"
|
||||
self.sender = msg["from"]
|
||||
self.jid = msg["from"].bare
|
||||
self.replyto = self.sender.full if self.type == "chat" else self.sender.bare
|
||||
self.arguments = msg["body"].split(" ")[1:]
|
||||
self.msg = msg
|
||||
|
||||
def _update(self, text):
|
||||
xmpp.send_message(mto=self.replyto, mtype=self.type, mbody=text)
|
||||
|
||||
def debug(self, *args, **kwargs):
|
||||
"""Show debug info"""
|
||||
return pprint.pformat(self.__dict__)
|
||||
|
||||
def help(self, *args, **kwargs):
|
||||
"""List available commands"""
|
||||
#taken from https://www.python.org/dev/peps/pep-0257/#handling-docstring-indentation
|
||||
def trim(docstring):
|
||||
if not docstring:
|
||||
return ''
|
||||
# Convert tabs to spaces (following the normal Python rules)
|
||||
# and split into a list of lines:
|
||||
lines = docstring.expandtabs().splitlines()
|
||||
# Determine minimum indentation (first line doesn't count):
|
||||
indent = 500
|
||||
for line in lines[1:]:
|
||||
stripped = line.lstrip()
|
||||
if stripped:
|
||||
indent = min(indent, len(line) - len(stripped))
|
||||
# Remove indentation (first line is special):
|
||||
trimmed = [lines[0].strip()]
|
||||
if indent < 500:
|
||||
for line in lines[1:]:
|
||||
trimmed.append(line[indent:].rstrip())
|
||||
# Strip off trailing and leading blank lines:
|
||||
while trimmed and not trimmed[-1]:
|
||||
trimmed.pop()
|
||||
while trimmed and not trimmed[0]:
|
||||
trimmed.pop(0)
|
||||
# Return a single string:
|
||||
return '\n'.join(trimmed)
|
||||
|
||||
if len(self.arguments) == 0:
|
||||
methods = [func for func in dir(self) if not func.startswith("_") and callable(getattr(self, func))]
|
||||
reply = "Available commands:"
|
||||
for method in methods:
|
||||
docstring = getattr(self, method).__doc__
|
||||
if docstring is None:
|
||||
docstring = "No description available"
|
||||
reply += "\n"+method+" ("+docstring.split("\n")[0]+")"
|
||||
return reply
|
||||
else:
|
||||
method = getattr(self,self.arguments[0])
|
||||
reply = trim(method.__doc__)
|
||||
return reply
|
||||
|
||||
class GateMessageHandler(MessageHandler):
|
||||
def configure(hndl, self):
|
||||
"""Get config/set config options"""
|
||||
config_exclude = ['jid', 'tg_phone']
|
||||
|
||||
option = hndl.arguments[0] if len(hndl.arguments) >= 1 else None
|
||||
value = hndl.arguments[1] if len(hndl.arguments) == 2 else None
|
||||
|
||||
if value is not None and option not in config_exclude:
|
||||
self.db_connection.execute("update accounts set {} = ? where jid = ?".format(option), (value,hndl.jid,) )
|
||||
self.accounts[hndl.jid] = self.db_connection.execute("SELECT * FROM accounts where jid = ?", (hndl.jid,) ).fetchone()
|
||||
|
||||
message = "=== Your current configuration ===\n\n"
|
||||
for param, value in self.accounts[hndl.jid].items():
|
||||
message = message + "<%s>: %s" % (param, value) + "\n"
|
||||
message = message + "\nTo modify some option, please, send !configure param value"
|
||||
return message
|
||||
|
||||
def login(hndl, self): #1 arg
|
||||
"""Initiates Telegram session
|
||||
|
||||
Usage: !login <phone number in international format>"""
|
||||
if len(hndl.arguments) != 1:
|
||||
return "Wrong number of arguments"
|
||||
|
||||
phone_no = arguments[0]
|
||||
|
||||
hndl._update("Please wait...")
|
||||
self.spawn_tg_client(hndl.jid, phone_no)
|
||||
|
||||
if self.tg_connections[hndl.jid].is_user_authorized():
|
||||
self.send_presence(pto=jid, pfrom=self.boundjid.bare, ptype='online', pstatus='connected')
|
||||
return "You are already authenticated in Telegram"
|
||||
else:
|
||||
# remove old sessions for this JID #
|
||||
self.db_connection.execute("DELETE from accounts where jid = ?", (hndl.jid, ) )
|
||||
self.tg_connections[hndl.jid].send_code_request(phone_no)
|
||||
self._update('Gate is connected. Telegram should send SMS message to you.')
|
||||
return 'Please enter one-time code via !code 12345.'
|
||||
|
||||
def code(hndl, self):
|
||||
|
||||
code = hndl.arguments[0]
|
||||
jid = hndl.jid
|
||||
|
||||
if not self.tg_connections[jid].is_user_authorized():
|
||||
try:
|
||||
hndl._update('Trying authenticate...')
|
||||
self.tg_connections[jid].sign_in(self.tg_phones[jid], code)
|
||||
except SessionPasswordNeededError:
|
||||
self.gate_reply_message(iq, 'Two-factor authentication detected.')
|
||||
self.gate_reply_message(iq, 'Please enter your password via !password abc123.')
|
||||
return
|
||||
|
||||
if self.tg_connections[jid].is_user_authorized():
|
||||
self.send_presence(pto=jid, pfrom=self.boundjid.bare, ptype='online', pstatus='connected')
|
||||
self.gate_reply_message(iq, 'Authentication successful. Initiating Telegram...')
|
||||
self.db_connection.execute("INSERT INTO accounts(jid, tg_phone) VALUES(?, ?)", (jid, self.tg_phones[jid],))
|
||||
self.accounts[jid] = self.db_connection.execute("SELECT * FROM accounts where jid = ?", (jid,) ).fetchone()
|
||||
self.init_tg(jid)
|
||||
|
||||
else:
|
||||
return 'Authentication failed.'
|
||||
else:
|
||||
return 'You are already authenticated. Please use !logout before new login.'
|
||||
|
||||
def password(hndl, self):
|
||||
password = hndl.arguments[1]
|
||||
jid = self.jid
|
||||
|
||||
if not self.tg_connections[jid].is_user_authorized():
|
||||
self.gate_reply_message(iq, 'Checking password...')
|
||||
self.tg_connections[jid].sign_in(password=password)
|
||||
|
||||
if self.tg_connections[jid].is_user_authorized():
|
||||
self.send_presence(pto=jid, pfrom=self.boundjid.bare, ptype='online', pstatus='connected')
|
||||
self.gate_reply_message(iq, 'Authentication successful. Initiating Telegram...')
|
||||
self.db_connection.execute("INSERT INTO accounts(jid, tg_phone) VALUES(?, ?)", (jid, self.tg_phones[jid],))
|
||||
self.accounts[jid] = self.db_connection.execute("SELECT * FROM accounts where jid = ?", (jid,) ).fetchone()
|
||||
self.init_tg(jid)
|
||||
|
||||
else:
|
||||
self.gate_reply_message(iq, 'Authentication failed.')
|
||||
else:
|
||||
self.gate_reply_message(iq, 'You are already authenticated. Please use !logout before new login.')
|
||||
|
||||
def list_sessions(hndl, self):
|
||||
if not self.tg_connections[hndl.jid].is_user_authorized():
|
||||
return "Error"
|
||||
|
||||
sessions = self.tg_connections[hndl.jid].invoke(GetAuthorizationsRequest())
|
||||
return str(sessions)
|
||||
|
||||
def reload_dialogs(hndl, self):
|
||||
if not self.tg_connections[hndl.jid].is_user_authorized():
|
||||
return "Error"
|
||||
|
||||
self.tg_process_dialogs(hndl.jid)
|
||||
return "Dialogs reloadad"
|
||||
|
||||
def logout(hndl, self):
|
||||
self.tg_connections[hndl.jid].log_out()
|
||||
self.db_connection.execute("DELETE FROM accounts WHERE jid = ?", (hndl.jid,))
|
||||
return 'Your Telegram session was deleted'
|
||||
|
||||
def add(self): #1 arg
|
||||
result = self.tg_connections[jid].get_entity(parsed[1])
|
||||
if type(result) == User:
|
||||
tg_peer = InputPeerUser( result.id, result.access_hash )
|
||||
result = self.tg_connections[jid].invoke( SendMessageRequest(tg_peer, 'Hello! I just want to add you in my contact list.', generate_random_long() ) )
|
||||
elif type(result) == Channel:
|
||||
tg_peer = InputPeerChannel( result.id, result.access_hash )
|
||||
self.tg_connections[jid].invoke(JoinChannelRequest( InputPeerChannel(result.id, result.access_hash) ) )
|
||||
else:
|
||||
self.gate_reply_message(iq, 'Sorry, nothing found.')
|
||||
return
|
||||
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
def join(self): #1 arg
|
||||
link = parsed[1].split('/') # https://t.me/joinchat/HrCmckx_SkMbSGFLhXCvSg
|
||||
self.tg_connections[jid].invoke(ImportChatInviteRequest(link[4]))
|
||||
time.sleep(1)
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
def group(self): #2 args
|
||||
# group name? #
|
||||
groupname = parsed[1]
|
||||
|
||||
# group users? #
|
||||
groupuser = self.tg_connections[jid].get_entity(parsed[2])
|
||||
|
||||
# we re ready to make group
|
||||
self.tg_connections[jid].invoke(CreateChatRequest([groupuser], groupname))
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
def channel(self): #1 arg
|
||||
groupname = parsed[1]
|
||||
self.tg_connections[jid].invoke(CreateChannelRequest(groupname, groupname, broadcast = True))
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
def supergroup(self): #1 arg
|
||||
groupname = parsed[1]
|
||||
self.tg_connections[jid].invoke(CreateChannelRequest(groupname, groupname, megagroup = True))
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
def username(self): #1 arg
|
||||
username = parsed[1]
|
||||
self.tg_connections[jid].invoke(UpdateUsernameRequest(username))
|
||||
|
||||
def name(self): #1 or 2 args
|
||||
firstname = parsed[1]
|
||||
lastname = parsed[2] if len(parsed) > 2 else None
|
||||
self.tg_connections[jid].invoke(UpdateProfileRequest(first_name = firstname, last_name = lastname))
|
||||
|
||||
def about(self): #>0 args
|
||||
about = iq['body'][7:]
|
||||
self.tg_connections[jid].invoke(UpdateProfileRequest(about = about))
|
||||
|
||||
def import_contact(self): #2 args
|
||||
phone = parsed[1]
|
||||
firstname = parsed[2]
|
||||
lastname = parsed[3] if len(parsed) > 3 else None
|
||||
|
||||
contact = InputPhoneContact(client_id=generate_random_long(), phone=phone, first_name=firstname, last_name=lastname)
|
||||
self.tg_connections[jid].invoke(ImportContactsRequest([contact]))
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
def roster(hndl, self):
|
||||
response = "Telegram chats:\n"
|
||||
for jid,tid in self.contact_list[hndl.jid].items():
|
||||
response += "{}: {}\n".format(tid, jid)
|
||||
return response
|
||||
|
||||
|
||||
def process_command(self, msg):
|
||||
"""
|
||||
Commands to gateway, users or chats (starts with !)
|
||||
:param iq:
|
||||
:return:
|
||||
"""
|
||||
parsed = iq['body'].split(' ')
|
||||
jid = iq['from'].bare
|
||||
logging.info("received message "+str(msg["body"])+" from "+str(msg["from"]))
|
||||
is_command = msg["body"].startswith("!") and msg["body"][1] != "_"
|
||||
|
||||
if is_command:
|
||||
command = msg["body"].split(" ")[0][1:]
|
||||
handler = self.GateMessageHandler(msg)._handler
|
||||
try:
|
||||
reply = str(handler(self))
|
||||
except Exception as e:
|
||||
if self.config["debug"]:
|
||||
reply = traceback.format_exc()
|
||||
else:
|
||||
if isinstance(e, NotAuthorizedError):
|
||||
reply = str(e)
|
||||
else:
|
||||
logging.error("Exception in command from {}, command was '{}'".format(msg["from"],msg["body"]))
|
||||
traceback.print_exc()
|
||||
reply = "Internal error, please contact Sysadmin"
|
||||
if reply is not None:
|
||||
self.gate_reply_message(msg, reply)
|
||||
#msg.reply(reply).send()
|
||||
|
||||
parsed = msg['body'].split(' ')
|
||||
jid = msg['from'].bare
|
||||
|
||||
if parsed[0] == '!help':
|
||||
self.gate_reply_message(iq, '=== Available gateway commands ===:\n\n'
|
||||
self.gate_reply_message(msg, '=== Available gateway commands ===:\n\n'
|
||||
|
||||
'!help - Displays this text\n'
|
||||
'!login +123456789 - Initiates Telegram session\n'
|
||||
@@ -299,149 +560,6 @@ class XMPPTelegram(ComponentXMPP):
|
||||
|
||||
'!roster - Lists yout TG roster\n'
|
||||
)
|
||||
elif parsed[0] == '!configure':
|
||||
config_exclude = ['jid', 'tg_phone']
|
||||
if len(parsed) > 2 and parsed[1] not in config_exclude:
|
||||
self.db_connection.execute("update accounts set {} = ? where jid = ?".format(parsed[1]), (parsed[2],jid,) )
|
||||
self.accounts[jid] = self.db_connection.execute("SELECT * FROM accounts where jid = ?", (jid,) ).fetchone()
|
||||
|
||||
message = "=== Your current configuration ===\n\n"
|
||||
for param, value in self.accounts[jid].items():
|
||||
message = message + "<%s>: %s" % (param, value) + "\n"
|
||||
message = message + "\nTo modify some option, please, send !configure param value"
|
||||
self.gate_reply_message(iq, message)
|
||||
|
||||
|
||||
elif parsed[0] == '!login': # --------------------------------------------------
|
||||
self.gate_reply_message(iq, 'Please wait...')
|
||||
self.spawn_tg_client(jid, parsed[1])
|
||||
|
||||
if self.tg_connections[jid].is_user_authorized():
|
||||
self.send_presence(pto=jid, pfrom=self.boundjid.bare, ptype='online', pstatus='connected')
|
||||
self.gate_reply_message(iq, 'You are already authenticated in Telegram.')
|
||||
else:
|
||||
# remove old sessions for this JID #
|
||||
self.db_connection.execute("DELETE from accounts where jid = ?", (jid, ) )
|
||||
self.tg_connections[jid].send_code_request(parsed[1])
|
||||
self.gate_reply_message(iq, 'Gate is connected. Telegram should send SMS message to you.')
|
||||
self.gate_reply_message(iq, 'Please enter one-time code via !code 12345.')
|
||||
|
||||
elif parsed[0] in ['!code', '!password']: # --------------------------------------------------
|
||||
if not self.tg_connections[jid].is_user_authorized():
|
||||
if parsed[0] == '!code':
|
||||
try:
|
||||
self.gate_reply_message(iq, 'Trying authenticate...')
|
||||
self.tg_connections[jid].sign_in(self.tg_phones[jid], parsed[1])
|
||||
except SessionPasswordNeededError:
|
||||
self.gate_reply_message(iq, 'Two-factor authentication detected.')
|
||||
self.gate_reply_message(iq, 'Please enter your password via !password abc123.')
|
||||
return
|
||||
|
||||
if parsed[0] == '!password':
|
||||
self.gate_reply_message(iq, 'Checking password...')
|
||||
self.tg_connections[jid].sign_in(password=parsed[1])
|
||||
|
||||
if self.tg_connections[jid].is_user_authorized():
|
||||
self.send_presence(pto=jid, pfrom=self.boundjid.bare, ptype='online', pstatus='connected')
|
||||
self.gate_reply_message(iq, 'Authentication successful. Initiating Telegram...')
|
||||
self.db_connection.execute("INSERT INTO accounts(jid, tg_phone) VALUES(?, ?)", (jid, self.tg_phones[jid],))
|
||||
self.accounts[jid] = self.db_connection.execute("SELECT * FROM accounts where jid = ?", (jid,) ).fetchone()
|
||||
self.init_tg(jid)
|
||||
|
||||
else:
|
||||
self.gate_reply_message(iq, 'Authentication failed.')
|
||||
else:
|
||||
self.gate_reply_message(iq, 'You are already authenticated. Please use !logout before new login.')
|
||||
|
||||
elif parsed[0] == '!list_sessions': # --------------------------------------------------
|
||||
if not self.tg_connections[jid].is_user_authorized():
|
||||
self.gate_reply_message(iq, 'Error.')
|
||||
return
|
||||
|
||||
sessions = self.tg_connections[jid].invoke(GetAuthorizationsRequest())
|
||||
elif parsed[0] == '!reload_dialogs':
|
||||
if not self.tg_connections[jid].is_user_authorized():
|
||||
self.gate_reply_message(iq, 'Error.')
|
||||
return
|
||||
self.tg_process_dialogs(jid)
|
||||
self.gate_reply_message(iq, 'Dialogs reloaded.')
|
||||
|
||||
elif parsed[0] == '!logout': # --------------------------------------------------
|
||||
self.tg_connections[jid].log_out()
|
||||
self.db_connection.execute("DELETE FROM accounts WHERE jid = ?", (jid,))
|
||||
self.gate_reply_message(iq, 'Your Telegram session was deleted')
|
||||
|
||||
elif parsed[0] == '!add': # add user
|
||||
result = self.tg_connections[jid].get_entity(parsed[1])
|
||||
if type(result) == User:
|
||||
tg_peer = InputPeerUser( result.id, result.access_hash )
|
||||
result = self.tg_connections[jid].invoke( SendMessageRequest(tg_peer, 'Hello! I just want to add you in my contact list.', generate_random_long() ) )
|
||||
elif type(result) == Channel:
|
||||
tg_peer = InputPeerChannel( result.id, result.access_hash )
|
||||
self.tg_connections[jid].invoke(JoinChannelRequest( InputPeerChannel(result.id, result.access_hash) ) )
|
||||
else:
|
||||
self.gate_reply_message(iq, 'Sorry, nothing found.')
|
||||
return
|
||||
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
elif parsed[0] == '!join': # join chat by link
|
||||
link = parsed[1].split('/') # https://t.me/joinchat/HrCmckx_SkMbSGFLhXCvSg
|
||||
self.tg_connections[jid].invoke(ImportChatInviteRequest(link[4]))
|
||||
time.sleep(1)
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
elif parsed[0] == '!group' and len(parsed) >= 3: # create new group
|
||||
# group name? #
|
||||
groupname = parsed[1]
|
||||
|
||||
# group users? #
|
||||
groupuser = self.tg_connections[jid].get_entity(parsed[2])
|
||||
|
||||
# we re ready to make group
|
||||
self.tg_connections[jid].invoke(CreateChatRequest([groupuser], groupname))
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
elif parsed[0] == '!channel' and len(parsed) >= 2:
|
||||
groupname = parsed[1]
|
||||
self.tg_connections[jid].invoke(CreateChannelRequest(groupname, groupname, broadcast = True))
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
elif parsed[0] == '!supergroup' and len(parsed) >= 2:
|
||||
groupname = parsed[1]
|
||||
self.tg_connections[jid].invoke(CreateChannelRequest(groupname, groupname, megagroup = True))
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
elif parsed[0] == '!username' and len(parsed) >= 2:
|
||||
username = parsed[1]
|
||||
self.tg_connections[jid].invoke(UpdateUsernameRequest(username))
|
||||
|
||||
elif parsed[0] == '!name' and len(parsed) >= 2:
|
||||
firstname = parsed[1]
|
||||
lastname = parsed[2] if len(parsed) > 2 else None
|
||||
self.tg_connections[jid].invoke(UpdateProfileRequest(first_name = firstname, last_name = lastname))
|
||||
|
||||
elif parsed[0] == '!about' and len(parsed) >= 2:
|
||||
about = iq['body'][7:]
|
||||
self.tg_connections[jid].invoke(UpdateProfileRequest(about = about))
|
||||
|
||||
elif parsed[0] == '!import' and len(parsed) >= 3:
|
||||
phone = parsed[1]
|
||||
firstname = parsed[2]
|
||||
lastname = parsed[3] if len(parsed) > 3 else None
|
||||
|
||||
contact = InputPhoneContact(client_id=generate_random_long(), phone=phone, first_name=firstname, last_name=lastname)
|
||||
self.tg_connections[jid].invoke(ImportContactsRequest([contact]))
|
||||
self.tg_process_dialogs(jid)
|
||||
|
||||
elif parsed[0] == '!roster': # create new channel
|
||||
response = "Telegram chats:\n"
|
||||
for jid,tid in self.contact_list[jid].items():
|
||||
response += "{}: {}\n".format(tid, jid)
|
||||
self.gate_reply_message(iq, response)
|
||||
|
||||
else: # --------------------------------------------------
|
||||
self.gate_reply_message(iq, 'Unknown command. Try !help for list all commands.')
|
||||
|
||||
def process_chat_user_command(self, iq):
|
||||
parsed = iq['body'].split(' ')
|
||||
|
||||
Reference in New Issue
Block a user