work started on connecting Tox to XMPP
parent
4fbe434b71
commit
aa93da9f37
17
links
17
links
|
@ -1,20 +1,9 @@
|
|||
http://spectrum.im/
|
||||
http://spectrum.im/documentation/tutorials/gateway_mode.html
|
||||
https://github.com/hanzz/libtransport/blob/master/plugin/python/NetworkPlugin.py
|
||||
|
||||
http://delx.net.au/projects/pymsnt/devel.html
|
||||
|
||||
http://xmpppy.sourceforge.net/
|
||||
https://github.com/normanr/irc-transport/blob/master/irc.py
|
||||
https://github.com/normanr/irc-transport/blob/master/IRC-Transport-Howtouse.html
|
||||
http://xmpppy.sourceforge.net/apidocs/index.html
|
||||
|
||||
https://www.ejabberd.im/node/5134
|
||||
http://xmpp.org/extensions/xep-0114.html
|
||||
http://sleekxmpp.com/getting_started/component.html
|
||||
https://twistedmatrix.com/trac/wiki/XMPPServerArchitecture
|
||||
|
||||
https://github.com/aitjcize/tox-irc-sync/blob/master/tox-irc-sync.py
|
||||
https://github.com/ntoll/xmppComponent/blob/master/todo_component.py
|
||||
|
||||
https://wiki.tox.im/Nodes
|
||||
https://blog.tox.im/running-a-bootstrap-node/
|
||||
https://blog.tox.im/running-a-bootstrap-node/
|
||||
https://github.com/fritzy/SleekXMPP/wiki/Stanzas:-Presence
|
|
@ -46,7 +46,7 @@ else:
|
|||
def tdbg(txt):
|
||||
print ("TOXMPP :: %s" % txt)
|
||||
|
||||
class EchoComponent(ComponentXMPP):
|
||||
class ToXMPPComponent(ComponentXMPP):
|
||||
"""
|
||||
A simple SleekXMPP component that echoes messages.
|
||||
"""
|
||||
|
@ -77,7 +77,54 @@ class EchoComponent(ComponentXMPP):
|
|||
self.add_event_handler('presence_subscribed', self.handle_presence_subscribed)
|
||||
self.add_event_handler('presence_unsubscribe', self.handle_presence_unsubscribe)
|
||||
self.add_event_handler('presence_unsubscribed', self.handle_presence_unsubscribed)
|
||||
# log bound jid
|
||||
tdbg('+-- bound jid is: %s' % self.boundjid.bare)
|
||||
|
||||
|
||||
def _verify_toxid(self, toxid):
|
||||
"""
|
||||
Checking if a given string is a valid ToxID, i.e. is 76 chars
|
||||
long hex (i.e. A-Fa-f0-9) string
|
||||
https://libtoxcore.so/core_concepts.html
|
||||
TODO: verifying the checksum?
|
||||
"""
|
||||
try:
|
||||
# hex?
|
||||
int(toxid, 16)
|
||||
# 76 chars?
|
||||
if len(toxid) != 76:
|
||||
return False;
|
||||
# doesn't contain 0x in front?
|
||||
if s.lower()[0:2] == '0x':
|
||||
return False;
|
||||
# AOK
|
||||
return True
|
||||
|
||||
except ValueError:
|
||||
# apparently, not hex
|
||||
return False
|
||||
|
||||
|
||||
def _verify_jid(self, jid):
|
||||
"""
|
||||
Checking if a given (local) JID is correct, i.e. is either
|
||||
self.boundjid.bare, or <valid-ToxID>@<self.boundjid.bare>.
|
||||
"""
|
||||
|
||||
# this component's address?
|
||||
if jid == self.boundjid.bare:
|
||||
return True
|
||||
|
||||
# nah, <something>@<something>; let's make sure it's
|
||||
# <valid-ToxID>@<self.boundjid.bare>!
|
||||
if not self._verify_toxid(jid.user):
|
||||
return False
|
||||
if jid.domain != self.boundjid.bare:
|
||||
return False
|
||||
|
||||
# we're done here.
|
||||
return True
|
||||
|
||||
|
||||
def message(self, msg):
|
||||
"""
|
||||
|
@ -98,38 +145,43 @@ class EchoComponent(ComponentXMPP):
|
|||
# outgoing reply's 'from' JID.
|
||||
msg.reply("Thanks for sending\n%(body)s" % msg).send()
|
||||
|
||||
# DEBUG
|
||||
|
||||
# handle a contact getting online
|
||||
def handle_got_online(self, presence):
|
||||
tdbg('got online: %s for %s' % (presence['from'], presence['to']))
|
||||
roster_item = self.roster[presence['to']][presence['from']]
|
||||
|
||||
tdbg('+-- subscription: %s' % roster_item['subscription'])
|
||||
# debug
|
||||
tdbg('+-- subscription: %s' % roster_item['subscription'])
|
||||
tdbg('roster\n%s\n' % self.roster);
|
||||
|
||||
|
||||
# DEBUG
|
||||
# handle a contact getting offline
|
||||
def handle_got_offline(self, presence):
|
||||
tdbg('got offline: %s for %s' % (presence['from'], presence['to']))
|
||||
#roster_item = self.roster[presence['to']][presence['from']]
|
||||
# debug
|
||||
tdbg('roster\n%s\n' % self.roster);
|
||||
|
||||
# Why doesn't this work?
|
||||
# handle a presence probe
|
||||
def handle_presence_probe(self, presence):
|
||||
tdbg('a presence probe from %s to %s; NOT replying...' % (presence['from'], presence['to']))
|
||||
#roster_item = self.roster[presence['to']][presence['from']]
|
||||
# debug
|
||||
tdbg('roster\n%s\n' % self.roster);
|
||||
# Populate the presence reply with the agent's current status.
|
||||
#self.sendPresence(pto=presence['from'], pfrom=presence['to'], pstatus="Busy studying XMPP", pshow="chat")
|
||||
|
||||
|
||||
|
||||
# handle subscription
|
||||
# handle subscription notification
|
||||
def handle_presence_subscribe(self, presence):
|
||||
tdbg('a subscription request from %s for %s' % (presence['from'], presence['to']))
|
||||
roster_item = self.roster[presence['to']][presence['from']]
|
||||
|
||||
# is the "to" JID sane?
|
||||
# it should either be a <valid ToxID>@<self.boundjid.bare>
|
||||
if not self._verify_jid(presence['to']):
|
||||
tdbg('+-- jid fail! unauthorizing...')
|
||||
roster_item.unauthorize()
|
||||
return False;
|
||||
|
||||
# If the subscription request is accepted.
|
||||
tdbg('+-- accepting...')
|
||||
roster_item.authorize()
|
||||
|
@ -149,9 +201,17 @@ class EchoComponent(ComponentXMPP):
|
|||
|
||||
tdbg('+-- subscription: %s' % roster_item['subscription'])
|
||||
# we want to be subscribed to all JIDs that allow us to
|
||||
# as long as they're subscribing to sane JIDs on our side
|
||||
# so, reality check
|
||||
if not self._verify_jid(presence['to']):
|
||||
tdbg('+-- jid fail! unauthorizing...')
|
||||
roster_item.unauthorize()
|
||||
return False;
|
||||
|
||||
# we're here, "our" JID is sane, let's roll
|
||||
if roster_item['subscription'] not in ('to', 'both'):
|
||||
roster_item.subscribe()
|
||||
tdbg('+-- subscription: %s' % roster_item['subscription'])
|
||||
roster_item.subscribe()
|
||||
tdbg('+-- subscribing to: %s' % roster_item['subscription'])
|
||||
|
||||
# pretty simple, we accept all and show our status to all
|
||||
roster_item.send_presence()
|
||||
|
@ -234,11 +294,11 @@ if __name__ == '__main__':
|
|||
logging.basicConfig(level=opts.loglevel,
|
||||
format='%(levelname)-8s %(message)s')
|
||||
|
||||
# Setup the EchoComponent and register plugins. Note that while plugins
|
||||
# Setup the ToXMPPComponent and register plugins. Note that while plugins
|
||||
# may have interdependencies, the order in which you register them does
|
||||
# not matter.
|
||||
xmpp = EchoComponent(opts.jid, opts.password, opts.server, opts.port)
|
||||
xmpp.register_plugin('YAMLRoster', module=yaml_roster) # YAML Roster backend
|
||||
xmpp = ToXMPPComponent(opts.jid, opts.password, opts.server, opts.port)
|
||||
xmpp.register_plugin('YAMLRoster', module=yaml_roster) # YAML Roster flat-file backend
|
||||
xmpp.register_plugin('xep_0030') # Service Discovery
|
||||
xmpp.register_plugin('xep_0004') # Data Forms
|
||||
xmpp.register_plugin('xep_0060') # PubSub
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# @file echo-no-av.py
|
||||
# @author Michał "rysiek" Woźniak <rysiek@hackerspace.pl>
|
||||
# @author Wei-Ning Huang (AZ) <aitjcize@gmail.com>
|
||||
#
|
||||
# Copyright (C) 2015 Michał "rysiek" Woźniak <rysiek@hackerspace.pl>
|
||||
# Copyright (C) 2013 - 2014 Wei-Ning Huang (AZ) <aitjcize@gmail.com>
|
||||
|
||||
"""
|
||||
ToXMPP: The Tox<->XMPP Transport/Gateway SleekXMPP-based Component
|
||||
|
||||
Copyright (C) 2015 Michał "rysiek" Woźniak <rysiek@hackerspace.pl>
|
||||
ToxID: 3FA2E5273F0C368576FE120B374664E3B41E2CDF21639AFED3DC301490FFB01FAAA47B78D5F4
|
||||
This file is part of ToXMPP.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
from pytox import Tox
|
||||
|
||||
from time import sleep
|
||||
from os.path import exists
|
||||
import random
|
||||
|
||||
# local debug server when running under Docker
|
||||
DEBUG_SERVER = [
|
||||
"172.17.42.1",
|
||||
33445,
|
||||
"3FA2E5273F0C368576FE120B374664E3B41E2CDF21639AFED3DC301490FFB01FAAA47B78D5F4" # change to your ID when testing!
|
||||
],
|
||||
|
||||
class ToxNode(Tox):
|
||||
|
||||
# where should we save Tox data to?
|
||||
datafile='echo.data'
|
||||
|
||||
# servers
|
||||
servers= [
|
||||
# public Tox bootstraping servers
|
||||
# IDs here are 64 characters long as they are pure public keys, without nospam+checksum parts:
|
||||
# https://libtoxcore.so/core_concepts.html
|
||||
# https://wiki.tox.im/Servers
|
||||
[
|
||||
"80.232.246.79",
|
||||
33445,
|
||||
"0B8DCEAA7BDDC44BB11173F987CAE3566A2D7057D8DD3CC642BD472B9391002A"
|
||||
],
|
||||
[
|
||||
"178.21.112.187",
|
||||
33445,
|
||||
"4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057"
|
||||
],
|
||||
[
|
||||
"80.232.246.79",
|
||||
33445,
|
||||
"0B8DCEAA7BDDC44BB11173F987CAE3566A2D7057D8DD3CC642BD472B9391002A"
|
||||
],
|
||||
]
|
||||
|
||||
# has the connection status been checked lately
|
||||
checked = False
|
||||
|
||||
# init
|
||||
def __init__(self, datafile='echo.data', server=False, name="EchoBot"):
|
||||
|
||||
# let parent do its thing
|
||||
Tox.__init__()
|
||||
|
||||
# set the datafile name
|
||||
self.datafile = datafile
|
||||
|
||||
# get the data, if the datafile exists
|
||||
if exists(self.datafile):
|
||||
self.load_from_file(self.datafile)
|
||||
|
||||
# do we have a server kwarg?
|
||||
if server:
|
||||
# use that
|
||||
self.servers = [server]
|
||||
|
||||
# yay, a name
|
||||
self.set_name(name)
|
||||
|
||||
# debug/info
|
||||
print('ID: %s' % self.get_address())
|
||||
|
||||
self.connect()
|
||||
# we don't want audio-video for now
|
||||
#self.av = AV(self, 1)
|
||||
|
||||
|
||||
# handle object teardown
|
||||
def __del__(self):
|
||||
# just save the data
|
||||
self.save_to_file(self.datafile)
|
||||
|
||||
|
||||
# make the connection
|
||||
def connect(self):
|
||||
print('connecting...')
|
||||
# use random from the available servers
|
||||
# if a single server has been provided, we'll use that anyway
|
||||
server = random.choice(self.servers)
|
||||
# bootstrap!
|
||||
self.bootstrap_from_address(server[0], server[1], server[2])
|
||||
|
||||
# this has to be called ~20 times per second
|
||||
def do(self):
|
||||
# check the status
|
||||
status = self.isconnected()
|
||||
|
||||
# should we inform about the fact that we're connected?
|
||||
if not checked and status:
|
||||
print('Connected to DHT.')
|
||||
checked = True
|
||||
|
||||
# should we connect, and inform about the fact that we're disconnected?
|
||||
if checked and not status:
|
||||
print('Disconnected from DHT.')
|
||||
self.connect()
|
||||
checked = False
|
||||
|
||||
# run the underlying Tox routine
|
||||
self.do()
|
||||
|
||||
|
||||
# handling friend request
|
||||
def on_friend_request(self, pk, message):
|
||||
print('Friend request from %s: %s' % (pk, message))
|
||||
self.add_friend_norequest(pk)
|
||||
print('Accepted.')
|
||||
|
||||
|
||||
# handling a message
|
||||
def on_friend_message(self, friendId, message):
|
||||
name = self.get_name(friendId)
|
||||
print('%s: %s' % (name, message))
|
||||
print('EchoBot: %s' % message)
|
||||
self.send_message(friendId, message)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Setup the command line arguments.
|
||||
|
||||
# get the datafile from cmdline args, if available
|
||||
if len(sys.argv) == 2:
|
||||
t = EchoBot(datafile=sys.argv[1])
|
||||
else:
|
||||
t = EchoBot()
|
||||
|
||||
# run
|
||||
while True:
|
||||
try:
|
||||
t.do()
|
||||
sleep(0.01)
|
||||
except KeyboardInterrupt:
|
||||
del t
|
Loading…
Reference in New Issue