11import socket
22import time
33import requests
4+ import gzip
45import asyncio
56import shutil
7+ import logging
68import os .path
79import geoip2 .database
810import geoip2 .errors
911
1012from cloudbot import hook
1113
14+ logger = logging .getLogger ("cloudbot" )
15+
1216DB_URL = "http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz"
1317PATH = "./data/GeoLite2-City.mmdb"
1418
1822def fetch_db ():
1923 r = requests .get (DB_URL , stream = True )
2024 if r .status_code == 200 :
21- with open (PATH , 'wb ' ) as f :
22- r . raw . decode_content = True
23- shutil .copyfileobj (r . raw , f )
25+ with gzip . open (r . raw , 'rb ' ) as infile :
26+ with open ( PATH , 'wb' ) as outfile :
27+ shutil .copyfileobj (infile , outfile )
2428
2529
26- def update_db ():
27- global geoip_reader
30+ def update_db (existing_reader ):
31+ """
32+ Updates the DB.
33+ """
2834 if os .path .isfile (PATH ):
2935 # check if file is over 2 weeks old
3036 if time .time () - os .path .getmtime (PATH ) > (14 * 24 * 60 * 60 ):
31- geoip_reader = None
37+ # geoip is outdated, re-download
3238 fetch_db ()
33- geoip_reader = geoip2 .database .Reader (PATH )
39+ return geoip2 .database .Reader (PATH )
3440 else :
35- if not geoip_reader :
36- geoip_reader = geoip2 .database .Reader (PATH )
41+ # geoip is good, create a reader or return existing reader
42+ if not existing_reader :
43+ try :
44+ return geoip2 .database .Reader (PATH )
45+ except :
46+ fetch_db ()
47+ return geoip2 .database .Reader (PATH )
48+ else :
49+ return existing_reader
3750 else :
38- geoip_reader = None
51+ # no geoip file
3952 fetch_db ()
40- geoip_reader = geoip2 .database .Reader (PATH )
53+ return geoip2 .database .Reader (PATH )
54+
55+
56+ def check_db (loop ):
57+ """
58+ runs update_db in an executor thread and sets geoip_reader to the result
59+ """
60+ global geoip_reader
61+ db = yield from loop .run_in_executor (None , update_db , geoip_reader )
62+ if db :
63+ geoip_reader = db
4164
4265
4366@asyncio .coroutine
4467@hook .onload
4568def load_geoip (loop ):
46- loop . run_in_executor ( None , update_db )
69+ asyncio . async ( check_db ( loop ), loop = loop )
4770
4871
72+ @asyncio .coroutine
4973@hook .command
50- def geoip (text , reply ):
74+ def geoip (text , reply , loop ):
75+ global geoip_reader
76+
5177 if not geoip_reader :
5278 return "GeoIP database is updating, please wait a minute"
5379
5480 try :
55- ip = socket .gethostbyname ( text )
81+ ip = yield from loop . run_in_executor ( None , socket .gethostbyname , text )
5682 except socket .gaierror :
5783 return "Invalid input."
5884
5985 try :
60- location_data = geoip_reader .city ( ip )
86+ location_data = yield from loop . run_in_executor ( None , geoip_reader .city , ip )
6187 except geoip2 .errors .AddressNotFoundError :
6288 return "Sorry, I can't locate that in my database."
6389
@@ -71,4 +97,4 @@ def geoip(text, reply):
7197 reply ("\x02 Country:\x02 {country} ({cc}), \x02 City:\x02 {city}{region}" .format (** data ))
7298
7399 # check the DB
74- update_db ( )
100+ asyncio . async ( check_db ( loop ), loop = loop )
0 commit comments