def start(self, bot, update, user_data): if not update.message.chat.type == 'private': update.message.reply_text( 'This command can only be used in a private conversation') return ConversationHandler.END user_data['stack'] = [] user_data['acc'] = None user_data['user_msg'] = False user = update.message.from_user message = update.message Logger.log_debug("User %s started config conversation." % user.first_name) chats = list(Cache.get_admin_chats(message.from_user.id)) chat_names = [(chat_id, Cache.get_chat_title(chat_id)) for chat_id in chats] global_button = [[ InlineKeyboardButton(text='Global config', callback_data=HandlerGroup.GLOBAL) ]] if str(self.bot.admin_chat) in chats else [] buttons = [[InlineKeyboardButton(text=name, callback_data=str(id))] for (id, name) in chat_names] buttons.append( [InlineKeyboardButton(text='Exit', callback_data=str(self.EXIT))]) reply_markup = InlineKeyboardMarkup(global_button + buttons) message.reply_text('Select a chat to configure', reply_markup=reply_markup) return self.SELECT_CHAT
def remove_end(self, bot, update, user_data): if update.callback_query.data[1] == '3': self.send_or_edit(bot, user_data, update.callback_query.message, 'Maybe next time') return ConversationHandler.END try: idx = int(update.callback_query.data[3:]) chat_id = user_data['chat_id'] if update.callback_query.data[1] == '0': handler_group = self.bot.message_handlers elif update.callback_query.data[1] == '1': handler_group = self.bot.tick_handlers else: handler_group = self.bot.button_handlers name, _ = handler_group.list(chat_id)[idx] handler_group.remove(chat_id, name) Logger.log_info('Removed \'%s\'' % name) self.send_or_edit(bot, user_data, update.callback_query.message, 'Removed \'%s\'' % name) return ConversationHandler.END except: traceback.print_exc()
def update_cache(self): Logger.log_debug('Updating cache') self.message_handlers.update() self.button_handlers.update() self.tick_handlers.update() Cache.store_cache() Config.store_config(self)
def propagate(self, bot, message, target, exclude): res = [] do_copy = len(self.children) > 1 for child in self.children: res2 = child.call(bot, copy.copy(message) if do_copy else message, target, copy.copy(exclude) if do_copy else exclude) if res2 is None: Logger.log_error(msg='Handler %s returned None instead of [..]' % type(child).__name__, chat=message.chat.id) else: res.extend(res2) return res
def set_config_location(cls, loc): Logger.log_trace('Config location set to \'%s\'' % loc) if not os.path.exists(loc): os.makedirs(loc) if os.path.isfile(loc): Logger.log_error( 'The specified config location must be a folder, \'%s\' is a file' % loc) raise Exception() cls.config_location = os.path.join(loc, 'bot_config.json')
def on_receive_message(self, bot, message): """ Global message handler. Forwards the messages to the other handlers if applicable. Always calls the handler that checks if the bot was added to a group. """ try: for handler in self._global_handlers: self.message_handlers._call_handler(handler, bot, message) self.message_handlers.call(bot, message) except: Logger.log_error('Exception while handling message') traceback.print_exc()
def applicator(*args, **kwargs): try: return function(*args, **kwargs) except Exception as ex: has_chat_info = (len(args) > 2 and isinstance(args[2], Update) and args[2].effective_chat) chat_id = args[2].effective_chat.id if has_chat_info else None Logger.log_exception( ex, 'Exception while handling conversation in \'%s\'' % function.__name__, chat=chat_id) traceback.print_exc() return ConversationHandler.END
def on_receive_tick(self, bot, job): try: time = datetime.now() text = time.strftime('%Y-%m-%d %H:%M:%S') messages = [ Message(-1, None, time, Chat(chat_id, 'tick_group %s' % chat_id), text=text) for chat_id in Cache.list_chat_ids() ] self.tick_handlers.call(bot, messages) except: Logger.log_error('Exception while handling tick') traceback.print_exc()
def store_config(cls, bot): Logger.log_debug('Serializing config') message_handlers, global_message_handlers = bot.message_handlers.to_dict( ) button_handlers, global_button_handlers = bot.button_handlers.to_dict() tick_handlers, global_tick_handlers = bot.tick_handlers.to_dict() config = { 'handlers': message_handlers, 'handlers_global': global_message_handlers, 'tick_handlers': tick_handlers, 'tick_handlers_global': global_tick_handlers, 'button_handlers': button_handlers, 'button_handlers_global': global_button_handlers } with open(cls.config_location, 'w') as config_file: Logger.log_debug('Writing config to disk') json.dump(config, config_file)
def on_receive_callback(self, bot, update): try: query = update.callback_query if query.message.reply_to_message: reply_to = query.message.reply_to_message text = '%s_%s' % (query.data, query.message.reply_to_message.text) else: reply_to = None text = query.data message = Message(-1, query.from_user, query.message.date, query.message.chat, text=text, reply_to_message=reply_to) self.button_handlers.call(bot, message) except: Logger.log_error('Exception while handling button event') traceback.print_exc()
def load_config(cls, bot): if os.path.exists(cls.config_location): with open(cls.config_location, 'r') as config_file: Logger.log_debug('Loading config') content = config_file.read() try: config = json.loads(content) except json.JSONDecodeError: config = {} Logger.log_error('Malformed config file') message_handlers = config[ 'handlers'] if 'handlers' in config else {} global_message_handlers = config[ 'handlers_global'] if 'handlers_global' in config else {} button_handlers = config[ 'button_handlers'] if 'button_handlers' in config else {} global_button_handlers = config['button_handlers_global'] if 'button_handlers_global' in config else {} tick_handlers = config[ 'tick_handlers'] if 'tick_handlers' in config else {} global_tick_handlers = config['tick_handlers_global'] if 'tick_handlers_global' in config else {} bot.message_handlers.from_dict(message_handlers, global_message_handlers) bot.button_handlers.from_dict(button_handlers, global_button_handlers) bot.tick_handlers.from_dict(tick_handlers, global_tick_handlers) else: Logger.log_error( 'Config file does not exist (This error can be ignored on the initial run)' ) bot.init_global_handlers()
def start(self): """ Start the bot. """ self.updater.start_polling() # Set up the tick trigger self.dispatcher.job_queue.run_repeating( self.on_receive_tick, timedelta(minutes=1), first=timedelta(seconds=60 - datetime.now().second)) self.dispatcher.job_queue.run_repeating( lambda b, j, self=self: self.update_cache(), timedelta(days=1), first=timedelta(hours=24 - datetime.now().hour)) Logger.log_info('%s started' % self.bot.first_name) Cache.store_cache() Config.store_config(self) self.updater.idle()
def _handle_stack(self, bot, msg, user_data): stack = user_data['stack'] if not stack: handler = user_data['acc'] chat_id = user_data['chat_id'] name = user_data['name'] type = user_data['type'] if type == 'tick': self.bot.tick_handlers.register(chat=chat_id, handler=handler, name=name) Logger.log_info('Added tick handler \'%s\'' % name) elif type == 'msg': self.bot.message_handlers.register(chat=chat_id, name=name, handler=handler) Logger.log_info('Added message handler \'%s\'' % name) elif type == 'button': self.bot.button_handlers.register(chat=chat_id, name=name, handler=handler) Logger.log_info('Added button handler \'%s\'' % name) else: message = 'Unknown handler type %s' % type Logger.log_error(message) self.send_or_edit(bot, user_data, msg, message) return ConversationHandler.END self.send_or_edit(bot, user_data, msg, 'Hander added!') return ConversationHandler.END (stage, data, idx) = stack[-1] try: current = self.HANDLERS[idx] (stage, data, res) = current.create(stage, data, user_data['acc']) stack[-1] = (stage, data, idx) user_data['acc'] = None if isinstance(res, Send): self.send_or_edit(bot, user_data, msg, res.msg, res.buttons) return self.ADD_HANDLER_STEP elif isinstance(res, Done): user_data['acc'] = res.handler user_data['stack'] = user_data['stack'][:-1] return self._handle_stack(bot, msg, user_data) elif isinstance(res, AskChild): name = current.get_name() message = 'Do you want to add a child handler for \'%s\'' % name buttons = [[ InlineKeyboardButton(text='yes', callback_data='-2'), InlineKeyboardButton(text='no', callback_data='-1') ]] if msg.text != message: self.send_or_edit(bot, user_data, msg, message, buttons) else: print('Not sending duplicate message') return self.ADD_HANDLER_CHILD elif isinstance(res, AskCacheKey): user_data['acc'] = res.default buttons = [[ InlineKeyboardButton(text='Create own key', callback_data=str(self.ADD)) ], [ InlineKeyboardButton(text='Use existing key', callback_data=str( self.COPY)) ]] self.send_or_edit(bot, user_data, msg, 'Select a cache key, or create a new one', buttons) return self.ADD_HANDLER_CACHE_KEY elif isinstance(res, AskAPIKey): stack = user_data['stack'] stack.append((0, None, stack[-1][2])) buttons = [[ InlineKeyboardButton(text='Create own API key', callback_data=str(self.ADD)) ], [ InlineKeyboardButton( text='Use existing API key', callback_data=str(self.COPY)) ]] self.send_or_edit(bot, user_data, msg, 'Select an API key, or create a new one', buttons) return self.ADD_HANDLER_API_KEY else: raise Exception('Unknown response: %s' % res) except CreateException as ex: traceback.print_exc() self.send_or_edit( bot, user_data, msg, 'Implementation missing! Please report your steps to the developer' ) return ConversationHandler.END except Exception as ex: traceback.print_exc() self.send_or_edit( bot, user_data, msg, 'Error while processing handler create event! Please report your steps to the developer' ) return ConversationHandler.END
def call(self, bot, message, target, exclude): Logger.log_info('Added %s as bot admin' % message.from_user.first_name) Cache.add_chat_admin(message.chat.id, message.from_user.id) return []
def init_logger(self): config = {} if self.admin_chat: config[str(self.admin_chat)] = 2 Logger.init(self.bot, config)
def on_exit(self): Logger.log_info(msg='Shutting down') Cache.store_cache() Config.store_config(self)