diff options
Diffstat (limited to 'pyload/interaction/InteractionManager.py')
-rw-r--r-- | pyload/interaction/InteractionManager.py | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/pyload/interaction/InteractionManager.py b/pyload/interaction/InteractionManager.py new file mode 100644 index 000000000..36d457323 --- /dev/null +++ b/pyload/interaction/InteractionManager.py @@ -0,0 +1,166 @@ +# -*- coding: utf-8 -*- +""" + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + + @author: RaNaN +""" +from threading import Lock +from time import time +from base64 import standard_b64encode + +from new_collections import OrderedDict + +from pyload.utils import lock, bits_set +from pyload.Api import Interaction as IA +from pyload.Api import InputType, Input + +from InteractionTask import InteractionTask + +class InteractionManager: + """ + Class that gives ability to interact with the user. + Arbitrary tasks with predefined output and input types can be set off. + """ + + # number of seconds a client is classified as active + CLIENT_THRESHOLD = 60 + NOTIFICATION_TIMEOUT = 60 * 60 * 30 + MAX_NOTIFICATIONS = 50 + + def __init__(self, core): + self.lock = Lock() + self.core = core + self.tasks = OrderedDict() #task store, for all outgoing tasks + + self.last_clients = {} + self.ids = 0 #uniue interaction ids + + def isClientConnected(self, user): + return self.last_clients.get(user, 0) + self.CLIENT_THRESHOLD > time() + + @lock + def work(self): + # old notifications will be removed + for n in [k for k, v in self.tasks.iteritems() if v.timedOut()]: + del self.tasks[n] + + # keep notifications count limited + n = [k for k,v in self.tasks.iteritems() if v.type == IA.Notification] + n.reverse() + for v in n[:self.MAX_NOTIFICATIONS]: + del self.tasks[v] + + @lock + def createNotification(self, title, content, desc="", plugin="", owner=None): + """ 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, IA.Notification, Input(InputType.Text, None, content), title, desc, plugin, + owner=owner) + self.ids += 1 + self.queueTask(task) + return task + + @lock + def createQueryTask(self, input, desc, plugin="", owner=None): + # input type was given, create a input widget + if type(input) == int: + input = Input(input) + if not isinstance(input, Input): + raise TypeError("'Input' class expected not '%s'" % type(input)) + + task = InteractionTask(self.ids, IA.Query, input, _("Query"), desc, plugin, owner=owner) + self.ids += 1 + self.queueTask(task) + return task + + @lock + def createCaptchaTask(self, img, format, filename, plugin="", type=InputType.Text, owner=None): + """ Createss a new captcha task. + + :param img: image content (not base encoded) + :param format: img format + :param type: :class:`InputType` + :return: + """ + if type == 'textual': + type = InputType.Text + elif type == 'positional': + type = InputType.Click + + input = Input(type, data=[standard_b64encode(img), format, filename]) + + #todo: title desc plugin + task = InteractionTask(self.ids, IA.Captcha, input, + _("Captcha request"), _("Please solve the captcha."), plugin, owner=owner) + + self.ids += 1 + self.queueTask(task) + return task + + @lock + def removeTask(self, task): + if task.iid in self.tasks: + del self.tasks[task.iid] + self.core.evm.dispatchEvent("interaction:deleted", task.iid) + + @lock + def getTaskByID(self, iid): + return self.tasks.get(iid, None) + + @lock + def getTasks(self, user, mode=IA.All): + # update last active clients + self.last_clients[user] = time() + + # filter current mode + tasks = [t for t in self.tasks.itervalues() if mode == IA.All or bits_set(t.type, mode)] + # filter correct user / or shared + tasks = [t for t in tasks if user is None or user == t.owner or t.shared] + + return tasks + + def isTaskWaiting(self, user, mode=IA.All): + tasks = [t for t in self.getTasks(user, mode) if not t.type == IA.Notification or not t.seen] + return len(tasks) > 0 + + def queueTask(self, task): + cli = self.isClientConnected(task.owner) + + # set waiting times based on threshold + if cli: + task.setWaiting(self.CLIENT_THRESHOLD) + else: # TODO: higher threshold after client connects? + task.setWaiting(self.CLIENT_THRESHOLD / 3) + + if task.type == IA.Notification: + task.setWaiting(self.NOTIFICATION_TIMEOUT) # notifications are valid for 30h + + for plugin in self.core.addonManager.activePlugins(): + try: + plugin.newInteractionTask(task) + except: + self.core.print_exc() + + self.tasks[task.iid] = task + self.core.evm.dispatchEvent("interaction:added", task) + + +if __name__ == "__main__": + it = InteractionTask()
\ No newline at end of file |