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 add_handler_select_cache_key_callback(self, bot, update, user_data): stack = user_data['stack'] current_idx = stack[-1][2] user_id = update.callback_query.from_user.id # Get the chats where the user is admin. # If it includes the admin chat, we also add the GLOBAL key chats_where_admin = list(Cache.get_admin_chats(user_id)) if (str(self.bot.admin_chat) in chats_where_admin): chats_where_admin.append('GLOBAL') keys = [ key for chat_id in chats_where_admin for key in Cache.get_chat_keys(chat_id) ] if len(keys) == 0: message = 'You do not have access to any existing keys, please send me a valid key' buttons = None else: message = 'Select a key' buttons = [[ InlineKeyboardButton(text=key, callback_data='0_' + str(key)) ] for key in keys] self.send_or_edit(bot, user_data, update.callback_query.message, message, buttons) return self.ADD_HANDLER_CACHE_KEY
def toggle_standalone(self, bot, update, user_data): chat_id = user_data['chat_id'] new_state = not Cache.chat_is_standalone(chat_id) Cache.chat_set_standalone(chat_id, new_state) self.send_or_edit( bot, user_data, update.callback_query.message, '%s global handlers' % ('Disabled' if new_state else 'Enabled')) return ConversationHandler.END
def __init__(self, messages, show_preview, buttons, parse_mode): super(AbstractSendMessage, self).__init__(RandomMessageHandler.get_random_id(), []) self.add_options(messages) self.parse_mode = parse_mode self.show_preview = show_preview self.buttons = buttons Cache.config_entry(self._id, False)
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 call(self, bot, message, target, exclude): time = datetime.now().timestamp() if self.log_chat: chat_id = str(message.chat.id) Cache.put([self.cache_key, 'chat', chat_id], time) if self.log_user: user_id = str(message.from_user.id) Cache.put([self.cache_key, 'user', user_id], time) return []
def call(self, bot, message, target, exclude): if message.migrate_from_chat_id: old_id = str(message.migrate_from_chat_id) new_id = str(message.chat.id) Cache.migrate(old_id, new_id) self._bot.message_handlers.migrate(old_id, new_id) self._bot.button_handlers.migrate(old_id, new_id) self._bot.tick_handlers.migrate(old_id, new_id) return [] else: raise FilterException()
def call(self, bot, msg, target, exclude): time = datetime.now() cached = Cache.get([self.cache_key, self.type, self.id]) if cached: last_activity = datetime.fromtimestamp(cached) else: last_activity = datetime.fromtimestamp(0) if (last_activity + self.timedelta) <= time: Cache.put([self.cache_key, self.type, self.id], time.timestamp()) return self.propagate(bot, msg, target, exclude) else: raise FilterException()
def on_receive(self, bot, update): if update.message: message = update.message elif update.edited_message: message = update.edited_message else: return chat = message.chat if chat.title: # If not private chat Cache.set_chat_title(chat.id, chat.title) if message.caption: message.text = message.caption self.on_receive_message(bot, message)
def call(self, bot, message, target, exclude): chat_id = message.chat.id user_id = message.from_user.id if Cache.is_chat_admin(chat_id, user_id): return self.propagate(bot, message, target, exclude) else: raise FilterException()
def copy_start(self, bot, update, user_data): msg = update.callback_query.message chats = Cache.get_admin_chats(update._effective_user.id) chat_names = [(chat_id, Cache.get_chat_title(chat_id)) for chat_id in chats] if chat_names: 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] message = 'Select a chat to copy a handler from' self.send_or_edit(bot, user_data, msg, message, global_button + buttons) return self.COPY_HANDLER else: self.send_or_edit(bot, user_data, msg, 'No chats to copy from') return ConversationHandler.END
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 edit_chat(self, bot, update, user_data): query = update.callback_query chat_id = query.data user_data['chat_id'] = chat_id chat_name = 'global handlers' if chat_id == HandlerGroup.GLOBAL else Cache.get_chat_title( chat_id) toggle_global_text = '%s global handlers' % ( 'Enable' if Cache.chat_is_standalone(chat_id) else 'Disable') buttons = [[ InlineKeyboardButton(text='Add a handler', callback_data=str(self.ADD)), InlineKeyboardButton(text='Remove handler', callback_data=str(self.REMOVE)) ], [ InlineKeyboardButton(text='Edit a handler', callback_data=str(self.EDIT)), InlineKeyboardButton(text='Copy a handler', callback_data=str(self.COPY)) ]] toggle_button = [[ InlineKeyboardButton(text=toggle_global_text, callback_data=str(self.TOGGLE)) ]] if chat_id != HandlerGroup.GLOBAL else [] exit_button = [[ InlineKeyboardButton(text='Exit', callback_data=str(self.EXIT)) ]] bot.edit_message_text( chat_id=query.message.chat_id, message_id=query.message.message_id, text='Select what to configure for chat \'%s\'' % chat_name, reply_markup=InlineKeyboardMarkup(buttons + toggle_button + exit_button)) return self.SELECT_ACTION
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 add_handler_select_api_key_callback(self, bot, update, user_data): user_id = update.callback_query.from_user.id chat_id = user_data['chat_id'] keys = [key for key in Cache.get_api_keys(chat_id)] if keys: message = 'You do not have access to any existing API keys, please send me a valid key name' buttons = None else: message = 'Select an API key' buttons = [[ InlineKeyboardButton(text=key, callback_data='0_' + str(key)) ] for key in keys] user_data['stack'] = user_data['stack'][:-1] self.send_or_edit(bot, user_data, update.callback_query.message, message, buttons) return self.ADD_HANDLER_API_KEY
def _select_random_id(self, exclude): ''' Select a random id excluding the ones in `exclude`. ''' options = Cache.get(self._id) size = len(options) if size == 1: return next(iter(options)) if len(exclude) is size: exclude = [] keys = list(options.keys()) while True: rand = random.randrange(size) id = keys[rand] if id not in exclude: return id
def add_handler_cache_key_msg(self, bot, update, user_data): user_data['user_msg'] = True if update.message.text: key = update.message.text.strip() if Cache.contains(key) or len(key) >= 32 or key.startswith('$'): message = 'Invalid key. Either it is already in use, starts with a $, or it is too long' self.send_or_edit(bot, user_data, update.message, message) return self.ADD_HANDLER_CACHE_KEY else: default = user_data['acc'] user_data['acc'] = key Cache.config_entry(key, True) if not Cache.contains(key): Cache.put(key, default) Cache.add_chat_key(key, user_data['chat_id']) return self._handle_stack(bot, update.message, user_data) else: message = 'Invalid key. It must contain text' self.send_or_edit(bot, user_data, update.message, message) return self.ADD_HANDLER_CACHE_KEY
def _add_options(self, options, get_value): """ options == [(k, v)] -> direct key value store options == [v] && get_value == None -> store values indexed options == [x] && get_value == lambda(x) -> store result of lambda indexed """ for idx, option in enumerate(options): idx = '%s_%d' % (self._id, idx) if isinstance(option, tuple): (key, val) = option Cache.put([self._id, key], val) else: if get_value is None: Cache.put([self._id, idx], option) else: # Make sure that we only apply the load action when it is not in the cache yet. # This because the load action might be very expensive if not Cache.contains([self._id, idx]): Cache.put([self._id, idx], get_value(option))
def add_handler_api_key_msg(self, bot, update, user_data): user_data['user_msg'] = True if update.message.text: val = update.message.text.strip() (stage, data, idx) = user_data['stack'][-1] if stage == 0 and (Cache.contains(val) or len(val) >= 32 or val.startswith('$')): message = 'Invalid API key name. Either it is already in use, it starts with a $, or it is too long' self.send_or_edit(bot, user_data, update.message, message) else: (stage, data, res) = self.HANDLERS[idx].create_api(stage, data, val) user_data['stack'][-1] = (stage, data, idx) if isinstance(res, Send): self.send_or_edit(bot, user_data, update.message, res.msg, res.buttons) return self.ADD_HANDLER_API_KEY elif isinstance(res, Done): (key, value) = res.handler user_data['acc'] = key Cache.config_entry(key, True) Cache.add_api_key(key, user_data['chat_id']) Cache.put(key, value, encrypt=True) user_data['stack'] = user_data['stack'][:-1] return self._handle_stack(bot, update.message, user_data) else: print(stage, data, res) self.send_or_edit( bot, user_data, update.message, 'Unexpected API creation state! Please report your steps to the developer' ) return ConversationHandler.END else: message = 'Invalid reply. It must contain text' self.send_or_edit(bot, user_data, update.message, message) return self.ADD_HANDLER_API_KEY
def on_exit(self): Logger.log_info(msg='Shutting down') Cache.store_cache() Config.store_config(self)
def list_options(self): vals = Cache.get(self._id) return [vals[key] for key in vals]
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 clear(self): Cache.clear(self._id)
def on_update(self, bot): if not Cache.contains(self.cache_key): Cache.put(self.cache_key, {})
def __init__(self, id, children, do_cache=False): super(RandomMessageHandler, self).__init__(children) self._id = id Cache.config_entry(id, do_cache)
def create(cls, stage, data, arg): buttons = [[ InlineKeyboardButton(text='User', callback_data='user'), InlineKeyboardButton(text='Chat', callback_data='chat') ]] if stage == 0: return (1, data['user_id'], Send('Do you want to filter on user or chat inactivity?', buttons=buttons)) elif stage == 1: if isinstance(arg, str): check_user = arg == 'user' if check_user: return ( 2, 'user', Send( 'Now forward me a message from the user to filter on' )) else: chats = [[ InlineKeyboardButton( text=Cache.get_chat_title(chat_id), callback_data='c_%s' % chat_id) ] for chat_id in Cache.get_admin_chats(data)] return (2, 'chat', Send('Which chat should be filtered on?', buttons=chats)) else: return (1, None, Send('Please use the buttons', buttons=buttons)) elif stage == 2: default = {'chat': {}, 'user': {}} if isinstance(arg, str): return (3, (data, arg[2:]), AskCacheKey(default)) elif arg.forward_from: id = arg.forward_from.id return (3, (data, id), AskCacheKey(default)) else: return ( 2, data, Send( 'I asked you to forward a message, it shouldn\'t be too hard ' )) elif stage == 3 and arg: return (4, (data[0], data[1], arg), Send('Send me the amout of minutes of inactivity')) elif stage == 4: if arg.text and re.match(r'[\d]+$', arg.text): type, id, key = data minutes = int(arg.text) return (5, (type, id, key, minutes, []), AskChild()) else: return (4, arg, Send('Invalid input, try again')) elif stage == 5: if isinstance(arg, MessageHandler): data[4].append(arg) return (5, data, AskChild()) else: type, id, key, minutes, children = data return (-1, None, Done( cls._from_dict( { 'type': type, 'id': id, 'key': key, 'timedelta': (0, minutes * 60) }, children))) else: print(stage, data, arg) raise CreateException('Invalid create state for activity')
def select_random_option(self, exclude=None): id = self._select_random_id(exclude if exclude else []) val = Cache.get([self._id, id]) return (id, val)