Skip to content
Browse files

Renamed "Packet" into "Message".

* BufferingBot.py, inkblot.py

Moved some settings from inkblot.py to config.py
* inkblot.py, config.py

Fixed some problems of ipw plugin
* plugins/ipw.py

Created lib/ directory, imported some files
* lib/*
* plugins/ipw.py

git-svn-id: https://dev.upnl.org/svn/ircbot2/trunk@46 66f668a4-574d-4515-85ec-6e73d9c367eb
  • Loading branch information...
1 parent fefe7c5 commit b0877968c34338e5e2e292654920a7f7c8986a92 klutzy committed
Showing with 310 additions and 72 deletions.
  1. +53 −53 BufferingBot.py
  2. +5 −1 config.py
  3. +28 −14 inkblot.py
  4. 0 lib/__init__.py
  5. +215 −0 lib/whois.py
  6. +9 −4 plugins/ipw.py
View
106 BufferingBot.py
@@ -21,30 +21,30 @@ def new_f(self, *args):
return new_f
return decorator
-class Packet():
+class Message():
def __init__(self, command, arguments, timestamp=None):
self.command = command
self.arguments = arguments
self.timestamp = time.time() if timestamp is None else timestamp
def __repr__(self):
- return '<Packet %s %s %s>' % (
+ return '<Message %s %s %s>' % (
repr(self.command),
repr(self.arguments),
repr(self.timestamp)
)
- def __cmp__(self, packet):
- return cmp(self.timestamp, packet.timestamp)
+ def __cmp__(self, message):
+ return cmp(self.timestamp, message.timestamp)
def is_system_message(self):
if self.command in ['privmsg', 'privnotice']:
return self.arguments[1].startswith('--') # XXX
return False
-class PacketBuffer(object):
- """Buffer of Packet objects, sorted by their timestamp.
- If some of its Packet's timestamp lags over self.timeout, it purges all the queue.
+class MessageBuffer(object):
+ """Buffer of Message objects, sorted by their timestamp.
+ If some of its Message's timestamp lags over self.timeout, it purges all the queue.
Note that this uses heapq mechanism hence not thread-safe.
"""
@@ -61,8 +61,8 @@ def _dump(self):
def peek(self):
return self.heap[0]
- def push(self, packet):
- return heapq.heappush(self.heap, packet)
+ def push(self, message):
+ return heapq.heappush(self.heap, message)
def _pop(self):
if not self.heap:
@@ -78,29 +78,29 @@ def purge(self):
stale = time.time() - self.timeout
line_counts = collections.defaultdict(int)
while self.heap:
- packet = self.peek()
- if packet.timestamp > stale:
+ message = self.peek()
+ if message.timestamp > stale:
break
- if packet.command in ['join']: # XXX
+ if message.command in ['join']: # XXX
break
- packet = self._pop()
- if packet.command in ['privmsg', 'privnotice']:
+ message = self._pop()
+ if message.command in ['privmsg', 'privnotice']:
try:
- target, message = packet.arguments
+ target, message = message.arguments
except:
traceback.print_exc()
- self.push(packet)
+ self.push(message)
return
- if not packet.is_system_message():
+ if not message.is_system_message():
line_counts[target] += 1
for target, line_count in line_counts.iteritems():
message = "-- Message lags over %f seconds. Skipping %d line(s).." \
% (self.timeout, line_count)
- packet = Packet(
+ message = Message(
command = 'privmsg',
arguments = (target, message)
)
- self.push(packet)
+ self.push(message)
def has_buffer_by_command(self, command):
return any(_.command == command for _ in self.heap)
@@ -111,7 +111,7 @@ def __init__(self, network_list, nickname, realname,
ircbot.SingleServerIRCBot.__init__(self, network_list, nickname,
realname, reconnection_interval,
use_ssl)
- self.buffer = PacketBuffer(10.0)
+ self.buffer = MessageBuffer(10.0)
self.last_tick = 0
self.on_tick()
@@ -121,13 +121,13 @@ def on_tick(self):
return
self.flood_control()
- def get_delay(self, packet):
+ def get_delay(self, message):
# TODO: per-network configuration
delay = 0
- if packet.command == 'privmsg':
+ if message.command == 'privmsg':
delay = 2
try:
- target, msg = packet.arguments
+ target, msg = message.arguments
delay = 0.5 + len(msg) / 35.
except:
traceback.print_exc()
@@ -136,13 +136,13 @@ def get_delay(self, packet):
return delay
def flood_control(self):
- """Delays message according to the length of packet.
+ """Delays message according to the length of message.
As you see, this doesn't acquire any lock hence thread-unsafe.
"""
if not self.connection.is_connected():
self._connect()
return
- packet = None
+ message = None
local = False
if len(self.buffer):
print '--- buffer ---'
@@ -152,52 +152,52 @@ def flood_control(self):
def pop_buffer(self, buffer):
if not buffer:
return
- packet = buffer.peek()
- if packet.command == 'privmsg':
+ message = buffer.peek()
+ if message.command == 'privmsg':
try:
- target, msg = packet.arguments
+ target, msg = message.arguments
if irclib.is_channel(target) and target not in self.channels:
return
except:
traceback.print_exc()
return
- delay = self.get_delay(packet)
+ delay = self.get_delay(message)
tick = time.time()
if self.last_tick + delay > tick:
return
- self.process_packet(packet)
- packet_ = buffer.pop()
- if packet != packet_:
- print packet
- print packet_
+ self.process_message(message)
+ message_ = buffer.pop()
+ if message != message_:
+ print message
+ print message_
assert False
self.last_tick = tick
- def process_packet(self, packet):
+ def process_message(self, message):
try:
if False:
pass
- elif packet.command == 'join':
- self.connection.join(*packet.arguments)
- elif packet.command == 'mode':
- self.connection.mode(*packet.arguments)
- elif packet.command == 'privmsg':
- self.connection.privmsg(*packet.arguments)
- elif packet.command == 'privnotice':
- self.connection.privnotice(*packet.arguments)
- elif packet.command == 'topic':
- self.connection.topic(*packet.arguments)
- elif packet.command == 'who':
- self.connection.who(*packet.arguments)
- elif packet.command == 'whois':
- self.connection.whois(*packet.arguments)
+ elif message.command == 'join':
+ self.connection.join(*message.arguments)
+ elif message.command == 'mode':
+ self.connection.mode(*message.arguments)
+ elif message.command == 'privmsg':
+ self.connection.privmsg(*message.arguments)
+ elif message.command == 'privnotice':
+ self.connection.privnotice(*message.arguments)
+ elif message.command == 'topic':
+ self.connection.topic(*message.arguments)
+ elif message.command == 'who':
+ self.connection.who(*message.arguments)
+ elif message.command == 'whois':
+ self.connection.whois(*message.arguments)
except irclib.ServerNotConnectedError:
- self.push_packet(packet)
+ self.push_message(message)
self._connect()
except:
traceback.print_exc()
- self.push_packet(packet)
+ self.push_message(message)
- def push_packet(self, packet):
- self.buffer.push(packet)
+ def push_message(self, message):
+ self.buffer.push(message)
View
6 config.py
@@ -1,4 +1,8 @@
# coding: utf-8
{
- 'version': 2010010801, # increment this and save to reload
+ 'version': 2010010805, # increment this and save to reload
+
+ 'channels': ['#wikipedia-ko'],
+ 'server': ('irc.freenode.net', 6666),
+ 'nickname': 'inkblot',
}
View
42 inkblot.py
@@ -7,9 +7,9 @@
import collections
import irclib
-irclib.DEBUG = 1
+irclib.DEBUG = 0
-from BufferingBot import BufferingBot, Packet
+from BufferingBot import BufferingBot, Message
def periodic(period):
"""Decorate a class instance method so that the method would be
@@ -28,25 +28,36 @@ def new_f(self, *args):
class Inkblot(BufferingBot):
def __init__(self, config_file_name):
- BufferingBot.__init__(self, [('irc.freenode.net', 6665)], 'inkblot', 'bot run by wikipedia-ko/PuzzletChung')
- self.plugins = []
+ self.config = None
self.config_file_name = config_file_name
+ self.config_timestamp = self.get_config_time()
+ self.config = eval(open(self.config_file_name).read())
+
+ server = self.config['server']
+ nickname = self.config['nickname']
+ BufferingBot.__init__(self, [server], nickname, 'bot')
+
+ self.plugins = []
+
self.handlers = collections.defaultdict(list)
- self.config_timestamp = os.stat(self.config_file_name).st_mtime
- data = eval(open(self.config_file_name).read())
- self.version = data['version']
+
+ self.version = self.config['version']
self.load_plugins()
+
self.connection.add_global_handler('welcome', self._on_connected)
self.check_config_file()
def _on_connected(self, connection, event):
- self.connection.join('#puzzlet')
- self.connection.join('#wikipedia-ko')
+ for chan in self.config['channels']:
+ self.connection.join(chan)
+
+ def get_config_time(self):
+ return os.stat(self.config_file_name).st_mtime
@periodic(1)
def check_config_file(self):
try:
- t = os.stat(self.config_file_name).st_mtime
+ t = self.get_config_time()
if t <= self.config_timestamp:
return
self.reload()
@@ -66,16 +77,18 @@ def reply(self, event, message):
source = irclib.nm_to_n(source)
if eventtype in ('privmsg', 'pubmsg'):
reply_to = target if irclib.is_channel(target) else source
- self.buffer.push(Packet('privmsg', (reply_to, message)))
+ self.buffer.push(Message('privmsg', (reply_to, message)))
def reload(self):
- print "reloading"
+ print "reloading..."
data = eval(open(self.config_file_name).read())
+ self.config = data
if self.version >= data['version']:
return
self.config_timestamp = os.stat(self.config_file_name).st_mtime
self.version = data['version']
self.reload_plugins()
+ print "reloaded."
def load_plugins(self):
import_path = os.path.join(INKBLOT_ROOT, 'plugins') # XXX
@@ -108,7 +121,7 @@ def handler(connection, event):
reply = event.target() # XXX
tb = traceback.format_exc()
print tb
- self.buffer.push(Packet('privmsg', (reply, tb.splitlines()[-1])))
+ self.buffer.push(Message('privmsg', (reply, tb.splitlines()[-1])))
self.handlers[action].append(handler)
self.connection.add_global_handler(action, handler, 0)
self.plugins.append(plugin)
@@ -126,9 +139,10 @@ def main():
profile = sys.argv[1]
if not profile:
profile = 'config'
- print profile
+ print "profile:", profile
config_file_name = os.path.join(INKBLOT_ROOT, '%s.py' % profile)
inkblot = Inkblot(config_file_name)
+ print "Inkblot.start()"
inkblot.start()
if __name__ == '__main__':
View
0 lib/__init__.py
No changes.
View
215 lib/whois.py
@@ -0,0 +1,215 @@
+#!/usr/bin/env python
+# -*- coding: cp949 -*-
+# vim: ts=8 sts=4 et
+#
+# Copyright 2000 Dominic Mitchell <dom@happygiraffe.net>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+#
+
+"""
+Python Whois tools.
+
+Server(domain) - Return an appropriate whois server for a domain.
+Whois(domain, server=None) - Return whois output for domain.
+"""
+
+__rcs_id__='$Id: whois.py 103 2005-05-15 12:42:13Z barosl $'
+__version__='$Revision: 1.2 $'[11:-2]
+
+import os
+import re
+import sys
+import string
+import socket
+
+# Obtain full list from http://www.geektools.com/dist/whoislist.gz.
+
+whoislist = "./whoislist"
+
+INTERNIC = "whois.crsnic.net"
+
+# In case whoislist isn't present.
+defaultservers = {
+ '.com': INTERNIC,
+ '.net': INTERNIC,
+ '.org': INTERNIC,
+ '.edu': INTERNIC,
+ '.uk': 'whois.nic.uk',
+}
+servers = {}
+
+def Server(domain):
+ """Return the whois server for domain."""
+
+ server = INTERNIC # Default.
+ for tld in servers.keys():
+ l = len(tld)
+ if domain[-l:] == tld:
+ server = servers[tld]
+ break
+
+ return server
+
+def digest_ARIN(winfo):
+ r = {}
+ try:
+ r[u'owner'] = re.findall(r'OrgName:\s+(.+)', winfo)[0].decode('latin1')
+ r[u'netname'] = re.findall(r'NetName:\s+(.*)', winfo)[0].decode('latin1')
+ r[u'netblock'] = re.findall(r'NetRange:\s+([0-9\.]+) - ([0-9\.]+)', winfo)[0]
+ except IndexError:
+ r[u'owner'], r[u'netname'] = re.findall(r'(.+) (.+) \(NET-', winfo)[0]
+ r[u'netblock'] = re.findall(r'([0-9\.]+) - ([0-9\.]+)', winfo)[0]
+ r[u'source'] = u'ARIN'
+ return r
+
+def digest_RIPE(winfo): # Used by RIPE, APNIC
+ r = {}
+ r[u'netblock'] = re.findall(r'inetnum:\s+([0-9\.]+) {1,2}- {1,2}([0-9\.]+)', winfo)[0]
+ r[u'netname'] = re.findall(r'netname:\s+(.+)', winfo)[0].decode('latin1')
+ r[u'owner'] = re.findall(r'descr:\s+(.+)', winfo)[0].decode('latin1')
+ try:
+ r[u'route'] = re.findall(r'route:[\s0-9\./]+\ndescr:\s+(.*)', winfo)[0].decode('latin1')
+ except IndexError:
+ pass
+ r[u'source'] = re.findall(r'source:\s+(.+)', winfo)[0].decode('latin1')
+ return r
+
+def digest_KRNIC(winfo):
+ r = {}
+ try:
+ r[u'netblock'] = re.findall(r'IPv4주소\s+: ([0-9\.]+)-([0-9\.]+)', winfo)[0]
+ except IndexError:
+ r[u'netblock'] = u'no block' # ISP Only Format
+ r[u'netname'] = re.findall(r'서비스명\s+: (.+)', winfo)[0].decode('cp949')
+ r[u'owner'] = re.findall(r'기 관 명\s+: (.+)', winfo)[0].decode('cp949')
+ else:
+ r[u'netname'] = re.findall(r'네트워크 이름\s+: (.+)', winfo)[0].decode('cp949')
+ r[u'owner'] = re.findall(r'기관명\s+: (.+)', winfo)[0].decode('cp949')
+ try: r[u'route'] = re.findall(r'연결 ISP명\s+: (.+)', winfo)[0].decode('cp949')
+ except IndexError: r[u'route'] = u'unknown'
+ r[u'source'] = u'KRNIC'
+ return r
+
+def digest_JPNIC(winfo):
+ r = {}
+ try:
+ r[u'netblock'] = re.findall(r'a\. \[Network Number\]\s+([0-9\.]+)-([0-9\.]+)', winfo)[0]
+ except:
+ r[u'netblock'] = re.findall(r'a\. \[Network Number\]\s+([0-9\.]+)', winfo)[0].decode('ascii')
+ r[u'netname'] = re.findall(r'b\. \[Network Name\]\s+(.+)', winfo)[0].decode('shift-jis')
+ r[u'owner'] = re.findall(r'g\. \[Organization\]\s+(.+)', winfo)[0].decode('shift-jis')
+ r[u'source'] = u'JPNIC'
+ return r
+
+_=re.compile
+ipwregion = { # whois server search suffix, redirect match, digest
+ 'ARIN': ('whois.arin.net', '', None, digest_ARIN),
+ 'RIPE': ('whois.ripe.net', '', _('(be found in the RIPE database at whois.ripe.net)|(European Regional Internet Registry/RIPE NCC|RIPE Network Coordination Centre)'), digest_RIPE),
+ 'APNIC': ('whois.apnic.net', '', _('refer to the APNIC Whois Database'), digest_RIPE),
+
+ 'KRNIC': ('whois.nic.or.kr', '', _('(For more information, using KRNIC Whois Database)|(please refer to the KRNIC Whois DB)'), digest_KRNIC),
+ 'JPNIC': ('whois.nic.ad.jp', '/e', _('(JPNIC whois server at whois.nic.ad.jp)|(Japan Network Information Center \(NETBLK-JAPAN-NET\))'), digest_JPNIC),
+}
+
+def IPWhois(addr):
+ ip = socket.gethostbyname(addr) # this may cause socket.gaierror exception
+
+ winfo = Whois(ip, ipwregion['ARIN'][0])
+ wdigest = ipwregion['ARIN'][3]
+ while 1:
+ for server, suffix, redir, digest in ipwregion.values():
+ if redir and redir.search(winfo):
+ winfo = Whois(ip+suffix, server)
+ wdigest = digest
+ break
+ else:
+ break
+
+ r = wdigest(winfo)
+ r[u'ipv4addr'] = ip.decode('ascii')
+ if isinstance(r[u'netblock'], tuple):
+ r[u'netblock'] = u'-'.join(r[u'netblock'])
+ return r
+
+
+def Whois(domain, server=None):
+ """Return the whois output for a domain."""
+
+ if server == None:
+ server = Server(domain)
+
+ server = socket.gethostbyname(server)
+ port = socket.getservbyname('whois', 'tcp')
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect((server, port))
+
+ # The protocol itself.
+ sock.send(domain + '\r\n')
+ #sock.shutdown(1) # No more sends. XXX: this doesn't work on RIPE
+ data = ''
+ while 1:
+ newdata = sock.recv(16384)
+ if not newdata: break
+ data = data + newdata
+ sock.close()
+ # Zap any CR's we see.
+ if string.find(data, '\r') >= 0:
+ data = string.join(string.split(data, '\r'), '')
+ return data
+
+def WhoisList(domain, server=None):
+ """Return the whois output for a domain, as a list of lines."""
+ data = Whois(domain, server)
+ if string.find(data, '\r') >= 0:
+ data = string.join(string.split(data, '\r'), '')
+ data = string.split(data, '\n')
+ # Tidying.
+ if data[-1] == '': del data[-1]
+ return data
+
+def _init():
+ """Initialise the servers dict from a file."""
+ global servers
+
+ try:
+ # To create this module, run listmgr.py
+ import whoislist
+ servers = whoislist.servers
+ except ImportError:
+ servers = defaultservers
+
+# Call when module is first imported.
+_init()
+
+if __name__ == '__main__':
+ try:
+ from pprint import pprint
+ name = sys.argv[1]
+ dom = sys.argv[2]
+ print "%% whois -h %s '%s'" % (dom, name)
+ pprint(WhoisList(name, dom))
+ except IndexError:
+ print "usage: whoisserver.py name domain"
+
+# vim: ai et sw=4 ts=4
View
13 plugins/ipw.py
@@ -1,17 +1,21 @@
import re
-import whois; reload(whois)
-
-from BufferingBot import Packet
+from lib import whois; reload(whois)
def on_msg(bot, connection, event):
msg = event.arguments()[0]
- if event.source in ['uniko', 'kouni']:
+ # XXX
+ source = str(event.source)
+ if '=' in source: source = source.split('=',1)[1]
+ if '~' in source: source = source.split('~',1)[1]
+ if source in ['uniko', 'kouni', '|']:
msg = re.sub(r'^<.*?> ', '', msg, 1)
if not msg.startswith('!ipw'):
return
ip = msg.strip()
+ ip = ip.split(' ',1)[1]
+ print 'ip:', ip
res = whois.IPWhois(ip.encode('utf-8'))
output = u''
output += u'[\x1f%s\x1f] ' % res['source']
@@ -23,6 +27,7 @@ def on_msg(bot, connection, event):
output += u' \x02%(owner)s\x02 (%(route)s/%(netname)s, %(netblock)s)' % res
else:
output += u' : \x02%(owner)s\x02 (%(netname)s, %(netblock)s)' % res
+ print 'output:', output
bot.reply(event, output)
def on_privmsg(bot, connection, event):

0 comments on commit b087796

Please sign in to comment.
Something went wrong with that request. Please try again.