Skip to content

Instantly share code, notes, and snippets.

@marianoguerra
Created November 6, 2012 10:36
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marianoguerra/4023941 to your computer and use it in GitHub Desktop.
Save marianoguerra/4023941 to your computer and use it in GitHub Desktop.
otr support for sleekxmpp
import pyotr
from sleekxmpp.xmlstream.stanzabase import JID
import logging
log = logging.getLogger(__name__)
from plugin import BasePlugin
import tabs
from tabs import ConversationTab
class Plugin(BasePlugin):
def init(self):
self.contacts = {}
# a dict of {full-JID: OTR object}
self.add_event_handler('conversation_say_after', self.on_conversation_say)
self.add_event_handler('conversation_msg', self.on_conversation_msg)
self.add_tab_command(ConversationTab, 'otr', self.command_otr, "Usage: /otr <start|end>\notr: Start or stop OTR for the current conversation", self.otr_completion)
ConversationTab.add_information_element('otr', self.display_encryption_status)
def cleanup(self):
ConversationTab.remove_information_element('otr')
self.del_tab_command(ConversationTab, 'otr')
def otr_special(self, tab, typ):
def helper(msg):
tab.add_message('%s: %s' % (typ, msg.decode()))
return helper
def otr_on_state_change(self, tab):
def helper(old, new):
old = self.otr_state(old)
new = self.otr_state(new)
tab.add_message('OTR state has changed from %s to %s' % (old, new))
return helper
def get_otr(self, tab):
if tab not in self.contacts:
self.contacts[tab] = pyotr.OTR(on_error=self.otr_special(tab, 'Error'), on_warn=self.otr_special(tab, 'Warn'), on_state_change=self.otr_on_state_change(tab))
return self.contacts[tab]
def on_conversation_say(self, message, tab):
"""
Feed the message through the OTR filter
"""
to = message['to']
if not message['body']:
# there’s nothing to encrypt if this is a chatstate, for example
return
otr_state = self.get_otr(tab)
# Not sure what to do with xhtml bodies, and I don't like them anyway ;)
del message['xhtml_im']
say = otr_state.transform_msg(message['body'].encode())
if say is not None:
message['body'] = say.decode()
else:
del message['body']
def on_conversation_msg(self, message, tab):
"""
Feed the message through the OTR filter
"""
fro = message['from']
if not message['body']:
# there’s nothing to decrypt if this is a chatstate, for example
return
otr_state = self.get_otr(tab)
# Not sure what to do with xhtml bodies, and I don't like them anyway ;)
del message['xhtml_im']
display, reply = otr_state.handle_msg(message['body'].encode())
#self.core.information('D: {!r}, R: {!r}'.format(display, reply))
if display is not None:
message['body'] = display.decode()
else:
del message['body']
if reply is not None:
self.otr_say(tab, reply.decode())
@staticmethod
def otr_state(state):
if state == pyotr.MSG_STATE_PLAINTEXT:
return 'plaintext'
elif state == pyotr.MSG_STATE_ENCRYPTED:
return 'encrypted'
elif state == pyotr.MSG_STATE_FINISHED:
return 'finished'
def display_encryption_status(self, jid):
"""
Returns the status of encryption for the associated jid. This is to be used
in the ConversationTab’s InfoWin.
"""
tab = self.core.get_tab_by_name(jid, tabs.ConversationTab)
if tab not in self.contacts:
return ''
state = self.otr_state(self.contacts[tab].state)
return ' OTR: %s' % (state,)
def otr_say(self, tab, line):
msg = self.core.xmpp.make_message(tab.get_name())
msg['type'] = 'chat'
msg['body'] = line
msg.send()
def command_otr(self, args):
"""
A command to start or end OTR encryption
"""
args = args.split()
if not args:
return self.core.command_help("otr")
if isinstance(self.core.current_tab(), ConversationTab):
jid = JID(self.core.current_tab().get_name())
command = args[0]
if command == 'start':
otr_state = self.get_otr(self.core.current_tab())
self.otr_say(self.core.current_tab(), otr_state.start().decode())
elif command == 'end':
otr_state = self.get_otr(self.core.current_tab())
msg = otr_state.end()
if msg is not None:
self.otr_say(self.core.current_tab(), msg.decode())
elif command == 'fpr':
otr_state = self.get_otr(self.core.current_tab())
our = otr_state.our_fpr
if our:
our = hex(int.from_bytes(our, 'big'))[2:].ljust(40).upper()
their = otr_state.their_fpr
if their:
their = hex(int.from_bytes(their, 'big'))[2:].ljust(40).upper()
self.core.current_tab().add_message('Your: %s Their: %s' % (our, their))
self.core.refresh_window()
def otr_completion(self, the_input):
return the_input.auto_completion(['start', 'end'], ' ')
@patrickGodin
Copy link

Hi,
I need your help, im new in python and i don't know how i can integrate in my script.
Please tell me where and how do i integrate the plugin.

Thank you

`#!/usr/bin/env python

-- coding: utf-8 --

"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.

See the file LICENSE for copying permission.

"""

import sys
import logging
import getpass
from optparse import OptionParser

import sleekxmpp
import ssl

Python versions before 3.0 do not use UTF-8 encoding

by default. To ensure that Unicode is handled properly

throughout SleekXMPP, we will set the default encoding

ourselves to UTF-8.

if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf-8')
else:
raw_input = input

class SendMsgBot(sleekxmpp.ClientXMPP):

"""
A basic SleekXMPP bot that will log in, send a message,
and then log out.
"""

def __init__(self, jid, password, recipient, message):
    sleekxmpp.ClientXMPP.__init__(self, jid, password)

    # The message we wish to send, and the JID that
    # will receive it.
    self.recipient = recipient
    self.msg = message

    # The session_start event will be triggered when
    # the bot establishes its connection with the server
    # and the XML streams are ready for use. We want to
    # listen for this event so that we we can initialize
    # our roster.
    self.add_event_handler("session_start", self.start)

def start(self, event):
    """
    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()

    self.send_message(mto=self.recipient,
                      mbody=self.msg,
                      mtype='chat')

    # Using wait=True ensures that the send queue will be
    # emptied before ending the session.
    self.disconnect(wait=True)

if name == 'main':
# Setup the command line arguments.
optp = OptionParser()

# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR', action='store_const', dest='loglevel', const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG', action='store_const', dest='loglevel', const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM', action='store_const', dest='loglevel', const=5, default=logging.INFO)

# JID and password options.
optp.add_option("-j", "--jid", dest="jid", help="JID to use")
optp.add_option("-p", "--password", dest="password", help="password to use")
optp.add_option("-t", "--to", dest="to", help="JID to send the message to")
optp.add_option("-m", "--message", dest="message", help="message to send")

opts, args = optp.parse_args()

# Setup logging.
logging.basicConfig(level=opts.loglevel, format='%(levelname)-8s %(message)s')

if opts.jid is None:
    opts.jid = raw_input("Username: ")
if opts.password is None:
    opts.password = getpass.getpass("Password: ")
if opts.to is None:
    opts.to = raw_input("Send To: ")
if opts.message is None:
    opts.message = raw_input("Message: ")

# Setup the EchoBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = SendMsgBot(opts.jid, opts.password, opts.to, opts.message)
#xmpp.register_plugin('xep_0030') # Service Discovery
#xmpp.register_plugin('xep_0199') # XMPP Ping

# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
#xmpp.ssl_version = ssl.PROTOCOL_SSLv3

# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "cert.txt"

# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect(('jabb.im', 5222)):
    # If you do not have the dnspython library installed, you will need
    # to manually specify the name of the server if it does not match
    # the one in the JID. For example, to use Google Talk you would
    # need to use:
    #
    # if xmpp.connect(('talk.google.com', 5222)):
    #     ...
    xmpp.process(block=True)
    print("Done")
else:
    print("Unable to connect.")`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment