Refactoring messages

This commit is contained in:
nemunaire 2014-03-13 21:28:12 +01:00
parent 073f34af26
commit bd26db6cca
3 changed files with 111 additions and 76 deletions

View File

@ -122,18 +122,43 @@ class IRCServer(server.Server):
if msg.channel in self.channels: if msg.channel in self.channels:
self.channels[msg.channel].treat(msg.cmd, msg) self.channels[msg.channel].treat(msg.cmd, msg)
def filter_receivers(self, receivers, sender=None):
"""Return a filtered list of authorized channels and users"""
if self.allow_all:
return receivers
filtered = list()
for chan in receivers:
if (
# Is the channel authorized?
(chan in self.channels and
# Is the sender on the channel?
(
sender is None or
sender in self.channels[chan].people
)) or
# Accept private messages?
(self.listen_nick and chan == self.nick)
):
filtered.append(chan)
def accepted_channel(self, chan, sender=None): def accepted_channel(self, chan, sender=None):
"""Return True if the channel (or the user) is authorized""" """Return True if the channel (or the user) is authorized"""
if self.allow_all: if self.allow_all:
return True return True
elif self.listen_nick: elif self.listen_nick:
return (chan in self.channels and (sender is None or sender in return (chan in self.channels and (sender is None or sender in
self.channels[chan].people) self.channels[chan].people)
) or chan == self.nick ) or chan == self.nick
else: else:
return chan in self.channels and (sender is None or sender return chan in self.channels and (sender is None or sender
in self.channels[chan].people) in self.channels[chan].people)
def join(self, chan, password=None, force=False): def join(self, chan, password=None, force=False):
"""Join a channel""" """Join a channel"""
if force or (chan is not None and if force or (chan is not None and

View File

@ -41,10 +41,12 @@ class MessageConsumer:
def treat_in(self, context, msg): def treat_in(self, context, msg):
"""Treat the input message""" """Treat the input message"""
if msg.cmd == "PING": if msg.cmd == "PING":
self.srv.send_pong(msg.content) self.srv.send_pong(msg.params[0])
else: else:
# TODO: Manage credits # TODO: Manage credits
if msg.channel is None or self.srv.accepted_channel(msg.channel): msg.receivers = self.srv.filter_receivers(msg.receivers)
if len(msg.receivers) == 0:
# All messages # All messages
context.treat_pre(msg, self.srv) context.treat_pre(msg, self.srv)

View File

@ -39,99 +39,107 @@ def save():
global filename global filename
credits.BANLIST.save(filename) credits.BANLIST.save(filename)
mgx = re.compile(b'''^(?:@(?P<tags>[^ ]+)\ )?
(?::(?P<prefix>
(?P<nick>[a-zA-Z][^!@ ]*)
(?: !(?P<user>[^@ ]+))?
(?:@(?P<host>[^ ]+))?
)\ )?
(?P<command>(?:[a-zA-Z]+|[0-9]{3}))
(?P<params>(?:\ [^:][^ ]*)*)(?:\ :(?P<trailing>.*))?
$''', re.X)
class Message: class Message:
def __init__ (self, line, timestamp, private = False): def __init__(self, raw_line, timestamp, private = False):
self.raw = line self.raw = raw_line.rstrip() # remove trailing crlf
self.time = timestamp self.tags = { 'time': timestamp }
self.channel = None self.params = list()
self.content = b''
self.ctcp = False
line = line.rstrip() #remove trailing 'rn'
words = line.split(b' ') p = mgx.match(raw_line.rstrip())
if words[0][0] == 58: #58 is : in ASCII table
self.sender = words[0][1:].decode() # Parse tags if exists: @aaa=bbb;ccc;example.com/ddd=eee
self.cmd = words[1].decode() if p.group("tags"):
for tgs in p.group("tags").decode().split(';'):
tag = tgs.split('=')
if len(tag) > 1:
self.add_tag(tag[0], tag[1])
else: else:
self.cmd = words[0].decode() self.add_tag(tag[0])
self.sender = None
if self.cmd == 'PING': # Parse prefix if exists: :nick!user@host.com
self.content = words[1] self.prefix = self.decode(p.group("prefix"))
elif self.sender is not None: self.nick = self.decode(p.group("nick"))
self.nick = (self.sender.split('!'))[0] self.user = self.decode(p.group("user"))
if self.nick != self.sender: self.host = self.decode(p.group("host"))
self.realname = (self.sender.split('!'))[1]
else:
self.realname = self.nick
self.sender = self.nick + "!" + self.realname
if len(words) > 2: # Parse command
self.channel = self.pickWords(words[2:]).decode() self.cmd = p.group("command").decode()
# Parse params
if p.group("params"):
for param in p.group("params").strip().split(b' '):
self.params.append(param)
if p.group("trailing"):
self.params.append(p.group("trailing"))
# Special commands
if self.cmd == 'PRIVMSG': if self.cmd == 'PRIVMSG':
# Check for CTCP request self.receivers = self.params[0].decode().split(',')
self.ctcp = len(words[3]) > 1 and (words[3][0] == 0x01 or words[3][1] == 0x01)
self.content = self.pickWords(words[3:])
elif self.cmd == '353' and len(words) > 3:
for i in range(2, len(words)):
if words[i][0] == 58:
self.content = words[i:]
#Remove the first :
self.content[0] = self.content[0][1:]
self.channel = words[i-1].decode()
break
elif self.cmd == 'NICK':
self.content = self.pickWords(words[2:])
elif self.cmd == 'MODE':
self.content = words[3:]
elif self.cmd == '332':
self.channel = words[3]
self.content = self.pickWords(words[4:])
else:
#print (line)
self.content = self.pickWords(words[3:])
else:
print (line)
if self.cmd == 'PRIVMSG':
self.channel = words[2].decode()
self.content = b' '.join(words[3:])
self.decode()
if self.cmd == 'PRIVMSG':
self.parse_content()
self.private = private
def parse_content(self):
"""Parse or reparse the message content"""
# If CTCP, remove 0x01 # If CTCP, remove 0x01
if self.ctcp: if len(self.params[1]) > 1 and (self.params[1][0] == 0x01 or self.params[1][1] == 0x01):
self.content = self.content[1:len(self.content)-1] self.is_ctcp = True
self.text = self.decode(self.params[1][1:len(self.params[1])-1])
else:
self.is_ctcp = False
self.text = self.decode(self.params[1])
# Split content by words # Split content by words
try: try:
self.cmds = shlex.split(self.content) self.cmds = shlex.split(self.text)
except ValueError: except ValueError:
self.cmds = self.content.split(' ') self.cmds = self.text.split(' ')
def pickWords(self, words): elif self.cmd == '353': # RPL_NAMREPLY
"""Parse last argument of a line: can be a single word or a sentence starting with :""" self.receivers = [ self.params[0].decode() ]
if len(words) > 0 and len(words[0]) > 0: self.nicks = self.params[1].decode().split(" ")
if words[0][0] == 58:
return b' '.join(words[0:])[1:]
else:
return words[0]
else:
return b''
def decode(self): elif self.cmd == '332':
self.receivers = [ self.params[0].decode() ]
self.topic = self.params[1].decode().split(" ")
else:
for i in range(0, len(self.params)-1):
self.params[i] = self.decode(self.params[i])
print(self)
def add_tag(self, key, value=None):
"""Add an IRCv3.2 Message Tags"""
# Treat special tags
if key == "time":
value = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
# Store tag
self.tags[key] = value
def decode(self, s):
"""Decode the content string usign a specific encoding""" """Decode the content string usign a specific encoding"""
if isinstance(self.content, bytes): if isinstance(s, bytes):
try: try:
self.content = self.content.decode() s = s.decode()
except UnicodeDecodeError: except UnicodeDecodeError:
#TODO: use encoding from config file #TODO: use encoding from config file
self.content = self.content.decode('utf-8', 'replace') s = s.decode('utf-8', 'replace')
return s
def __str__(self):
return "Message " + str(self.__dict__)
def authorize_DEPRECATED(self): def authorize_DEPRECATED(self):
"""Is nemubot listening for the sender on this channel?""" """Is nemubot listening for the sender on this channel?"""