Some fixes for Python 3 port
parent
797ef1d43f
commit
91741757ed
195
ircbot.py
195
ircbot.py
|
|
@ -1,4 +1,7 @@
|
||||||
# Copyright (C) 1999--2002 Joel Rosdahl
|
#! -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (C) 1999-2002 Joel Rosdahl
|
||||||
|
# Portions Copyring © 2011-2012 Jason R. Coombs
|
||||||
#
|
#
|
||||||
# This library is free software; you can redistribute it and/or
|
# This library is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
|
@ -15,24 +18,20 @@
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
#
|
#
|
||||||
# Joel Rosdahl <joel@rosdahl.net>
|
# Joel Rosdahl <joel@rosdahl.net>
|
||||||
#
|
|
||||||
# $Id: ircbot.py,v 1.23 2008/09/11 07:38:30 keltus Exp $
|
|
||||||
|
|
||||||
"""ircbot -- Simple IRC bot library.
|
"""
|
||||||
|
ircbot -- Simple IRC bot library.
|
||||||
|
|
||||||
This module contains a single-server IRC bot class that can be used to
|
This module contains a single-server IRC bot class that can be used to
|
||||||
write simpler bots.
|
write simpler bots.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from UserDict import UserDict
|
|
||||||
|
|
||||||
from irclib import SimpleIRCClient
|
import irclib
|
||||||
from irclib import nm_to_n, irc_lower, all_events
|
from irclib import nm_to_n
|
||||||
from irclib import parse_channel_modes, is_channel
|
|
||||||
from irclib import ServerConnectionError
|
|
||||||
|
|
||||||
class SingleServerIRCBot(SimpleIRCClient):
|
class SingleServerIRCBot(irclib.SimpleIRCClient):
|
||||||
"""A single-server IRC bot class.
|
"""A single-server IRC bot class.
|
||||||
|
|
||||||
The bot tries to reconnect if it is disconnected.
|
The bot tries to reconnect if it is disconnected.
|
||||||
|
|
@ -62,11 +61,11 @@ class SingleServerIRCBot(SimpleIRCClient):
|
||||||
connections.
|
connections.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SimpleIRCClient.__init__(self)
|
super(SingleServerIRCBot, self).__init__()
|
||||||
self.channels = IRCDict()
|
self.channels = IRCDict()
|
||||||
self.server_list = server_list
|
self.server_list = server_list
|
||||||
if not reconnection_interval or reconnection_interval < 0:
|
if not reconnection_interval or reconnection_interval < 0:
|
||||||
reconnection_interval = 2**31
|
reconnection_interval = 2 ** 31
|
||||||
self.reconnection_interval = reconnection_interval
|
self.reconnection_interval = reconnection_interval
|
||||||
|
|
||||||
self._nickname = nickname
|
self._nickname = nickname
|
||||||
|
|
@ -75,7 +74,8 @@ class SingleServerIRCBot(SimpleIRCClient):
|
||||||
"namreply", "nick", "part", "quit"]:
|
"namreply", "nick", "part", "quit"]:
|
||||||
self.connection.add_global_handler(i,
|
self.connection.add_global_handler(i,
|
||||||
getattr(self, "_on_" + i),
|
getattr(self, "_on_" + i),
|
||||||
-10)
|
-20)
|
||||||
|
|
||||||
def _connected_checker(self):
|
def _connected_checker(self):
|
||||||
"""[Internal]"""
|
"""[Internal]"""
|
||||||
if not self.connection.is_connected():
|
if not self.connection.is_connected():
|
||||||
|
|
@ -94,7 +94,7 @@ class SingleServerIRCBot(SimpleIRCClient):
|
||||||
self._nickname,
|
self._nickname,
|
||||||
password,
|
password,
|
||||||
ircname=self._realname)
|
ircname=self._realname)
|
||||||
except ServerConnectionError:
|
except irclib.ServerConnectionError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _on_disconnect(self, c, e):
|
def _on_disconnect(self, c, e):
|
||||||
|
|
@ -123,9 +123,9 @@ class SingleServerIRCBot(SimpleIRCClient):
|
||||||
|
|
||||||
def _on_mode(self, c, e):
|
def _on_mode(self, c, e):
|
||||||
"""[Internal]"""
|
"""[Internal]"""
|
||||||
modes = parse_channel_modes(" ".join(e.arguments()))
|
modes = irclib.parse_channel_modes(" ".join(e.arguments()))
|
||||||
t = e.target()
|
t = e.target()
|
||||||
if is_channel(t):
|
if irclib.is_channel(t):
|
||||||
ch = self.channels[t]
|
ch = self.channels[t]
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
if mode[0] == "+":
|
if mode[0] == "+":
|
||||||
|
|
@ -244,71 +244,10 @@ class SingleServerIRCBot(SimpleIRCClient):
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Start the bot."""
|
"""Start the bot."""
|
||||||
self._connect()
|
self._connect()
|
||||||
SimpleIRCClient.start(self)
|
super(SingleServerIRCBot, self).start()
|
||||||
|
|
||||||
|
|
||||||
class IRCDict:
|
class Channel(object):
|
||||||
"""A dictionary suitable for storing IRC-related things.
|
|
||||||
|
|
||||||
Dictionary keys a and b are considered equal if and only if
|
|
||||||
irc_lower(a) == irc_lower(b)
|
|
||||||
|
|
||||||
Otherwise, it should behave exactly as a normal dictionary.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, dict=None):
|
|
||||||
self.data = {}
|
|
||||||
self.canon_keys = {} # Canonical keys
|
|
||||||
if dict is not None:
|
|
||||||
self.update(dict)
|
|
||||||
def __repr__(self):
|
|
||||||
return repr(self.data)
|
|
||||||
def __cmp__(self, dict):
|
|
||||||
if isinstance(dict, IRCDict):
|
|
||||||
return cmp(self.data, dict.data)
|
|
||||||
else:
|
|
||||||
return cmp(self.data, dict)
|
|
||||||
def __len__(self):
|
|
||||||
return len(self.data)
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self.data[self.canon_keys[irc_lower(key)]]
|
|
||||||
def __setitem__(self, key, item):
|
|
||||||
if key in self:
|
|
||||||
del self[key]
|
|
||||||
self.data[key] = item
|
|
||||||
self.canon_keys[irc_lower(key)] = key
|
|
||||||
def __delitem__(self, key):
|
|
||||||
ck = irc_lower(key)
|
|
||||||
del self.data[self.canon_keys[ck]]
|
|
||||||
del self.canon_keys[ck]
|
|
||||||
def __iter__(self):
|
|
||||||
return iter(self.data)
|
|
||||||
def __contains__(self, key):
|
|
||||||
return self.has_key(key)
|
|
||||||
def clear(self):
|
|
||||||
self.data.clear()
|
|
||||||
self.canon_keys.clear()
|
|
||||||
def copy(self):
|
|
||||||
if self.__class__ is UserDict:
|
|
||||||
return UserDict(self.data)
|
|
||||||
import copy
|
|
||||||
return copy.copy(self)
|
|
||||||
def keys(self):
|
|
||||||
return self.data.keys()
|
|
||||||
def items(self):
|
|
||||||
return self.data.items()
|
|
||||||
def values(self):
|
|
||||||
return self.data.values()
|
|
||||||
def has_key(self, key):
|
|
||||||
return irc_lower(key) in self.canon_keys
|
|
||||||
def update(self, dict):
|
|
||||||
for k, v in dict.items():
|
|
||||||
self.data[k] = v
|
|
||||||
def get(self, key, failobj=None):
|
|
||||||
return self.data.get(key, failobj)
|
|
||||||
|
|
||||||
|
|
||||||
class Channel:
|
|
||||||
"""A class for keeping information about an IRC channel.
|
"""A class for keeping information about an IRC channel.
|
||||||
|
|
||||||
This class can be improved a lot.
|
This class can be improved a lot.
|
||||||
|
|
@ -354,14 +293,15 @@ class Channel:
|
||||||
del d[nick]
|
del d[nick]
|
||||||
|
|
||||||
def change_nick(self, before, after):
|
def change_nick(self, before, after):
|
||||||
self.userdict[after] = 1
|
self.userdict[after] = self.userdict.pop(before)
|
||||||
del self.userdict[before]
|
|
||||||
if before in self.operdict:
|
if before in self.operdict:
|
||||||
self.operdict[after] = 1
|
self.operdict[after] = self.operdict.pop(before)
|
||||||
del self.operdict[before]
|
|
||||||
if before in self.voiceddict:
|
if before in self.voiceddict:
|
||||||
self.voiceddict[after] = 1
|
self.voiceddict[after] = self.voiceddict.pop(before)
|
||||||
del self.voiceddict[before]
|
|
||||||
|
def set_userdetails(self, nick, details):
|
||||||
|
if nick in self.userdict:
|
||||||
|
self.userdict[nick] = details
|
||||||
|
|
||||||
def set_mode(self, mode, value=None):
|
def set_mode(self, mode, value=None):
|
||||||
"""Set mode on the channel.
|
"""Set mode on the channel.
|
||||||
|
|
@ -424,15 +364,88 @@ class Channel:
|
||||||
|
|
||||||
def limit(self):
|
def limit(self):
|
||||||
if self.has_limit():
|
if self.has_limit():
|
||||||
return self.modes[l]
|
return self.modes["l"]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def has_key(self):
|
def has_key(self):
|
||||||
return self.has_mode("k")
|
return self.has_mode("k")
|
||||||
|
|
||||||
def key(self):
|
# from jaraco.util.dictlib
|
||||||
if self.has_key():
|
class KeyTransformingDict(dict):
|
||||||
return self.modes["k"]
|
"""
|
||||||
else:
|
A dict subclass that transforms the keys before they're used.
|
||||||
return None
|
Subclasses may override the default key_transform to customize behavior.
|
||||||
|
"""
|
||||||
|
@staticmethod
|
||||||
|
def key_transform(key):
|
||||||
|
return key
|
||||||
|
|
||||||
|
def __init__(self, *args, **kargs):
|
||||||
|
super(KeyTransformingDict, self).__init__()
|
||||||
|
# build a dictionary using the default constructs
|
||||||
|
d = dict(*args, **kargs)
|
||||||
|
# build this dictionary using transformed keys.
|
||||||
|
for item in d.items():
|
||||||
|
self.__setitem__(*item)
|
||||||
|
|
||||||
|
def __setitem__(self, key, val):
|
||||||
|
key = self.key_transform(key)
|
||||||
|
super(KeyTransformingDict, self).__setitem__(key, val)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
key = self.key_transform(key)
|
||||||
|
return super(KeyTransformingDict, self).__getitem__(key)
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
key = self.key_transform(key)
|
||||||
|
return super(KeyTransformingDict, self).__contains__(key)
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
key = self.key_transform(key)
|
||||||
|
return super(KeyTransformingDict, self).__delitem__(key)
|
||||||
|
|
||||||
|
def setdefault(self, key, *args, **kwargs):
|
||||||
|
key = self.key_transform(key)
|
||||||
|
return super(KeyTransformingDict, self).setdefault(key, *args, **kwargs)
|
||||||
|
|
||||||
|
def pop(self, key, *args, **kwargs):
|
||||||
|
key = self.key_transform(key)
|
||||||
|
return super(KeyTransformingDict, self).pop(key, *args, **kwargs)
|
||||||
|
|
||||||
|
class IRCDict(KeyTransformingDict):
|
||||||
|
"""
|
||||||
|
A dictionary of names whose keys are case-insensitive according to the
|
||||||
|
IRC RFC rules.
|
||||||
|
|
||||||
|
>>> d = IRCDict({'[This]': 'that'}, A='foo')
|
||||||
|
|
||||||
|
The dict maintains the original case:
|
||||||
|
>>> d.keys()
|
||||||
|
['A', '[This]']
|
||||||
|
|
||||||
|
But the keys can be referenced with a different case
|
||||||
|
>>> d['a']
|
||||||
|
'foo'
|
||||||
|
|
||||||
|
>>> d['{this}']
|
||||||
|
'that'
|
||||||
|
|
||||||
|
>>> d['{THIS}']
|
||||||
|
'that'
|
||||||
|
|
||||||
|
>>> '{thiS]' in d
|
||||||
|
True
|
||||||
|
|
||||||
|
This should work for operations like delete and pop as well.
|
||||||
|
>>> d.pop('A')
|
||||||
|
'foo'
|
||||||
|
>>> del d['{This}']
|
||||||
|
>>> len(d)
|
||||||
|
0
|
||||||
|
"""
|
||||||
|
@staticmethod
|
||||||
|
def key_transform(key):
|
||||||
|
if isinstance(key, basestring):
|
||||||
|
key = irclib.IRCFoldedCase(key)
|
||||||
|
return key
|
||||||
|
|
|
||||||
366
irclib.py
366
irclib.py
|
|
@ -1,4 +1,7 @@
|
||||||
# Copyright (C) 1999--2002 Joel Rosdahl
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (C) 1999-2002 Joel Rosdahl
|
||||||
|
# Portions Copyright © 2011 Jason R. Coombs
|
||||||
#
|
#
|
||||||
# This library is free software; you can redistribute it and/or
|
# This library is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
|
@ -15,10 +18,9 @@
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
#
|
#
|
||||||
# keltus <keltus@users.sourceforge.net>
|
# keltus <keltus@users.sourceforge.net>
|
||||||
#
|
|
||||||
# $Id: irclib.py,v 1.47 2008/09/25 22:00:59 keltus Exp $
|
|
||||||
|
|
||||||
"""irclib -- Internet Relay Chat (IRC) protocol client library.
|
"""
|
||||||
|
irclib -- Internet Relay Chat (IRC) protocol client library.
|
||||||
|
|
||||||
This library is intended to encapsulate the IRC protocol at a quite
|
This library is intended to encapsulate the IRC protocol at a quite
|
||||||
low level. It provides an event-driven IRC client framework. It has
|
low level. It provides an event-driven IRC client framework. It has
|
||||||
|
|
@ -66,12 +68,19 @@ import re
|
||||||
import select
|
import select
|
||||||
import socket
|
import socket
|
||||||
import string
|
import string
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
import types
|
import types
|
||||||
|
import ssl as ssl_mod
|
||||||
|
import datetime
|
||||||
|
|
||||||
VERSION = 0, 4, 8
|
try:
|
||||||
DEBUG = 0
|
import pkg_resources
|
||||||
|
_pkg = pkg_resources.require('python-irclib')[0]
|
||||||
|
VERSION = tuple(int(res) for res in re.findall('\d+', _pkg.version))
|
||||||
|
except Exception:
|
||||||
|
VERSION = ()
|
||||||
|
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
# ----
|
# ----
|
||||||
|
|
@ -93,7 +102,7 @@ class IRCError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class IRC:
|
class IRC(object):
|
||||||
"""Class that handles one or several IRC server connections.
|
"""Class that handles one or several IRC server connections.
|
||||||
|
|
||||||
When an IRC object has been instantiated, it can be used to create
|
When an IRC object has been instantiated, it can be used to create
|
||||||
|
|
@ -112,12 +121,12 @@ class IRC:
|
||||||
|
|
||||||
irc = irclib.IRC()
|
irc = irclib.IRC()
|
||||||
server = irc.server()
|
server = irc.server()
|
||||||
server.connect(\"irc.some.where\", 6667, \"my_nickname\")
|
server.connect("irc.some.where", 6667, "my_nickname")
|
||||||
server.privmsg(\"a_nickname\", \"Hi there!\")
|
server.privmsg("a_nickname", "Hi there!")
|
||||||
irc.process_forever()
|
irc.process_forever()
|
||||||
|
|
||||||
This will connect to the IRC server irc.some.where on port 6667
|
This will connect to the IRC server irc.some.where on port 6667
|
||||||
using the nickname my_nickname and send the message \"Hi there!\"
|
using the nickname my_nickname and send the message "Hi there!"
|
||||||
to the nickname a_nickname.
|
to the nickname a_nickname.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -157,7 +166,7 @@ class IRC:
|
||||||
self.fn_to_add_timeout = fn_to_add_timeout
|
self.fn_to_add_timeout = fn_to_add_timeout
|
||||||
self.connections = []
|
self.connections = []
|
||||||
self.handlers = {}
|
self.handlers = {}
|
||||||
self.delayed_commands = [] # list of tuples in the format (time, function, arguments)
|
self.delayed_commands = [] # list of DelayedCommands
|
||||||
|
|
||||||
self.add_global_handler("ping", _ping_ponger, -42)
|
self.add_global_handler("ping", _ping_ponger, -42)
|
||||||
|
|
||||||
|
|
@ -187,13 +196,14 @@ class IRC:
|
||||||
|
|
||||||
See documentation for IRC.__init__.
|
See documentation for IRC.__init__.
|
||||||
"""
|
"""
|
||||||
t = time.time()
|
|
||||||
while self.delayed_commands:
|
while self.delayed_commands:
|
||||||
if t >= self.delayed_commands[0][0]:
|
command = self.delayed_commands[0]
|
||||||
self.delayed_commands[0][1](*self.delayed_commands[0][2])
|
if not command.due():
|
||||||
del self.delayed_commands[0]
|
|
||||||
else:
|
|
||||||
break
|
break
|
||||||
|
command.function(*command.arguments)
|
||||||
|
if isinstance(command, PeriodicCommand):
|
||||||
|
self._schedule_command(command.next())
|
||||||
|
del self.delayed_commands[0]
|
||||||
|
|
||||||
def process_once(self, timeout=0):
|
def process_once(self, timeout=0):
|
||||||
"""Process data from connections once.
|
"""Process data from connections once.
|
||||||
|
|
@ -252,7 +262,7 @@ class IRC:
|
||||||
|
|
||||||
The handler functions are called in priority order (lowest
|
The handler functions are called in priority order (lowest
|
||||||
number is highest priority). If a handler function returns
|
number is highest priority). If a handler function returns
|
||||||
\"NO MORE\", no more handlers will be called.
|
"NO MORE", no more handlers will be called.
|
||||||
"""
|
"""
|
||||||
if not event in self.handlers:
|
if not event in self.handlers:
|
||||||
self.handlers[event] = []
|
self.handlers[event] = []
|
||||||
|
|
@ -264,7 +274,6 @@ class IRC:
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
||||||
event -- Event type (a string).
|
event -- Event type (a string).
|
||||||
|
|
||||||
handler -- Callback function.
|
handler -- Callback function.
|
||||||
|
|
||||||
Returns 1 on success, otherwise 0.
|
Returns 1 on success, otherwise 0.
|
||||||
|
|
@ -281,28 +290,39 @@ class IRC:
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
||||||
at -- Execute at this time (standard \"time_t\" time).
|
at -- Execute at this time (standard "time_t" time).
|
||||||
|
|
||||||
function -- Function to call.
|
function -- Function to call.
|
||||||
|
|
||||||
arguments -- Arguments to give the function.
|
arguments -- Arguments to give the function.
|
||||||
"""
|
"""
|
||||||
self.execute_delayed(at-time.time(), function, arguments)
|
command = DelayedCommand.at_time(at, function, arguments)
|
||||||
|
self._schedule_command(command)
|
||||||
|
|
||||||
def execute_delayed(self, delay, function, arguments=()):
|
def execute_delayed(self, delay, function, arguments=()):
|
||||||
"""Execute a function after a specified time.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
|
|
||||||
delay -- How many seconds to wait.
|
|
||||||
|
|
||||||
function -- Function to call.
|
|
||||||
|
|
||||||
arguments -- Arguments to give the function.
|
|
||||||
"""
|
"""
|
||||||
bisect.insort(self.delayed_commands, (delay+time.time(), function, arguments))
|
Execute a function after a specified time.
|
||||||
|
|
||||||
|
delay -- How many seconds to wait.
|
||||||
|
function -- Function to call.
|
||||||
|
arguments -- Arguments to give the function.
|
||||||
|
"""
|
||||||
|
command = DelayedCommand(delay, function, arguments)
|
||||||
|
self._schedule_command(command)
|
||||||
|
|
||||||
|
def execute_every(self, period, function, arguments=()):
|
||||||
|
"""
|
||||||
|
Execute a function every 'period' seconds.
|
||||||
|
|
||||||
|
period -- How often to run (always waits this long for first).
|
||||||
|
function -- Function to call.
|
||||||
|
arguments -- Arguments to give the function.
|
||||||
|
"""
|
||||||
|
command = PeriodicCommand(period, function, arguments)
|
||||||
|
self._schedule_command(command)
|
||||||
|
|
||||||
|
def _schedule_command(self, command):
|
||||||
|
bisect.insort(self.delayed_commands, command)
|
||||||
if self.fn_to_add_timeout:
|
if self.fn_to_add_timeout:
|
||||||
self.fn_to_add_timeout(delay)
|
self.fn_to_add_timeout(total_seconds(command.delay))
|
||||||
|
|
||||||
def dcc(self, dcctype="chat"):
|
def dcc(self, dcctype="chat"):
|
||||||
"""Creates and returns a DCCConnection object.
|
"""Creates and returns a DCCConnection object.
|
||||||
|
|
@ -321,7 +341,8 @@ class IRC:
|
||||||
def _handle_event(self, connection, event):
|
def _handle_event(self, connection, event):
|
||||||
"""[Internal]"""
|
"""[Internal]"""
|
||||||
h = self.handlers
|
h = self.handlers
|
||||||
for handler in h.get("all_events", []) + h.get(event.eventtype(), []):
|
th = sorted(h.get("all_events", []) + h.get(event.eventtype(), []))
|
||||||
|
for handler in th:
|
||||||
if handler[1](connection, event) == "NO MORE":
|
if handler[1](connection, event) == "NO MORE":
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -331,9 +352,48 @@ class IRC:
|
||||||
if self.fn_to_remove_socket:
|
if self.fn_to_remove_socket:
|
||||||
self.fn_to_remove_socket(connection._get_socket())
|
self.fn_to_remove_socket(connection._get_socket())
|
||||||
|
|
||||||
|
class DelayedCommand(datetime.datetime):
|
||||||
|
"""
|
||||||
|
A command to be executed after some delay (seconds or timedelta).
|
||||||
|
"""
|
||||||
|
def __new__(cls, delay, function, arguments):
|
||||||
|
if not isinstance(delay, datetime.timedelta):
|
||||||
|
delay = datetime.timedelta(seconds=delay)
|
||||||
|
at = datetime.datetime.utcnow() + delay
|
||||||
|
cmd = datetime.datetime.__new__(DelayedCommand, at.year,
|
||||||
|
at.month, at.day, at.hour, at.minute, at.second,
|
||||||
|
at.microsecond, at.tzinfo)
|
||||||
|
cmd.delay = delay
|
||||||
|
cmd.function = function
|
||||||
|
cmd.arguments = arguments
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
def at_time(cls, at, function, arguments):
|
||||||
|
"""
|
||||||
|
Construct a DelayedCommand to come due at `at`, where `at` may be
|
||||||
|
a datetime or timestamp.
|
||||||
|
"""
|
||||||
|
if isinstance(at, int):
|
||||||
|
at = datetime.datetime.utcfromtimestamp(at)
|
||||||
|
delay = at - datetime.datetime.utcnow()
|
||||||
|
return cls(delay, function, arguments)
|
||||||
|
at_time = classmethod(at_time)
|
||||||
|
|
||||||
|
def due(self):
|
||||||
|
return datetime.datetime.utcnow() >= self
|
||||||
|
|
||||||
|
class PeriodicCommand(DelayedCommand):
|
||||||
|
"""
|
||||||
|
Like a deferred command, but expect this command to run every delay
|
||||||
|
seconds.
|
||||||
|
"""
|
||||||
|
def next(self):
|
||||||
|
return PeriodicCommand(self.delay, self.function,
|
||||||
|
self.arguments)
|
||||||
|
|
||||||
_rfc_1459_command_regexp = re.compile("^(:(?P<prefix>[^ ]+) +)?(?P<command>[^ ]+)( *(?P<argument> .+))?")
|
_rfc_1459_command_regexp = re.compile("^(:(?P<prefix>[^ ]+) +)?(?P<command>[^ ]+)( *(?P<argument> .+))?")
|
||||||
|
|
||||||
class Connection:
|
class Connection(object):
|
||||||
"""Base class for IRC connections.
|
"""Base class for IRC connections.
|
||||||
|
|
||||||
Must be overridden.
|
Must be overridden.
|
||||||
|
|
@ -342,7 +402,7 @@ class Connection:
|
||||||
self.irclibobj = irclibobj
|
self.irclibobj = irclibobj
|
||||||
|
|
||||||
def _get_socket():
|
def _get_socket():
|
||||||
raise IRCError, "Not overridden"
|
raise IRCError("Not overridden")
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
### Convenience wrappers.
|
### Convenience wrappers.
|
||||||
|
|
@ -353,6 +413,8 @@ class Connection:
|
||||||
def execute_delayed(self, delay, function, arguments=()):
|
def execute_delayed(self, delay, function, arguments=()):
|
||||||
self.irclibobj.execute_delayed(delay, function, arguments)
|
self.irclibobj.execute_delayed(delay, function, arguments)
|
||||||
|
|
||||||
|
def execute_every(self, period, function, arguments=()):
|
||||||
|
self.irclibobj.execute_every(period, function, arguments)
|
||||||
|
|
||||||
class ServerConnectionError(IRCError):
|
class ServerConnectionError(IRCError):
|
||||||
pass
|
pass
|
||||||
|
|
@ -373,7 +435,7 @@ class ServerConnection(Connection):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, irclibobj):
|
def __init__(self, irclibobj):
|
||||||
Connection.__init__(self, irclibobj)
|
super(ServerConnection, self).__init__(irclibobj)
|
||||||
self.connected = 0 # Not connected yet.
|
self.connected = 0 # Not connected yet.
|
||||||
self.socket = None
|
self.socket = None
|
||||||
self.ssl = None
|
self.ssl = None
|
||||||
|
|
@ -432,11 +494,11 @@ class ServerConnection(Connection):
|
||||||
self.socket.bind((self.localaddress, self.localport))
|
self.socket.bind((self.localaddress, self.localport))
|
||||||
self.socket.connect((self.server, self.port))
|
self.socket.connect((self.server, self.port))
|
||||||
if ssl:
|
if ssl:
|
||||||
self.ssl = socket.ssl(self.socket)
|
self.ssl = ssl_mod.wrap_socket(self.socket)
|
||||||
except socket.error, x:
|
except socket.error as x:
|
||||||
self.socket.close()
|
self.socket.close()
|
||||||
self.socket = None
|
self.socket = None
|
||||||
raise ServerConnectionError, "Couldn't connect to socket: %s" % x
|
raise ServerConnectionError("Couldn't connect to socket: %s" % x)
|
||||||
self.connected = 1
|
self.connected = 1
|
||||||
if self.irclibobj.fn_to_add_socket:
|
if self.irclibobj.fn_to_add_socket:
|
||||||
self.irclibobj.fn_to_add_socket(self.socket)
|
self.irclibobj.fn_to_add_socket(self.socket)
|
||||||
|
|
@ -488,10 +550,10 @@ class ServerConnection(Connection):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.ssl:
|
if self.ssl:
|
||||||
new_data = self.ssl.read(2**14)
|
new_data = self.ssl.read(2 ** 14)
|
||||||
else:
|
else:
|
||||||
new_data = self.socket.recv(2**14)
|
new_data = self.socket.recv(2 ** 14)
|
||||||
except socket.error, x:
|
except socket.error:
|
||||||
# The server hung up.
|
# The server hung up.
|
||||||
self.disconnect("Connection reset by peer")
|
self.disconnect("Connection reset by peer")
|
||||||
return
|
return
|
||||||
|
|
@ -500,14 +562,14 @@ class ServerConnection(Connection):
|
||||||
self.disconnect("Connection reset by peer")
|
self.disconnect("Connection reset by peer")
|
||||||
return
|
return
|
||||||
|
|
||||||
lines = _linesep_regexp.split(self.previous_buffer + new_data)
|
lines = _linesep_regexp.split(self.previous_buffer + str(new_data))
|
||||||
|
|
||||||
# Save the last, unfinished line.
|
# Save the last, unfinished line.
|
||||||
self.previous_buffer = lines.pop()
|
self.previous_buffer = lines.pop()
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print "FROM SERVER:", line
|
print("FROM SERVER:", line)
|
||||||
|
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
|
|
@ -569,15 +631,15 @@ class ServerConnection(Connection):
|
||||||
|
|
||||||
m = list(m)
|
m = list(m)
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print "command: %s, source: %s, target: %s, arguments: %s" % (
|
print("command: %s, source: %s, target: %s, arguments: %s" % (
|
||||||
command, prefix, target, m)
|
command, prefix, target, m))
|
||||||
self._handle_event(Event(command, prefix, target, m))
|
self._handle_event(Event(command, prefix, target, m))
|
||||||
if command == "ctcp" and m[0] == "ACTION":
|
if command == "ctcp" and m[0] == "ACTION":
|
||||||
self._handle_event(Event("action", prefix, target, m[1:]))
|
self._handle_event(Event("action", prefix, target, m[1:]))
|
||||||
else:
|
else:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print "command: %s, source: %s, target: %s, arguments: %s" % (
|
print("command: %s, source: %s, target: %s, arguments: %s" % (
|
||||||
command, prefix, target, [m])
|
command, prefix, target, [m]))
|
||||||
self._handle_event(Event(command, prefix, target, [m]))
|
self._handle_event(Event(command, prefix, target, [m]))
|
||||||
else:
|
else:
|
||||||
target = None
|
target = None
|
||||||
|
|
@ -595,8 +657,8 @@ class ServerConnection(Connection):
|
||||||
command = "umode"
|
command = "umode"
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print "command: %s, source: %s, target: %s, arguments: %s" % (
|
print("command: %s, source: %s, target: %s, arguments: %s" % (
|
||||||
command, prefix, target, arguments)
|
command, prefix, target, arguments))
|
||||||
self._handle_event(Event(command, prefix, target, arguments))
|
self._handle_event(Event(command, prefix, target, arguments))
|
||||||
|
|
||||||
def _handle_event(self, event):
|
def _handle_event(self, event):
|
||||||
|
|
@ -660,7 +722,7 @@ class ServerConnection(Connection):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.socket.close()
|
self.socket.close()
|
||||||
except socket.error, x:
|
except socket.error:
|
||||||
pass
|
pass
|
||||||
self.socket = None
|
self.socket = None
|
||||||
self._handle_event(Event("disconnect", self.server, "", [message]))
|
self._handle_event(Event("disconnect", self.server, "", [message]))
|
||||||
|
|
@ -743,10 +805,13 @@ class ServerConnection(Connection):
|
||||||
|
|
||||||
def part(self, channels, message=""):
|
def part(self, channels, message=""):
|
||||||
"""Send a PART command."""
|
"""Send a PART command."""
|
||||||
if type(channels) == types.StringType:
|
channels = always_iterable(channels)
|
||||||
self.send_raw("PART " + channels + (message and (" " + message)))
|
cmd_parts = [
|
||||||
else:
|
'PART',
|
||||||
self.send_raw("PART " + ",".join(channels) + (message and (" " + message)))
|
','.join(channels),
|
||||||
|
]
|
||||||
|
if message: cmd_parts.append(message)
|
||||||
|
self.send_raw(' '.join(cmd_parts))
|
||||||
|
|
||||||
def pass_(self, password):
|
def pass_(self, password):
|
||||||
"""Send a PASS command."""
|
"""Send a PASS command."""
|
||||||
|
|
@ -780,20 +845,19 @@ class ServerConnection(Connection):
|
||||||
"""Send raw string to the server.
|
"""Send raw string to the server.
|
||||||
|
|
||||||
The string will be padded with appropriate CR LF.
|
The string will be padded with appropriate CR LF.
|
||||||
From here everything need to be encoded in str from utf-8
|
|
||||||
"""
|
"""
|
||||||
string=string.encode('utf-8')
|
|
||||||
|
|
||||||
if self.socket is None:
|
if self.socket is None:
|
||||||
raise ServerNotConnectedError, "Not connected."
|
raise ServerNotConnectedError("Not connected.")
|
||||||
try:
|
try:
|
||||||
if self.ssl:
|
if self.ssl:
|
||||||
self.ssl.write(string + "\r\n")
|
self.ssl.write(string + "\r\n")
|
||||||
else:
|
else:
|
||||||
self.socket.send(string + "\r\n")
|
#print(type(string + "\r\n"))
|
||||||
|
obj = string + "\r\n"
|
||||||
|
self.socket.send(obj.encode())
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print "TO SERVER:", string
|
print("TO SERVER:", string)
|
||||||
except socket.error, x:
|
except socket.error:
|
||||||
# Ouch!
|
# Ouch!
|
||||||
self.disconnect("Connection reset by peer.")
|
self.disconnect("Connection reset by peer.")
|
||||||
|
|
||||||
|
|
@ -865,7 +929,7 @@ class DCCConnection(Connection):
|
||||||
method on an IRC object.
|
method on an IRC object.
|
||||||
"""
|
"""
|
||||||
def __init__(self, irclibobj, dcctype):
|
def __init__(self, irclibobj, dcctype):
|
||||||
Connection.__init__(self, irclibobj)
|
super(DCCConnection, self).__init__(irclibobj)
|
||||||
self.connected = 0
|
self.connected = 0
|
||||||
self.passive = 0
|
self.passive = 0
|
||||||
self.dcctype = dcctype
|
self.dcctype = dcctype
|
||||||
|
|
@ -891,8 +955,8 @@ class DCCConnection(Connection):
|
||||||
self.passive = 0
|
self.passive = 0
|
||||||
try:
|
try:
|
||||||
self.socket.connect((self.peeraddress, self.peerport))
|
self.socket.connect((self.peeraddress, self.peerport))
|
||||||
except socket.error, x:
|
except socket.error as x:
|
||||||
raise DCCConnectionError, "Couldn't connect to socket: %s" % x
|
raise DCCConnectionError("Couldn't connect to socket: %s" % x)
|
||||||
self.connected = 1
|
self.connected = 1
|
||||||
if self.irclibobj.fn_to_add_socket:
|
if self.irclibobj.fn_to_add_socket:
|
||||||
self.irclibobj.fn_to_add_socket(self.socket)
|
self.irclibobj.fn_to_add_socket(self.socket)
|
||||||
|
|
@ -916,8 +980,8 @@ class DCCConnection(Connection):
|
||||||
self.socket.bind((socket.gethostbyname(socket.gethostname()), 0))
|
self.socket.bind((socket.gethostbyname(socket.gethostname()), 0))
|
||||||
self.localaddress, self.localport = self.socket.getsockname()
|
self.localaddress, self.localport = self.socket.getsockname()
|
||||||
self.socket.listen(10)
|
self.socket.listen(10)
|
||||||
except socket.error, x:
|
except socket.error as x:
|
||||||
raise DCCConnectionError, "Couldn't bind socket: %s" % x
|
raise DCCConnectionError("Couldn't bind socket: %s" % x)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def disconnect(self, message=""):
|
def disconnect(self, message=""):
|
||||||
|
|
@ -933,7 +997,7 @@ class DCCConnection(Connection):
|
||||||
self.connected = 0
|
self.connected = 0
|
||||||
try:
|
try:
|
||||||
self.socket.close()
|
self.socket.close()
|
||||||
except socket.error, x:
|
except socket.error:
|
||||||
pass
|
pass
|
||||||
self.socket = None
|
self.socket = None
|
||||||
self.irclibobj._handle_event(
|
self.irclibobj._handle_event(
|
||||||
|
|
@ -950,16 +1014,16 @@ class DCCConnection(Connection):
|
||||||
self.socket = conn
|
self.socket = conn
|
||||||
self.connected = 1
|
self.connected = 1
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print "DCC connection from %s:%d" % (
|
print("DCC connection from %s:%d" % (
|
||||||
self.peeraddress, self.peerport)
|
self.peeraddress, self.peerport))
|
||||||
self.irclibobj._handle_event(
|
self.irclibobj._handle_event(
|
||||||
self,
|
self,
|
||||||
Event("dcc_connect", self.peeraddress, None, None))
|
Event("dcc_connect", self.peeraddress, None, None))
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
new_data = self.socket.recv(2**14)
|
new_data = self.socket.recv(2 ** 14)
|
||||||
except socket.error, x:
|
except socket.error:
|
||||||
# The server hung up.
|
# The server hung up.
|
||||||
self.disconnect("Connection reset by peer")
|
self.disconnect("Connection reset by peer")
|
||||||
return
|
return
|
||||||
|
|
@ -975,7 +1039,7 @@ class DCCConnection(Connection):
|
||||||
|
|
||||||
# Save the last, unfinished line.
|
# Save the last, unfinished line.
|
||||||
self.previous_buffer = chunks[-1]
|
self.previous_buffer = chunks[-1]
|
||||||
if len(self.previous_buffer) > 2**14:
|
if len(self.previous_buffer) > 2 ** 14:
|
||||||
# Bad peer! Naughty peer!
|
# Bad peer! Naughty peer!
|
||||||
self.disconnect()
|
self.disconnect()
|
||||||
return
|
return
|
||||||
|
|
@ -988,11 +1052,11 @@ class DCCConnection(Connection):
|
||||||
target = None
|
target = None
|
||||||
for chunk in chunks:
|
for chunk in chunks:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print "FROM PEER:", chunk
|
print("FROM PEER:", chunk)
|
||||||
arguments = [chunk]
|
arguments = [chunk]
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print "command: %s, source: %s, target: %s, arguments: %s" % (
|
print("command: %s, source: %s, target: %s, arguments: %s" % (
|
||||||
command, prefix, target, arguments)
|
command, prefix, target, arguments))
|
||||||
self.irclibobj._handle_event(
|
self.irclibobj._handle_event(
|
||||||
self,
|
self,
|
||||||
Event(command, prefix, target, arguments))
|
Event(command, prefix, target, arguments))
|
||||||
|
|
@ -1012,12 +1076,12 @@ class DCCConnection(Connection):
|
||||||
if self.dcctype == "chat":
|
if self.dcctype == "chat":
|
||||||
self.socket.send("\n")
|
self.socket.send("\n")
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print "TO PEER: %s\n" % string
|
print("TO PEER: %s\n" % string)
|
||||||
except socket.error, x:
|
except socket.error:
|
||||||
# Ouch!
|
# Ouch!
|
||||||
self.disconnect("Connection reset by peer.")
|
self.disconnect("Connection reset by peer.")
|
||||||
|
|
||||||
class SimpleIRCClient:
|
class SimpleIRCClient(object):
|
||||||
"""A simple single-server IRC client class.
|
"""A simple single-server IRC client class.
|
||||||
|
|
||||||
This is an example of an object-oriented wrapper of the IRC
|
This is an example of an object-oriented wrapper of the IRC
|
||||||
|
|
@ -1047,6 +1111,9 @@ class SimpleIRCClient:
|
||||||
|
|
||||||
def _dispatcher(self, c, e):
|
def _dispatcher(self, c, e):
|
||||||
"""[Internal]"""
|
"""[Internal]"""
|
||||||
|
if DEBUG:
|
||||||
|
print("irclib.py:_dispatcher:%s" % e.eventtype())
|
||||||
|
|
||||||
m = "on_" + e.eventtype()
|
m = "on_" + e.eventtype()
|
||||||
if hasattr(self, m):
|
if hasattr(self, m):
|
||||||
getattr(self, m)(c, e)
|
getattr(self, m)(c, e)
|
||||||
|
|
@ -1117,7 +1184,7 @@ class SimpleIRCClient:
|
||||||
self.ircobj.process_forever()
|
self.ircobj.process_forever()
|
||||||
|
|
||||||
|
|
||||||
class Event:
|
class Event(object):
|
||||||
"""Class representing an IRC event."""
|
"""Class representing an IRC event."""
|
||||||
def __init__(self, eventtype, source, target, arguments=None):
|
def __init__(self, eventtype, source, target, arguments=None):
|
||||||
"""Constructor of Event objects.
|
"""Constructor of Event objects.
|
||||||
|
|
@ -1186,16 +1253,6 @@ def mask_matches(nick, mask):
|
||||||
|
|
||||||
_special = "-[]\\`^{}"
|
_special = "-[]\\`^{}"
|
||||||
nick_characters = string.ascii_letters + string.digits + _special
|
nick_characters = string.ascii_letters + string.digits + _special
|
||||||
_ircstring_translation = string.maketrans(string.ascii_uppercase + "[]\\^",
|
|
||||||
string.ascii_lowercase + "{}|~")
|
|
||||||
|
|
||||||
def irc_lower(s):
|
|
||||||
"""Returns a lowercased string.
|
|
||||||
|
|
||||||
The definition of lowercased comes from the IRC specification (RFC
|
|
||||||
1459).
|
|
||||||
"""
|
|
||||||
return s.translate(_ircstring_translation)
|
|
||||||
|
|
||||||
def _ctcp_dequote(message):
|
def _ctcp_dequote(message):
|
||||||
"""[Internal] Dequote a message according to CTCP specifications.
|
"""[Internal] Dequote a message according to CTCP specifications.
|
||||||
|
|
@ -1231,14 +1288,14 @@ def _ctcp_dequote(message):
|
||||||
|
|
||||||
messages = []
|
messages = []
|
||||||
i = 0
|
i = 0
|
||||||
while i < len(chunks)-1:
|
while i < len(chunks) - 1:
|
||||||
# Add message if it's non-empty.
|
# Add message if it's non-empty.
|
||||||
if len(chunks[i]) > 0:
|
if len(chunks[i]) > 0:
|
||||||
messages.append(chunks[i])
|
messages.append(chunks[i])
|
||||||
|
|
||||||
if i < len(chunks)-2:
|
if i < len(chunks) - 2:
|
||||||
# Aye! CTCP tagged data ahead!
|
# Aye! CTCP tagged data ahead!
|
||||||
messages.append(tuple(chunks[i+1].split(" ", 1)))
|
messages.append(tuple(chunks[i + 1].split(" ", 1)))
|
||||||
|
|
||||||
i = i + 2
|
i = i + 2
|
||||||
|
|
||||||
|
|
@ -1310,12 +1367,12 @@ def parse_nick_modes(mode_string):
|
||||||
"""Parse a nick mode string.
|
"""Parse a nick mode string.
|
||||||
|
|
||||||
The function returns a list of lists with three members: sign,
|
The function returns a list of lists with three members: sign,
|
||||||
mode and argument. The sign is \"+\" or \"-\". The argument is
|
mode and argument. The sign is "+" or "-". The argument is
|
||||||
always None.
|
always None.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
>>> irclib.parse_nick_modes(\"+ab-c\")
|
>>> parse_nick_modes("+ab-c")
|
||||||
[['+', 'a', None], ['+', 'b', None], ['-', 'c', None]]
|
[['+', 'a', None], ['+', 'b', None], ['-', 'c', None]]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -1325,12 +1382,12 @@ def parse_channel_modes(mode_string):
|
||||||
"""Parse a channel mode string.
|
"""Parse a channel mode string.
|
||||||
|
|
||||||
The function returns a list of lists with three members: sign,
|
The function returns a list of lists with three members: sign,
|
||||||
mode and argument. The sign is \"+\" or \"-\". The argument is
|
mode and argument. The sign is "+" or "-". The argument is
|
||||||
None if mode isn't one of \"b\", \"k\", \"l\", \"v\" or \"o\".
|
None if mode isn't one of "b", "k", "l", "v" or "o".
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
>>> irclib.parse_channel_modes(\"+ab-c foo\")
|
>>> parse_channel_modes("+ab-c foo")
|
||||||
[['+', 'a', None], ['+', 'b', 'foo'], ['-', 'c', None]]
|
[['+', 'a', None], ['+', 'b', 'foo'], ['-', 'c', None]]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -1356,7 +1413,7 @@ def _parse_modes(mode_string, unary_modes=""):
|
||||||
if ch in "+-":
|
if ch in "+-":
|
||||||
sign = ch
|
sign = ch
|
||||||
elif ch == " ":
|
elif ch == " ":
|
||||||
collecting_arguments = 1
|
pass
|
||||||
elif ch in unary_modes:
|
elif ch in unary_modes:
|
||||||
if len(args) >= arg_count + 1:
|
if len(args) >= arg_count + 1:
|
||||||
modes.append([sign, ch, args[arg_count]])
|
modes.append([sign, ch, args[arg_count]])
|
||||||
|
|
@ -1496,7 +1553,7 @@ numeric_events = {
|
||||||
"423": "noadmininfo",
|
"423": "noadmininfo",
|
||||||
"424": "fileerror",
|
"424": "fileerror",
|
||||||
"431": "nonicknamegiven",
|
"431": "nonicknamegiven",
|
||||||
"432": "erroneusnickname", # Thiss iz how its speld in thee RFC.
|
"432": "erroneusnickname", # Thiss iz how its speld in thee RFC.
|
||||||
"433": "nicknameinuse",
|
"433": "nicknameinuse",
|
||||||
"436": "nickcollision",
|
"436": "nickcollision",
|
||||||
"437": "unavailresource", # "Nick temporally unavailable"
|
"437": "unavailresource", # "Nick temporally unavailable"
|
||||||
|
|
@ -1511,7 +1568,7 @@ numeric_events = {
|
||||||
"462": "alreadyregistered",
|
"462": "alreadyregistered",
|
||||||
"463": "nopermforhost",
|
"463": "nopermforhost",
|
||||||
"464": "passwdmismatch",
|
"464": "passwdmismatch",
|
||||||
"465": "yourebannedcreep", # I love this one...
|
"465": "yourebannedcreep", # I love this one...
|
||||||
"466": "youwillbebanned",
|
"466": "youwillbebanned",
|
||||||
"467": "keyset",
|
"467": "keyset",
|
||||||
"471": "channelisfull",
|
"471": "channelisfull",
|
||||||
|
|
@ -1560,4 +1617,105 @@ protocol_events = [
|
||||||
"pong",
|
"pong",
|
||||||
]
|
]
|
||||||
|
|
||||||
all_events = generated_events + protocol_events + numeric_events.values()
|
all_events = generated_events + protocol_events + list(numeric_events.values())
|
||||||
|
|
||||||
|
# from jaraco.util.itertools
|
||||||
|
def always_iterable(item):
|
||||||
|
"""
|
||||||
|
Given an object, always return an iterable. If the item is not
|
||||||
|
already iterable, return a tuple containing only the item.
|
||||||
|
|
||||||
|
>>> always_iterable([1,2,3])
|
||||||
|
[1, 2, 3]
|
||||||
|
>>> always_iterable('foo')
|
||||||
|
('foo',)
|
||||||
|
>>> always_iterable(None)
|
||||||
|
(None,)
|
||||||
|
>>> always_iterable(xrange(10))
|
||||||
|
xrange(10)
|
||||||
|
"""
|
||||||
|
if isinstance(item, basestring) or not hasattr(item, '__iter__'):
|
||||||
|
item = item,
|
||||||
|
return item
|
||||||
|
|
||||||
|
# from jaraco.util.string
|
||||||
|
class FoldedCase(str):
|
||||||
|
"""
|
||||||
|
A case insensitive string class; behaves just like str
|
||||||
|
except compares equal when the only variation is case.
|
||||||
|
>>> s = FoldedCase('hello world')
|
||||||
|
|
||||||
|
>>> s == 'Hello World'
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> 'Hello World' == s
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> s.index('O')
|
||||||
|
4
|
||||||
|
|
||||||
|
>>> s.split('O')
|
||||||
|
['hell', ' w', 'rld']
|
||||||
|
|
||||||
|
>>> names = map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])
|
||||||
|
>>> names.sort()
|
||||||
|
>>> names
|
||||||
|
['alpha', 'Beta', 'GAMMA']
|
||||||
|
"""
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.lower() < other.lower()
|
||||||
|
|
||||||
|
def __gt__(self, other):
|
||||||
|
return self.lower() > other.lower()
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.lower() == other.lower()
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.lower())
|
||||||
|
|
||||||
|
# cache lower since it's likely to be called frequently.
|
||||||
|
def lower(self):
|
||||||
|
self._lower = super(FoldedCase, self).lower()
|
||||||
|
self.lower = lambda: self._lower
|
||||||
|
return self._lower
|
||||||
|
|
||||||
|
def index(self, sub):
|
||||||
|
return self.lower().index(sub.lower())
|
||||||
|
|
||||||
|
def split(self, splitter=' ', maxsplit=0):
|
||||||
|
pattern = re.compile(re.escape(splitter), re.I)
|
||||||
|
return pattern.split(self, maxsplit)
|
||||||
|
|
||||||
|
class IRCFoldedCase(FoldedCase):
|
||||||
|
"""
|
||||||
|
A version of FoldedCase that honors the IRC specification for lowercased
|
||||||
|
strings (RFC 1459).
|
||||||
|
|
||||||
|
>>> IRCFoldedCase('Foo^').lower()
|
||||||
|
'foo~'
|
||||||
|
>>> IRCFoldedCase('[this]') == IRCFoldedCase('{THIS}')
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
translation = str.maketrans(
|
||||||
|
string.ascii_uppercase + r"[]\^",
|
||||||
|
string.ascii_lowercase + r"{}|~",
|
||||||
|
)
|
||||||
|
|
||||||
|
def lower(self):
|
||||||
|
return self.translate(self.translation)
|
||||||
|
|
||||||
|
# for compatibility
|
||||||
|
def irc_lower(str):
|
||||||
|
return IRCFoldedCase(str).lower()
|
||||||
|
|
||||||
|
def total_seconds(td):
|
||||||
|
"""
|
||||||
|
Python 2.7 adds a total_seconds method to timedelta objects.
|
||||||
|
See http://docs.python.org/library/datetime.html#datetime.timedelta.total_seconds
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result = td.total_seconds()
|
||||||
|
except AttributeError:
|
||||||
|
result = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
|
||||||
|
return result
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue