Introduce Response class and a new way to send message to user

Remove old send_* in Message class
!more is now a built-in command
This commit is contained in:
Némunaire 2012-08-30 15:29:11 +02:00
parent c3c0b216d4
commit 5e52c4dbbb
4 changed files with 204 additions and 104 deletions

View File

@ -37,13 +37,22 @@ class Consumer(threading.Thread):
# Create, parse and treat the message # Create, parse and treat the message
try: try:
msg = Message(srv, raw, time, prvt) msg = Message(srv, raw, time, prvt)
msg.treat(self.context.hooks) res = msg.treat(self.context.hooks)
except: except:
print ("\033[1;31mERROR:\033[0m occurred during the " print ("\033[1;31mERROR:\033[0m occurred during the "
"processing of the message: %s" % raw) "processing of the message: %s" % raw)
exc_type, exc_value, exc_traceback = sys.exc_info() exc_type, exc_value, exc_traceback = sys.exc_info()
traceback.print_exception(exc_type, exc_value, traceback.print_exception(exc_type, exc_value,
exc_traceback) exc_traceback)
return
# Send message
if res is not None:
if isinstance(res, list):
for r in res:
srv.send_response(r)
else:
srv.send_response(res)
except queue.Empty: except queue.Empty:
pass pass

View File

@ -24,6 +24,7 @@ import time
import credits import credits
from credits import Credits from credits import Credits
from DCC import DCC from DCC import DCC
from response import Response
import xmlparser import xmlparser
CREDITS = {} CREDITS = {}
@ -121,97 +122,73 @@ class Message:
@property @property
def is_owner(self): def is_owner(self):
return self.nick == self.srv.owner return self.nick == self.srv.owner
def send_msg(self, channel, msg, cmd = "PRIVMSG", endl = "\r\n"):
if CREDITS[self.realname].speak():
self.srv.send_msg_verified (self.sender, channel, msg, cmd, endl)
def send_global(self, msg, cmd = "PRIVMSG", endl = "\r\n"):
if CREDITS[self.realname].speak():
self.srv.send_global (msg, cmd, endl)
def send_chn(self, msg):
"""Send msg on the same channel as receive message"""
if (self.srv.isDCC() and self.channel == self.srv.nick) or CREDITS[self.realname].speak():
if self.channel == self.srv.nick:
self.send_snd (msg)
else:
self.srv.send_msg (self.channel, msg)
def send_snd(self, msg):
"""Send msg to the person who send the original message"""
if self.srv.isDCC(self.sender) or CREDITS[self.realname].speak():
self.srv.send_msg_usr (self.sender, msg)
def authorize(self): def authorize(self):
if self.srv.isDCC(self.sender): """Is nemubot listening for the sender on this channel?"""
return True if self.srv.isDCC(self.sender):
elif self.realname not in CREDITS: return True
CREDITS[self.realname] = Credits(self.realname) elif self.realname not in CREDITS:
elif self.content[0] == '`': CREDITS[self.realname] = Credits(self.realname)
return True elif self.content[0] == '`':
elif not CREDITS[self.realname].ask(): return True
return False elif not CREDITS[self.realname].ask():
return self.srv.accepted_channel(self.channel) return False
return self.srv.accepted_channel(self.channel)
def treat(self, hooks): def treat(self, hooks):
if self.cmd == "PING": """Parse and treat the message"""
self.pong () if self.cmd == "PING":
elif self.cmd == "PRIVMSG" and self.ctcp: self.srv.send_pong(self.content)
self.parsectcp () elif self.cmd == "PRIVMSG" and self.ctcp:
elif self.cmd == "PRIVMSG" and self.authorize(): self.parsectcp()
self.parsemsg (hooks) elif self.cmd == "PRIVMSG" and self.authorize():
elif self.channel in self.srv.channels: return self.parsemsg (hooks)
if self.cmd == "353": elif self.channel in self.srv.channels:
self.srv.channels[self.channel].parse353(self) if self.cmd == "353":
elif self.cmd == "332": self.srv.channels[self.channel].parse353(self)
self.srv.channels[self.channel].parse332(self) elif self.cmd == "332":
elif self.cmd == "MODE": self.srv.channels[self.channel].parse332(self)
self.srv.channels[self.channel].mode(self) elif self.cmd == "MODE":
elif self.cmd == "JOIN": self.srv.channels[self.channel].mode(self)
self.srv.channels[self.channel].join(self.nick) elif self.cmd == "JOIN":
elif self.cmd == "PART": self.srv.channels[self.channel].join(self.nick)
self.srv.channels[self.channel].part(self.nick) elif self.cmd == "PART":
elif self.cmd == "TOPIC": self.srv.channels[self.channel].part(self.nick)
self.srv.channels[self.channel].topic = self.content elif self.cmd == "TOPIC":
elif self.cmd == "NICK": self.srv.channels[self.channel].topic = self.content
for chn in self.srv.channels.keys(): elif self.cmd == "NICK":
self.srv.channels[chn].nick(self.nick, self.content) for chn in self.srv.channels.keys():
elif self.cmd == "QUIT": self.srv.channels[chn].nick(self.nick, self.content)
for chn in self.srv.channels.keys(): elif self.cmd == "QUIT":
self.srv.channels[chn].part(self.nick) for chn in self.srv.channels.keys():
self.srv.channels[chn].part(self.nick)
return None
def pong (self):
self.srv.s.send(("PONG %s\r\n" % self.content).encode ())
def parsectcp(self): def parsectcp(self):
if self.content == '\x01CLIENTINFO\x01': """Parse CTCP requests"""
self.srv.send_ctcp(self.sender, "CLIENTINFO TIME USERINFO VERSION CLIENTINFO") if self.content == '\x01CLIENTINFO\x01':
elif self.content == '\x01TIME\x01': self.srv.send_ctcp(self.sender, "CLIENTINFO TIME USERINFO VERSION CLIENTINFO")
self.srv.send_ctcp(self.sender, "TIME %s" % (datetime.now())) elif self.content == '\x01TIME\x01':
elif self.content == '\x01USERINFO\x01': self.srv.send_ctcp(self.sender, "TIME %s" % (datetime.now()))
self.srv.send_ctcp(self.sender, "USERINFO %s" % (self.srv.realname)) elif self.content == '\x01USERINFO\x01':
elif self.content == '\x01VERSION\x01': self.srv.send_ctcp(self.sender, "USERINFO %s" % (self.srv.realname))
self.srv.send_ctcp(self.sender, "VERSION nemubot v%s" % self.srv.context.version_txt) elif self.content == '\x01VERSION\x01':
elif self.content[:9] == '\x01DCC CHAT': self.srv.send_ctcp(self.sender, "VERSION nemubot v%s" % self.srv.context.version_txt)
words = self.content[1:len(self.content) - 1].split(' ') elif self.content[:9] == '\x01DCC CHAT':
ip = self.srv.toIP(int(words[3])) words = self.content[1:len(self.content) - 1].split(' ')
conn = DCC(self.srv, self.sender) ip = self.srv.toIP(int(words[3]))
if conn.accept_user(ip, int(words[4])): conn = DCC(self.srv, self.sender)
self.srv.dcc_clients[conn.sender] = conn if conn.accept_user(ip, int(words[4])):
conn.send_dcc("Hello %s!" % conn.nick) self.srv.dcc_clients[conn.sender] = conn
else: conn.send_dcc("Hello %s!" % conn.nick)
print ("DCC: unable to connect to %s:%s" % (ip, words[4])) else:
elif self.content == '\x01NEMUBOT\x01': print ("DCC: unable to connect to %s:%s" % (ip, words[4]))
self.srv.send_ctcp(self.sender, "NEMUBOT %f" % self.srv.context.version) elif self.content == '\x01NEMUBOT\x01':
elif self.content[:7] != '\x01ACTION': self.srv.send_ctcp(self.sender, "NEMUBOT %f" % self.srv.context.version)
print (self.content) elif self.content[:7] != '\x01ACTION':
self.srv.send_ctcp(self.sender, "ERRMSG Unknown or unimplemented CTCP request") print (self.content)
self.srv.send_ctcp(self.sender, "ERRMSG Unknown or unimplemented CTCP request")
def reparsemsg(self): def reparsemsg(self):
if self.hooks is not None: if self.hooks is not None:
@ -230,11 +207,11 @@ class Message:
# Treat ping # Treat ping
if re.match(".*(m[' ]?entends?[ -]+tu|h?ear me|do you copy|ping)", if re.match(".*(m[' ]?entends?[ -]+tu|h?ear me|do you copy|ping)",
messagel) is not None: messagel) is not None:
self.send_chn ("%s: pong"%(self.nick)) return Response(message="pong", channel=self.channel, nick=self.nick)
# Ask hooks # Ask hooks
else: else:
hooks.treat_ask(self) return hooks.treat_ask(self)
#Owner commands #Owner commands
elif self.content[0] == '`' and self.sender == self.srv.owner: elif self.content[0] == '`' and self.sender == self.srv.owner:
@ -281,24 +258,33 @@ class Message:
except AttributeError: except AttributeError:
continue continue
elif self.cmd[0] == "more":
if self.channel == self.srv.nick:
if self.nick in self.srv.moremessages:
return self.srv.moremessages[self.nick]
else:
if self.channel in self.srv.moremessages:
return self.srv.moremessages[self.channel]
elif self.cmd[0] == "dcctest": elif self.cmd[0] == "dcctest":
print("dcctest for", self.sender) print("dcctest for", self.sender)
self.srv.send_dcc("Test DCC", self.sender) self.srv.send_dcc("Test DCC", self.sender)
elif self.cmd[0] == "pvdcctest": elif self.cmd[0] == "pvdcctest":
print("dcctest") print("dcctest")
self.send_snd("Test DCC") return Response(message="Test DCC", nick=self.nick)
elif self.cmd[0] == "dccsendtest": elif self.cmd[0] == "dccsendtest":
print("dccsendtest") print("dccsendtest")
conn = DCC(self.srv, self.sender) conn = DCC(self.srv, self.sender)
conn.send_file("bot_sample.xml") conn.send_file("bot_sample.xml")
else: else:
hooks.treat_cmd(self) return hooks.treat_cmd(self)
else: else:
hooks.treat_answer(self) res = hooks.treat_answer(self)
# Assume the message starts with nemubot: # Assume the message starts with nemubot:
if self.private: if res is None and self.private:
hooks.treat_ask(self) return hooks.treat_ask(self)
return res
# def parseOwnerCmd(self, cmd): # def parseOwnerCmd(self, cmd):

99
response.py Normal file
View File

@ -0,0 +1,99 @@
# -*- coding: utf-8 -*-
# Nemubot is a modulable IRC bot, built around XML configuration files.
# Copyright (C) 2012 Mercier Pierre-Olivier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class Response:
def __init__(self, message=None, channel=None, nick=None, server=None,
nomore="No more message", title=None, more="(suite) "):
self.nomore = nomore
self.more = more
self.title = title
self.messages = list()
if message is not None:
self.messages.append(message)
self.elt = 0 # Next element to display
self.channel = channel
self.nick = nick
self.alone = True
def append_message(self, message):
self.alone = False
self.messages.append(message)
@property
def empty(self):
return len(self.messages) <= 0
def get_message(self):
if self.alone and len(self.messages) > 1:
self.alone = False
if self.empty:
return self.nomore
msg = ""
if self.channel is not None and self.nick is not None:
msg += self.nick + ": "
if self.title is not None:
if self.elt > 0:
msg += self.title + " " + self.more + ": "
else:
msg += self.title + ": "
if self.elt > 0:
msg += "... "
elts = self.messages[0][self.elt:]
if isinstance(elts, list):
for e in elts:
if len(msg) + len(e) > 430:
msg += "..."
self.alone = False
return msg
else:
msg += e + ", "
self.elt += 1
self.messages.pop(0)
self.elt = 0
return msg[:len(msg)-2]
else:
if len(elts) <= 432:
self.messages.pop(0)
self.elt = 0
return msg + elts
else:
words = elts.split(' ')
if len(words[0]) > 432 - len(msg):
self.elt += 432 - len(msg)
return msg + elts[:self.elt] + "..."
for w in words:
if len(msg) + len(w) > 431:
msg += "..."
self.alone = False
return msg
else:
msg += w + " "
self.elt += len(w) + 1
self.messages.pop(0)
self.elt = 0
return msg

View File

@ -46,6 +46,8 @@ class Server(threading.Thread):
chan = channel.Channel(chn, self) chan = channel.Channel(chn, self)
self.channels[chan.name] = chan self.channels[chan.name] = chan
self.moremessages = dict()
threading.Thread.__init__(self) threading.Thread.__init__(self)
def isDCC(self, to=None): def isDCC(self, to=None):
@ -72,13 +74,6 @@ class Server(threading.Thread):
else: else:
return None return None
@property
def partner(self):
if self.node.hasAttribute("partner"):
return self.node["partner"]
else:
return None
@property @property
def ip(self): def ip(self):
"""Convert common IP representation to little-endian integer representation""" """Convert common IP representation to little-endian integer representation"""
@ -112,6 +107,21 @@ class Server(threading.Thread):
def id(self): def id(self):
return self.host + ":" + str(self.port) return self.host + ":" + str(self.port)
def send_pong(self, cnt):
self.s.send(("PONG %s\r\n" % cnt).encode ())
def send_response(self, res):
if res.channel is not None:
self.send_msg(res.channel, res.get_message())
if not res.alone:
self.moremessages[res.channel] = res
elif res.nick is not None:
self.send_msg_usr(res.nick, res.get_message())
if not res.alone:
self.moremessages[res.nick] = res
def send_ctcp(self, to, msg, cmd = "NOTICE", endl = "\r\n"): def send_ctcp(self, to, msg, cmd = "NOTICE", endl = "\r\n"):
"""Send a message as CTCP response""" """Send a message as CTCP response"""
if msg is not None and to is not None: if msg is not None and to is not None:
@ -143,10 +153,6 @@ class Server(threading.Thread):
else: else:
self.s.send (("%s %s :%s%s" % (cmd, channel, line[0:442]+"...", endl)).encode ()) self.s.send (("%s %s :%s%s" % (cmd, channel, line[0:442]+"...", endl)).encode ())
def send_msg_prtn(self, msg):
"""Send a message to partner bot"""
self.send_msg_final(self.partner, msg)
def send_msg_usr(self, user, msg): def send_msg_usr(self, user, msg):
"""Send a message to a user instead of a channel""" """Send a message to a user instead of a channel"""
if user is not None and user[0] != "#": if user is not None and user[0] != "#":