### # Copyright (c) SpiderDave # Copyright (c) 2020, oddluck # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions, and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions, and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the author of this software nor the name of # contributors to this software may be used to endorse or promote products # derived from this software without specific prior written consent. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. ### import time import supybot.conf as conf import supybot.utils as utils from supybot.commands import * import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks import os, errno import supybot.registry as registry import supybot.ircdb as ircdb import tarfile import gzip import socket try: import geoip2.database except: raise ImportError("The Geo plugin requires geoip2 be installed. Load aborted.") class Geo(callbacks.Plugin): threaded = True """ Geolocation using GeoLiteCity """ def make_sure_path_exists(self, path): try: os.makedirs(path) except OSError as exception: if exception.errno != errno.EEXIST: raise def geo(self, irc, msg, args, stuff): """[ | | ] Geolocation of an ip, hostname, or nick. Nick must be in channel. """ channel = msg.args[0] try: reader = geoip2.database.Reader( "%s%sgeo%sGeoLite2-City.mmdb" % (conf.supybot.directories.data(), os.sep, os.sep) ) except: irc.reply( "Error: GeoLite2-City database not found, attempting to update..." ) try: self.do_update() irc.reply("Update finished.") except: irc.reply("Update failed.") return if not irc.isChannel(channel): private = True else: private = False if not private and stuff.lower() in ( nick.lower() for nick in list(irc.state.channels[channel].users) ): try: stuff = irc.state.nickToHostmask(stuff).split("@")[1] ip = socket.gethostbyname(stuff) except: irc.reply("Invalid hostname {0}".format(stuff)) return elif not utils.net.isIP(stuff): try: ip = socket.gethostbyname(stuff) except: irc.reply("Invalid nick/hostname {0}".format(stuff)) return elif utils.net.isIP(stuff): ip = stuff else: irc.reply("invalid nick or hostname/ip {0}".format(stuff)) try: res = reader.city(ip) if res: irc.reply( "%s, %s, %s" % ( res.city.name, res.subdivisions.most_specific.name, res.country.name, ) ) else: irc.reply("No results found") except: irc.reply("No results found") geo = wrap(geo, ["text"]) def update(self, irc, msg, args): """ Update geoip database """ try: irc.reply("Updating data file...") self.do_update() irc.reply("Update finished.") except: irc.reply("Update failed.") return update = wrap(update) def do_update(self): """update the geo files""" now = int(time.time()) try: lastupdate = self.registryValue("datalastupdated") self.log.info("Last update: %s seconds ago." % lastupdate) except registry.NonExistentRegistryEntry: self.log.info( "supybot.plugins.%s.datalastupdate not set. Creating..." % self.name ) conf.registerGlobalValue( conf.supybot.plugins.get(self.name), "datalastupdated", registry.PositiveInteger( 1, """An integer representing the time since epoch the .dat file was last updated.""", ), ) self.log.info("...success!") lastupdate = 1 self.log.info("Last update: Unknown/Never") # if (now-lastupdate)>604800: # updated weekly if 1 == 1: self.setRegistryValue("datalastupdated", now) self.log.info("Starting update of Geo data files...") self.getfile() return def getfile(self): """grabs the data file""" license = self.registryValue("licenseKey") u = "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key={}&suffix=tar.gz".format( license ) f = "%s%sgeo%sGeoLite2-City.tar.gz" % ( conf.supybot.directories.data(), os.sep, os.sep, ) f2 = "%s%sgeo%sGeoLite2-City.tar" % ( conf.supybot.directories.data(), os.sep, os.sep, ) self.make_sure_path_exists( r"%s%sgeo" % (conf.supybot.directories.data(), os.sep) ) path = "%s%sgeo" % (conf.supybot.directories.data(), os.sep) self.log.info("Starting download: %s" % f) h = utils.web.getUrl(u) if h: tempfile = open(f, "w+b") if tempfile: tempfile.write(h) tempfile.close() self.log.info("Completed: %s" % f) self.log.info("Unzipping: %s" % f) f_in = gzip.open(f, "rb") f_out = open(f2, "wb") f_out.writelines(f_in) f_out.close() f_in.close() self.log.info("Finished Unzipping: %s" % f) self.log.info("Untarring: %s" % f2) tar = tarfile.open(f2) tar.getmembers() for member in tar.getmembers(): if "GeoLite2-City.mmdb" in member.name: member.name = "GeoLite2-City.mmdb" self.log.info(member.name) tar.extract(member, path=path) self.log.info("Finished Untarring: %s" % f2) tar.close() os.remove(f) os.remove(f2) else: self.log.info("Could not download: %s" % f) return Class = Geo