| 1 |  |  | #!/usr/bin/env python | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  | # -*- coding: utf-8 -*- | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | import importlib | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  | import inspect | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | import os | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  | import sys | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | from time import sleep | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  | import click | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  | import telegram | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | import config | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | from database import DB | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  | from log import logger | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  | from commands import BotCommand | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |  | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 18 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 19 |  |  | class VereseBot(telegram.Bot): | 
            
                                                                        
                            
            
                                    
            
            
                | 20 |  |  |     def __init__(self): | 
            
                                                                        
                            
            
                                    
            
            
                | 21 |  |  |         self.commands = [] | 
            
                                                                        
                            
            
                                    
            
            
                | 22 |  |  |         self._stay_awake = 30 | 
            
                                                                        
                            
            
                                    
            
            
                | 23 |  |  |         self.db = DB() | 
            
                                                                        
                            
            
                                    
            
            
                | 24 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 25 |  |  |         self.import_commands() | 
            
                                                                        
                            
            
                                    
            
            
                | 26 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 27 |  |  |         # Connect to Telegram | 
            
                                                                        
                            
            
                                    
            
            
                | 28 |  |  |         super(VereseBot, self).__init__(token=config.token) | 
            
                                                                        
                            
            
                                    
            
            
                | 29 |  |  |         self.queue = {} | 
            
                                                                        
                            
            
                                    
            
            
                | 30 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 31 |  |  |     def import_commands(self): | 
            
                                                                        
                            
            
                                    
            
            
                | 32 |  |  |         for command_file in sorted(os.listdir('commands')): | 
            
                                                                        
                            
            
                                    
            
            
                | 33 |  |  |             if command_file.endswith('.pyc') or command_file.startswith('__'): | 
            
                                                                        
                            
            
                                    
            
            
                | 34 |  |  |                 continue | 
            
                                                                        
                            
            
                                    
            
            
                | 35 |  |  |             mod = importlib.import_module('commands.{}'.format(command_file[:-3])) | 
            
                                                                        
                            
            
                                    
            
            
                | 36 |  |  |             for member_name, member_type in inspect.getmembers(mod): | 
            
                                                                        
                            
            
                                    
            
            
                | 37 |  |  |                 if inspect.isclass(member_type) and issubclass(member_type, BotCommand): | 
            
                                                                        
                            
            
                                    
            
            
                | 38 |  |  |                     self.commands.append(member_type) | 
            
                                                                        
                            
            
                                    
            
            
                | 39 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 40 |  |  |     def say(self, reply_to_message, text, reply_markup=None, markdown=False): | 
            
                                                                        
                            
            
                                    
            
            
                | 41 |  |  |         # The telegram library doesn't play well with unicode, oh well. | 
            
                                                                        
                            
            
                                    
            
            
                | 42 |  |  |         text = text.encode('utf-8') if isinstance(text, unicode) else text | 
            
                                                                        
                            
            
                                    
            
            
                | 43 |  |  |         reply_to_message_id = reply_to_message.message_id if reply_markup else None | 
            
                                                                        
                            
            
                                    
            
            
                | 44 |  |  |         parse_mode = telegram.ParseMode.MARKDOWN if markdown else None | 
            
                                                                        
                            
            
                                    
            
            
                | 45 |  |  |         return self.sendMessage(chat_id=reply_to_message.chat.id, | 
            
                                                                        
                            
            
                                    
            
            
                | 46 |  |  |                                 text=text, | 
            
                                                                        
                            
            
                                    
            
            
                | 47 |  |  |                                 reply_to_message_id=reply_to_message_id, | 
            
                                                                        
                            
            
                                    
            
            
                | 48 |  |  |                                 reply_markup=reply_markup, | 
            
                                                                        
                            
            
                                    
            
            
                | 49 |  |  |                                 parse_mode=parse_mode) | 
            
                                                                        
                            
            
                                    
            
            
                | 50 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 51 |  |  |     def get_updates(self): | 
            
                                                                        
                            
            
                                    
            
            
                | 52 |  |  |         logger.debug('') | 
            
                                                                        
                            
            
                                    
            
            
                | 53 |  |  |         last_update = self.db.root.last_update | 
            
                                                                        
                            
            
                                    
            
            
                | 54 |  |  |         updates = self.getUpdates(offset=last_update) | 
            
                                                                        
                            
            
                                    
            
            
                | 55 |  |  |         return updates | 
            
                                                                        
                            
            
                                    
            
            
                | 56 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 57 |  |  |     def poll(self): | 
            
                                                                        
                            
            
                                    
            
            
                | 58 |  |  |         updates = self.get_updates() | 
            
                                                                        
                            
            
                                    
            
            
                | 59 |  |  |         self.process_updates(updates) | 
            
                                                                        
                            
            
                                    
            
            
                | 60 |  |  |         if updates: | 
            
                                                                        
                            
            
                                    
            
            
                | 61 |  |  |             self._stay_awake += 30 | 
            
                                                                        
                            
            
                                    
            
            
                | 62 |  |  |         else: | 
            
                                                                        
                            
            
                                    
            
            
                | 63 |  |  |             if not self._stay_awake: | 
            
                                                                        
                            
            
                                    
            
            
                | 64 |  |  |                 logger.debug('Entering sleep mode') | 
            
                                                                        
                            
            
                                    
            
            
                | 65 |  |  |                 return 4 | 
            
                                                                        
                            
            
                                    
            
            
                | 66 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 67 |  |  |         self._stay_awake = self._stay_awake - 1 if self._stay_awake > 0 else 0 | 
            
                                                                        
                            
            
                                    
            
            
                | 68 |  |  |         return 1 | 
            
                                                                        
                            
            
                                    
            
            
                | 69 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 70 |  |  |     def process_updates(self, updates): | 
            
                                                                        
                            
            
                                    
            
            
                | 71 |  |  |         last_update = self.db.root.last_update | 
            
                                                                        
                            
            
                                    
            
            
                | 72 |  |  |         for update in filter(lambda x: x.update_id > last_update, updates): | 
            
                                                                        
                            
            
                                    
            
            
                | 73 |  |  |             self.process_message(update.message) | 
            
                                                                        
                            
            
                                    
            
            
                | 74 |  |  |             self.db.root.last_update = update.update_id | 
            
                                                                        
                            
            
                                    
            
            
                | 75 |  |  |             self.db.commit() | 
            
                                                                        
                            
            
                                    
            
            
                | 76 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 77 |  |  |     def process_message(self, message): | 
            
                                                                        
                            
            
                                    
            
            
                | 78 |  |  |         # Update stats | 
            
                                                                        
                            
            
                                    
            
            
                | 79 |  |  |         self.db.root.stats['number_of_messages'] += 1 | 
            
                                                                        
                            
            
                                    
            
            
                | 80 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 81 |  |  |         # Register user | 
            
                                                                        
                            
            
                                    
            
            
                | 82 |  |  |         user = self.db.get_or_create_user(message.from_user.id)[0] | 
            
                                                                        
                            
            
                                    
            
            
                | 83 |  |  |         user.update(message.from_user.first_name, | 
            
                                                                        
                            
            
                                    
            
            
                | 84 |  |  |                     message.from_user.last_name, | 
            
                                                                        
                            
            
                                    
            
            
                | 85 |  |  |                     message.from_user.username) | 
            
                                                                        
                            
            
                                    
            
            
                | 86 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 87 |  |  |         # Register tab | 
            
                                                                        
                            
            
                                    
            
            
                | 88 |  |  |         tab, created = self.db.get_or_create_tab(message.chat.id) | 
            
                                                                        
                            
            
                                    
            
            
                | 89 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 90 |  |  |         # Register user in tab | 
            
                                                                        
                            
            
                                    
            
            
                | 91 |  |  |         tab.register_user(user.id) | 
            
                                                                        
                            
            
                                    
            
            
                | 92 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 93 |  |  |         reply_to_id = False | 
            
                                                                        
                            
            
                                    
            
            
                | 94 |  |  |         if isinstance(message.chat, telegram.user.User): | 
            
                                                                        
                            
            
                                    
            
            
                | 95 |  |  |             reply_to_id = message.message_id | 
            
                                                                        
                            
            
                                    
            
            
                | 96 |  |  |         elif message.reply_to_message: | 
            
                                                                        
                            
            
                                    
            
            
                | 97 |  |  |             reply_to_id = message.reply_to_message.message_id | 
            
                                                                        
                            
            
                                    
            
            
                | 98 |  |  |         if reply_to_id: | 
            
                                                                        
                            
            
                                    
            
            
                | 99 |  |  |             key = '{}_{}'.format(message.chat.id, reply_to_id) | 
            
                                                                        
                            
            
                                    
            
            
                | 100 |  |  |             if key in self.queue: | 
            
                                                                        
                            
            
                                    
            
            
                | 101 |  |  |                 k = self.queue.pop(key) | 
            
                                                                        
                            
            
                                    
            
            
                | 102 |  |  |                 logger.debug('Calling queued {}'.format(k)) | 
            
                                                                        
                            
            
                                    
            
            
                | 103 |  |  |                 k(message) | 
            
                                                                        
                            
            
                                    
            
            
                | 104 |  |  |                 return | 
            
                                                                        
                            
            
                                    
            
            
                | 105 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 106 |  |  |         for cmd in self.commands: | 
            
                                                                        
                            
            
                                    
            
            
                | 107 |  |  |             if cmd.match(message): | 
            
                                                                        
                            
            
                                    
            
            
                | 108 |  |  |                 logger.debug('Calling process_{}'.format(cmd)) | 
            
                                                                        
                            
            
                                    
            
            
                | 109 |  |  |                 cmd(bot=self)(message) | 
            
                                                                        
                            
            
                                    
            
            
                | 110 |  |  |                 break | 
            
                                                                        
                            
            
                                    
            
            
                | 111 |  |  |         else: | 
            
                                                                        
                            
            
                                    
            
            
                | 112 |  |  |             self.say(message, 'Are you drunk matey?') | 
            
                                                                        
                            
            
                                    
            
            
                | 113 |  |  |             logger.debug('No command found: {}'.format(message.message_id)) | 
            
                                                                        
                            
            
                                    
            
            
                | 114 |  |  |             return | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  | @click.command() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 |  |  | @click.option('--webserver/--polling', default=True) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  | def main(webserver): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 |  |  |     """Verese Telegram Bot.""" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 |  |  |     bot = VereseBot() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 |  |  |     if webserver: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 |  |  |         import json | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  |         from urlparse import urlparse | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 |  |  |         from bottle import request, route, run | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 |  |  |         if not config.webhook: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 130 |  |  |             print 'Set webhook' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 131 |  |  |             sys.exit(-1) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 132 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 133 |  |  |         bot.setWebhook(config.webhook) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 134 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 135 |  |  |         @route(urlparse(config.webhook).path or '/', method='POST') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 136 |  |  |         def handle_message(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 137 |  |  |             data = json.load(request.body) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 138 |  |  |             update = telegram.Update.de_json(data) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 139 |  |  |             bot.process_updates([update]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 140 |  |  |             return 'OK' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 141 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 142 |  |  |         @route('/', method='GET') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 143 |  |  |         def home(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 144 |  |  |             return 'Hi :)' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 145 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 146 |  |  |         try: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 147 |  |  |             run(host='0.0.0.0', port=config.port, reloader=False) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 148 |  |  |         finally: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 149 |  |  |             bot.db.close() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 |  |  |     else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 152 |  |  |         bot.setWebhook('') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 153 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 154 |  |  |         try: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 155 |  |  |             while True: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 156 |  |  |                 timeout = bot.poll() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 157 |  |  |                 sleep(timeout) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 158 |  |  |         finally: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 159 |  |  |             bot.db.close() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 160 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 161 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 162 |  |  | if __name__ == "__main__": | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 163 |  |  |     main() | 
            
                                                        
            
                                    
            
            
                | 164 |  |  |  |