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
Example #4
0
 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)
Example #5
0
    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)
Example #6
0
    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 []
Example #7
0
    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()
Example #8
0
    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()
Example #9
0
    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)
Example #10
0
    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()
Example #11
0
 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
Example #12
0
    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()
Example #13
0
    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
Example #14
0
 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()
Example #15
0
    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
Example #16
0
    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
Example #17
0
 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
Example #18
0
    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))
Example #19
0
    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
Example #20
0
 def on_exit(self):
     Logger.log_info(msg='Shutting down')
     Cache.store_cache()
     Config.store_config(self)
Example #21
0
 def list_options(self):
     vals = Cache.get(self._id)
     return [vals[key] for key in vals]
Example #22
0
 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 []
Example #23
0
 def clear(self):
     Cache.clear(self._id)
Example #24
0
 def on_update(self, bot):
     if not Cache.contains(self.cache_key):
         Cache.put(self.cache_key, {})
Example #25
0
 def __init__(self, id, children, do_cache=False):
     super(RandomMessageHandler, self).__init__(children)
     self._id = id
     Cache.config_entry(id, do_cache)
Example #26
0
 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')
Example #27
0
    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)