Changes most of nemubot modules to packages
This commit is contained in:
parent
7b7bbd99e5
commit
316e6878ba
13 changed files with 420 additions and 1247 deletions
|
@ -3,7 +3,6 @@
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from datetime import datetime
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
@ -12,6 +11,8 @@ from module_state import ModuleState
|
||||||
|
|
||||||
nemubotversion = 3.0
|
nemubotversion = 3.0
|
||||||
|
|
||||||
|
from . import Manager
|
||||||
|
|
||||||
def help_tiny ():
|
def help_tiny ():
|
||||||
"""Line inserted in the response to the command !help"""
|
"""Line inserted in the response to the command !help"""
|
||||||
return "events manager"
|
return "events manager"
|
||||||
|
@ -19,63 +20,23 @@ def help_tiny ():
|
||||||
def help_full ():
|
def help_full ():
|
||||||
return "This module store a lot of events: ny, we, vacs, " + (", ".join(DATAS.index.keys())) + "\n!eventslist: gets list of timer\n!start /something/: launch a timer"
|
return "This module store a lot of events: ny, we, vacs, " + (", ".join(DATAS.index.keys())) + "\n!eventslist: gets list of timer\n!start /something/: launch a timer"
|
||||||
|
|
||||||
class Manager(threading.Thread):
|
|
||||||
def __init__(self):
|
|
||||||
self.stop = False
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
|
|
||||||
def alertEnd(self, evt):
|
|
||||||
global newStrendEvt, SRVS, DATAS
|
|
||||||
#Send the message on each matched servers
|
|
||||||
for server in SRVS.keys():
|
|
||||||
if not evt.hasAttribute("server") or server == evt["server"]:
|
|
||||||
if evt["channel"] == SRVS[server].nick:
|
|
||||||
SRVS[server].send_msg_usr(evt["proprio"], "%s: %s arrivé à échéance." % (evt["proprio"], evt["name"]))
|
|
||||||
else:
|
|
||||||
SRVS[server].send_msg(evt["channel"], "%s: %s arrivé à échéance." % (evt["proprio"], evt["name"]))
|
|
||||||
DATAS.delChild(DATAS.index[evt["name"]])
|
|
||||||
save()
|
|
||||||
newStrendEvt.set()
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
global DATAS
|
|
||||||
while not self.stop:
|
|
||||||
newStrendEvt.clear()
|
|
||||||
closer = None
|
|
||||||
#Gets the closer event
|
|
||||||
for evt in DATAS.index.keys():
|
|
||||||
if DATAS.index[evt].hasAttribute("end") and (closer is None or DATAS.index[evt].getDate("end") < closer.getDate("end")) and DATAS.index[evt].getDate("end") > datetime.now():
|
|
||||||
closer = DATAS.index[evt]
|
|
||||||
if closer is not None and closer.hasAttribute("end"):
|
|
||||||
#print ("Closer: %s à %s"%(closer.name, closer["end"]))
|
|
||||||
timeleft = (closer.getDate("end") - datetime.now()).seconds
|
|
||||||
timer = threading.Timer(timeleft, self.alertEnd, (closer,))
|
|
||||||
timer.start()
|
|
||||||
#print ("Start timer (%ds)"%timeleft)
|
|
||||||
|
|
||||||
newStrendEvt.wait()
|
|
||||||
|
|
||||||
if closer is not None and closer.hasAttribute("end") and closer.getDate("end") > datetime.now():
|
|
||||||
timer.cancel()
|
|
||||||
self.threadManager = None
|
|
||||||
|
|
||||||
|
|
||||||
threadManager = None
|
threadManager = None
|
||||||
newStrendEvt = threading.Event()
|
|
||||||
|
|
||||||
def load():
|
def load():
|
||||||
global DATAS, threadManager
|
global DATAS, SRVS, threadManager
|
||||||
#Define the index
|
#Define the index
|
||||||
DATAS.setIndex("name")
|
DATAS.setIndex("name")
|
||||||
#Load the manager
|
#Load the manager
|
||||||
threadManager = Manager()
|
Manager.save = save
|
||||||
|
threadManager = Manager(DATAS, SRVS)
|
||||||
threadManager.start()
|
threadManager.start()
|
||||||
|
|
||||||
def close():
|
def close():
|
||||||
global threadManager
|
global threadManager
|
||||||
if threadManager is not None:
|
if threadManager is not None:
|
||||||
threadManager.stop = True
|
threadManager.stop = True
|
||||||
newStrendEvt.set()
|
Manager.newStrendEvt.set()
|
||||||
|
|
||||||
|
|
||||||
def parseanswer(msg):
|
def parseanswer(msg):
|
||||||
|
@ -122,7 +83,7 @@ def parseanswer(msg):
|
||||||
strnd["end"] = datetime.now() + timedelta(days=int(result.group(1)))
|
strnd["end"] = datetime.now() + timedelta(days=int(result.group(1)))
|
||||||
else:
|
else:
|
||||||
strnd["end"] = datetime.now() + timedelta(seconds=int(result.group(1)))
|
strnd["end"] = datetime.now() + timedelta(seconds=int(result.group(1)))
|
||||||
newStrendEvt.set()
|
Manager.newStrendEvt.set()
|
||||||
msg.send_snd ("%s commencé le %s et se terminera le %s."% (msg.cmd[1], datetime.now(), strnd.getDate("end")))
|
msg.send_snd ("%s commencé le %s et se terminera le %s."% (msg.cmd[1], datetime.now(), strnd.getDate("end")))
|
||||||
except:
|
except:
|
||||||
msg.send_snd ("Impossible de définir la fin de %s."% (msg.cmd[1]))
|
msg.send_snd ("Impossible de définir la fin de %s."% (msg.cmd[1]))
|
||||||
|
@ -139,7 +100,7 @@ def parseanswer(msg):
|
||||||
msg.send_chn ("%s a duré %s." % (msg.cmd[1], msg.just_countdown(datetime.now () - DATAS.index[msg.cmd[1]].getDate("start"))))
|
msg.send_chn ("%s a duré %s." % (msg.cmd[1], msg.just_countdown(datetime.now () - DATAS.index[msg.cmd[1]].getDate("start"))))
|
||||||
if DATAS.index[msg.cmd[1]]["proprio"] == msg.sender or (msg.cmd[0] == "forceend" and msg.sender == msg.srv.owner):
|
if DATAS.index[msg.cmd[1]]["proprio"] == msg.sender or (msg.cmd[0] == "forceend" and msg.sender == msg.srv.owner):
|
||||||
DATAS.delChild(DATAS.index[msg.cmd[1]])
|
DATAS.delChild(DATAS.index[msg.cmd[1]])
|
||||||
newStrendEvt.set()
|
Manager.newStrendEvt.set()
|
||||||
save()
|
save()
|
||||||
else:
|
else:
|
||||||
msg.send_snd ("Vous ne pouvez pas terminer le compteur %s, créé par %s."% (msg.cmd[1], DATAS.index[msg.cmd[1]]["proprio"]))
|
msg.send_snd ("Vous ne pouvez pas terminer le compteur %s, créé par %s."% (msg.cmd[1], DATAS.index[msg.cmd[1]]["proprio"]))
|
418
modules/qcm.py
418
modules/qcm.py
|
@ -1,418 +0,0 @@
|
||||||
# coding=utf-8
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
import http.client
|
|
||||||
import hashlib
|
|
||||||
import re
|
|
||||||
import random
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
from urllib.parse import quote
|
|
||||||
|
|
||||||
from module_state import ModuleState
|
|
||||||
import module_states_file as xmlparser
|
|
||||||
|
|
||||||
nemubotversion = 3.0
|
|
||||||
|
|
||||||
def help_tiny ():
|
|
||||||
"""Line inserted in the response to the command !help"""
|
|
||||||
return "MCQ module, working with http://bot.nemunai.re/"
|
|
||||||
|
|
||||||
def help_full ():
|
|
||||||
return "!qcm [/nbQuest/] [/theme/]"
|
|
||||||
|
|
||||||
class QuestionFile:
|
|
||||||
def __init__(self, filename):
|
|
||||||
self.questions = xmlparser.parse_file(filename)
|
|
||||||
self.questions.setIndex("xml:id")
|
|
||||||
|
|
||||||
def getQuestion(self, ident):
|
|
||||||
if ident in self.questions.index:
|
|
||||||
return Question(self.questions.index[ident])
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
class Course:
|
|
||||||
def __init__(self, iden):
|
|
||||||
global COURSES
|
|
||||||
if iden in COURSES.index:
|
|
||||||
self.node = COURSES.index[iden]
|
|
||||||
else:
|
|
||||||
self.node = { "code":"N/A", "name":"N/A", "branch":"N/A" }
|
|
||||||
|
|
||||||
@property
|
|
||||||
def id(self):
|
|
||||||
return self.node["xml:id"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def code(self):
|
|
||||||
return self.node["code"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return self.node["name"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def branch(self):
|
|
||||||
return self.node["branch"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def validated(self):
|
|
||||||
return int(self.node["validated"]) > 0
|
|
||||||
|
|
||||||
|
|
||||||
class User:
|
|
||||||
def __init__(self, iden):
|
|
||||||
global USERS
|
|
||||||
if iden in USERS.index:
|
|
||||||
self.node = USERS.index[iden]
|
|
||||||
else:
|
|
||||||
self.node = { "username":"N/A", "email":"N/A" }
|
|
||||||
|
|
||||||
@property
|
|
||||||
def id(self):
|
|
||||||
return self.node["xml:id"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def username(self):
|
|
||||||
return self.node["username"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def email(self):
|
|
||||||
return self.node["email"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def validated(self):
|
|
||||||
return int(self.node["validated"]) > 0
|
|
||||||
|
|
||||||
|
|
||||||
class Question:
|
|
||||||
def __init__(self, node):
|
|
||||||
self.node = node
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ident(self):
|
|
||||||
return self.node["xml:id"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def id(self):
|
|
||||||
return self.node["xml:id"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def question(self):
|
|
||||||
return self.node["question"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def course(self):
|
|
||||||
return Course(self.node["course"])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def answers(self):
|
|
||||||
return self.node.getNodes("answer")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def validator(self):
|
|
||||||
return User(self.node["validator"])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def writer(self):
|
|
||||||
return User(self.node["writer"])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def validated(self):
|
|
||||||
return self.node["validated"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def addedtime(self):
|
|
||||||
return datetime.fromtimestamp(float(self.node["addedtime"]))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def author(self):
|
|
||||||
return User(self.node["writer"])
|
|
||||||
|
|
||||||
def report(self, raison="Sans raison"):
|
|
||||||
conn = http.client.HTTPConnection(CONF.getNode("server")["url"])
|
|
||||||
try:
|
|
||||||
conn.request("GET", "report.php?id=" + hashlib.md5(self.id.encode()).hexdigest() + "&raison=" + quote(raison))
|
|
||||||
except socket.gaierror:
|
|
||||||
print ("[%s] impossible de récupérer la page %s."%(s, p))
|
|
||||||
return False
|
|
||||||
res = conn.getresponse()
|
|
||||||
conn.close()
|
|
||||||
return (res.status == http.client.OK)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def tupleInfo(self):
|
|
||||||
return (self.author.username, self.validator.username, self.addedtime)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def bestAnswer(self):
|
|
||||||
best = self.answers[0]
|
|
||||||
for answer in self.answers:
|
|
||||||
if best.getInt("score") < answer.getInt("score"):
|
|
||||||
best = answer
|
|
||||||
return best["answer"]
|
|
||||||
|
|
||||||
def isCorrect(self, msg):
|
|
||||||
msg = msg.lower().replace(" ", "")
|
|
||||||
for answer in self.answers:
|
|
||||||
if msg == answer["answer"].lower().replace(" ", ""):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def getScore(self, msg):
|
|
||||||
msg = msg.lower().replace(" ", "")
|
|
||||||
for answer in self.answers:
|
|
||||||
if msg == answer["answer"].lower().replace(" ", ""):
|
|
||||||
return answer.getInt("score")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
class Session:
|
|
||||||
def __init__(self, srv, chan, sender):
|
|
||||||
self.questions = list()
|
|
||||||
self.current = -1
|
|
||||||
self.score = 0
|
|
||||||
self.good = 0
|
|
||||||
self.bad = 0
|
|
||||||
self.trys = 0
|
|
||||||
self.timer = None
|
|
||||||
self.server = srv
|
|
||||||
self.channel = chan
|
|
||||||
self.sender = sender
|
|
||||||
|
|
||||||
def addQuestion(self, ident):
|
|
||||||
if ident not in self.questions:
|
|
||||||
self.questions.append(ident)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def next_question(self):
|
|
||||||
self.trys = 0
|
|
||||||
self.current += 1
|
|
||||||
return self.question
|
|
||||||
|
|
||||||
@property
|
|
||||||
def question(self):
|
|
||||||
if self.current >= 0 and self.current < len(self.questions):
|
|
||||||
global QUESTIONS
|
|
||||||
return Question(QUESTIONS.index[self.questions[self.current]])
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def askNext(self, bfr = ""):
|
|
||||||
global SESSIONS
|
|
||||||
self.timer = None
|
|
||||||
nextQ = self.next_question()
|
|
||||||
if nextQ is not None:
|
|
||||||
if self.channel == "nemubot":
|
|
||||||
self.server.send_msg_final(self.sender, "%s%s" % (bfr, nextQ.question))
|
|
||||||
elif self.sender != self.channel:
|
|
||||||
self.server.send_msg_final(self.channel, "%s: %s%s" % (self.sender, bfr, nextQ.question))
|
|
||||||
else:
|
|
||||||
self.server.send_msg_final(self.channel, "%s%s" % (bfr, nextQ.question))
|
|
||||||
else:
|
|
||||||
if self.good > 1:
|
|
||||||
goodS = "s"
|
|
||||||
else:
|
|
||||||
goodS = ""
|
|
||||||
if self.channel == "nemubot":
|
|
||||||
self.server.send_msg_final(self.sender, "%sFini, tu as donné %d bonne%s réponse%s sur %d questions." % (self.sender, bfr, self.good, goodS, goodS, len(self.questions)))
|
|
||||||
elif self.sender != self.channel:
|
|
||||||
self.server.send_msg_final(self.channel, "%s: %sFini, tu as donné %d bonne%s réponse%s sur %d questions." % (self.sender, bfr, self.good, goodS, goodS, len(self.questions)))
|
|
||||||
else:
|
|
||||||
self.server.send_msg_final(self.channel, "%sFini, vous avez donné %d bonne%s réponse%s sur %d questions." % (bfr, self.good, goodS, goodS, len(self.questions)))
|
|
||||||
del SESSIONS[self.sender]
|
|
||||||
|
|
||||||
def prepareNext(self, lag = 3):
|
|
||||||
if self.timer is None:
|
|
||||||
self.timer = threading.Timer(lag, self.askNext)
|
|
||||||
self.timer.start()
|
|
||||||
|
|
||||||
QUESTIONS = None
|
|
||||||
COURSES = None
|
|
||||||
USERS = None
|
|
||||||
SESSIONS = dict()
|
|
||||||
|
|
||||||
def load():
|
|
||||||
CONF.setIndex("name", "file")
|
|
||||||
|
|
||||||
def buildSession(msg, categ = None, nbQuest = 10, channel = False):
|
|
||||||
global QUESTIONS, COURSES, USERS
|
|
||||||
if QUESTIONS is None:
|
|
||||||
QUESTIONS = xmlparser.parse_file(CONF.index["main"]["url"])
|
|
||||||
QUESTIONS.setIndex("xml:id")
|
|
||||||
COURSES = xmlparser.parse_file(CONF.index["courses"]["url"])
|
|
||||||
COURSES.setIndex("xml:id")
|
|
||||||
USERS = xmlparser.parse_file(CONF.index["users"]["url"])
|
|
||||||
USERS.setIndex("xml:id")
|
|
||||||
#Remove no validated questions
|
|
||||||
keys = list()
|
|
||||||
for k in QUESTIONS.index.keys():
|
|
||||||
keys.append(k)
|
|
||||||
for ques in keys:
|
|
||||||
if QUESTIONS.index[ques]["validated"] != "1" or QUESTIONS.index[ques]["reported"] == "1":
|
|
||||||
del QUESTIONS.index[ques]
|
|
||||||
|
|
||||||
#Apply filter
|
|
||||||
QS = list()
|
|
||||||
if categ is not None and len(categ) > 0:
|
|
||||||
#Find course id corresponding to categ
|
|
||||||
courses = list()
|
|
||||||
for c in COURSES.childs:
|
|
||||||
if c["code"] in categ:
|
|
||||||
courses.append(c["xml:id"])
|
|
||||||
|
|
||||||
#Keep only questions matching course or branch
|
|
||||||
for q in QUESTIONS.index.keys():
|
|
||||||
if (QUESTIONS.index[q]["branch"] is not None and QUESTIONS.index[q]["branch"].find(categ)) or QUESTIONS.index[q]["course"] in courses:
|
|
||||||
QS.append(q)
|
|
||||||
else:
|
|
||||||
for q in QUESTIONS.index.keys():
|
|
||||||
QS.append(q)
|
|
||||||
|
|
||||||
nbQuest = min(nbQuest, len(QS))
|
|
||||||
|
|
||||||
if channel:
|
|
||||||
sess = Session(msg.srv, msg.channel, msg.channel)
|
|
||||||
else:
|
|
||||||
sess = Session(msg.srv, msg.channel, msg.sender)
|
|
||||||
maxQuest = len(QS) - 1
|
|
||||||
for i in range(0, nbQuest):
|
|
||||||
while True:
|
|
||||||
q = QS[random.randint(0, maxQuest)]
|
|
||||||
if sess.addQuestion(q):
|
|
||||||
break
|
|
||||||
if channel:
|
|
||||||
SESSIONS[msg.channel] = sess
|
|
||||||
else:
|
|
||||||
SESSIONS[msg.sender] = sess
|
|
||||||
|
|
||||||
|
|
||||||
def askQuestion(msg, bfr = ""):
|
|
||||||
SESSIONS[msg.sender].askNext(bfr)
|
|
||||||
|
|
||||||
def parseanswer(msg):
|
|
||||||
global DATAS, SESSIONS, COURSES, QUESTIONS, USERS
|
|
||||||
if msg.cmd[0] == "qcm" or msg.cmd[0] == "qcmchan" or msg.cmd[0] == "simulateqcm":
|
|
||||||
if msg.sender in SESSIONS:
|
|
||||||
if len(msg.cmd) > 1:
|
|
||||||
if msg.cmd[1] == "stop" or msg.cmd[1] == "end":
|
|
||||||
sess = SESSIONS[msg.sender]
|
|
||||||
if sess.good > 1: goodS = "s"
|
|
||||||
else: goodS = ""
|
|
||||||
msg.send_chn("%s: Fini, tu as donné %d bonne%s réponse%s sur %d questions." % (msg.sender, sess.good, goodS, goodS, sess.current))
|
|
||||||
del SESSIONS[msg.sender]
|
|
||||||
return True
|
|
||||||
elif msg.cmd[1] == "next" or msg.cmd[1] == "suivant" or msg.cmd[1] == "suivante":
|
|
||||||
askQuestion(msg)
|
|
||||||
return True
|
|
||||||
msg.send_chn("%s: tu as déjà une session de QCM en cours, finis-la avant d'en commencer une nouvelle." % msg.sender)
|
|
||||||
elif msg.channel in SESSIONS:
|
|
||||||
if len(msg.cmd) > 1:
|
|
||||||
if msg.cmd[1] == "stop" or msg.cmd[1] == "end":
|
|
||||||
sess = SESSIONS[msg.channel]
|
|
||||||
if sess.good > 1: goodS = "s"
|
|
||||||
else: goodS = ""
|
|
||||||
msg.send_chn("Fini, vous avez donné %d bonne%s réponse%s sur %d questions." % (sess.good, goodS, goodS, sess.current))
|
|
||||||
del SESSIONS[msg.channel]
|
|
||||||
return True
|
|
||||||
elif msg.cmd[1] == "next" or msg.cmd[1] == "suivant" or msg.cmd[1] == "suivante":
|
|
||||||
SESSIONS[msg.channel].prepareNext(1)
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
nbQuest = 10
|
|
||||||
filtre = list()
|
|
||||||
if len(msg.cmd) > 1:
|
|
||||||
for cmd in msg.cmd[1:]:
|
|
||||||
try:
|
|
||||||
tmp = int(cmd)
|
|
||||||
nbQuest = tmp
|
|
||||||
except ValueError:
|
|
||||||
filtre.append(cmd.upper())
|
|
||||||
if len(filtre) == 0:
|
|
||||||
filtre = None
|
|
||||||
if msg.channel in SESSIONS:
|
|
||||||
msg.send_snd("Il y a deja une session de QCM sur ce chan.")
|
|
||||||
else:
|
|
||||||
buildSession(msg, filtre, nbQuest, msg.cmd[0] == "qcmchan")
|
|
||||||
if msg.cmd[0] == "qcm":
|
|
||||||
askQuestion(msg)
|
|
||||||
elif msg.cmd[0] == "qcmchan":
|
|
||||||
SESSIONS[msg.channel].askNext()
|
|
||||||
else:
|
|
||||||
msg.send_chn("QCM de %d questions" % len(SESSIONS[msg.sender].questions))
|
|
||||||
del SESSIONS[msg.sender]
|
|
||||||
return True
|
|
||||||
elif msg.sender in SESSIONS:
|
|
||||||
if msg.cmd[0] == "info" or msg.cmd[0] == "infoquestion":
|
|
||||||
msg.send_chn("Cette question a été écrite par %s et validée par %s, le %s" % SESSIONS[msg.sender].question.tupleInfo)
|
|
||||||
return True
|
|
||||||
elif msg.cmd[0] == "report" or msg.cmd[0] == "reportquestion":
|
|
||||||
if len(msg.cmd) == 1:
|
|
||||||
msg.send_chn("Veuillez indiquer une raison de report")
|
|
||||||
elif SESSIONS[msg.sender].question.report(' '.join(msg.cmd[1:])):
|
|
||||||
msg.send_chn("Cette question vient d'être signalée.")
|
|
||||||
SESSIONS[msg.sender].askNext()
|
|
||||||
else:
|
|
||||||
msg.send_chn("Une erreur s'est produite lors du signalement de la question, veuillez recommencer plus tard.")
|
|
||||||
return True
|
|
||||||
elif msg.channel in SESSIONS:
|
|
||||||
if msg.cmd[0] == "info" or msg.cmd[0] == "infoquestion":
|
|
||||||
msg.send_chn("Cette question a été écrite par %s et validée par %s, le %s" % SESSIONS[msg.channel].question.tupleInfo)
|
|
||||||
return True
|
|
||||||
elif msg.cmd[0] == "report" or msg.cmd[0] == "reportquestion":
|
|
||||||
if len(msg.cmd) == 1:
|
|
||||||
msg.send_chn("Veuillez indiquer une raison de report")
|
|
||||||
elif SESSIONS[msg.channel].question.report(' '.join(msg.cmd[1:])):
|
|
||||||
msg.send_chn("Cette question vient d'être signalée.")
|
|
||||||
SESSIONS[msg.channel].prepareNext()
|
|
||||||
else:
|
|
||||||
msg.send_chn("Une erreur s'est produite lors du signalement de la question, veuillez recommencer plus tard.")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
if msg.cmd[0] == "listecours":
|
|
||||||
if COURSES is None:
|
|
||||||
msg.send_chn("La liste de cours n'est pas encore construite, lancez un QCM pour la construire.")
|
|
||||||
else:
|
|
||||||
lst = ""
|
|
||||||
for cours in COURSES.getNodes("course"):
|
|
||||||
lst += cours["code"] + " (" + cours["name"] + "), "
|
|
||||||
msg.send_chn("Liste des cours existants : " + lst[:len(lst)-2])
|
|
||||||
elif msg.cmd[0] == "refreshqcm":
|
|
||||||
QUESTIONS = None
|
|
||||||
COURSES = None
|
|
||||||
USERS = None
|
|
||||||
return False
|
|
||||||
|
|
||||||
def parseask(msg):
|
|
||||||
if msg.sender in SESSIONS:
|
|
||||||
dest = msg.sender
|
|
||||||
|
|
||||||
if SESSIONS[dest].question.isCorrect(msg.content):
|
|
||||||
SESSIONS[dest].good += 1
|
|
||||||
SESSIONS[dest].score += SESSIONS[dest].question.getScore(msg.content)
|
|
||||||
askQuestion(msg, "correct ; ")
|
|
||||||
else:
|
|
||||||
SESSIONS[dest].bad += 1
|
|
||||||
if SESSIONS[dest].trys == 0:
|
|
||||||
SESSIONS[dest].trys = 1
|
|
||||||
msg.send_chn("%s: non, essaie encore :p" % msg.sender)
|
|
||||||
else:
|
|
||||||
askQuestion(msg, "non, la bonne reponse était : %s ; " % SESSIONS[dest].question.bestAnswer)
|
|
||||||
return True
|
|
||||||
|
|
||||||
elif msg.channel in SESSIONS:
|
|
||||||
dest = msg.channel
|
|
||||||
|
|
||||||
if SESSIONS[dest].question.isCorrect(msg.content):
|
|
||||||
SESSIONS[dest].good += 1
|
|
||||||
SESSIONS[dest].score += SESSIONS[dest].question.getScore(msg.content)
|
|
||||||
msg.send_chn("%s: correct :)" % msg.sender)
|
|
||||||
SESSIONS[dest].prepareNext()
|
|
||||||
else:
|
|
||||||
SESSIONS[dest].bad += 1
|
|
||||||
msg.send_chn("%s: non, essaie encore :p" % msg.sender)
|
|
||||||
return True
|
|
||||||
return False
|
|
202
modules/qcm/__init__.py
Normal file
202
modules/qcm/__init__.py
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
import http.client
|
||||||
|
import re
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
nemubotversion = 3.0
|
||||||
|
|
||||||
|
def help_tiny ():
|
||||||
|
"""Line inserted in the response to the command !help"""
|
||||||
|
return "MCQ module, working with http://bot.nemunai.re/"
|
||||||
|
|
||||||
|
def help_full ():
|
||||||
|
return "!qcm [<nbQuest>] [<theme>]"
|
||||||
|
|
||||||
|
from . import Question
|
||||||
|
from . import Course
|
||||||
|
from . import Session
|
||||||
|
|
||||||
|
def load():
|
||||||
|
CONF.setIndex("name", "file")
|
||||||
|
|
||||||
|
def buildSession(msg, categ = None, nbQuest = 10, channel = False):
|
||||||
|
if Question.QUESTIONS is None:
|
||||||
|
Question.QUESTIONS = xmlparser.parse_file(CONF.index["main"]["url"])
|
||||||
|
Question.QUESTIONS.setIndex("xml:id")
|
||||||
|
Course.COURSES = xmlparser.parse_file(CONF.index["courses"]["url"])
|
||||||
|
Course.COURSES.setIndex("xml:id")
|
||||||
|
User.USERS = xmlparser.parse_file(CONF.index["users"]["url"])
|
||||||
|
User.USERS.setIndex("xml:id")
|
||||||
|
#Remove no validated questions
|
||||||
|
keys = list()
|
||||||
|
for k in Question.QUESTIONS.index.keys():
|
||||||
|
keys.append(k)
|
||||||
|
for ques in keys:
|
||||||
|
if Question.QUESTIONS.index[ques]["validated"] != "1" or Question.QUESTIONS.index[ques]["reported"] == "1":
|
||||||
|
del Question.QUESTIONS.index[ques]
|
||||||
|
|
||||||
|
#Apply filter
|
||||||
|
QS = list()
|
||||||
|
if categ is not None and len(categ) > 0:
|
||||||
|
#Find course id corresponding to categ
|
||||||
|
courses = list()
|
||||||
|
for c in Course.COURSES.childs:
|
||||||
|
if c["code"] in categ:
|
||||||
|
courses.append(c["xml:id"])
|
||||||
|
|
||||||
|
#Keep only questions matching course or branch
|
||||||
|
for q in Question.QUESTIONS.index.keys():
|
||||||
|
if (Question.QUESTIONS.index[q]["branch"] is not None and Question.QUESTIONS.index[q]["branch"].find(categ)) or Question.QUESTIONS.index[q]["course"] in courses:
|
||||||
|
QS.append(q)
|
||||||
|
else:
|
||||||
|
for q in Question.QUESTIONS.index.keys():
|
||||||
|
QS.append(q)
|
||||||
|
|
||||||
|
nbQuest = min(nbQuest, len(QS))
|
||||||
|
|
||||||
|
if channel:
|
||||||
|
sess = Session.Session(msg.srv, msg.channel, msg.channel)
|
||||||
|
else:
|
||||||
|
sess = Session.Session(msg.srv, msg.channel, msg.sender)
|
||||||
|
maxQuest = len(QS) - 1
|
||||||
|
for i in range(0, nbQuest):
|
||||||
|
while True:
|
||||||
|
q = QS[random.randint(0, maxQuest)]
|
||||||
|
if sess.addQuestion(q):
|
||||||
|
break
|
||||||
|
if channel:
|
||||||
|
Session.SESSIONS[msg.channel] = sess
|
||||||
|
else:
|
||||||
|
Session.SESSIONS[msg.sender] = sess
|
||||||
|
|
||||||
|
|
||||||
|
def askQuestion(msg, bfr = ""):
|
||||||
|
Session.SESSIONS[msg.sender].askNext(bfr)
|
||||||
|
|
||||||
|
def parseanswer(msg):
|
||||||
|
global DATAS
|
||||||
|
if msg.cmd[0] == "qcm" or msg.cmd[0] == "qcmchan" or msg.cmd[0] == "simulateqcm":
|
||||||
|
if msg.sender in Session.SESSIONS:
|
||||||
|
if len(msg.cmd) > 1:
|
||||||
|
if msg.cmd[1] == "stop" or msg.cmd[1] == "end":
|
||||||
|
sess = Session.SESSIONS[msg.sender]
|
||||||
|
if sess.good > 1: goodS = "s"
|
||||||
|
else: goodS = ""
|
||||||
|
msg.send_chn("%s: Fini, tu as donné %d bonne%s réponse%s sur %d questions." % (msg.sender, sess.good, goodS, goodS, sess.current))
|
||||||
|
del Session.SESSIONS[msg.sender]
|
||||||
|
return True
|
||||||
|
elif msg.cmd[1] == "next" or msg.cmd[1] == "suivant" or msg.cmd[1] == "suivante":
|
||||||
|
askQuestion(msg)
|
||||||
|
return True
|
||||||
|
msg.send_chn("%s: tu as déjà une session de QCM en cours, finis-la avant d'en commencer une nouvelle." % msg.sender)
|
||||||
|
elif msg.channel in Session.SESSIONS:
|
||||||
|
if len(msg.cmd) > 1:
|
||||||
|
if msg.cmd[1] == "stop" or msg.cmd[1] == "end":
|
||||||
|
sess = Session.SESSIONS[msg.channel]
|
||||||
|
if sess.good > 1: goodS = "s"
|
||||||
|
else: goodS = ""
|
||||||
|
msg.send_chn("Fini, vous avez donné %d bonne%s réponse%s sur %d questions." % (sess.good, goodS, goodS, sess.current))
|
||||||
|
del Session.SESSIONS[msg.channel]
|
||||||
|
return True
|
||||||
|
elif msg.cmd[1] == "next" or msg.cmd[1] == "suivant" or msg.cmd[1] == "suivante":
|
||||||
|
Session.SESSIONS[msg.channel].prepareNext(1)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
nbQuest = 10
|
||||||
|
filtre = list()
|
||||||
|
if len(msg.cmd) > 1:
|
||||||
|
for cmd in msg.cmd[1:]:
|
||||||
|
try:
|
||||||
|
tmp = int(cmd)
|
||||||
|
nbQuest = tmp
|
||||||
|
except ValueError:
|
||||||
|
filtre.append(cmd.upper())
|
||||||
|
if len(filtre) == 0:
|
||||||
|
filtre = None
|
||||||
|
if msg.channel in Session.SESSIONS:
|
||||||
|
msg.send_snd("Il y a deja une session de QCM sur ce chan.")
|
||||||
|
else:
|
||||||
|
buildSession(msg, filtre, nbQuest, msg.cmd[0] == "qcmchan")
|
||||||
|
if msg.cmd[0] == "qcm":
|
||||||
|
askQuestion(msg)
|
||||||
|
elif msg.cmd[0] == "qcmchan":
|
||||||
|
Session.SESSIONS[msg.channel].askNext()
|
||||||
|
else:
|
||||||
|
msg.send_chn("QCM de %d questions" % len(Session.SESSIONS[msg.sender].questions))
|
||||||
|
del Session.SESSIONS[msg.sender]
|
||||||
|
return True
|
||||||
|
elif msg.sender in Session.SESSIONS:
|
||||||
|
if msg.cmd[0] == "info" or msg.cmd[0] == "infoquestion":
|
||||||
|
msg.send_chn("Cette question a été écrite par %s et validée par %s, le %s" % Session.SESSIONS[msg.sender].question.tupleInfo)
|
||||||
|
return True
|
||||||
|
elif msg.cmd[0] == "report" or msg.cmd[0] == "reportquestion":
|
||||||
|
if len(msg.cmd) == 1:
|
||||||
|
msg.send_chn("Veuillez indiquer une raison de report")
|
||||||
|
elif Session.SESSIONS[msg.sender].question.report(' '.join(msg.cmd[1:])):
|
||||||
|
msg.send_chn("Cette question vient d'être signalée.")
|
||||||
|
Session.SESSIONS[msg.sender].askNext()
|
||||||
|
else:
|
||||||
|
msg.send_chn("Une erreur s'est produite lors du signalement de la question, veuillez recommencer plus tard.")
|
||||||
|
return True
|
||||||
|
elif msg.channel in Session.SESSIONS:
|
||||||
|
if msg.cmd[0] == "info" or msg.cmd[0] == "infoquestion":
|
||||||
|
msg.send_chn("Cette question a été écrite par %s et validée par %s, le %s" % Session.SESSIONS[msg.channel].question.tupleInfo)
|
||||||
|
return True
|
||||||
|
elif msg.cmd[0] == "report" or msg.cmd[0] == "reportquestion":
|
||||||
|
if len(msg.cmd) == 1:
|
||||||
|
msg.send_chn("Veuillez indiquer une raison de report")
|
||||||
|
elif Session.SESSIONS[msg.channel].question.report(' '.join(msg.cmd[1:])):
|
||||||
|
msg.send_chn("Cette question vient d'être signalée.")
|
||||||
|
Session.SESSIONS[msg.channel].prepareNext()
|
||||||
|
else:
|
||||||
|
msg.send_chn("Une erreur s'est produite lors du signalement de la question, veuillez recommencer plus tard.")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
if msg.cmd[0] == "listecours":
|
||||||
|
if Course.COURSES is None:
|
||||||
|
msg.send_chn("La liste de cours n'est pas encore construite, lancez un QCM pour la construire.")
|
||||||
|
else:
|
||||||
|
lst = ""
|
||||||
|
for cours in Course.COURSES.getNodes("course"):
|
||||||
|
lst += cours["code"] + " (" + cours["name"] + "), "
|
||||||
|
msg.send_chn("Liste des cours existants : " + lst[:len(lst)-2])
|
||||||
|
elif msg.cmd[0] == "refreshqcm":
|
||||||
|
Question.QUESTIONS = None
|
||||||
|
Course.COURSES = None
|
||||||
|
User.USERS = None
|
||||||
|
return False
|
||||||
|
|
||||||
|
def parseask(msg):
|
||||||
|
if msg.sender in Session.SESSIONS:
|
||||||
|
dest = msg.sender
|
||||||
|
|
||||||
|
if Session.SESSIONS[dest].question.isCorrect(msg.content):
|
||||||
|
Session.SESSIONS[dest].good += 1
|
||||||
|
Session.SESSIONS[dest].score += Session.SESSIONS[dest].question.getScore(msg.content)
|
||||||
|
askQuestion(msg, "correct ; ")
|
||||||
|
else:
|
||||||
|
Session.SESSIONS[dest].bad += 1
|
||||||
|
if Session.SESSIONS[dest].trys == 0:
|
||||||
|
Session.SESSIONS[dest].trys = 1
|
||||||
|
msg.send_chn("%s: non, essaie encore :p" % msg.sender)
|
||||||
|
else:
|
||||||
|
askQuestion(msg, "non, la bonne reponse était : %s ; " % Session.SESSIONS[dest].question.bestAnswer)
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif msg.channel in Session.SESSIONS:
|
||||||
|
dest = msg.channel
|
||||||
|
|
||||||
|
if Session.SESSIONS[dest].question.isCorrect(msg.content):
|
||||||
|
Session.SESSIONS[dest].good += 1
|
||||||
|
Session.SESSIONS[dest].score += Session.SESSIONS[dest].question.getScore(msg.content)
|
||||||
|
msg.send_chn("%s: correct :)" % msg.sender)
|
||||||
|
Session.SESSIONS[dest].prepareNext()
|
||||||
|
else:
|
||||||
|
Session.SESSIONS[dest].bad += 1
|
||||||
|
msg.send_chn("%s: non, essaie encore :p" % msg.sender)
|
||||||
|
return True
|
||||||
|
return False
|
|
@ -1,19 +1,12 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
import random
|
|
||||||
import sys
|
|
||||||
import threading
|
|
||||||
from datetime import timedelta
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
from module_state import ModuleState
|
|
||||||
from wrapper import Wrapper
|
|
||||||
|
|
||||||
nemubotversion = 3.0
|
nemubotversion = 3.0
|
||||||
|
|
||||||
|
from . import GameUpdater
|
||||||
|
|
||||||
channels = "#nemutest #42sh #ykar #epitagueule"
|
channels = "#nemutest #42sh #ykar #epitagueule"
|
||||||
LASTSEEN = dict ()
|
LASTSEEN = dict ()
|
||||||
temps = dict ()
|
temps = dict ()
|
||||||
|
@ -21,9 +14,14 @@ temps = dict ()
|
||||||
SCORES = None
|
SCORES = None
|
||||||
|
|
||||||
def load():
|
def load():
|
||||||
global DATAS, SCORES
|
global DATAS, SCORES, CONF
|
||||||
DATAS.setIndex("name", "player")
|
DATAS.setIndex("name", "player")
|
||||||
SCORES = QDWrapper()
|
SCORES = QDWrapper(DATAS)
|
||||||
|
GameUpdater.SCORES = SCORES
|
||||||
|
GameUpdater.CONF = CONF
|
||||||
|
GameUpdater.save = save
|
||||||
|
GameUpdater.getUser = getUser
|
||||||
|
|
||||||
|
|
||||||
def help_tiny ():
|
def help_tiny ():
|
||||||
"""Line inserted in the response to the command !help"""
|
"""Line inserted in the response to the command !help"""
|
||||||
|
@ -33,150 +31,6 @@ def help_full ():
|
||||||
return "!42: display scores\n!42 help: display the performed calculate\n!42 manche: display information about current round\n!42 /who/: show the /who/'s scores"
|
return "!42: display scores\n!42 help: display the performed calculate\n!42 manche: display information about current round\n!42 /who/: show the /who/'s scores"
|
||||||
|
|
||||||
|
|
||||||
class QDWrapper(Wrapper):
|
|
||||||
def __init__(self):
|
|
||||||
global DATAS
|
|
||||||
Wrapper.__init__(self)
|
|
||||||
self.DATAS = DATAS
|
|
||||||
self.stateName = "player"
|
|
||||||
self.attName = "name"
|
|
||||||
|
|
||||||
def __getitem__(self, i):
|
|
||||||
if i in self.cache:
|
|
||||||
return self.cache[i]
|
|
||||||
else:
|
|
||||||
sc = Score()
|
|
||||||
sc.parse(Wrapper.__getitem__(self, i))
|
|
||||||
self.cache[i] = sc
|
|
||||||
return sc
|
|
||||||
|
|
||||||
class Score:
|
|
||||||
"""Manage the user's scores"""
|
|
||||||
def __init__(self):
|
|
||||||
#FourtyTwo
|
|
||||||
self.ftt = 0
|
|
||||||
#TwentyThree
|
|
||||||
self.twt = 0
|
|
||||||
self.pi = 0
|
|
||||||
self.notfound = 0
|
|
||||||
self.tententen = 0
|
|
||||||
self.leet = 0
|
|
||||||
self.great = 0
|
|
||||||
self.bad = 0
|
|
||||||
self.triche = 0
|
|
||||||
self.last = None
|
|
||||||
self.changed = False
|
|
||||||
|
|
||||||
def parse(self, item):
|
|
||||||
self.ftt = item.getInt("fourtytwo")
|
|
||||||
self.twt = item.getInt("twentythree")
|
|
||||||
self.pi = item.getInt("pi")
|
|
||||||
self.notfound = item.getInt("notfound")
|
|
||||||
self.tententen = item.getInt("tententen")
|
|
||||||
self.leet = item.getInt("leet")
|
|
||||||
self.great = item.getInt("great")
|
|
||||||
self.bad = item.getInt("bad")
|
|
||||||
self.triche = item.getInt("triche")
|
|
||||||
|
|
||||||
def save(self, state):
|
|
||||||
state.setAttribute("fourtytwo", self.ftt)
|
|
||||||
state.setAttribute("twentythree", self.twt)
|
|
||||||
state.setAttribute("pi", self.pi)
|
|
||||||
state.setAttribute("notfound", self.notfound)
|
|
||||||
state.setAttribute("tententen", self.tententen)
|
|
||||||
state.setAttribute("leet", self.leet)
|
|
||||||
state.setAttribute("great", self.great)
|
|
||||||
state.setAttribute("bad", self.bad)
|
|
||||||
state.setAttribute("triche", self.triche)
|
|
||||||
|
|
||||||
def merge(self, other):
|
|
||||||
self.ftt += other.ftt
|
|
||||||
self.twt += other.twt
|
|
||||||
self.pi += other.pi
|
|
||||||
self.notfound += other.notfound
|
|
||||||
self.tententen += other.tententen
|
|
||||||
self.leet += other.leet
|
|
||||||
self.great += other.great
|
|
||||||
self.bad += other.bad
|
|
||||||
self.triche += other.triche
|
|
||||||
|
|
||||||
def newWinner(self):
|
|
||||||
self.ftt = 0
|
|
||||||
self.twt = 0
|
|
||||||
self.pi = 1
|
|
||||||
self.notfound = 1
|
|
||||||
self.tententen = 0
|
|
||||||
self.leet = 1
|
|
||||||
self.great = -1
|
|
||||||
self.bad = -4
|
|
||||||
self.triche = 0
|
|
||||||
|
|
||||||
def isWinner(self):
|
|
||||||
return self.great >= 42
|
|
||||||
|
|
||||||
def playFtt(self):
|
|
||||||
if self.canPlay():
|
|
||||||
self.ftt += 1
|
|
||||||
def playTwt(self):
|
|
||||||
if self.canPlay():
|
|
||||||
self.twt += 1
|
|
||||||
def playSuite(self):
|
|
||||||
self.canPlay()
|
|
||||||
self.twt += 1
|
|
||||||
self.great += 1
|
|
||||||
def playPi(self):
|
|
||||||
if self.canPlay():
|
|
||||||
self.pi += 1
|
|
||||||
def playNotfound(self):
|
|
||||||
if self.canPlay():
|
|
||||||
self.notfound += 1
|
|
||||||
def playTen(self):
|
|
||||||
if self.canPlay():
|
|
||||||
self.tententen += 1
|
|
||||||
def playLeet(self):
|
|
||||||
if self.canPlay():
|
|
||||||
self.leet += 1
|
|
||||||
def playGreat(self):
|
|
||||||
if self.canPlay():
|
|
||||||
self.great += 1
|
|
||||||
def playBad(self):
|
|
||||||
if self.canPlay():
|
|
||||||
self.bad += 1
|
|
||||||
self.great += 1
|
|
||||||
def playTriche(self):
|
|
||||||
self.triche += 1
|
|
||||||
def oupsTriche(self):
|
|
||||||
self.triche -= 1
|
|
||||||
def bonusQuestion(self):
|
|
||||||
return
|
|
||||||
|
|
||||||
def toTuple(self):
|
|
||||||
return (self.ftt, self.twt, self.pi, self.notfound, self.tententen, self.leet, self.great, self.bad, self.triche)
|
|
||||||
|
|
||||||
def canPlay(self):
|
|
||||||
ret = self.last == None or self.last.minute != datetime.now().minute or self.last.hour != datetime.now().hour or self.last.day != datetime.now().day
|
|
||||||
self.changed = self.changed or ret
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def hasChanged(self):
|
|
||||||
if self.changed:
|
|
||||||
self.changed = False
|
|
||||||
self.last = datetime.now()
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def score(self):
|
|
||||||
return (self.ftt * 2 + self.great * 5 + self.leet * 13.37 + (self.pi + 1) * 3.1415 * (self.notfound + 1) + self.tententen * 10 + self.twt - (self.bad + 1) * 10 * (self.triche * 5 + 1) + 7)
|
|
||||||
|
|
||||||
def details(self):
|
|
||||||
return "42: %d, 23: %d, leet: %d, pi: %d, 404: %d, 10: %d, great: %d, bad: %d, triche: %d = %d."%(self.ftt, self.twt, self.leet, self.pi, self.notfound, self.tententen, self.great, self.bad, self.triche, self.score())
|
|
||||||
|
|
||||||
|
|
||||||
def rev (tupl):
|
|
||||||
(k, v) = tupl
|
|
||||||
return (v.score(), k)
|
|
||||||
|
|
||||||
def parseanswer (msg):
|
def parseanswer (msg):
|
||||||
if msg.cmd[0] == "42" or msg.cmd[0] == "score" or msg.cmd[0] == "scores":
|
if msg.cmd[0] == "42" or msg.cmd[0] == "score" or msg.cmd[0] == "scores":
|
||||||
global SCORES
|
global SCORES
|
||||||
|
@ -261,14 +115,20 @@ def win(msg):
|
||||||
|
|
||||||
|
|
||||||
def parseask (msg):
|
def parseask (msg):
|
||||||
if len(DELAYED) > 0:
|
if len(GameUpdater.DELAYED) > 0:
|
||||||
if msg.sender in DELAYED:
|
if msg.sender in GameUpdater.DELAYED:
|
||||||
DELAYED[msg.sender].msg = msg.content
|
GameUpdater.DELAYED[msg.sender].msg = msg.content
|
||||||
DELAYED[msg.sender].delayEvnt.set()
|
GameUpdater.DELAYED[msg.sender].delayEvnt.set()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def rev (tupl):
|
||||||
|
(k, v) = tupl
|
||||||
|
return (v.score(), k)
|
||||||
|
|
||||||
|
|
||||||
def getUser(name):
|
def getUser(name):
|
||||||
global SCORES
|
global SCORES
|
||||||
if name not in SCORES:
|
if name not in SCORES:
|
||||||
|
@ -277,7 +137,7 @@ def getUser(name):
|
||||||
|
|
||||||
|
|
||||||
def parselisten (msg):
|
def parselisten (msg):
|
||||||
if len(DELAYED) > 0 and msg.sender in DELAYED and DELAYED[msg.sender].good(msg.content):
|
if len(GameUpdater.DELAYED) > 0 and msg.sender in GameUpdater.DELAYED and GameUpdater.DELAYED[msg.sender].good(msg.content):
|
||||||
msg.send_chn("%s: n'oublie pas le nemubot: devant ta réponse pour qu'elle soit prise en compte !" % msg.sender)
|
msg.send_chn("%s: n'oublie pas le nemubot: devant ta réponse pour qu'elle soit prise en compte !" % msg.sender)
|
||||||
|
|
||||||
bfrseen = None
|
bfrseen = None
|
||||||
|
@ -285,8 +145,8 @@ def parselisten (msg):
|
||||||
bfrseen = LASTSEEN[msg.realname]
|
bfrseen = LASTSEEN[msg.realname]
|
||||||
LASTSEEN[msg.realname] = datetime.now()
|
LASTSEEN[msg.realname] = datetime.now()
|
||||||
|
|
||||||
# if msg.channel == "#nemutest" and msg.sender not in DELAYED:
|
# if msg.channel == "#nemutest" and msg.sender not in GameUpdater.DELAYED:
|
||||||
if msg.channel != "#nemutest" and msg.sender not in DELAYED:
|
if msg.channel != "#nemutest" and msg.sender not in GameUpdater.DELAYED:
|
||||||
|
|
||||||
if re.match("^(42|quarante[- ]?deux).{,2}$", msg.content.strip().lower()):
|
if re.match("^(42|quarante[- ]?deux).{,2}$", msg.content.strip().lower()):
|
||||||
if msg.time.minute == 10 and msg.time.second == 10 and msg.time.hour == 10:
|
if msg.time.minute == 10 and msg.time.second == 10 and msg.time.hour == 10:
|
||||||
|
@ -355,77 +215,3 @@ def parselisten (msg):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
DELAYED = dict()
|
|
||||||
|
|
||||||
class DelayedTuple:
|
|
||||||
def __init__(self, regexp, great):
|
|
||||||
self.delayEvnt = threading.Event()
|
|
||||||
self.msg = None
|
|
||||||
self.regexp = regexp
|
|
||||||
self.great = great
|
|
||||||
|
|
||||||
def triche(self, res):
|
|
||||||
if res is not None:
|
|
||||||
return re.match(".*" + self.regexp + ".*", res.lower() + " ") is None
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def perfect(self, res):
|
|
||||||
if res is not None:
|
|
||||||
return re.match(".*" + self.great + ".*", res.lower() + " ") is not None
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def wait(self, timeout):
|
|
||||||
self.delayEvnt.wait(timeout)
|
|
||||||
|
|
||||||
LASTQUESTION = 99999
|
|
||||||
|
|
||||||
class GameUpdater(threading.Thread):
|
|
||||||
def __init__(self, msg, bfrseen):
|
|
||||||
self.msg = msg
|
|
||||||
self.bfrseen = bfrseen
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
global DELAYED, LASTQUESTION
|
|
||||||
|
|
||||||
if self.bfrseen is not None:
|
|
||||||
seen = datetime.now() - self.bfrseen
|
|
||||||
rnd = random.randint(0, int(seen.seconds/90))
|
|
||||||
else:
|
|
||||||
rnd = 1
|
|
||||||
|
|
||||||
if rnd != 0:
|
|
||||||
QUESTIONS = CONF.getNodes("question")
|
|
||||||
|
|
||||||
if self.msg.channel == "#nemutest":
|
|
||||||
quest = 9
|
|
||||||
else:
|
|
||||||
if LASTQUESTION >= len(QUESTIONS):
|
|
||||||
random.shuffle(QUESTIONS)
|
|
||||||
LASTQUESTION = 0
|
|
||||||
quest = LASTQUESTION
|
|
||||||
LASTQUESTION += 1
|
|
||||||
|
|
||||||
question = QUESTIONS[quest]["question"]
|
|
||||||
regexp = QUESTIONS[quest]["regexp"]
|
|
||||||
great = QUESTIONS[quest]["great"]
|
|
||||||
self.msg.send_chn("%s: %s" % (self.msg.sender, question))
|
|
||||||
|
|
||||||
DELAYED[self.msg.sender] = DelayedTuple(regexp, great)
|
|
||||||
|
|
||||||
DELAYED[self.msg.sender].wait(20)
|
|
||||||
|
|
||||||
if DELAYED[self.msg.sender].triche(DELAYED[self.msg.sender].msg):
|
|
||||||
getUser(self.msg.sender).playTriche()
|
|
||||||
self.msg.send_chn("%s: Tricheur !" % self.msg.sender)
|
|
||||||
elif DELAYED[self.msg.sender].perfect(DELAYED[self.msg.sender].msg):
|
|
||||||
if random.randint(0, 10) == 1:
|
|
||||||
getUser(self.msg.sender).bonusQuestion()
|
|
||||||
self.msg.send_chn("%s: Correct !" % self.msg.sender)
|
|
||||||
else:
|
|
||||||
self.msg.send_chn("%s: J'accepte" % self.msg.sender)
|
|
||||||
del DELAYED[self.msg.sender]
|
|
||||||
SCORES.save(self.msg.sender)
|
|
||||||
save()
|
|
|
@ -7,137 +7,28 @@ import _thread
|
||||||
import threading
|
import threading
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from module_state import ModuleState
|
from module_state import ModuleState
|
||||||
|
|
||||||
|
from .SiteSoutenances import SiteSoutenances
|
||||||
|
from .Delayed import Delayed
|
||||||
|
|
||||||
nemubotversion = 3.0
|
nemubotversion = 3.0
|
||||||
|
|
||||||
def getPage ():
|
|
||||||
conn = http.client.HTTPSConnection(CONF.getNode("server")["ip"])
|
|
||||||
conn.request("GET", CONF.getNode("server")["url"])
|
|
||||||
|
|
||||||
res = conn.getresponse()
|
|
||||||
data = res.read()
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
return data
|
|
||||||
|
|
||||||
class Soutenance:
|
|
||||||
def __init__(self):
|
|
||||||
self.hour = None
|
|
||||||
self.rank = 0
|
|
||||||
self.login = None
|
|
||||||
self.state = None
|
|
||||||
self.assistant = None
|
|
||||||
self.start = None
|
|
||||||
self.end = None
|
|
||||||
|
|
||||||
DELAYED = dict()
|
DELAYED = dict()
|
||||||
|
|
||||||
class Delayed:
|
def help_tiny():
|
||||||
def __init__(self, name):
|
|
||||||
self.name = name
|
|
||||||
self.res = None
|
|
||||||
self.evt = threading.Event()
|
|
||||||
|
|
||||||
def wait(self, timeout):
|
|
||||||
self.evt.clear()
|
|
||||||
self.evt.wait(timeout)
|
|
||||||
|
|
||||||
class SiteSoutenances:
|
|
||||||
def __init__(self, page):
|
|
||||||
save = False
|
|
||||||
self.souts = list()
|
|
||||||
self.updated = datetime.now ()
|
|
||||||
last = None
|
|
||||||
for line in page.split("\n"):
|
|
||||||
if re.match("</tr>", line) is not None:
|
|
||||||
save = False
|
|
||||||
elif re.match("<tr.*>", line) is not None:
|
|
||||||
save = True
|
|
||||||
last = Soutenance()
|
|
||||||
self.souts.append(last)
|
|
||||||
elif save:
|
|
||||||
result = re.match("<td[^>]+>(.*)</td>", line)
|
|
||||||
if last.hour is None:
|
|
||||||
try:
|
|
||||||
last.hour = datetime.fromtimestamp (time.mktime (time.strptime (result.group(1), "%Y-%m-%d %H:%M")))
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
elif last.rank == 0:
|
|
||||||
last.rank = int (result.group(1))
|
|
||||||
elif last.login == None:
|
|
||||||
last.login = result.group(1)
|
|
||||||
elif last.state == None:
|
|
||||||
last.state = result.group(1)
|
|
||||||
elif last.assistant == None:
|
|
||||||
last.assistant = result.group(1)
|
|
||||||
elif last.start == None:
|
|
||||||
try:
|
|
||||||
last.start = datetime.fromtimestamp (time.mktime (time.strptime (result.group(1), "%Y-%m-%d %H:%M")))
|
|
||||||
except ValueError:
|
|
||||||
last.start = None
|
|
||||||
elif last.end == None:
|
|
||||||
try:
|
|
||||||
last.end = datetime.fromtimestamp (time.mktime (time.strptime (result.group(1), "%Y-%m-%d %H:%M")))
|
|
||||||
except ValueError:
|
|
||||||
last.end = None
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
if self.findLast() is not None and datetime.now () - self.updated > timedelta(minutes=2):
|
|
||||||
return None
|
|
||||||
elif datetime.now () - self.updated < timedelta(hours=1):
|
|
||||||
return self
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def findAssistants(self):
|
|
||||||
h = {}
|
|
||||||
for s in self.souts:
|
|
||||||
if s.assistant is not None and s.assistant != "":
|
|
||||||
h[s.assistant] = (s.start, s.end)
|
|
||||||
return h
|
|
||||||
|
|
||||||
|
|
||||||
def findLast(self):
|
|
||||||
close = None
|
|
||||||
for s in self.souts:
|
|
||||||
if (s.state != "En attente" and s.start is not None and (close is None or close.rank < s.rank or close.hour.day > s.hour.day)) and (close is None or s.hour - close.hour < timedelta(seconds=2499)):
|
|
||||||
close = s
|
|
||||||
return close
|
|
||||||
|
|
||||||
def findAll(self, login):
|
|
||||||
ss = list()
|
|
||||||
for s in self.souts:
|
|
||||||
if s.login == login:
|
|
||||||
ss.append(s)
|
|
||||||
return ss
|
|
||||||
|
|
||||||
def findClose(self, login):
|
|
||||||
ss = self.findAll(login)
|
|
||||||
close = None
|
|
||||||
for s in ss:
|
|
||||||
if close is not None:
|
|
||||||
print (close.hour)
|
|
||||||
print (s.hour)
|
|
||||||
if close is None or (close.hour < s.hour and close.hour.day >= datetime.datetime().day):
|
|
||||||
close = s
|
|
||||||
return close
|
|
||||||
|
|
||||||
|
|
||||||
def help_tiny ():
|
|
||||||
"""Line inserted in the response to the command !help"""
|
"""Line inserted in the response to the command !help"""
|
||||||
return "EPITA ING1 defenses module"
|
return "EPITA ING1 defenses module"
|
||||||
|
|
||||||
def help_full ():
|
def help_full():
|
||||||
return "!soutenance: gives information about current defenses state\n!soutenance /who/: gives the date of the next defense of /who/.\n!soutenances /who/: gives all defense dates of /who/"
|
return "!soutenance: gives information about current defenses state\n!soutenance <who>: gives the date of the next defense of /who/.\n!soutenances <who>: gives all defense dates of /who/"
|
||||||
|
|
||||||
datas = None
|
datas = None
|
||||||
THREAD = None
|
THREAD = None
|
||||||
wait = list()
|
wait = list()
|
||||||
|
|
||||||
def parseanswer (msg):
|
def parseanswer(msg):
|
||||||
global THREAD, wait
|
global THREAD, wait
|
||||||
if msg.cmd[0] == "soutenance" or msg.cmd[0] == "soutenances":
|
if msg.cmd[0] == "soutenance" or msg.cmd[0] == "soutenances":
|
||||||
if THREAD is None:
|
if THREAD is None:
|
||||||
|
@ -148,6 +39,22 @@ def parseanswer (msg):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def parseask(msg):
|
||||||
|
if len(DELAYED) > 0 and msg.sender == msg.srv.partner:
|
||||||
|
treat = False
|
||||||
|
for part in msg.content.split(';'):
|
||||||
|
if part is None:
|
||||||
|
continue
|
||||||
|
for d in DELAYED.keys():
|
||||||
|
if DELAYED[d].res is None and part.find(DELAYED[d].name) >= 0:
|
||||||
|
result = re.match(".* est (.*[^.])\.?", part)
|
||||||
|
if result is not None:
|
||||||
|
DELAYED[d].res = result.group(1)
|
||||||
|
DELAYED[d].evt.set()
|
||||||
|
return treat
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def startSoutenance (msg):
|
def startSoutenance (msg):
|
||||||
global datas, THREAD, wait
|
global datas, THREAD, wait
|
||||||
|
|
||||||
|
@ -224,17 +131,16 @@ def startSoutenance (msg):
|
||||||
startSoutenance(wait.pop())
|
startSoutenance(wait.pop())
|
||||||
|
|
||||||
|
|
||||||
def parseask (msg):
|
def getPage():
|
||||||
if len(DELAYED) > 0 and msg.sender == msg.srv.partner:
|
conn = http.client.HTTPConnection(CONF.getNode("server")["ip"])
|
||||||
treat = False
|
try:
|
||||||
for part in msg.content.split(';'):
|
conn.request("GET", CONF.getNode("server")["url"])
|
||||||
if part is None:
|
|
||||||
continue
|
res = conn.getresponse()
|
||||||
for d in DELAYED.keys():
|
data = res.read()
|
||||||
if DELAYED[d].res is None and part.find(DELAYED[d].name) >= 0:
|
except:
|
||||||
result = re.match(".* est (.*[^.])\.?", part)
|
print ("[%s] impossible de récupérer la page %s."%(s, p))
|
||||||
if result is not None:
|
return ""
|
||||||
DELAYED[d].res = result.group(1)
|
|
||||||
DELAYED[d].evt.set()
|
conn.close()
|
||||||
return treat
|
return data
|
||||||
return False
|
|
|
@ -1,206 +0,0 @@
|
||||||
# coding=utf-8
|
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
from datetime import datetime
|
|
||||||
from datetime import date
|
|
||||||
import http.client
|
|
||||||
import hashlib
|
|
||||||
import sys
|
|
||||||
import traceback
|
|
||||||
import socket
|
|
||||||
import time
|
|
||||||
import base64
|
|
||||||
import threading
|
|
||||||
from urllib.parse import unquote
|
|
||||||
|
|
||||||
from module_state import ModuleState
|
|
||||||
|
|
||||||
nemubotversion = 3.0
|
|
||||||
|
|
||||||
import atom
|
|
||||||
|
|
||||||
def help_tiny ():
|
|
||||||
"""Line inserted in the response to the command !help"""
|
|
||||||
return "Alert on changes on websites"
|
|
||||||
|
|
||||||
def help_full ():
|
|
||||||
return "This module is autonomous you can't interract with it."
|
|
||||||
|
|
||||||
WATCHER = None
|
|
||||||
|
|
||||||
def load():
|
|
||||||
global WATCHER, DATAS
|
|
||||||
#Load the watcher
|
|
||||||
WATCHER = Watcher()
|
|
||||||
for site in DATAS.getNodes("watch"):
|
|
||||||
s = Site(site)
|
|
||||||
WATCHER.addServer(s)
|
|
||||||
WATCHER.start()
|
|
||||||
|
|
||||||
def close():
|
|
||||||
global WATCHER
|
|
||||||
if WATCHER is not None:
|
|
||||||
WATCHER.stop = True
|
|
||||||
WATCHER.newSrv.set()
|
|
||||||
|
|
||||||
|
|
||||||
class Watcher(threading.Thread):
|
|
||||||
def __init__(self):
|
|
||||||
self.servers = list()
|
|
||||||
self.stop = False
|
|
||||||
self.newSrv = threading.Event()
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
|
|
||||||
def addServer(self, server):
|
|
||||||
self.servers.append(server)
|
|
||||||
self.newSrv.set()
|
|
||||||
|
|
||||||
def check(self, closer):
|
|
||||||
closer.check()
|
|
||||||
self.newSrv.set()
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
while not self.stop:
|
|
||||||
self.newSrv.clear()
|
|
||||||
closer = None
|
|
||||||
#Gets the closer server update
|
|
||||||
for server in self.servers:
|
|
||||||
if server.update < datetime.now():
|
|
||||||
#print ("Closer now: %s à %s"%(server.url, server.update))
|
|
||||||
self.check(server)
|
|
||||||
elif closer is None or server.update < closer.update:
|
|
||||||
closer = server
|
|
||||||
if closer is not None:
|
|
||||||
#print ("Closer: %s à %s"%(closer.url, closer.update))
|
|
||||||
timeleft = (closer.update - datetime.now()).seconds
|
|
||||||
timer = threading.Timer(timeleft, self.check, (closer,))
|
|
||||||
timer.start()
|
|
||||||
#print ("Start timer (%ds)"%timeleft)
|
|
||||||
|
|
||||||
self.newSrv.wait()
|
|
||||||
|
|
||||||
if closer is not None and closer.update is not None and closer.update > datetime.now():
|
|
||||||
timer.cancel()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.stop = True
|
|
||||||
self.newSrv.set()
|
|
||||||
|
|
||||||
|
|
||||||
class Site:
|
|
||||||
def __init__(self, item):
|
|
||||||
self.server = item.getAttribute("server")
|
|
||||||
self.page = item.getAttribute("page")
|
|
||||||
if len(self.page) <= 0 or self.page[0] != "/":
|
|
||||||
self.page = "/" + self.page
|
|
||||||
if item.hasAttribute("type"):
|
|
||||||
self.type = item.getAttribute("type")
|
|
||||||
else:
|
|
||||||
self.type = "hash"
|
|
||||||
self.message = item.getAttribute("message")
|
|
||||||
|
|
||||||
if item.hasAttribute("time"):
|
|
||||||
self.updateTime = item.getInt("time")
|
|
||||||
else:
|
|
||||||
self.updateTime = 60
|
|
||||||
self.lastChange = datetime.now()
|
|
||||||
self.lastpage = None
|
|
||||||
|
|
||||||
self.channels = list()
|
|
||||||
for channel in item.getNodes('channel'):
|
|
||||||
self.channels.append(channel.getAttribute("name"))
|
|
||||||
|
|
||||||
self.categories = dict()
|
|
||||||
for category in item.getNodes('category'):
|
|
||||||
self.categories[category.getAttribute("term")] = category.getAttribute("part")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def update(self):
|
|
||||||
if self.lastpage is None:
|
|
||||||
return self.lastChange
|
|
||||||
else:
|
|
||||||
return self.lastChange + timedelta(seconds=self.updateTime)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def url(self):
|
|
||||||
return self.server + self.page
|
|
||||||
|
|
||||||
def send_message (self, msg):
|
|
||||||
global SRVS
|
|
||||||
if len(self.channels) > 0:
|
|
||||||
for server in SRVS.keys():
|
|
||||||
for chan in self.channels:
|
|
||||||
SRVS[server].send_msg (chan, msg)
|
|
||||||
else:
|
|
||||||
for server in SRVS.keys():
|
|
||||||
SRVS[server].send_global (msg)
|
|
||||||
|
|
||||||
def treat_atom (self, content):
|
|
||||||
change=False
|
|
||||||
f = atom.Atom (content)
|
|
||||||
if self.lastpage is not None:
|
|
||||||
diff = self.lastpage.diff (f)
|
|
||||||
if len(diff) > 0:
|
|
||||||
print ("[%s] Page differ!"%self.server)
|
|
||||||
diff.reverse()
|
|
||||||
for d in diff:
|
|
||||||
if self.message.count("%s") == 2 and len(self.categories) > 0:
|
|
||||||
if d.category is None or d.category not in self.categories:
|
|
||||||
messageI = self.message % (self.categories[""], "%s")
|
|
||||||
else:
|
|
||||||
messageI = self.message % (self.categories[d.category], "%s")
|
|
||||||
self.send_message (messageI % unquote (d.link))
|
|
||||||
elif self.message.count("%s") == 2:
|
|
||||||
if f.id == youtube.idAtom:
|
|
||||||
youtube.send_global (d.link2, self.message % (d.title, unquote (d.link)))
|
|
||||||
else:
|
|
||||||
self.send_message (self.message % (d.title, unquote (d.link)))
|
|
||||||
elif self.message.count("%s") == 1:
|
|
||||||
self.send_message(self.message % unquote (d.title))
|
|
||||||
else:
|
|
||||||
self.send_message(self.message)
|
|
||||||
change=True
|
|
||||||
return (f, change)
|
|
||||||
|
|
||||||
def check (self):
|
|
||||||
try:
|
|
||||||
#print ("Check %s"%(self.url))
|
|
||||||
(status, content) = getPage(self.server, self.page)
|
|
||||||
if content is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.type == "atom":
|
|
||||||
(self.lastpage, change) = self.treat_atom (content)
|
|
||||||
else:
|
|
||||||
hash = hashlib.sha224(content).hexdigest()
|
|
||||||
if hash != self.lastpage:
|
|
||||||
if self.lastpage is not None:
|
|
||||||
self.send_message (self.message)
|
|
||||||
self.lastpage = hash
|
|
||||||
|
|
||||||
self.lastChange = datetime.now()
|
|
||||||
|
|
||||||
# if self.updateTime < 10:
|
|
||||||
# self.updateTime = 10
|
|
||||||
# if self.updateTime > 400:
|
|
||||||
# self.updateTime = 400
|
|
||||||
except:
|
|
||||||
print ("Une erreur est survenue lors de la récupération de la page " + self.server + "/" + self.page)
|
|
||||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
||||||
traceback.print_exception(exc_type, exc_value, exc_traceback)
|
|
||||||
self.updateTime *= 2
|
|
||||||
|
|
||||||
|
|
||||||
def getPage(s, p):
|
|
||||||
conn = http.client.HTTPConnection(s)
|
|
||||||
try:
|
|
||||||
conn.request("GET", p)
|
|
||||||
|
|
||||||
res = conn.getresponse()
|
|
||||||
data = res.read()
|
|
||||||
except socket.gaierror:
|
|
||||||
print ("[%s] impossible de récupérer la page %s."%(s, p))
|
|
||||||
return (None, None)
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
return (res.status, data)
|
|
32
modules/watchWebsite/__init__.py
Normal file
32
modules/watchWebsite/__init__.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
nemubotversion = 3.0
|
||||||
|
|
||||||
|
from .Watcher import Watcher
|
||||||
|
from .Site import Site
|
||||||
|
|
||||||
|
def help_tiny ():
|
||||||
|
"""Line inserted in the response to the command !help"""
|
||||||
|
return "Alert on changes on websites"
|
||||||
|
|
||||||
|
def help_full ():
|
||||||
|
return "This module is autonomous you can't interract with it."
|
||||||
|
|
||||||
|
|
||||||
|
WATCHER = None
|
||||||
|
|
||||||
|
|
||||||
|
def load():
|
||||||
|
global WATCHER, DATAS
|
||||||
|
#Load the watcher
|
||||||
|
WATCHER = Watcher()
|
||||||
|
for site in DATAS.getNodes("watch"):
|
||||||
|
s = Site(site)
|
||||||
|
WATCHER.addServer(s)
|
||||||
|
WATCHER.start()
|
||||||
|
|
||||||
|
def close():
|
||||||
|
global WATCHER
|
||||||
|
if WATCHER is not None:
|
||||||
|
WATCHER.stop = True
|
||||||
|
WATCHER.newSrv.set()
|
|
@ -13,102 +13,25 @@ from urllib.parse import unquote
|
||||||
|
|
||||||
from module_state import ModuleState
|
from module_state import ModuleState
|
||||||
|
|
||||||
|
from . import User
|
||||||
|
from .UpdatedStorage import UpdatedStorage
|
||||||
|
from .Delayed import Delayed
|
||||||
|
|
||||||
nemubotversion = 3.0
|
nemubotversion = 3.0
|
||||||
|
|
||||||
THREAD = None
|
THREAD = None
|
||||||
search = list()
|
search = list()
|
||||||
|
|
||||||
class UpdatedStorage:
|
|
||||||
def __init__(self):
|
|
||||||
sock = connect_to_ns(CONF.getNode("server")["url"],
|
|
||||||
CONF.getNode("server").getInt("port"))
|
|
||||||
self.users = dict()
|
|
||||||
if sock != None:
|
|
||||||
users = list_users(sock)
|
|
||||||
if users is not None:
|
|
||||||
for l in users:
|
|
||||||
u = User(l)
|
|
||||||
if u.login not in self.users:
|
|
||||||
self.users[u.login] = list()
|
|
||||||
self.users[u.login].append(u)
|
|
||||||
self.lastUpdate = datetime.now ()
|
|
||||||
else:
|
|
||||||
self.users = None
|
|
||||||
sock.close()
|
|
||||||
else:
|
|
||||||
self.users = None
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
if datetime.now () - self.lastUpdate < timedelta(minutes=10):
|
|
||||||
return self
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class User(object):
|
|
||||||
def __init__(self, line):
|
|
||||||
fields = line.split()
|
|
||||||
self.login = fields[1]
|
|
||||||
self.ip = fields[2]
|
|
||||||
self.location = fields[8]
|
|
||||||
self.promo = fields[9]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def sm(self):
|
|
||||||
for sm in CONF.getNodes("sm"):
|
|
||||||
if self.ip.startswith(sm["ip"]):
|
|
||||||
return sm["name"]
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def poste(self):
|
|
||||||
if self.sm is None:
|
|
||||||
if self.ip.startswith('10.'):
|
|
||||||
return 'quelque part sur le PIE (%s)'%self.ip
|
|
||||||
else:
|
|
||||||
return "chez lui"
|
|
||||||
else:
|
|
||||||
if self.ip.startswith('10.247') or self.ip.startswith('10.248') or self.ip.startswith('10.249') or self.ip.startswith('10.250'):
|
|
||||||
return "en " + self.sm + " rangée " + self.ip.split('.')[2] + " poste " + self.ip.split('.')[3]
|
|
||||||
else:
|
|
||||||
return "en " + self.sm
|
|
||||||
|
|
||||||
def __cmp__(self, other):
|
|
||||||
return cmp(self.login, other.login)
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return hash(self.login)
|
|
||||||
|
|
||||||
def connect_to_ns(server, port):
|
|
||||||
try:
|
|
||||||
s = socket.socket()
|
|
||||||
s.settimeout(3)
|
|
||||||
s.connect((server, port))
|
|
||||||
except socket.error:
|
|
||||||
return None
|
|
||||||
s.recv(8192)
|
|
||||||
return s
|
|
||||||
|
|
||||||
def list_users(sock):
|
|
||||||
try:
|
|
||||||
sock.send('list_users\n'.encode())
|
|
||||||
buf = ''
|
|
||||||
while True:
|
|
||||||
tmp = sock.recv(8192).decode()
|
|
||||||
buf += tmp
|
|
||||||
if '\nrep 002' in tmp or tmp == '':
|
|
||||||
break
|
|
||||||
return buf.split('\n')[:-2]
|
|
||||||
except socket.error:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def help_tiny ():
|
def help_tiny ():
|
||||||
"""Line inserted in the response to the command !help"""
|
"""Line inserted in the response to the command !help"""
|
||||||
return "Find a user on the PIE"
|
return "Find a user on the PIE"
|
||||||
|
|
||||||
def help_full ():
|
def help_full ():
|
||||||
return "!whereis /who/: gives the position of /who/.\n!whereare /who/ [/other who/ ...]: gives the position of /who/."
|
return "!whereis <who>: gives the position of /who/.\n!whereare <who> [<other who> ...]: gives the position of these <who>.\n!peoplein <sm>: gives the number of people in this /sm/.\n!ip <who>: gets the IP adress of /who/.\n!whoison <location>: gives the name or the number (if > 15) of people at this /location/.\n!whoisin <sm>: gives the name or the number of people in this /sm/"
|
||||||
|
|
||||||
|
def load():
|
||||||
|
global CONF
|
||||||
|
User.CONF = CONF
|
||||||
|
|
||||||
datas = None
|
datas = None
|
||||||
|
|
||||||
|
@ -117,7 +40,7 @@ def startWhereis(msg):
|
||||||
if datas is not None:
|
if datas is not None:
|
||||||
datas = datas.update ()
|
datas = datas.update ()
|
||||||
if datas is None:
|
if datas is None:
|
||||||
datas = UpdatedStorage()
|
datas = UpdatedStorage(CONF.getNode("server")["url"], CONF.getNode("server").getInt("port"))
|
||||||
if datas is None or datas.users is None:
|
if datas is None or datas.users is None:
|
||||||
msg.send_chn("Hmm c'est embarassant, serait-ce la fin du monde ou juste netsoul qui est mort ?")
|
msg.send_chn("Hmm c'est embarassant, serait-ce la fin du monde ou juste netsoul qui est mort ?")
|
||||||
return
|
return
|
||||||
|
@ -178,10 +101,6 @@ def whoison(msg):
|
||||||
DELAYED = dict()
|
DELAYED = dict()
|
||||||
delayEvnt = threading.Event()
|
delayEvnt = threading.Event()
|
||||||
|
|
||||||
class Delayed:
|
|
||||||
def __init__(self):
|
|
||||||
self.names = dict()
|
|
||||||
|
|
||||||
def whereis_msg(msg):
|
def whereis_msg(msg):
|
||||||
names = list()
|
names = list()
|
||||||
for name in msg.cmd:
|
for name in msg.cmd:
|
|
@ -1,2 +0,0 @@
|
||||||
<?xml version="1.0" ?>
|
|
||||||
<nemubotmodule name="ycc" />
|
|
|
@ -3,7 +3,8 @@
|
||||||
import http.client
|
import http.client
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import threading
|
|
||||||
|
from .Tinyfier import Tinyfier
|
||||||
|
|
||||||
nemubotversion = 3.0
|
nemubotversion = 3.0
|
||||||
|
|
||||||
|
@ -15,24 +16,6 @@ def help_full ():
|
||||||
return "TODO"
|
return "TODO"
|
||||||
|
|
||||||
|
|
||||||
class Tinyfier(threading.Thread):
|
|
||||||
def __init__(self, url, msg):
|
|
||||||
self.url = url
|
|
||||||
self.msg = msg
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
(status, page) = getPage("ycc.fr", "/redirection/create/" + self.url)
|
|
||||||
if status == http.client.OK and len(page) < 100:
|
|
||||||
srv = re.match(".*((ht|f)tps?://|www.)([^/ ]+).*", self.url)
|
|
||||||
if srv is None:
|
|
||||||
self.msg.send_chn("Mauvaise URL : %s" % (self.url))
|
|
||||||
else:
|
|
||||||
self.msg.send_chn("URL pour %s : %s" % (srv.group(3), page.decode()))
|
|
||||||
else:
|
|
||||||
print ("ERROR: ycc.fr seem down?")
|
|
||||||
self.msg.send_chn("La situation est embarassante, il semblerait que YCC soit down :(")
|
|
||||||
|
|
||||||
def parseanswer(msg):
|
def parseanswer(msg):
|
||||||
global LAST_URLS
|
global LAST_URLS
|
||||||
if msg.cmd[0] == "ycc":
|
if msg.cmd[0] == "ycc":
|
||||||
|
@ -42,7 +25,7 @@ def parseanswer(msg):
|
||||||
t = Tinyfier(url, msg)
|
t = Tinyfier(url, msg)
|
||||||
t.start()
|
t.start()
|
||||||
else:
|
else:
|
||||||
msg.send_chn("%s: je n'ai pas d'autre URL a reduire" % msg.sender)
|
msg.send_chn("%s: je n'ai pas d'autre URL reduire" % msg.sender)
|
||||||
else:
|
else:
|
||||||
if len(msg.cmd) < 6:
|
if len(msg.cmd) < 6:
|
||||||
for url in msg.cmd[1:]:
|
for url in msg.cmd[1:]:
|
||||||
|
@ -66,17 +49,3 @@ def parselisten (msg):
|
||||||
LAST_URLS[msg.channel] = list(res.group(1))
|
LAST_URLS[msg.channel] = list(res.group(1))
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def getPage(s, p):
|
|
||||||
conn = http.client.HTTPConnection(s)
|
|
||||||
try:
|
|
||||||
conn.request("GET", p)
|
|
||||||
except socket.gaierror:
|
|
||||||
print ("[%s] impossible de récupérer la page %s."%(s, p))
|
|
||||||
return None
|
|
||||||
|
|
||||||
res = conn.getresponse()
|
|
||||||
data = res.read()
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
return (res.status, data)
|
|
12
nemubot.py
12
nemubot.py
|
@ -10,9 +10,19 @@ servers = dict()
|
||||||
|
|
||||||
prompt = __import__ ("prompt")
|
prompt = __import__ ("prompt")
|
||||||
|
|
||||||
|
#Add modules dir path
|
||||||
|
if os.path.isdir("./modules/"):
|
||||||
|
modules_path = os.path.realpath(os.path.abspath("./modules/"))
|
||||||
|
if modules_path not in sys.path:
|
||||||
|
sys.path.insert(0, modules_path)
|
||||||
|
|
||||||
|
#Load given files
|
||||||
if len(sys.argv) >= 2:
|
if len(sys.argv) >= 2:
|
||||||
for arg in sys.argv[1:]:
|
for arg in sys.argv[1:]:
|
||||||
prompt.load_file(arg, servers)
|
if os.path.isfile(arg):
|
||||||
|
prompt.load_file(arg, servers)
|
||||||
|
elif os.path.isdir(arg):
|
||||||
|
sys.path.insert(1, arg)
|
||||||
|
|
||||||
print ("Nemubot ready, my PID is %i!" % (os.getpid()))
|
print ("Nemubot ready, my PID is %i!" % (os.getpid()))
|
||||||
while prompt.launch(servers):
|
while prompt.launch(servers):
|
||||||
|
|
178
prompt.py
178
prompt.py
|
@ -1,4 +1,5 @@
|
||||||
import imp
|
import imp
|
||||||
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -92,12 +93,12 @@ def launch(servers):
|
||||||
# #
|
# #
|
||||||
##########################
|
##########################
|
||||||
|
|
||||||
def mod_save(mod, datas_path, config):
|
def mod_save(mod, datas_path):
|
||||||
mod.DATAS.save(datas_path + "/" + config["name"] + ".xml")
|
mod.DATAS.save(datas_path + "/" + mod.name + ".xml")
|
||||||
mod.print ("Saving!")
|
mod.print ("Saving!")
|
||||||
|
|
||||||
def mod_has_access(mod, config, msg):
|
def mod_has_access(mod, config, msg):
|
||||||
if config.hasNode("channel"):
|
if config is not None and config.hasNode("channel"):
|
||||||
for chan in config.getChilds("channel"):
|
for chan in config.getChilds("channel"):
|
||||||
if (chan["server"] is None or chan["server"] == msg.srv.id) and (chan["channel"] is None or chan["channel"] == msg.channel):
|
if (chan["server"] is None or chan["server"] == msg.srv.id) and (chan["channel"] is None or chan["channel"] == msg.channel):
|
||||||
return True
|
return True
|
||||||
|
@ -111,93 +112,106 @@ def mod_has_access(mod, config, msg):
|
||||||
# #
|
# #
|
||||||
##########################
|
##########################
|
||||||
|
|
||||||
|
def load_module_from_name(name, servers, config=None):
|
||||||
|
try:
|
||||||
|
#Import the module code
|
||||||
|
mod = __import__(name)
|
||||||
|
try:
|
||||||
|
if mod.nemubotversion < 3.0:
|
||||||
|
print (" Module `%s' is not compatible with this version." % name)
|
||||||
|
return
|
||||||
|
|
||||||
|
#Set module common functions and datas
|
||||||
|
mod.name = name
|
||||||
|
mod.print = lambda msg: print("[%s] %s"%(mod.name, msg))
|
||||||
|
mod.DATAS = xmlparser.parse_file(datas_path + "/" + name + ".xml")
|
||||||
|
mod.CONF = config
|
||||||
|
mod.SRVS = servers
|
||||||
|
mod.has_access = lambda msg: mod_has_access(mod, config, msg)
|
||||||
|
mod.save = lambda: mod_save(mod, datas_path)
|
||||||
|
|
||||||
|
#Load dependancies
|
||||||
|
if mod.CONF is not None and mod.CONF.hasNode("dependson"):
|
||||||
|
mod.MODS = dict()
|
||||||
|
for depend in mod.CONF.getNodes("dependson"):
|
||||||
|
for md in MODS:
|
||||||
|
if md.name == depend["name"]:
|
||||||
|
mod.MODS[md.name] = md
|
||||||
|
break
|
||||||
|
if depend["name"] not in mod.MODS:
|
||||||
|
print ("\033[1;31mERROR:\033[0m in module `%s', module `%s' require by this module but is not loaded." % (mod.name,depend["name"]))
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
test = mod.parseask
|
||||||
|
except AttributeError:
|
||||||
|
print ("\033[1;35mWarning:\033[0m in module `%s', no function parseask defined." % mod.name)
|
||||||
|
mod.parseask = lambda x: False
|
||||||
|
|
||||||
|
try:
|
||||||
|
test = mod.parseanswer
|
||||||
|
except AttributeError:
|
||||||
|
print ("\033[1;35mWarning:\033[0m in module `%s', no function parseanswer defined." % mod.name)
|
||||||
|
mod.parseanswer = lambda x: False
|
||||||
|
|
||||||
|
try:
|
||||||
|
test = mod.parselisten
|
||||||
|
except AttributeError:
|
||||||
|
print ("\033[1;35mWarning:\033[0m in module `%s', no function parselisten defined." % mod.name)
|
||||||
|
mod.parselisten = lambda x: False
|
||||||
|
|
||||||
|
try:
|
||||||
|
mod.load()
|
||||||
|
print (" Module `%s' successfully loaded." % name)
|
||||||
|
except AttributeError:
|
||||||
|
print (" Module `%s' successfully added." % name)
|
||||||
|
#TODO: don't append already running modules
|
||||||
|
MODS.append(mod)
|
||||||
|
except AttributeError:
|
||||||
|
print (" Module `%s' is not a nemubot module." % name)
|
||||||
|
for srv in servers:
|
||||||
|
servers[srv].update_mods(MODS)
|
||||||
|
except IOError:
|
||||||
|
print (" Module `%s' not loaded: unable to find module implementation." % name)
|
||||||
|
except ImportError:
|
||||||
|
print ("\033[1;31mERROR:\033[0m Module attached to the file `%s' not loaded. Is this file existing?" % name)
|
||||||
|
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||||
|
traceback.print_exception(exc_type, exc_value, exc_traceback)
|
||||||
|
|
||||||
def load_module(config, servers):
|
def load_module(config, servers):
|
||||||
global MODS
|
global MODS
|
||||||
if config.hasAttribute("name"):
|
if config.hasAttribute("name"):
|
||||||
try:
|
load_module_from_name(config["name"], servers, config)
|
||||||
#Import the module code
|
|
||||||
mod = imp.load_source(config["name"], modules_path + "/" + config["name"] + ".py")
|
|
||||||
try:
|
|
||||||
if mod.nemubotversion < 3.0:
|
|
||||||
print (" Module `%s' is not compatible with this version." % config["name"])
|
|
||||||
return
|
|
||||||
|
|
||||||
#Set module common functions and datas
|
|
||||||
mod.name = config["name"]
|
|
||||||
mod.print = lambda msg: print("[%s] %s"%(mod.name, msg))
|
|
||||||
mod.DATAS = xmlparser.parse_file(datas_path + "/" + config["name"] + ".xml")
|
|
||||||
mod.CONF = config
|
|
||||||
mod.SRVS = servers
|
|
||||||
mod.has_access = lambda msg: mod_has_access(mod, config, msg)
|
|
||||||
mod.save = lambda: mod_save(mod, datas_path, config)
|
|
||||||
|
|
||||||
#Load dependancies
|
|
||||||
if mod.CONF.hasNode("dependson"):
|
|
||||||
mod.MODS = dict()
|
|
||||||
for depend in mod.CONF.getNodes("dependson"):
|
|
||||||
for md in MODS:
|
|
||||||
if md.name == depend["name"]:
|
|
||||||
mod.MODS[md.name] = md
|
|
||||||
break
|
|
||||||
if depend["name"] not in mod.MODS:
|
|
||||||
print ("\033[1;35mERROR:\033[0m in module `%s', module `%s' require by this module but is not loaded." % (mod.name,depend["name"]))
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
test = mod.parseask
|
|
||||||
except AttributeError:
|
|
||||||
print ("\033[1;35mWarning:\033[0m in module `%s', no function parseask defined." % mod.name)
|
|
||||||
mod.parseask = lambda x: False
|
|
||||||
|
|
||||||
try:
|
|
||||||
test = mod.parseanswer
|
|
||||||
except AttributeError:
|
|
||||||
print ("\033[1;35mWarning:\033[0m in module `%s', no function parseanswer defined." % mod.name)
|
|
||||||
mod.parseanswer = lambda x: False
|
|
||||||
|
|
||||||
try:
|
|
||||||
test = mod.parselisten
|
|
||||||
except AttributeError:
|
|
||||||
print ("\033[1;35mWarning:\033[0m in module `%s', no function parselisten defined." % mod.name)
|
|
||||||
mod.parselisten = lambda x: False
|
|
||||||
|
|
||||||
try:
|
|
||||||
mod.load()
|
|
||||||
print (" Module `%s' successfully loaded." % config["name"])
|
|
||||||
except AttributeError:
|
|
||||||
print (" Module `%s' successfully added." % config["name"])
|
|
||||||
#TODO: don't append already running modules
|
|
||||||
MODS.append(mod)
|
|
||||||
except AttributeError:
|
|
||||||
print (" Module `%s' is not a nemubot module." % config["name"])
|
|
||||||
for srv in servers:
|
|
||||||
servers[srv].update_mods(MODS)
|
|
||||||
except IOError:
|
|
||||||
print (" Module `%s' not loaded: unable to find module implementation." % config["name"])
|
|
||||||
|
|
||||||
|
|
||||||
def load_file(filename, servers):
|
def load_file(filename, servers):
|
||||||
"""Realy load a file"""
|
"""Realy load a file"""
|
||||||
global MODS
|
global MODS
|
||||||
config = xmlparser.parse_file(filename)
|
if os.path.isfile(filename):
|
||||||
if config.getName() == "nemubotconfig" or config.getName() == "config":
|
config = xmlparser.parse_file(filename)
|
||||||
#Preset each server in this file
|
if config.getName() == "nemubotconfig" or config.getName() == "config":
|
||||||
for serveur in config.getNodes("server"):
|
#Preset each server in this file
|
||||||
srv = server.Server(serveur, config["nick"], config["owner"], config["realname"])
|
for serveur in config.getNodes("server"):
|
||||||
if srv.id not in servers:
|
srv = server.Server(serveur, config["nick"], config["owner"], config["realname"])
|
||||||
servers[srv.id] = srv
|
if srv.id not in servers:
|
||||||
print (" Server `%s' successfully added." % srv.id)
|
servers[srv.id] = srv
|
||||||
else:
|
print (" Server `%s' successfully added." % srv.id)
|
||||||
print (" Server `%s' already added, skiped." % srv.id)
|
else:
|
||||||
if srv.autoconnect:
|
print (" Server `%s' already added, skiped." % srv.id)
|
||||||
srv.launch(MODS)
|
if srv.autoconnect:
|
||||||
#Load files asked by the configuration file
|
srv.launch(MODS)
|
||||||
for load in config.getNodes("load"):
|
#Load files asked by the configuration file
|
||||||
load_file(load["path"], servers)
|
for load in config.getNodes("load"):
|
||||||
elif config.getName() == "nemubotmodule":
|
load_file(load["path"], servers)
|
||||||
load_module(config, servers)
|
elif config.getName() == "nemubotmodule":
|
||||||
|
load_module(config, servers)
|
||||||
|
else:
|
||||||
|
print (" Can't load `%s'; this is not a valid nemubot configuration file." % filename)
|
||||||
|
elif os.path.isfile(filename + ".xml"):
|
||||||
|
load_file(filename + ".xml", servers)
|
||||||
|
elif os.path.isfile("./modules/" + filename + ".xml"):
|
||||||
|
load_file("./modules/" + filename + ".xml", servers)
|
||||||
else:
|
else:
|
||||||
print (" Can't load `%s'; this is not a valid nemubot configuration file." % filename)
|
load_module_from_name(filename, servers)
|
||||||
|
|
||||||
|
|
||||||
def load(cmds, servers):
|
def load(cmds, servers):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue