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:
parent
c3c0b216d4
commit
5e52c4dbbb
11
consumer.py
11
consumer.py
@ -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
|
||||||
|
170
message.py
170
message.py
@ -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
99
response.py
Normal 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
|
28
server.py
28
server.py
@ -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] != "#":
|
||||||
|
Loading…
Reference in New Issue
Block a user