First attempt at an example

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
Maxime “pep” Buquet 2019-02-19 00:09:44 +00:00
parent a72e81c329
commit 3061aa3372

212
examples/echo_client.py Normal file
View file

@ -0,0 +1,212 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp OMEMO plugin
Copyright (C) 2010 Nathanael C. Fritz
Copyright (C) 2019 Maxime pep Buquet <pep@bouah.net>
This file is part of slixmpp-omemo.
See the file LICENSE for copying permission.
"""
import os
import sys
import logging
from getpass import getpass
from argparse import ArgumentParser
from slixmpp import ClientXMPP, JID
from slixmpp.stanza import Message
from slixmpp_omemo import PluginCouldNotLoad, MissingOwnKey, EncryptionPrepareException
from slixmpp_omemo import UndecidedException, UntrustedException, NoAvailableSession
import slixmpp_omemo
log = logging.getLogger(__name__)
class EchoBot(ClientXMPP):
"""
A simple Slixmpp bot that will echo encrypted messages it receives, along
with a short thank you message.
For details on how to build a client with slixmpp, look at exemples in the
slixmpp repository.
"""
def __init__(self, jid, password):
ClientXMPP.__init__(self, jid, password)
self.add_event_handler("session_start", self.start)
self.add_event_handler("message", self.message)
self.add_event_handler("message_encryption", self.message)
def start(self, _event) -> None:
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.send_presence()
self.get_roster()
def message(self, msg: Message) -> None:
"""
Process incoming message stanzas. Be aware that this also
includes MUC messages and error messages. It is usually
a good idea to check the messages's type before processing
or sending replies.
Arguments:
msg -- The received message stanza. See the documentation
for stanza objects and the Message stanza to see
how it may be used.
"""
if msg['type'] not in ('chat', 'normal'):
return None
if not self['xep_0384'].is_encrypted(msg):
self.plain_reply(msg, 'This message was not encrypted.\n%(body)s' % msg)
return None
while True:
try:
body = self['xep_0384'].decrypt_message(msg)
self.encrypted_reply(msg, 'Thanks for sending\n%s' % body.decode("utf8"))
break
except (MissingOwnKey,):
# The message is missing our own key, it was not encrypted for
# us, and we can't decrypt it.
self.plain_reply(
msg,
'I can\'t decrypt this message as it is not encrypted for me.',
)
break
except (NoAvailableSession,) as exn:
# We received a message from that contained a session that we
# don't know about (deleted session storage, etc.). We can't
# decrypt the message, and it's going to be lost.
self.encrypted_reply(
msg,
'I can\'t decrypt this message as it uses an encrypted '
'session I don\'t know about.',
)
break
except (UndecidedException,) as exn:
# I don't think the comment below is correct.
# We should be able to read the message whatever the trust
# state. I think we want to force a decision only when
# sending. I think other clients also do this. Conversations,
# dino, etc. Same for UntrustedException, we can just let the
# user know that the sender is untrusted.
# We have not decided yet wether to trust the person sending
# us the message. We must explicitely tell slixmpp what to do.
# In this case, we will automatically trust. In a real
# application, this is where you would prompt the user to
# decide.
self['xep_0384'].trust(JID(exn.bare_jid), exn.device, exn.ik)
self.plain_reply(
msg,
'Adding %(device) of %(bare_jid)s in trusted devices.' % exn,
)
# Now that we added the device in the trust manager, we need
# to try and decrypt it again, (we let it loop).
except (UntrustedException,) as exn:
pass
except (EncryptionPrepareException,):
# Slixmpp tried its best, but there were errors it couldn't
# resolve. At this point you should have seen other exceptions
# and given a chance to resolve them already.
self.plain_reply(msg, 'I was not able to decrypt the message.')
break
return None
def plain_reply(self, original_msg, body) -> None:
"""
Helper to reply to messages
"""
mto = original_msg['from']
mtype = original_msg['type']
msg = self.make_message(mto=mto, mtype=mtype)
msg['body'] = body
msg.send()
def encrypted_reply(self, msg, body) -> None:
pass
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser(description=EchoBot.__doc__)
# Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
action="store_const", dest="loglevel",
const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
action="store_const", dest="loglevel",
const=logging.DEBUG, default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
# Data dir for omemo plugin
DATA_DIR = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'omemo',
)
parser.add_argument("--data-dir", dest="data_dir",
help="data directory", default=DATA_DIR)
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
# Setup the EchoBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
# Ensure OMEMO data dir is created
os.makedirs(args.data_dir, exist_ok=True)
xmpp = EchoBot(args.jid, args.password)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0199') # XMPP Ping
xmpp.register_plugin('xep_0380') # Explicit Message Encryption
try:
xmpp.register_plugin(
'xep_0384',
{
'data_dir': args.data_dir,
},
module=slixmpp_omemo,
) # OMEMO
except (PluginCouldNotLoad,):
log.exception('And error occured when loading the omemo plugin.')
sys.exit(1)
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process()