[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [gettor/master] Better debugging, error handling and mirrors option for xmpp
commit d71e6838bda2a158963228f321d1cb682c5f4297
Author: ilv <ilv@xxxxxxxxxxxxxxxxxxxxxxxx>
Date: Thu Aug 27 15:17:21 2015 -0300
Better debugging, error handling and mirrors option for xmpp
---
gettor/xmpp.py | 200 +++++++++++++++++++++--------------
lang/xmpp/i18n/en/LC_MESSAGES/en.po | 72 +++++++++++--
process_chat.py | 22 ++++
xmpp.cfg | 2 +
4 files changed, 207 insertions(+), 89 deletions(-)
diff --git a/gettor/xmpp.py b/gettor/xmpp.py
index e9d2a10..f9b025d 100644
--- a/gettor/xmpp.py
+++ b/gettor/xmpp.py
@@ -5,8 +5,8 @@
# :authors: Israel Leiva <ilv@xxxxxxxxxx>
# see also AUTHORS file
#
-# :copyright: (c) 2008-2014, The Tor Project, Inc.
-# (c) 2014, Israel Leiva
+# :copyright: (c) 2008-2015, The Tor Project, Inc.
+# (c) 2015, Israel Leiva
#
# :license: This is Free Software. See LICENSE for license information.
@@ -20,6 +20,7 @@ import logging
import ConfigParser
from sleekxmpp import ClientXMPP
+from sleekxmpp.xmlstream.stanzabase import JID
from sleekxmpp.exceptions import IqError, IqTimeout
import core
@@ -30,6 +31,14 @@ import blacklist
"""XMPP module for processing requests."""
+class ConfigError(Exception):
+ pass
+
+
+class InternalError(Exception):
+ pass
+
+
class Bot(ClientXMPP):
"""XMPP bot.
@@ -52,10 +61,11 @@ class Bot(ClientXMPP):
self.get_roster()
except IqError as err:
# error getting the roster
- # logging.error(err.iq['error']['condition'])
+ self.xmpp.log.error(err.iq['error']['condition'])
self.disconnect()
except IqTimeout:
# server is taking too long to respond
+ self.xmpp.log.error("Server is taking too long to respond")
self.disconnect()
def message(self, msg):
@@ -65,14 +75,6 @@ class Bot(ClientXMPP):
msg.reply(msg_to_send).send()
-class ConfigError(Exception):
- pass
-
-
-class InternalError(Exception):
- pass
-
-
class XMPP(object):
"""Receive and reply requests by XMPP.
@@ -95,75 +97,63 @@ class XMPP(object):
"""
# define a set of default values
- DEFAULT_CONFIG_FILE = 'xmpp.cfg'
-
- logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(message)s',
- datefmt="%Y-%m-%d %H:%M:%S")
- log = logging.getLogger(__name__)
+ default_cfg = 'xmpp.cfg'
config = ConfigParser.ConfigParser()
if cfg is None or not os.path.isfile(cfg):
- cfg = DEFAULT_CONFIG_FILE
-
- config.read(cfg)
+ cfg = default_cfg
try:
- self.user = config.get('account', 'user')
- except ConfigParser.Error as e:
- raise ConfigError("Couldn't read 'user' from 'account'")
+ with open(cfg) as f:
+ config.readfp(f)
+ except IOError:
+ raise ConfigError("File %s not found!" % cfg)
try:
+ self.user = config.get('account', 'user')
self.password = config.get('account', 'password')
- except ConfigParser.Error as e:
- raise ConfigError("Couldn't read 'password' from 'account'")
- try:
- self.core_cfg = config.get('general', 'core_cfg')
- except ConfigParser.Error as e:
- raise ConfigError("Couldn't read 'core_cfg' from 'general'")
+ self.mirrors = config.get('general', 'mirrors')
+ self.max_words = config.get('general', 'max_words')
+ self.max_words = int(self.max_words)
+ core_cfg = config.get('general', 'core_cfg')
+ self.core = core.Core(core_cfg)
+ self.i18ndir = config.get('i18n', 'dir')
- try:
blacklist_cfg = config.get('blacklist', 'cfg')
- self.bl = blacklist_cfg
- except ConfigParser.Error as e:
- raise ConfigError("Couldn't read 'cfg' from 'blacklist'")
-
- try:
+ self.bl = blacklist.Blacklist(blacklist_cfg)
self.bl_max_req = config.get('blacklist', 'max_requests')
self.bl_max_req = int(self.bl_max_req)
- except ConfigParser.Error as e:
- raise ConfigError("Couldn't read 'max_requests' from 'blacklist'")
-
- try:
self.bl_wait_time = config.get('blacklist', 'wait_time')
self.bl_wait_time = int(self.bl_wait_time)
- except ConfigParser.Error as e:
- raise ConfigError("Couldn't read 'wait_time' from 'blacklist'")
-
- try:
- self.i18ndir = config.get('i18n', 'dir')
- except ConfigParser.Error as e:
- raise ConfigError("Couldn't read 'dir' from 'i18n'")
- try:
logdir = config.get('log', 'dir')
logfile = os.path.join(logdir, 'xmpp.log')
- except ConfigParser.Error as e:
- raise ConfigError("Couldn't read 'dir' from 'log'")
-
- try:
loglevel = config.get('log', 'level')
+
except ConfigParser.Error as e:
- raise ConfigError("Couldn't read 'level' from 'log'")
+ raise ConfigError("Configuration error: %s" % str(e))
+ except blacklist.ConfigError as e:
+ raise InternalError("Blacklist error: %s" % str(e))
+ except core.ConfigError as e:
+ raise InternalError("Core error: %s" % str(e))
+
+ # logging
+ log = logging.getLogger(__name__)
- # establish log level and redirect to log file
- log.info('Redirecting logging to %s' % logfile)
+ logging_format = utils.get_logging_format()
+ date_format = utils.get_date_format()
+ formatter = logging.Formatter(logging_format, date_format)
+
+ log.info('Redirecting XMPP logging to %s' % logfile)
logfileh = logging.FileHandler(logfile, mode='a+')
+ logfileh.setFormatter(formatter)
logfileh.setLevel(logging.getLevelName(loglevel))
log.addHandler(logfileh)
# stop logging on stdout from now on
log.propagate = False
+ self.log = log
def start_bot(self):
"""Start the bot for handling requests.
@@ -171,6 +161,7 @@ class XMPP(object):
Start a new sleekxmpp bot.
"""
+ self.log.info("Starting the bot with account %s" % self.user)
xmpp = Bot(self.user, self.password, self)
xmpp.connect()
xmpp.process(block=True)
@@ -184,11 +175,11 @@ class XMPP(object):
"""
anon_acc = utils.get_sha256(account)
- bl = blacklist.Blacklist(self.bl)
try:
- bl.is_blacklisted(anon_acc, 'XMPP', self.bl_max_req,
- self.bl_wait_time)
+ self.bl.is_blacklisted(
+ anon_acc, 'XMPP', self.bl_max_req, self.bl_wait_time
+ )
return False
except blacklist.BlacklistError as e:
return True
@@ -203,13 +194,17 @@ class XMPP(object):
"""
# obtain the content in the proper language
- t = gettext.translation(lc, self.i18ndir, languages=[lc])
- _ = t.ugettext
+ self.log.debug("Trying to get translated text")
+ try:
+ t = gettext.translation(lc, self.i18ndir, languages=[lc])
+ _ = t.ugettext
- msgstr = _(msgid)
- return msgstr
+ msgstr = _(msgid)
+ return msgstr
+ except IOError as e:
+ raise ConfigError("%s" % str(e))
- def _parse_text(self, msg, core_obj):
+ def _parse_text(self, msg):
"""Parse the text part of a message.
Split the message in words and look for patterns for locale,
@@ -223,20 +218,21 @@ class XMPP(object):
"""
# core knows what OS are supported
- supported_os = core_obj.get_supported_os()
- supported_lc = core_obj.get_supported_lc()
+ supported_os = self.core.get_supported_os()
+ supported_lc = self.core.get_supported_lc()
+ self.log.debug("Parsing text")
# default values
req = {}
req['lc'] = 'en'
req['os'] = None
req['type'] = 'help'
+
found_lc = False
found_os = False
+ found_mirrors = False
# analyze every word
- # request shouldn't be more than 10 words long, so there should
- # be a limit for the amount of words
for word in msg.split(' '):
# look for lc and os
if not found_lc:
@@ -250,6 +246,14 @@ class XMPP(object):
found_os = True
req['os'] = os
req['type'] = 'links'
+ # mirrors
+ if not found_mirrors:
+ if re.match("mirrors?", word, re.IGNORECASE):
+ found_mirrors = True
+ req['type'] = 'mirrors'
+ if (found_lc and found_os) or (found_lc and found_mirrors):
+ break
+
return req
def parse_request(self, account, msg):
@@ -269,36 +273,70 @@ class XMPP(object):
reply = ''
status = ''
req = None
- core_obj = core.Core(self.core_cfg)
+ self.log.debug("Parsing request")
try:
if self._is_blacklisted(str(account)):
+ self.log.info("Request from blacklisted account!")
status = 'blacklisted'
bogus_request = True
+ # first let's find out how many words are in the message
+ # request shouldn't be longer than 3 words, but just in case
+ words = msg.split(' ')
+ if len(words) > self.max_words:
+ bogus_request = True
+ self.log.info("Message way too long")
+ status = 'error'
+ reply = self._get_msg('message_error', 'en')
+
if not bogus_request:
+ self.log.debug("Request seems legit, let's parse it")
# let's try to guess what the user is asking
- req = self._parse_text(str(msg), core_obj)
+ req = self._parse_text(str(msg))
if req['type'] == 'help':
+ self.log.debug("Type of request: help")
status = 'success'
- reply = self._get_msg('help', req['lc'])
- elif req['type'] == 'links':
+ reply = self._get_msg('help', 'en')
+
+ elif req['type'] == 'mirrors':
+ self.log.debug("Type of request: mirrors")
+ status = 'success'
+ reply = self._get_msg('mirrors', 'en')
try:
- links = core_obj.get_links("XMPP", req['os'],
- req['lc'])
- reply = self._get_msg('links', req['lc'])
- reply = reply % (req['os'], req['lc'], links)
-
- status = 'success'
- except (core.ConfigError, core.InternalError) as e:
- # if core failes, send the user an error message, but
- # keep going
- status = 'core_error'
- reply = self._get_msg('internal_error', req['lc'])
+ with open(self.mirrors, "r") as list_mirrors:
+ mirrors = list_mirrors.read()
+ reply = reply % mirrors
+ except IOError as e:
+ reply = self._get_msg('mirrors_unavailable', 'en')
+
+ elif req['type'] == 'links':
+ self.log.debug("Type of request: help")
+ links = self.core.get_links(
+ "XMPP",
+ req['os'],
+ req['lc']
+ )
+ reply = self._get_msg('links', 'en')
+ reply = reply % (req['os'], req['lc'], links)
+
+ status = 'success'
+
+ except (core.ConfigError, core.InternalError) as e:
+ # if core failes, send the user an error message, but keep going
+ self.log.error("Something went wrong internally: %s" % str(e))
+ status = 'core_error'
+ reply = self._get_msg('internal_error', req['lc'])
+
finally:
# keep stats
if req:
- core_obj.add_request_to_db()
+ self.log.debug("Adding request to database... ")
+ self.core.add_request_to_db()
+ if reply:
+ self.log.debug("Everything seems OK. Sending back the reply")
+ else:
+ self.log.debug("Nothing to reply!")
return reply
diff --git a/lang/xmpp/i18n/en/LC_MESSAGES/en.po b/lang/xmpp/i18n/en/LC_MESSAGES/en.po
index d242625..8cc715e 100644
--- a/lang/xmpp/i18n/en/LC_MESSAGES/en.po
+++ b/lang/xmpp/i18n/en/LC_MESSAGES/en.po
@@ -2,21 +2,77 @@ domain "en"
#: Links
msgid "links"
-msgstr "Links %s-%s:\n %s"
+msgstr "Hello there! this is the 'GetTor' robot.\n\
+\n\
+Below are the links for your request (Tor Browser for %s, %s package):\n\
+\n\
+%s\n\
+\n\
+===========================================================================\n\
+Still need help? If you have any questions, trouble connecting to Tor\n\
+network, or need to talk to a human, please contact our support team at:\n\
+\n\
+ help@xxxxxxxxxxxxxxxxx\n\
+\n\
+We are ready to answer your queries in English, Farsi, Chinese, Arabic,\n\
+French and Spanish."
-#: Links
-msgid "links_pt"
-msgstr "Links-PT for %s-%s:\n %s"
+#: Mirrors message
+msgid "mirrors"
+msgstr "Hello there! this is the 'GetTor' robot.\n\
+\n\
+Thank you for your request. Below you will find an updated list of mirrors\n\
+of Tor Project's website.\n\
+\n\
+%s\n\
+\n\
+Still need help? If you have any questions, trouble connecting to Tor\n\
+network, or need to talk to a human, please contact our support team at:\n\
+\n\
+ help@xxxxxxxxxxxxxxxxx\n\
+\n\
+We are ready to answer your queries in English, Farsi, Chinese, Arabic,\n\
+French and Spanish."
#: Help
msgid "help"
-msgstr "*help*"
+msgstr "Hello there! this is the 'GetTor' robot.\n\
+\n\
+Thank you for your request. I am here to help you download the latest\n\
+Tor Browser.\n\
+\n\
+Please reply to this message with one of the options below (where lc stands\n\
+for the locale of the package you want e.g. en):\n\
+\n\
+ windows lc\n\
+ linux lc\n\
+ osx lc\n\
+ mirrors\n\
+\n\
+The currently supported locales are:\n\
+\n\
+ en: English\n\
+\n\
+And I will send you the download/access instructions quickly."
-#: Unsupported locale
-msgid "unsupported_lc"
-msgstr "Unsupported locale"
+#: Mirrors unavailable message
+msgid "mirrors_unavailable"
+msgstr "Hello there! this is the 'GetTor' robot.\n\
+\n\
+I'm sorry, I can't send you mirrors right now, please try again later.\n\
+\n\
+Still need help? If you have any questions, trouble connecting to Tor\n\
+network, or need to talk to a human, please contact our support team at:\n\
+\n\
+ help@xxxxxxxxxxxxxxxxx\n\
+\n\
+We are ready to answer your queries in English, Farsi, Chinese, Arabic,\n\
+French and Spanish."
#: Internal error
msgid "internal_error"
msgstr "Internal error"
+#: Internal error
+msgid "message_error"
+msgstr "Message too long."
diff --git a/process_chat.py b/process_chat.py
new file mode 100644
index 0000000..6ad7b59
--- /dev/null
+++ b/process_chat.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+import logging
+
+import gettor.xmpp
+
+def main():
+ try:
+ bot = gettor.xmpp.XMPP()
+ bot.start_bot()
+ except gettor.xmpp.ConfigError as e:
+ print "Configuration error: %s" % str(e)
+ except gettor.xmpp.InternalError as e:
+ print "Core module not working: %s" % str(e)
+ except Exception as e:
+ # in case something unexpected happens
+ print "Unexpected error: %s" % str(e)
+
+if __name__ == '__main__':
+ main()
diff --git a/xmpp.cfg b/xmpp.cfg
index 0b2f547..d81d4df 100644
--- a/xmpp.cfg
+++ b/xmpp.cfg
@@ -5,6 +5,8 @@ password:
[general]
basedir: /path/to/gettor/xmpp/
core_cfg: /path/to/core.cfg
+max_words: 10
+db: /path/to/gettor.db
[blacklist]
cfg: /path/to/blacklist.cfg
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits