diff options
Diffstat (limited to 'module/interaction')
-rw-r--r-- | module/interaction/InteractionManager.py | 127 | ||||
-rw-r--r-- | module/interaction/InteractionTask.py | 17 |
2 files changed, 105 insertions, 39 deletions
diff --git a/module/interaction/InteractionManager.py b/module/interaction/InteractionManager.py index c547e1c97..0c125bdd4 100644 --- a/module/interaction/InteractionManager.py +++ b/module/interaction/InteractionManager.py @@ -15,10 +15,13 @@ @author: RaNaN """ -from traceback import print_exc from threading import Lock +from time import time -from module.utils import lock, bits_set +from new_collections import OrderedDict + +from module.utils import lock, bits_set, to_list +from module.Api import Input, Output from InteractionTask import InteractionTask @@ -28,69 +31,127 @@ class InteractionManager: Arbitary task with predefined output and input type can be set off. Asyncronous callbacks and default values keeps the ability to fallback if no user is present. """ + + # number of seconds a client is classified as active + CLIENT_THRESHOLD = 60 + def __init__(self, core): self.lock = Lock() self.core = core - self.tasks = [] #task store, for outgoing tasks only + self.tasks = OrderedDict() #task store, for outgoing tasks only + self.notifications = [] #list of notifications - self.last_clients = {} + self.last_clients = { + Output.Notification : 0, + Output.Captcha : 0, + Output.Query : 0, + } self.ids = 0 #only for internal purpose + def isClientConnected(self, mode=Output.All): + if mode == Output.All: + return max(self.last_clients.values()) + self.CLIENT_THRESHOLD <= time() + else: + self.last_clients.get(mode, 0) + self.CLIENT_THRESHOLD <= time() + + def updateClient(self, mode): + t = time() + for output in self.last_clients: + if bits_set(output, mode): + self.last_clients[output] = t + + @lock def work(self): - pass + # old notifications will be removed + for n in [x for x in self.notifications if x.timedOut()]: + self.notifications.remove(n) + + # store at most 100 notifications + del self.notifications[50:] + @lock - def newNotification(self): - pass + def createNotification(self, title, content, desc="", plugin=""): + """ Creates and queues a new Notification + + :param title: short title + :param content: text content + :param desc: short form of the notification + :param plugin: plugin name + :return: :class:`InteractionTask` + """ + task = InteractionTask(self.ids, Input.Text, [content], Output.Notification, "", title, desc, plugin) + self.ids += 1 + self.notifications.insert(0, task) + self.handleTask(task) + return task @lock - def newQueryTask(self): - pass + def newQueryTask(self, input, data, desc, default="", plugin=""): + task = InteractionTask(self.ids, input, to_list(data), Output.Query, default, _("Query"), desc, plugin) + self.ids += 1 + return task @lock - def newCaptchaTask(self, img, format, file, result_type): - task = InteractionTask(self.ids, img, format, file, result_type) + def newCaptchaTask(self, img, format, filename, plugin="", input=Input.Text): + #todo: title desc plugin + task = InteractionTask(self.ids, input, [img, format, filename],Output.Captcha, + "", _("Captcha request"), _("Please solve the captcha."), plugin) self.ids += 1 return task @lock def removeTask(self, task): - if task in self.tasks: - self.tasks.remove(task) + if task.iid in self.tasks: + del self.tasks[task.iid] @lock - def getTask(self): - for task in self.tasks: - return task + def getTask(self, mode=Output.All): + self.updateClient(mode) + + for task in self.tasks.itervalues(): + if mode == Output.All or bits_set(task.output, mode): + return task + + @lock + def getNotifications(self): + """retrieves notifications, old ones are only deleted after a while\ + client has to make sure itself to dont display it twice""" + for n in self.notifications: + n.setWaiting(self.CLIENT_THRESHOLD * 5, True) + #store notification for shorter period, lock the timeout + + return self.notifications + + def isTaskWaiting(self, mode=Output.All): + return self.getTask(mode) is not None @lock def getTaskByID(self, iid): - for task in self.tasks: - if task.id == iid: - return task + if iid in self.tasks: + task = self.tasks[iid] + del self.tasks[iid] + return task - def handleCaptcha(self, task): - cli = self.core.isClientConnected() + def handleTask(self, task): + cli = self.isClientConnected(task.output) - if cli: #client connected -> should solve the captcha - task.setWaiting(50) #wait 50 sec for response + if cli: #client connected -> should handle the task + task.setWaiting(self.CLIENT_THRESHOLD) # wait for response + + if task.output == Output.Notification: + task.setWaiting(60 * 60 * 30) # notifications are valid for 30h for plugin in self.core.addonManager.activePlugins(): try: - plugin.newCaptchaTask(task) + plugin.newInteractionTask(task) except: - if self.core.debug: - print_exc() - - if task.handler or cli: #the captcha was handled - self.tasks.append(task) - return True - - task.error = _("No Client connected for captcha decrypting") + self.core.print_exc() - return False + if task.output != Output.Notification: + self.tasks[task.iid] = task if __name__ == "__main__": diff --git a/module/interaction/InteractionTask.py b/module/interaction/InteractionTask.py index 7963a5c72..b372321b0 100644 --- a/module/interaction/InteractionTask.py +++ b/module/interaction/InteractionTask.py @@ -30,12 +30,14 @@ class InteractionTask(BaseInteractionTask): storage = None #: Timestamp when task expires wait_until = 0 - #: The received result as string representation + #: The received result result = None #: List of registered handles handler = None #: Error Message error = None + #: Timeout locked + locked = False def __init__(self, *args, **kwargs): BaseInteractionTask.__init__(self, *args, **kwargs) @@ -46,25 +48,28 @@ class InteractionTask(BaseInteractionTask): self.wait_until = 0 def convertResult(self, value): + #TODO: convert based on input/output return value def getResult(self): return self.result def setResult(self, value): - pass + self.result = self.convertResult(value) - def setWaiting(self, sec): - self.wait_until = max(time() + sec, self.wait_until) + def setWaiting(self, sec, lock=False): + if not self.locked: + self.wait_until = max(time() + sec, self.wait_until) + if lock: self.locked = True - def isWaiting(self, sec): + def isWaiting(self): if self.result or self.error or time() > self.waitUntil: return False return True def timedOut(self): - return time() > self.waitUntil + return time() > self.wait_until > 0 def correct(self): [x.taskCorrect(self) for x in self.handler] |