Convert nemuspeak as a module to nemubot
This commit is contained in:
parent
5e87843dda
commit
f927d5ab0a
132
modules/speak.py
Normal file
132
modules/speak.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
from queue import Queue
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
from hooks import hook
|
||||||
|
from message import TextMessage
|
||||||
|
from message.visitor import AbstractVisitor
|
||||||
|
|
||||||
|
nemubotversion = 3.4
|
||||||
|
|
||||||
|
queue = Queue()
|
||||||
|
spk_th = None
|
||||||
|
last = None
|
||||||
|
|
||||||
|
SMILEY = list()
|
||||||
|
CORRECTIONS = list()
|
||||||
|
|
||||||
|
def load(context):
|
||||||
|
for smiley in CONF.getNodes("smiley"):
|
||||||
|
if smiley.hasAttribute("txt") and smiley.hasAttribute("mood"):
|
||||||
|
SMILEY.append((smiley.getAttribute("txt"), smiley.getAttribute("mood")))
|
||||||
|
print ("%d smileys loaded" % len(SMILEY))
|
||||||
|
|
||||||
|
for correct in CONF.getNodes("correction"):
|
||||||
|
if correct.hasAttribute("bad") and correct.hasAttribute("good"):
|
||||||
|
CORRECTIONS.append((" " + (correct.getAttribute("bad") + " "), (" " + correct.getAttribute("good") + " ")))
|
||||||
|
print ("%d corrections loaded" % len(CORRECTIONS))
|
||||||
|
|
||||||
|
|
||||||
|
class Speaker(Thread):
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
global queue, spk_th
|
||||||
|
while not queue.empty():
|
||||||
|
sentence = queue.get_nowait()
|
||||||
|
lang = "fr"
|
||||||
|
print_debug(sentence)
|
||||||
|
subprocess.call(["espeak", "-v", lang, "--", sentence])
|
||||||
|
queue.task_done()
|
||||||
|
|
||||||
|
spk_th = None
|
||||||
|
|
||||||
|
|
||||||
|
class SpeakerVisitor(AbstractVisitor):
|
||||||
|
|
||||||
|
def __init__(self, last):
|
||||||
|
self.pp = ""
|
||||||
|
self.last = last
|
||||||
|
|
||||||
|
|
||||||
|
def visit_TextMessage(self, msg):
|
||||||
|
force = (self.last is None)
|
||||||
|
|
||||||
|
if force or msg.date - self.last.date > timedelta(0, 500):
|
||||||
|
self.pp += "A %d heure %d : " % (msg.date.hour, msg.date.minute)
|
||||||
|
force = True
|
||||||
|
|
||||||
|
if force or msg.channel != self.last.channel:
|
||||||
|
if msg.to_response == msg.to:
|
||||||
|
self.pp += "sur %s. " % (", ".join(msg.to))
|
||||||
|
else:
|
||||||
|
self.pp += "en message priver. "
|
||||||
|
|
||||||
|
action = False
|
||||||
|
if msg.message.find("ACTION ") == 0:
|
||||||
|
self.pp += "%s " % msg.frm
|
||||||
|
msg.message = msg.message.replace("ACTION ", "")
|
||||||
|
action = True
|
||||||
|
for (txt, mood) in SMILEY:
|
||||||
|
if msg.message.find(txt) >= 0:
|
||||||
|
self.pp += "%s %s : " % (msg.frm, mood)
|
||||||
|
msg.message = msg.message.replace(txt, "")
|
||||||
|
action = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not action and (force or msg.frm != self.last.frm):
|
||||||
|
self.pp += "%s dit : " % msg.frm
|
||||||
|
|
||||||
|
if re.match(".*https?://.*", msg.message) is not None:
|
||||||
|
msg.message = re.sub(r'https?://([^/]+)[^ ]*', " U.R.L \\1", msg.message)
|
||||||
|
|
||||||
|
self.pp += msg.message
|
||||||
|
|
||||||
|
|
||||||
|
def visit_DirectAsk(self, msg):
|
||||||
|
res = TextMessage("%s: %s" % (msg.designated, msg.message),
|
||||||
|
server=msg.server, date=msg.date,
|
||||||
|
to=msg.to, frm=msg.frm)
|
||||||
|
res.accept(self)
|
||||||
|
|
||||||
|
|
||||||
|
def visit_Command(self, msg):
|
||||||
|
res = TextMessage("Bang %s%s%s" % (msg.cmd,
|
||||||
|
" " if len(msg.args) else "",
|
||||||
|
" ".join(msg.args)),
|
||||||
|
server=msg.server, date=msg.date,
|
||||||
|
to=msg.to, frm=msg.frm)
|
||||||
|
res.accept(self)
|
||||||
|
|
||||||
|
|
||||||
|
def visit_OwnerCommand(self, msg):
|
||||||
|
res = TextMessage("Owner Bang %s%s%s" % (msg.cmd,
|
||||||
|
" " if len(msg.args) else "",
|
||||||
|
" ".join(msg.args)),
|
||||||
|
server=msg.server, date=msg.date,
|
||||||
|
to=msg.to, frm=msg.frm)
|
||||||
|
res.accept(self)
|
||||||
|
|
||||||
|
|
||||||
|
@hook("in")
|
||||||
|
def treat_for_speak(msg):
|
||||||
|
if not msg.frm_owner:
|
||||||
|
append_message(msg)
|
||||||
|
|
||||||
|
def append_message(msg):
|
||||||
|
global last, spk_th
|
||||||
|
|
||||||
|
if msg.message.find("TYPING ") == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
vprnt = SpeakerVisitor(last)
|
||||||
|
msg.accept(vprnt)
|
||||||
|
queue.put_nowait(vprnt.pp)
|
||||||
|
last = msg
|
||||||
|
|
||||||
|
if spk_th is None:
|
||||||
|
spk_th = Speaker()
|
||||||
|
spk_th.start()
|
186
nemuspeak.py
186
nemuspeak.py
@ -1,186 +0,0 @@
|
|||||||
#!/usr/bin/python3
|
|
||||||
# coding=utf-8
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import signal
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import traceback
|
|
||||||
from datetime import datetime
|
|
||||||
from datetime import timedelta
|
|
||||||
import _thread
|
|
||||||
|
|
||||||
if len(sys.argv) <= 1:
|
|
||||||
print ("This script takes exactly 1 arg: a XML config file")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def onSignal(signum, frame):
|
|
||||||
print ("\nSIGINT receive, saving states and close")
|
|
||||||
sys.exit (0)
|
|
||||||
signal.signal(signal.SIGINT, onSignal)
|
|
||||||
|
|
||||||
if len(sys.argv) == 3:
|
|
||||||
basedir = sys.argv[2]
|
|
||||||
else:
|
|
||||||
basedir = "./"
|
|
||||||
|
|
||||||
import xmlparser as msf
|
|
||||||
import message
|
|
||||||
from server.IRC import IRCServer
|
|
||||||
|
|
||||||
SMILEY = list()
|
|
||||||
CORRECTIONS = list()
|
|
||||||
g_queue = list()
|
|
||||||
talkEC = 0
|
|
||||||
stopSpk = 0
|
|
||||||
lastmsg = None
|
|
||||||
|
|
||||||
def speak(endstate):
|
|
||||||
global lastmsg, g_queue, talkEC, stopSpk
|
|
||||||
talkEC = 1
|
|
||||||
stopSpk = 0
|
|
||||||
|
|
||||||
if lastmsg is None:
|
|
||||||
lastmsg = message.Message(b":Quelqun!someone@p0m.fr PRIVMSG channel nothing", datetime.now())
|
|
||||||
|
|
||||||
while not stopSpk and len(g_queue) > 0:
|
|
||||||
srv, msg = g_queue.pop(0)
|
|
||||||
lang = "fr"
|
|
||||||
sentence = ""
|
|
||||||
force = 0
|
|
||||||
|
|
||||||
#Skip identic body
|
|
||||||
if msg.content == lastmsg.content:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if force or msg.time - lastmsg.time > timedelta(0, 500):
|
|
||||||
sentence += "A {0} heure {1} : ".format(msg.time.hour, msg.time.minute)
|
|
||||||
force = 1
|
|
||||||
|
|
||||||
if force or msg.channel != lastmsg.channel:
|
|
||||||
if msg.channel == srv.owner:
|
|
||||||
sentence += "En message priver. " #Just to avoid é :p
|
|
||||||
else:
|
|
||||||
sentence += "Sur " + msg.channel + ". "
|
|
||||||
force = 1
|
|
||||||
|
|
||||||
action = 0
|
|
||||||
if msg.content.find("ACTION ") == 1:
|
|
||||||
sentence += msg.nick + " "
|
|
||||||
msg.content = msg.content.replace("ACTION ", "")
|
|
||||||
action = 1
|
|
||||||
for (txt, mood) in SMILEY:
|
|
||||||
if msg.content.find(txt) >= 0:
|
|
||||||
sentence += msg.nick + (" %s : "%mood)
|
|
||||||
msg.content = msg.content.replace(txt, "")
|
|
||||||
action = 1
|
|
||||||
break
|
|
||||||
|
|
||||||
for (bad, good) in CORRECTIONS:
|
|
||||||
if msg.content.find(bad) >= 0:
|
|
||||||
msg.content = (" " + msg.content + " ").replace(bad, good)
|
|
||||||
|
|
||||||
if action == 0 and (force or msg.sender != lastmsg.sender):
|
|
||||||
sentence += msg.nick + " dit : "
|
|
||||||
|
|
||||||
if re.match(".*(https?://)?(www\\.)?ycc.fr/[a-z0-9A-Z]+.*", msg.content) is not None:
|
|
||||||
msg.content = re.sub("(https?://)?(www\\.)?ycc.fr/[a-z0-9A-Z]+", " U.R.L Y.C.C ", msg.content)
|
|
||||||
|
|
||||||
if re.match(".*https?://.*", msg.content) is not None:
|
|
||||||
msg.content = re.sub(r'https?://[^ ]+', " U.R.L ", msg.content)
|
|
||||||
|
|
||||||
if re.match("^ *[^a-zA-Z0-9 ][a-zA-Z]{2}[^a-zA-Z0-9 ]", msg.content) is not None:
|
|
||||||
if sentence != "":
|
|
||||||
intro = subprocess.call(["espeak", "-v", "fr", "--", sentence])
|
|
||||||
#intro.wait()
|
|
||||||
|
|
||||||
lang = msg.content[1:3].lower()
|
|
||||||
sentence = msg.content[4:]
|
|
||||||
else:
|
|
||||||
sentence += msg.content
|
|
||||||
|
|
||||||
spk = subprocess.call(["espeak", "-v", lang, "--", sentence])
|
|
||||||
#spk.wait()
|
|
||||||
|
|
||||||
lastmsg = msg
|
|
||||||
|
|
||||||
if not stopSpk:
|
|
||||||
talkEC = endstate
|
|
||||||
else:
|
|
||||||
talkEC = 1
|
|
||||||
|
|
||||||
|
|
||||||
class Server(IRCServer):
|
|
||||||
def treat_msg(self, line, private = False):
|
|
||||||
global stopSpk, talkEC, g_queue
|
|
||||||
try:
|
|
||||||
msg = message.Message (line, datetime.now(), private)
|
|
||||||
if msg.cmd == 'PING':
|
|
||||||
self.send_pong(msg.content)
|
|
||||||
elif msg.cmd == 'PRIVMSG' and self.accepted_channel(msg.channel):
|
|
||||||
if msg.nick != self.owner:
|
|
||||||
g_queue.append((self, msg))
|
|
||||||
if talkEC == 0:
|
|
||||||
_thread.start_new_thread(speak, (0,))
|
|
||||||
elif msg.content[0] == "`" and len(msg.content) > 1:
|
|
||||||
msg.cmds = msg.cmds[1:]
|
|
||||||
if msg.cmds[0] == "speak":
|
|
||||||
_thread.start_new_thread(speak, (0,))
|
|
||||||
elif msg.cmds[0] == "reset":
|
|
||||||
while len(g_queue) > 0:
|
|
||||||
g_queue.pop()
|
|
||||||
elif msg.cmds[0] == "save":
|
|
||||||
if talkEC == 0:
|
|
||||||
talkEC = 1
|
|
||||||
stopSpk = 1
|
|
||||||
elif msg.cmds[0] == "add":
|
|
||||||
self.channels.append(msg.cmds[1])
|
|
||||||
print (cmd[1] + " added to listened channels")
|
|
||||||
elif msg.cmds[0] == "del":
|
|
||||||
if self.channels.count(msg.cmds[1]) > 0:
|
|
||||||
self.channels.remove(msg.cmds[1])
|
|
||||||
print (msg.cmds[1] + " removed from listened channels")
|
|
||||||
else:
|
|
||||||
print (cmd[1] + " not in listened channels")
|
|
||||||
except:
|
|
||||||
print ("\033[1;31mERROR:\033[0m occurred during the processing of the message: %s" % line)
|
|
||||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
||||||
traceback.print_exception(exc_type, exc_value, exc_traceback)
|
|
||||||
|
|
||||||
|
|
||||||
config = msf.parse_file(sys.argv[1])
|
|
||||||
|
|
||||||
for smiley in config.getNodes("smiley"):
|
|
||||||
if smiley.hasAttribute("txt") and smiley.hasAttribute("mood"):
|
|
||||||
SMILEY.append((smiley.getAttribute("txt"), smiley.getAttribute("mood")))
|
|
||||||
print ("%d smileys loaded"%len(SMILEY))
|
|
||||||
|
|
||||||
for correct in config.getNodes("correction"):
|
|
||||||
if correct.hasAttribute("bad") and correct.hasAttribute("good"):
|
|
||||||
CORRECTIONS.append((" " + (correct.getAttribute("bad") + " "), (" " + correct.getAttribute("good") + " ")))
|
|
||||||
print ("%d corrections loaded"%len(CORRECTIONS))
|
|
||||||
|
|
||||||
for serveur in config.getNodes("server"):
|
|
||||||
srv = Server(serveur, config["nick"], config["owner"], config["realname"], serveur.hasAttribute("ssl"))
|
|
||||||
srv.launch(None)
|
|
||||||
|
|
||||||
def sighup_h(signum, frame):
|
|
||||||
global talkEC, stopSpk
|
|
||||||
sys.stdout.write ("Signal reçu ... ")
|
|
||||||
if os.path.exists("/tmp/isPresent"):
|
|
||||||
_thread.start_new_thread(speak, (0,))
|
|
||||||
print ("Morning!")
|
|
||||||
else:
|
|
||||||
print ("Sleeping!")
|
|
||||||
if talkEC == 0:
|
|
||||||
talkEC = 1
|
|
||||||
stopSpk = 1
|
|
||||||
signal.signal(signal.SIGHUP, sighup_h)
|
|
||||||
|
|
||||||
print ("Nemuspeak ready, waiting for new messages...")
|
|
||||||
prompt=""
|
|
||||||
while prompt != "quit":
|
|
||||||
prompt=sys.stdin.readlines ()
|
|
||||||
|
|
||||||
sys.exit(0)
|
|
@ -105,6 +105,9 @@ class IRC(SocketServer):
|
|||||||
self.ctcp_capabilities["USERINFO"] = lambda msg, cmds: "USERINFO %s" % self.realname
|
self.ctcp_capabilities["USERINFO"] = lambda msg, cmds: "USERINFO %s" % self.realname
|
||||||
self.ctcp_capabilities["VERSION"] = lambda msg, cmds: "VERSION nemubot v%s" % bot.__version__
|
self.ctcp_capabilities["VERSION"] = lambda msg, cmds: "VERSION nemubot v%s" % bot.__version__
|
||||||
|
|
||||||
|
# TODO: Temporary fix, waiting for hook based CTCP management
|
||||||
|
self.ctcp_capabilities["TYPING"] = lambda msg, cmds: None
|
||||||
|
|
||||||
self.logger.debug("CTCP capabilities setup: %s", ", ".join(self.ctcp_capabilities))
|
self.logger.debug("CTCP capabilities setup: %s", ", ".join(self.ctcp_capabilities))
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
<config nick="nemubot" realname="nemubot speaker" owner="someone">
|
<nemubotconfig nick="nemubot" realname="nemubot speaker" owner="someone">
|
||||||
|
|
||||||
<!-- Bitlbee/Minbif -->
|
<!-- Bitlbee/Minbif -->
|
||||||
<server server="nemunai.re" port="6666" password="bitlbee" allowall="true" />
|
<server host="nemunai.re" port="6666" password="bitlbee" autoconnect="true" caps="znc.in/server-time-iso" />
|
||||||
|
|
||||||
<!-- Rezosup -->
|
<!-- Rezosup -->
|
||||||
<server server="nemunai.re" port="6667">
|
<server server="nemunai.re" port="6667">
|
||||||
<channel name="#epita" />
|
<channel name="#epita" />
|
||||||
</server>
|
</server>
|
||||||
|
|
||||||
|
<module name="speak">
|
||||||
<correction bad="ca" good="ça" />
|
<correction bad="ca" good="ça" />
|
||||||
|
|
||||||
<smiley txt=":p" mood="tire la langue" />
|
<smiley txt=":p" mood="tire la langue" />
|
||||||
@ -29,5 +30,6 @@
|
|||||||
<smiley txt="/o/" mood="danse" />
|
<smiley txt="/o/" mood="danse" />
|
||||||
<smiley txt="\\o\\" mood="danse" />
|
<smiley txt="\\o\\" mood="danse" />
|
||||||
<smiley txt="\\o/" mood="fait une accolade" />
|
<smiley txt="\\o/" mood="fait une accolade" />
|
||||||
|
</module>
|
||||||
|
|
||||||
</config>
|
</nemubotconfig>
|
||||||
|
Loading…
Reference in New Issue
Block a user