123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877 |
- #!/usr/bin/env python
- # Location Bot
- #
- # Douglas Thrift
- #
- # $Id$
- from ConfigParser import NoOptionError, SafeConfigParser
- from datetime import datetime, timedelta
- import getpass
- import ircbot
- import irclib
- import oauth2 as oauth
- import os
- import psycopg2
- import psycopg2.extensions
- import pytz
- import re
- import sys
- import time
- import threading
- import urllib, urllib2
- import urlparse
- import warnings
- try:
- import simplejson as json
- except ImportError:
- import json
- class LocationBot(ircbot.SingleServerIRCBot):
- def __init__(self, bot = None):
- self.__config = SafeConfigParser()
- self.__config.read('locationbot.ini')
- try:
- nick = self.__config.sections()[0]
- except IndexError:
- sys.exit('No nick configured')
- servers = []
- try:
- for server in self.__config.get(nick, 'servers').split():
- server = server.split(':', 2)
- if len(server) == 1:
- servers.append((server[0], 6667))
- else:
- host = server[0]
- port = int(server[1])
- ssl = server[1].startswith('+')
- if len(server) == 3:
- servers.append((host, port, ssl, server[2]))
- else:
- servers.append((host, port, ssl))
- self.__admins = self.__config.get(nick, 'admins').split()
- self.__channels = set(self.__config.get(nick, 'channels').split())
- self.__dsn = self.__config.get(nick, 'dsn')
- self.__latitude_key = self.__config.get(nick, 'latitude_key')
- self.__latitude_consumer = oauth.Consumer(key = self.__latitude_key, secret = self.__config.get(nick, 'latitude_secret'))
- except NoOptionError, error:
- sys.exit(error)
- try:
- self.__hostname = self.__config.get(nick, 'hostname')
- except NoOptionError:
- self.__hostname = ''
- ircbot.SingleServerIRCBot.__init__(self, servers, nick, 'Location Bot')
- if bot is None:
- self.__geocode_cache = {}
- self.__locations = []
- self.__logins = {}
- self.__nick = None
- self.__reloading = False
- else:
- self.__geocode_cache = bot.__geocode_cache
- irclibobj = self.ircobj.connections[0].irclibobj
- self.ircobj.connections[0] = bot.ircobj.connections[0]
- self.ircobj.connections[0].irclibobj = irclibobj
- self.channels = bot.channels
- self.connection = bot.connection
- self.__locations = bot.__locations
- self.__logins = bot.__logins
- self.__nick = bot.__nick
- self.__reloading = True
- self.__geocode_cache_lock = threading.Lock()
- self.__geocode_cache_clean_timer_lock = threading.Lock()
- self.__geocode_cache_clean_timer = None
- self.__latitude_granularities = frozenset(['city', 'best'])
- self.__latitude_timer_lock = threading.Lock()
- self.__latitude_timer = None
- self.__locations_lock = threading.Lock()
- self.__quiting = False
- self.__variables = frozenset(['nick', 'secret', 'masks', 'channels', 'timezone', 'location', 'coordinates', 'latitude'])
- self.__geocode_variables = self.__variables.intersection(['location', 'coordinates'])
- self.__lists = self.__variables.intersection(['masks', 'channels'])
- self.__unsetable = self.__variables.difference(['nick', 'secret'])
- def __admin(self, nickmask):
- return _or(lambda admin: irclib.mask_matches(nickmask, admin), self.__admins)
- def __channel(self, nick, exclude = None):
- if exclude is not None:
- exclude = irclib.irc_lower(exclude)
- channels = map(lambda channel: channel[1], filter(lambda channel: irclib.irc_lower(channel[0]) == exclude, self.channels))
- else:
- channels = self.channels.values()
- return _or(lambda channel: channel.has_user(nick), channels)
- def __db(self):
- db = psycopg2.connect(self.__dsn)
- def point(value, cursor):
- if value is not None:
- return tuple(map(lambda a: float(a), re.match(r'^\(([^)]+),([^)]+)\)$', value).groups()))
- psycopg2.extensions.register_type(psycopg2.extensions.new_type((600,), 'point', point), db)
- return db, db.cursor()
- def __geocode(self, sensor, coordinates = None, location = None):
- parameters = {'sensor': 'true' if sensor else 'false'}
- with self.__geocode_cache_lock:
- if coordinates is not None:
- try:
- return self.__geocode_cache[coordinates][0]
- except KeyError:
- parameters['latlng'] = '%f,%f' % coordinates
- else:
- try:
- return self.__geocode_cache[coordinates][0]
- except KeyError:
- parameters['address'] = location
- geocode = json.load(urllib2.urlopen('http://maps.google.com/maps/api/geocode/json?' + urllib.urlencode(parameters)))
- status = geocode['status']
- if status != 'OK':
- if coordinates is not None and status == 'ZERO_RESULTS':
- return self.__geocode(sensor, location = parameters['latlng'])
- else:
- raise Exception(status)
- results = geocode['results']
- def _result():
- _location = result['geometry']['location']
- geocode = (_location['lat'], _location['lng']), result['formatted_address']
- with self.__geocode_cache_lock:
- self.__geocode_cache[coordinates if coordinates is not None else location] = (geocode, datetime.utcnow())
- return geocode
- if coordinates is not None:
- types = frozenset([
- 'country',
- 'administrative_area_level_1',
- 'administrative_area_level_2',
- 'administrative_area_level_3',
- 'colloquial_area',
- 'locality',
- 'sublocality',
- 'neighborhood',
- ])
- for result in results:
- if not types.isdisjoint(result['types']):
- return _result()
- result = results[0]
- return _result()
- def __geocode_cache_clean(self):
- now = datetime.utcnow()
- try:
- while now < self.__geocode_cache_clean_next:
- time.sleep((self.__geocode_cache_clean_next - now).seconds)
- now = datetime.utcnow()
- except AttributeError:
- self.__geocode_cache_length = timedelta(hours = 1)
- self.__geocode_cache_clean_next = now.replace(minute = 2, second = 30, microsecond = 0) + self.__geocode_cache_length
- with self.__geocode_cache_lock:
- for location, (geocode, created) in self.__geocode_cache.items():
- if now - created >= self.__geocode_cache_length:
- del self.__geocode_cache[location]
- with self.__geocode_cache_clean_timer_lock:
- self.__geocode_cache_clean_timer = threading.Timer((self.__geocode_cache_clean_next - datetime.utcnow()).seconds, self.__latitude)
- self.__geocode_cache_clean_timer.start()
- def __help(self, connection, nick, admin, login, arguments):
- command = irclib.irc_lower(arguments.split(None, 1)[0].lstrip('!')) if arguments else None
- commands = {
- 'help': ('[command]', 'show this help message'),
- 'status': ('[nick]', 'show where everybody or a nick is'),
- }
- if not login:
- commands.update({
- 'login': ('[nick] [secret]', 'log in as nick with secret or using masks'),
- 'register': ('[nick] secret', 'register as nick with secret'),
- })
- else:
- commands.update({
- 'logout': ('', 'log out as nick'),
- 'set': ('[variable [value]]', 'display or set variables'),
- 'unset': ('variable', 'unset a variable'),
- })
- if admin:
- commands.update({
- 'join': ('channel', 'join a channel'),
- 'part': ('channel [message]', 'part from a channel'),
- 'quit': ('[message]', 'quit and do not come back'),
- 'reload': ('', 'reload with more up to date code'),
- 'restart': ('', 'quit and join running more up to date code'),
- 'say': ('nick|channel message', 'say message to nick or channel'),
- 'who': ('', 'show who is logged in'),
- })
- connection.privmsg(nick, '\x02command arguments description\x0f')
- def help(command, arguments, description):
- connection.privmsg(nick, '%-11s %-23s %s' % (command, arguments, description))
- if command in commands:
- help(command, *commands[command])
- else:
- for command, (arguments, description) in sorted(commands.iteritems()):
- help(command, arguments, description)
- def __join(self, connection, nick, arguments):
- try:
- channel = arguments.split(None, 1)[0]
- except IndexError:
- return self.__help(connection, nick, True, False, 'join')
- connection.join(channel)
- self.__channels.add(channel)
- self.__config.set(self._nickname, 'channels', ' '.join(self.__channels))
- self.__write()
- connection.privmsg(nick, 'successfully joined channel ("%s")' % channel)
- def __latitude(self, granularity = None, token = None, secret = None):
- if granularity is not None:
- response, content = oauth.Client(self.__latitude_consumer, oauth.Token(token, secret)).request('https://www.googleapis.com/latitude/v1/currentLocation?' + urllib.urlencode({'granularity': granularity}), 'GET')
- if int(response['status']) != 200:
- raise Exception(content.strip())
- data = json.loads(content)['data']
- coordinates = (data['latitude'], data['longitude'])
- return datetime.fromtimestamp(int(data['timestampMs']) / 1e3, pytz.utc), coordinates, data.get('accuracy'), data.get('speed'), data.get('heading'), data.get('altitude'), data.get('altitudeAccuracy'), self.__geocode(False, coordinates = coordinates)[1]
- now = datetime.utcnow()
- try:
- while now < self.__latitude_next:
- time.sleep((self.__latitude_next - now).seconds)
- now = datetime.utcnow()
- except AttributeError:
- pass
- self.__latitude_next = now.replace(minute = now.minute - now.minute % 5, second = 0, microsecond = 0) + timedelta(minutes = 5)
- try:
- db, cursor = self.__db()
- cursor.execute('select nick, channels, location, latitude.granularity, token, latitude.secret from locationbot.nick join locationbot.latitude using (id)')
- for nick, channels, old_location, granularity, token, secret in cursor.fetchall():
- try:
- updated, coordinates, accuracy, speed, heading, altitude, altitude_accuracy, new_location = self.__latitude(granularity, token, secret)
- except Exception, error:
- print error
- continue
- cursor.execute('update locationbot.nick set granularity = %s, location = %s, coordinates = point %s, updated = %s where nick = %s', (granularity, new_location, coordinates, updated, nick))
- db.commit()
- self.__location(nick, channels, granularity, old_location, new_location, coordinates)
- except psycopg2.Error, error:
- print error
- with self.__latitude_timer_lock:
- self.__latitude_timer = threading.Timer((self.__latitude_next - datetime.utcnow()).seconds, self.__latitude)
- self.__latitude_timer.start()
- def __location(self, nick, channels, granularity, old_location, new_location, coordinates):
- if channels and new_location and new_location != old_location:
- with self.__locations_lock:
- for channel in self.__channels.intersection(channels):
- self.__locations.append((nick, channel, granularity, new_location, coordinates))
- def __login(self, connection, nickmask, nick, arguments = ''):
- login = nick
- if connection is not None:
- arguments = arguments.split(None, 1)
- if len(arguments) == 2:
- login, secret = arguments
- elif len(arguments) == 1:
- secret = arguments[0]
- else:
- secret = None
- else:
- secret = None
- if nick in self.__logins:
- login = self.__logins[nick][0]
- if connection is not None:
- return connection.privmsg(nick, 'already logged in as "%s"' % login)
- return login
- db, cursor = self.__db()
- def success():
- connection.privmsg(nick, 'successfully logged in as "%s"' % login)
- if secret is not None:
- cursor.execute('select true from locationbot.nick where nick = %s and secret = md5(%s)', (login, secret))
- if cursor.rowcount == 1:
- self.__logins[nick] = (login, nickmask)
- return success()
- cursor.execute('select nick, masks from locationbot.nick where nick in (%s, %s)', (login, secret if len(arguments) != 2 else None))
- for login, masks in cursor.fetchall():
- if _or(lambda mask: irclib.mask_matches(nickmask, mask), masks):
- self.__logins[nick] = (login, nickmask)
- return success() if connection else login
- if connection is not None:
- return connection.privmsg(nick, 'failed to log in as "%s"' % login)
- def __logout(self, connection, nick):
- connection.privmsg(nick, 'logged out as "%s"' % self.__logins.pop(nick)[0])
- def __part(self, connection, nick, arguments):
- arguments = arguments.split(None, 1)
- if len(arguments) == 2:
- channel, message = arguments
- message = ':' + message
- elif len(arguments) == 1:
- channel = arguments[0]
- message = ''
- else:
- return self.__help(connection, nick, True, False, 'part')
- if channel in self.__channels:
- connection.part(channel, message)
- self.__channels.remove(channel)
- self.__config.set(self._nickname, 'channels', ' '.join(self.__channels))
- self.__write()
- connection.privmsg(nick, 'successfully parted channel ("%s")' % channel)
- else:
- connection.privmsg(nick, 'not in channel ("%s")' % channel)
- def __quit(self, connection, nick, arguments):
- self.__reloading = True
- self.__quiting = True
- connection.privmsg(nick, 'quiting')
- self.disconnect(arguments)
- def __register(self, connection, nick, arguments):
- arguments = arguments.split(None, 1)
- if len(arguments) == 2:
- login, secret = arguments
- elif len(arguments) == 1:
- login = nick
- secret = arguments[0]
- else:
- return self.__help(connection, nick, False, False, 'register')
- db, cursor = self.__db()
- try:
- cursor.execute('insert into locationbot.nick (nick, secret) values (%s, md5(%s))', (login, secret))
- db.commit()
- except psycopg2.IntegrityError:
- return connection.privmsg(nick, 'nick ("%s") is already registered' % login)
- connection.privmsg(nick, 'nick ("%s") sucessfully registered' % login)
- def __reload(self, connection, nick):
- self.__nick = nick
- self.__reloading = True
- connection.privmsg(nick, 'reloading')
- def __restart(self, connection):
- connection.disconnect('restarting')
- os.execvp(sys.argv[0], sys.argv)
- def __say(self, connection, nick, arguments):
- try:
- nick_channel, message = arguments.split(None, 1)
- except ValueError:
- return self.__help(connection, nick, True, False, 'say')
- if irclib.is_channel(nick_channel):
- if nick_channel not in self.channels:
- return connection.privmsg(nick, 'not in channel ("%s")' % nick_channel)
- elif not self.__channel(nick_channel):
- return connection.privmsg(nick, 'nick ("%s") not in channel(s)' % nick_channel)
- elif nick_channel == connection.get_nickname():
- return connection.privmsg(nick, 'nice try')
- connection.privmsg(nick_channel, message)
- connection.privmsg(nick, 'successfully sent message ("%s") to nick/channel ("%s")' % (message, nick_channel))
- def __set(self, connection, nickmask, nick, login, arguments):
- arguments = arguments.split(None, 1)
- if len(arguments) == 2:
- variable, value = arguments
- elif len(arguments) == 1:
- variable = arguments[0]
- value = None
- else:
- variable = None
- value = None
- if variable is not None and variable not in self.__variables:
- return self.__unknown_variable(connection, nick, variable)
- db, cursor = self.__db()
- if value is None:
- variables = sorted(self.__variables) if variable is None else [variable]
- cursor.execute('select ' + ', '.join(map(lambda variable: "'%s'" % ('*' * 8) if variable == 'secret' else 'latitude.granularity, latitude.authorized' if variable == 'latitude' else variable, variables)) + ' from locationbot.nick join locationbot.latitude using (id) where nick = %s', (login,))
- values = list(cursor.fetchone())
- try:
- index = variables.index('latitude')
- values[index:index + 2] = ['%s (%s)' % (values[index], 'authorized' if values[index + 1] else 'unauthorized')]
- except ValueError:
- pass
- connection.privmsg(nick, '\x02variable value\x0f')
- for variable, value in zip(variables, values):
- connection.privmsg(nick, '%-11s %s' % (variable, ' '.join(value) if isinstance(value, list) else '%f,%f' % value if isinstance(value, tuple) else value))
- else:
- def invalid(value, variable = variable):
- connection.privmsg(nick, 'invalid %s ("%s")' % (variable, value))
- if variable in self.__lists:
- value = value.split()
- if variable == 'channels':
- for channel in value:
- if not irclib.is_channel(channel) or channel not in self.__channels:
- return invalid(channel, 'channel')
- elif variable == 'masks':
- _mask = re.compile('^.+!.+@.+$')
- for mask in value:
- if not _mask.match(mask):
- return invalid(mask, 'mask')
- elif variable == 'latitude':
- if value in self.__latitude_granularities:
- response, content = oauth.Client(self.__latitude_consumer).request('https://www.google.com/accounts/OAuthGetRequestToken', 'POST', urllib.urlencode({
- 'scope': 'https://www.googleapis.com/auth/latitude',
- 'oauth_callback': 'oob',
- }))
- if int(response['status']) != 200:
- raise Exception(content.strip())
- authorized = False
- else:
- cursor.execute('select channels, location, latitude.granularity, token, latitude.secret from locationbot.nick join locationbot.latitude using (id) where nick = %s and authorized = false', (login,))
- if cursor.rowcount == 0:
- return invalid(value)
- channels, old_location, granularity, token, secret = cursor.fetchone()
- token = oauth.Token(token, secret)
- token.set_verifier(value)
- response, content = oauth.Client(self.__latitude_consumer, token).request('https://www.google.com/accounts/OAuthGetAccessToken', 'GET')
- status = int(response['status'])
- if status == 400:
- return invalid(value)
- elif status != 200:
- raise Exception(content.strip())
- authorized = True
- data = dict(urlparse.parse_qsl(content))
- token = data['oauth_token']
- secret = data['oauth_token_secret']
- if not authorized:
- connection.privmsg(nick, 'go to https://www.google.com/latitude/apps/OAuthAuthorizeToken?' + urllib.urlencode({
- 'domain': self.__latitude_key,
- 'granularity': value,
- 'oauth_token': token,
- }))
- elif variable in self.__geocode_variables:
- cursor.execute('select channels, location from locationbot.nick where nick = %s', (login,))
- channels, old_location = cursor.fetchone()
- if variable == 'location':
- coordinates = None
- granularity = 'city'
- location = value
- else:
- coordinates = value.split(None, 1)
- if len(coordinates) == 1:
- coordinates = coordinates[0].split(',', 1)
- try:
- coordinates = tuple(map(lambda a: float(a), coordinates))
- except ValueError:
- return invalid(value)
- for coordinate in coordinates:
- if not -180.0 <= coordinate <= 180.0:
- return invalid(value)
- granularity = 'best'
- location = None
- geocode = self.__geocode(False, coordinates, location)
- new_location = geocode[1]
- if variable == 'location':
- coordinates = geocode[0]
- value = new_location
- else:
- value = coordinates
- elif variable == 'nick':
- _nick = value.split(None, 1)
- if len(_nick) != 1:
- return invalid(value)
- elif variable == 'timezone':
- if value not in pytz.all_timezones_set:
- return invalid(value)
- try:
- if variable in self.__geocode_variables:
- cursor.execute('update locationbot.nick set granularity = %s, location = %s, coordinates = point %s, updated = now() where nick = %s', (granularity, new_location, coordinates, login))
- elif variable == 'latitude':
- if authorized:
- cursor.execute('update locationbot.latitude set token = %s, secret = %s, authorized = %s from locationbot.nick where latitude.id = nick.id and nick = %s', (token, secret, authorized, login))
- else:
- cursor.execute('delete from locationbot.latitude using locationbot.nick where latitude.id = nick.id and nick = %s', (login,))
- cursor.execute('insert into locationbot.latitude (id, granularity, token, secret, authorized) select id, %s, %s, %s, %s from locationbot.nick where nick = %s', (value, token, secret, authorized, login))
- else:
- cursor.execute('update locationbot.nick set ' + variable + ' = ' + ('md5(%s)' if variable == 'secret' else '%s') + ' where nick = %s', (value, login))
- db.commit()
- except psycopg2.IntegrityError:
- if variable == 'nick':
- return connection.privmsg(nick, 'nick ("%s") is already registered' % value)
- raise
- connection.privmsg(nick, 'variable ("%s") successfully set to value ("%s")' % (variable, ' '.join(value) if isinstance(value, list) else '%f,%f' % value if isinstance(value, tuple) else value))
- if variable == 'nick':
- self.__logins[nick] = (value, nickmask)
- elif variable in self.__geocode_variables or variable == 'latitude' and authorized:
- if variable == 'latitude':
- updated, coordinates, accuracy, speed, heading, altitude, altitude_accuracy, new_location = self.__latitude(granularity, token, secret)
- cursor.execute('update locationbot.nick set granularity = %s, location = %s, coordinates = point %s, updated = %s where nick = %s', (granularity, new_location, coordinates, updated, login))
- self.__location(login, channels, granularity, old_location, new_location, coordinates)
- def __status(self, connection, nick, login, arguments):
- _nick = arguments.split(None, 1)[0] if arguments else None
- db, cursor = self.__db()
- cursor.execute('select nick, location, coordinates, updated from locationbot.nick where ' + ('nick = %s and ' if _nick is not None else '') + 'location is not null order by updated desc', (_nick,))
- if cursor.rowcount == 0:
- return connection.privmsg(nick, 'no location information for ' + ('"%s"' % _nick if _nick is not None else 'anybody'))
- locations = cursor.fetchall()
- if login is not None:
- cursor.execute('select timezone from locationbot.nick where nick = %s and timezone is not null', (login,))
- timezone = pytz.timezone(cursor.fetchone()[0]) if cursor.rowcount == 1 else pytz.utc
- else:
- timezone = pytz.utc
- connection.privmsg(nick, '\x02nick location when map\x0f')
- for _nick, location, coordinates, updated in locations:
- connection.privmsg(nick, '%-23s %-35s %-23s %s' % (_nick, location, timezone.normalize(updated.astimezone(timezone)).strftime('%Y-%m-%d %H:%M %Z'), self.__url(_nick, 'best', location, coordinates)))
- def __unknown(self, connection, nick, command):
- connection.privmsg(nick, 'unknown command ("%s"); try "help"' % command)
- def __unknown_variable(self, connection, nick, variable):
- connection.privmsg(nick, 'unknown variable ("%s")' % variable)
- def __unset(self, connection, nick, login, arguments):
- try:
- variable = irclib.irc_lower(arguments.split(None, 1)[0])
- except IndexError:
- return self.__help(connection, nick, False, login, 'unset')
- if variable not in self.__unsetable:
- if variable in self.__variables:
- return connection.privmsg(nick, 'variable ("%s") is not unsetable' % variable)
- return self.__unknown_variable(connection, nick, variable)
- db, cursor = self.__db()
- cursor.execute('update locationbot.nick set ' + ('location = null, coordinates = null, updated = null' if variable in self.__geocode_variables else variable + ' = null') + ' where nick = %s and ' + variable + ' is not null', (login,))
- db.commit()
- connection.privmsg(nick, 'variable ("%s") %s unset' % (variable, 'successfuly' if cursor.rowcount == 1 else 'already'))
- def __url(self, nick, granularity, location, coordinates):
- if granularity == 'best':
- location = '%f,%f' % coordinates
- return 'http://maps.google.com/maps?' + urllib.urlencode({'q': '%s (%s)' % (location, nick)})
- def __who(self, connection, nick):
- if self.__logins:
- connection.privmsg(nick, '\x02login nick nick mask\x0f')
- for login in sorted(self.__logins.values()):
- connection.privmsg(nick, '%-23s %s' % login)
- else:
- connection.privmsg(nick, 'nobody logged in')
- def __write(self):
- with open('locationbot.ini', 'w') as config:
- self.__config.write(config)
- def _connect(self):
- if len(self.server_list[0]) != 2:
- ssl = self.server_list[0][2]
- else:
- ssl = False
- if len(self.server_list[0]) == 4:
- password = self.server_list[0][3]
- else:
- password = None
- try:
- with warnings.catch_warnings():
- warnings.filterwarnings('ignore', r'socket\.ssl\(\) is deprecated\. Use ssl\.wrap_socket\(\) instead\.', DeprecationWarning)
- self.connect(self.server_list[0][0], self.server_list[0][1], self._nickname, password, getpass.getuser(), self._realname, localaddress = self.__hostname, ssl = ssl)
- except irclib.ServerConnectionError:
- pass
- def disconnect(self, message = 'oh no!'):
- ircbot.SingleServerIRCBot.disconnect(self, message)
- def error(self, error):
- print error
- self.connection.privmsg(self.__nick, 'an error occured')
- def get_version(self):
- return 'locationbot ' + sys.platform
- def on_kick(self, connection, event):
- nick = event.arguments()[0]
- if not self.__channel(nick, event.target()):
- self.__logins.pop(nick, None)
- def on_nick(self, connection, event):
- nickmask = event.source()
- login = self.__logins.pop(irclib.nm_to_n(nickmask), (None,))[0]
- if login is not None:
- nick = event.target()
- self.__logins[nick] = (login, nick + '!' + nm_to_uh(nickmask))
- def on_nicknameinuse(self, connection, event):
- connection.nick(connection.get_nickname() + '_')
- def on_part(self, connection, event):
- nick = irclib.nm_to_n(event.source())
- if not self.__channel(nick, event.target()):
- self.__logins.pop(nick, None)
- def on_privmsg(self, connection, event):
- nickmask = event.source()
- nick = irclib.nm_to_n(nickmask)
- admin = self.__admin(nickmask)
- if not admin and not self.__channel(nick):
- return
- try:
- login = self.__login(None, nickmask, nick)
- try:
- command, arguments = event.arguments()[0].split(None, 1)
- except ValueError:
- command = event.arguments()[0].strip()
- arguments = ''
- command = irclib.irc_lower(command.lstrip('!'))
- if command == 'help':
- self.__help(connection, nick, admin, login, arguments)
- elif command == 'login':
- self.__login(connection, nickmask, nick, arguments)
- elif command == 'status':
- self.__status(connection, nick, login, arguments)
- elif not login and command == 'register':
- self.__register(connection, nick, arguments)
- elif login and command == 'logout':
- self.__logout(connection, nick)
- elif login and command == 'set':
- self.__set(connection, nickmask, nick, login, arguments)
- elif login and command == 'unset':
- self.__unset(connection, nick, login, arguments)
- elif admin and command == 'join':
- self.__join(connection, nick, arguments)
- elif admin and command == 'part':
- self.__part(connection, nick, arguments)
- elif admin and command == 'quit':
- self.__quit(connection, nick, arguments)
- elif admin and command == 'reload':
- self.__reload(connection, nick)
- elif admin and command == 'restart':
- self.__restart(connection)
- elif admin and command == 'say':
- self.__say(connection, nick, arguments)
- elif admin and command == 'who':
- self.__who(connection, nick)
- else:
- self.__unknown(connection, nick, command)
- except Exception, error:
- print error
- connection.privmsg(nick, 'an error occurred')
- def on_quit(self, connection, event):
- self.__logins.pop(irclib.nm_to_n(event.source()), None)
- def on_welcome(self, connection, event):
- for channel in self.__channels:
- connection.join(channel)
- def start(self):
- latitude_thread = threading.Thread(None, self.__latitude)
- latitude_thread.daemon = True
- latitude_thread.start()
- geocode_cache_clean_thread = threading.Thread(None, self.__geocode_cache_clean)
- geocode_cache_clean_thread.daemon = True
- geocode_cache_clean_thread.start()
- if not self.__reloading:
- self._connect()
- else:
- self.__reloading = False
- for channel in self.__channels.symmetric_difference(self.channels.keys()):
- if channel in self.__channels:
- self.connection.join(channel)
- else:
- self.connection.part(channel)
- while not self.__reloading:
- if self.__locations_lock.acquire(False):
- if self.__locations and self.__channels.issubset(self.channels.keys()):
- for nick, channel, granularity, location, coordinates in self.__locations:
- self.connection.notice(channel, '%s is in %s %s' % (nick, location, self.__url(nick, granularity, location, coordinates)))
- self.__locations = []
- self.__locations_lock.release()
- self.ircobj.process_once(0.2)
- latitude_thread.join()
- geocode_cache_clean_thread.join()
- with self.__latitude_timer_lock:
- if self.__latitude_timer is not None:
- self.__latitude_timer.cancel()
- self.__latitude_timer.join()
- with self.__geocode_cache_clean_timer_lock:
- if self.__geocode_cache_clean_timer is not None:
- self.__geocode_cache_clean_timer.cancel()
- self.__geocode_cache_clean_timer.join()
- return not self.__quiting
- def success(self):
- self.connection.privmsg(self.__nick, 'successfully reloaded')
- def _or(function, values):
- for value in values:
- if function(value):
- return True
- return False
- if __name__ == '__main__':
- os.chdir(os.path.abspath(os.path.dirname(__file__)))
- pid = os.fork()
- if pid != 0:
- with open('locationbot.pid', 'w') as _file:
- _file.write('%u\n' % pid)
- sys.exit(0)
- sys.stdin = open('/dev/null')
- sys.stdout = open('locationbot.log', 'a', 1)
- sys.stderr = sys.stdout
- import locationbot
- bot = locationbot.LocationBot()
- try:
- while bot.start():
- import locationbot
- try:
- bot = reload(locationbot).LocationBot(bot)
- except (ImportError, SyntaxError), error:
- bot.error(error)
- else:
- bot.success()
- except KeyboardInterrupt:
- bot.disconnect()
- os.unlink('locationbot.pid')
|