| 
				
			 | 
			
			
				@@ -0,0 +1,172 @@ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				1
			 | 
			
			
				+# encoding: UTF-8 
			 | 
		
	
		
			
			| 
				
			 | 
			
				2
			 | 
			
			
				+### 
			 | 
		
	
		
			
			| 
				
			 | 
			
				3
			 | 
			
			
				+# Copyright (c) 2016, Toni Fadjukoff 
			 | 
		
	
		
			
			| 
				
			 | 
			
				4
			 | 
			
			
				+# All rights reserved. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				5
			 | 
			
			
				+# 
			 | 
		
	
		
			
			| 
				
			 | 
			
				6
			 | 
			
			
				+# Redistribution and use in source and binary forms, with or without 
			 | 
		
	
		
			
			| 
				
			 | 
			
				7
			 | 
			
			
				+# modification, are permitted provided that the following conditions are met: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				8
			 | 
			
			
				+# 
			 | 
		
	
		
			
			| 
				
			 | 
			
				9
			 | 
			
			
				+#   * Redistributions of source code must retain the above copyright notice, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				10
			 | 
			
			
				+#     this list of conditions, and the following disclaimer. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				11
			 | 
			
			
				+#   * Redistributions in binary form must reproduce the above copyright notice, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				12
			 | 
			
			
				+#     this list of conditions, and the following disclaimer in the 
			 | 
		
	
		
			
			| 
				
			 | 
			
				13
			 | 
			
			
				+#     documentation and/or other materials provided with the distribution. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				14
			 | 
			
			
				+#   * Neither the name of the author of this software nor the name of 
			 | 
		
	
		
			
			| 
				
			 | 
			
				15
			 | 
			
			
				+#     contributors to this software may be used to endorse or promote products 
			 | 
		
	
		
			
			| 
				
			 | 
			
				16
			 | 
			
			
				+#     derived from this software without specific prior written consent. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				17
			 | 
			
			
				+# 
			 | 
		
	
		
			
			| 
				
			 | 
			
				18
			 | 
			
			
				+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
			 | 
		
	
		
			
			| 
				
			 | 
			
				19
			 | 
			
			
				+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
			 | 
		
	
		
			
			| 
				
			 | 
			
				20
			 | 
			
			
				+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
			 | 
		
	
		
			
			| 
				
			 | 
			
				21
			 | 
			
			
				+# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
			 | 
		
	
		
			
			| 
				
			 | 
			
				22
			 | 
			
			
				+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
			 | 
		
	
		
			
			| 
				
			 | 
			
				23
			 | 
			
			
				+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
			 | 
		
	
		
			
			| 
				
			 | 
			
				24
			 | 
			
			
				+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
			 | 
		
	
		
			
			| 
				
			 | 
			
				25
			 | 
			
			
				+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
			 | 
		
	
		
			
			| 
				
			 | 
			
				26
			 | 
			
			
				+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				27
			 | 
			
			
				+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
			 | 
		
	
		
			
			| 
				
			 | 
			
				28
			 | 
			
			
				+# POSSIBILITY OF SUCH DAMAGE. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				29
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				30
			 | 
			
			
				+### 
			 | 
		
	
		
			
			| 
				
			 | 
			
				31
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				32
			 | 
			
			
				+import time 
			 | 
		
	
		
			
			| 
				
			 | 
			
				33
			 | 
			
			
				+from dateutil.rrule import * 
			 | 
		
	
		
			
			| 
				
			 | 
			
				34
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				35
			 | 
			
			
				+import supybot.utils as utils 
			 | 
		
	
		
			
			| 
				
			 | 
			
				36
			 | 
			
			
				+from supybot.commands import * 
			 | 
		
	
		
			
			| 
				
			 | 
			
				37
			 | 
			
			
				+import supybot.plugins as plugins 
			 | 
		
	
		
			
			| 
				
			 | 
			
				38
			 | 
			
			
				+import supybot.ircutils as ircutils 
			 | 
		
	
		
			
			| 
				
			 | 
			
				39
			 | 
			
			
				+import supybot.schedule as schedule 
			 | 
		
	
		
			
			| 
				
			 | 
			
				40
			 | 
			
			
				+import supybot.callbacks as callbacks 
			 | 
		
	
		
			
			| 
				
			 | 
			
				41
			 | 
			
			
				+try: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				42
			 | 
			
			
				+    from supybot.i18n import PluginInternationalization 
			 | 
		
	
		
			
			| 
				
			 | 
			
				43
			 | 
			
			
				+    _ = PluginInternationalization('Timer') 
			 | 
		
	
		
			
			| 
				
			 | 
			
				44
			 | 
			
			
				+except ImportError: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				45
			 | 
			
			
				+    # Placeholder that allows to run the plugin on a bot 
			 | 
		
	
		
			
			| 
				
			 | 
			
				46
			 | 
			
			
				+    # without the i18n module 
			 | 
		
	
		
			
			| 
				
			 | 
			
				47
			 | 
			
			
				+    _ = lambda x: x 
			 | 
		
	
		
			
			| 
				
			 | 
			
				48
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				49
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				50
			 | 
			
			
				+class Timer(callbacks.Plugin): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				51
			 | 
			
			
				+    """Command based scheduling for triggers""" 
			 | 
		
	
		
			
			| 
				
			 | 
			
				52
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				53
			 | 
			
			
				+    def __init__(self, irc): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				54
			 | 
			
			
				+        self.__parent = super(Timer, self) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				55
			 | 
			
			
				+        self.__parent.__init__(irc) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				56
			 | 
			
			
				+        self.events = {} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				57
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				58
			 | 
			
			
				+    def _makeCommandFunction(self, irc, msg, message): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				59
			 | 
			
			
				+        nickprefix = msg.prefix.split("!")[0] + ": " if ircutils.isChannel(msg.args[0]) else "" 
			 | 
		
	
		
			
			| 
				
			 | 
			
				60
			 | 
			
			
				+        message = format("echo %s%s", nickprefix, message) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				61
			 | 
			
			
				+        tokens = callbacks.tokenize(message) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				62
			 | 
			
			
				+        def f(): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				63
			 | 
			
			
				+            del self.events[str(f.eventId)] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				64
			 | 
			
			
				+            self.Proxy(irc.irc, msg, tokens) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				65
			 | 
			
			
				+        return f 
			 | 
		
	
		
			
			| 
				
			 | 
			
				66
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				67
			 | 
			
			
				+    @staticmethod 
			 | 
		
	
		
			
			| 
				
			 | 
			
				68
			 | 
			
			
				+    def _parseDate(arg): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				69
			 | 
			
			
				+        day = 0 
			 | 
		
	
		
			
			| 
				
			 | 
			
				70
			 | 
			
			
				+        month = 0 
			 | 
		
	
		
			
			| 
				
			 | 
			
				71
			 | 
			
			
				+        if len(arg) == 6 and arg[2] == arg[5] == "." and arg[0:2].isdigit() and arg[3:5].isdigit(): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				72
			 | 
			
			
				+                day = int(arg[0:2]) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				73
			 | 
			
			
				+                month = int(arg[3:5]) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				74
			 | 
			
			
				+        elif len(arg) == 5 and arg[2] == arg[4] == "." and arg[0:2].isdigit() and arg[3:4].isdigit(): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				75
			 | 
			
			
				+                day = int(arg[0:2]) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				76
			 | 
			
			
				+                month = int(arg[3:4]) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				77
			 | 
			
			
				+        elif len(arg) == 5 and arg[1] == arg[4] == "." and arg[0:1].isdigit() and arg[2:4].isdigit(): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				78
			 | 
			
			
				+                day = int(arg[0:1]) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				79
			 | 
			
			
				+                month = int(arg[2:4]) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				80
			 | 
			
			
				+        elif len(arg) == 4 and arg[1] == arg[3] == "." and arg[0:1].isdigit() and arg[2:3].isdigit(): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				81
			 | 
			
			
				+                day = int(arg[0:1]) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				82
			 | 
			
			
				+                month = int(arg[2:3]) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				83
			 | 
			
			
				+        if 0 < day < 32 and 0 < month < 13: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				84
			 | 
			
			
				+                return month, day 
			 | 
		
	
		
			
			| 
				
			 | 
			
				85
			 | 
			
			
				+        return None 
			 | 
		
	
		
			
			| 
				
			 | 
			
				86
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				87
			 | 
			
			
				+    @staticmethod 
			 | 
		
	
		
			
			| 
				
			 | 
			
				88
			 | 
			
			
				+    def _parseTime(arg): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				89
			 | 
			
			
				+        if ":" in arg: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				90
			 | 
			
			
				+            hours, minutes = arg.split(":") 
			 | 
		
	
		
			
			| 
				
			 | 
			
				91
			 | 
			
			
				+        elif "." in arg: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				92
			 | 
			
			
				+            hours, minutes = arg.split(".") 
			 | 
		
	
		
			
			| 
				
			 | 
			
				93
			 | 
			
			
				+        elif len(arg) == 4: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				94
			 | 
			
			
				+            hours = arg[0:2] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				95
			 | 
			
			
				+            minutes = arg[2:4] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				96
			 | 
			
			
				+        elif len(arg) == 3: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				97
			 | 
			
			
				+            hours = arg[0] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				98
			 | 
			
			
				+            minutes = arg[1:3] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				99
			 | 
			
			
				+        elif len(arg) in (1,2): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				100
			 | 
			
			
				+            hours = arg 
			 | 
		
	
		
			
			| 
				
			 | 
			
				101
			 | 
			
			
				+            minutes = "0" 
			 | 
		
	
		
			
			| 
				
			 | 
			
				102
			 | 
			
			
				+        else: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				103
			 | 
			
			
				+            hours = None 
			 | 
		
	
		
			
			| 
				
			 | 
			
				104
			 | 
			
			
				+            minutes = None 
			 | 
		
	
		
			
			| 
				
			 | 
			
				105
			 | 
			
			
				+        if hours == None or len(hours) not in (1,2) or len(minutes) not in (1,2): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				106
			 | 
			
			
				+            return 
			 | 
		
	
		
			
			| 
				
			 | 
			
				107
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				108
			 | 
			
			
				+        try: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				109
			 | 
			
			
				+            hours = int(hours) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				110
			 | 
			
			
				+        except ValueError as e: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				111
			 | 
			
			
				+            return 
			 | 
		
	
		
			
			| 
				
			 | 
			
				112
			 | 
			
			
				+        try: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				113
			 | 
			
			
				+            minutes = int(minutes) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				114
			 | 
			
			
				+        except ValueError as e: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				115
			 | 
			
			
				+            return 
			 | 
		
	
		
			
			| 
				
			 | 
			
				116
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				117
			 | 
			
			
				+        if minutes >= 60: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				118
			 | 
			
			
				+            hours += int(minutes / 60) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				119
			 | 
			
			
				+            minutes = minutes % 60 
			 | 
		
	
		
			
			| 
				
			 | 
			
				120
			 | 
			
			
				+        if hours >= 24: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				121
			 | 
			
			
				+            hours = hours % 24 
			 | 
		
	
		
			
			| 
				
			 | 
			
				122
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				123
			 | 
			
			
				+        return hours, minutes  
			 | 
		
	
		
			
			| 
				
			 | 
			
				124
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				125
			 | 
			
			
				+    def _resolveTime(self, irc, dateArg, timeArg): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				126
			 | 
			
			
				+        month = mday = hours = minutes = None 
			 | 
		
	
		
			
			| 
				
			 | 
			
				127
			 | 
			
			
				+        if dateArg != None: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				128
			 | 
			
			
				+            (month, mday) = self._parseDate(dateArg) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				129
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				130
			 | 
			
			
				+        (hours, minutes) = self._parseTime(timeArg) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				131
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				132
			 | 
			
			
				+        return list(rrule(MINUTELY, count = 1, bymonth = month, bymonthday = mday, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				133
			 | 
			
			
				+                byhour = hours, byminute = minutes))[0] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				134
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				135
			 | 
			
			
				+    def _testDate(date): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				136
			 | 
			
			
				+        return Timer._parseDate(date) != None 
			 | 
		
	
		
			
			| 
				
			 | 
			
				137
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				138
			 | 
			
			
				+    def _testTime(time): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				139
			 | 
			
			
				+        return Timer._parseTime(time) != None 
			 | 
		
	
		
			
			| 
				
			 | 
			
				140
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				141
			 | 
			
			
				+    def _scheduleOnce(self, irc, msg, message, scheduleTime): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				142
			 | 
			
			
				+        #irc.reply(format("Scheduled at %s", scheduleTime)) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				143
			 | 
			
			
				+        f = self._makeCommandFunction(irc, msg, message) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				144
			 | 
			
			
				+        id = schedule.addEvent(f, scheduleTime) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				145
			 | 
			
			
				+        f.eventId = id 
			 | 
		
	
		
			
			| 
				
			 | 
			
				146
			 | 
			
			
				+        self.events[str(id)] = f 
			 | 
		
	
		
			
			| 
				
			 | 
			
				147
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				148
			 | 
			
			
				+    def timer(self, irc, msg, args, dateArg, timeArg, message): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				149
			 | 
			
			
				+        """[date] <time> <message> 
			 | 
		
	
		
			
			| 
				
			 | 
			
				150
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				151
			 | 
			
			
				+        Set a message to be played back later. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				152
			 | 
			
			
				+        """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				153
			 | 
			
			
				+        scheduleTime = self._resolveTime(irc, dateArg, timeArg).timestamp() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				154
			 | 
			
			
				+        self._scheduleOnce(irc, msg, message, scheduleTime) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				155
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				156
			 | 
			
			
				+    timer = wrap(timer, [optional(('something', None, _testDate)), ('something', None, _testTime), 'text']) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				157
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				158
			 | 
			
			
				+    def food(self, irc, msg, args, minutes, message): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				159
			 | 
			
			
				+        """[minuutit] [viesti] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				160
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				161
			 | 
			
			
				+        Huomauta viestillä muutaman minuutin päästä. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				162
			 | 
			
			
				+        Pikakomennot: pizza 12 minuuttia, makaronilaatikko 60 minuuttia, ranskalaiset 30 minuuttia 
			 | 
		
	
		
			
			| 
				
			 | 
			
				163
			 | 
			
			
				+        """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				164
			 | 
			
			
				+        irc.reply(format('Meikä kiekaisee sitten %i minuutin päästä %s', minutes, message)) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				165
			 | 
			
			
				+        scheduleTime = time.time() + minutes * 60 
			 | 
		
	
		
			
			| 
				
			 | 
			
				166
			 | 
			
			
				+        self._scheduleOnce(irc, msg, message, scheduleTime) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				167
			 | 
			
			
				+    food = wrap(food, ['positiveInt', 'text']) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				168
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				169
			 | 
			
			
				+Class = Timer 
			 | 
		
	
		
			
			| 
				
			 | 
			
				170
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				171
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				172
			 | 
			
			
				+# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: 
			 |