示例#1
0
def git_pull(update: Update, context: CallbackContext, updater: Updater):
    """Pull the latest changes from git repository"""

    query = update.callback_query
    if query:
        context.bot.answer_callback_query(callback_query_id=query.id, text='Pulling from git repository now.')

    try:
        pull_result = subprocess.check_output(["git", "pull"], stderr=subprocess.STDOUT).decode()
    except subprocess.CalledProcessError as e:
        text = f"{get_emoji('bug')} *Bug Report*\n\n" \
               f"Failed to pull latest changes from github: `{e.output.decode()}`"
        notify_devs(text=text)
        return

    text = f"{get_emoji('info')} *Updating Bot*\n\n" \
           f"Pulled the latest changes from git repository:\n" \
           f"`{pull_result}`\n"

    up_to_date = 'Already up to date'

    if up_to_date not in pull_result:
        text += "Restarting bot now."

    notify_devs(text=text)

    if up_to_date not in pull_result:
        restart(update, context, updater, notify=False)
示例#2
0
 def func_wrapper(update: Update, context: CallbackContext, *args, **kwargs):
     user = update.effective_user
     if user.id not in bot_devs:
         warning = f"Unauthorized access to admin function by user {user.first_name} (#{user.id} / @{user.username})"
         logger.warning(warning)
         text = f"{get_emoji('warning')} *Unauthorized Access*\n\n" \
                f"{warning}"
         notify_devs(text=text)
         return
     return func(update, context, *args, **kwargs)
示例#3
0
def error(update: Update, context: CallbackContext):
    """Handle Errors caused by Updates."""
    # get traceback
    trace = "".join(traceback.format_tb(sys.exc_info()[2]))

    error_details = ""

    # inform user
    if update:
        if update.effective_message:
            (chat_id, msg_id, user_id, username) = extract_ids(update)
            lang = profile.get_language(
                context.chat_data) if profile.get_language(
                    context.chat_data) else 'en'
            text = f"{get_emoji('bug')} *{get_text(lang, 'error_occurred_title')}*\n\n" \
                   f"{get_text(lang, 'error_occurred_message').format(provider=bot_provider)}"
            keyboard = [[
                InlineKeyboardButton(
                    text=
                    f"{get_emoji('overview')} {get_text(lang, 'overview')}",
                    callback_data='overview')
            ]]
            message_user(bot=context.bot,
                         chat_id=chat_id,
                         chat_data=context.chat_data,
                         message_type=MessageType.message,
                         payload=text,
                         keyboard=keyboard,
                         category=MessageCategory.main)

        user_obj = update.effective_user
        chat_obj = update.effective_chat

        if user_obj:
            error_details += f' with the user {mention_markdown(user_obj.id, user_obj.first_name)}'

        if chat_obj:
            error_details += f' within the {chat_obj.type} chat _{chat_obj.title}_'
            if chat_obj.username:
                error_details += f' (@{chat_obj.username})'

        # only add the poll id if there is neither a user nor a chat associated with this update
        if not error_details and update.poll:
            error_details += f' with the poll id {update.poll.id}.'

    # construct bug report for devs
    text = f"{get_emoji('bug')} *Bug Report*\n\n" \
           f"The error `{context.error}` happened{error_details}.\n\n" \
           f"Traceback:\n" \
           f"`{trace}`"
    notify_devs(text=text)

    # raise the error again, so the logger module can catch it
    raise
示例#4
0
def restart(update: Update, context: CallbackContext, updater: Updater, notify=True):
    """Restart the bot upon admin command"""

    query = update.callback_query
    if query:
        context.bot.answer_callback_query(callback_query_id=query.id, text='Restarting bot now.')

    def stop_and_restart():
        """Gracefully stop the Updater and replace the current process with a new one"""
        updater.stop()
        os.execl(sys.executable, sys.executable, *sys.argv)

    if notify:
        text = f"{get_emoji('info')} *Restarting Bot*\n\n" \
               f"Bot is restarting."
        notify_devs(text=text)

    Thread(target=stop_and_restart).start()
示例#5
0
def load_quests(context: CallbackContext):
    db = mysql.connector.connect(host=mysql_host,
                                 port=mysql_port,
                                 user=mysql_user,
                                 passwd=mysql_password,
                                 database=mysql_db)

    global latest_quest_scan

    midnight = datetime.combine(datetime.today(), time.min).timestamp()

    # make sure only quests from today get loaded
    if midnight > latest_quest_scan:
        latest_quest_scan = midnight

    cursor = db.cursor()
    cursor.execute(
        "SELECT "
        "ps.pokestop_id,"
        "ps.name, "
        "ps.latitude, "
        "ps.longitude, "
        "q.quest_timestamp, "
        "q.quest_pokemon_id, "
        "q.quest_item_id, "
        "q.quest_item_amount, "
        "q.quest_template "
        "FROM trs_quest as q "
        "LEFT JOIN pokestop as ps "
        "ON q.GUID = ps.pokestop_id "
        "WHERE q.quest_timestamp >= %s "
        "AND q.quest_stardust = 0 "
        "ORDER BY q.quest_timestamp DESC", (latest_quest_scan, ))

    result = cursor.fetchall()

    db.close()

    unknown_tasks = {}

    for (stop_id, stop_name, latitude, longitude, timestamp, pokemon_id,
         item_id, item_amount, task_id) in result:

        # skip quest if older than the existing quest entry
        if stop_id in quests and quests[stop_id].timestamp > timestamp:
            continue

        # remember quest rewards
        if pokemon_id != 0 and pokemon_id not in quest_pokemon_list:
            quest_pokemon_list.append(pokemon_id)
        if item_id != 0 and item_id not in quest_items_list:
            quest_items_list.append(item_id)

        # create new quest object
        quests[stop_id] = Quest(stop_id=stop_id,
                                stop_name=stop_name,
                                latitude=latitude,
                                longitude=longitude,
                                timestamp=timestamp,
                                pokemon_id=pokemon_id,
                                item_id=item_id,
                                item_amount=item_amount,
                                task_id=task_id)

        if get_task_by_id('en', task_id) == task_id:
            unknown_tasks[task_id] = quests[stop_id]

        if timestamp > latest_quest_scan:
            latest_quest_scan = timestamp + 1

    if unknown_tasks:
        text = f"{get_emoji('bug')} *Bug Report*\n\n" \
               f"The following tasks are unknown:\n\n"
        # gather unknown tasks
        for quest in unknown_tasks.values():
            text += f"`task: {quest.task_id}\n" \
                    f"pokemon_id: {quest.pokemon_id}\n" \
                    f"item_id: {quest.item_id}\n" \
                    f"item_amount: {quest.item_amount}`\n\n"
        # inform devs
        notify_devs(text=text)

    quest_pokemon_list.sort()
    quest_items_list.sort()

    logger.info(
        f"{len(result)} new quests loaded from DB. Total quest count: {len(quests)}"
    )
示例#6
0
def main():
    logger.info("Starting Bot.")

    # request object for bot
    request = Request(con_pool_size=8)

    # use message queue bot version
    if bot_use_message_queue:
        logger.info("Using MessageQueue to avoid flood limits.")
        # enable message queue with production limits
        msg_queue = messagequeue.MessageQueue(all_burst_limit=29,
                                              all_time_limit_ms=1017,
                                              group_burst_limit=20,
                                              group_time_limit_ms=60000)
        # create a message queue bot
        bot = MQBot(bot_token, request=request, msg_queue=msg_queue)
    # use regular bot
    else:
        logger.info("Using no MessageQueue. You may run into flood limits.")
        # use the default telegram bot (without message queue)
        bot = Bot(bot_token, request=request)

    set_bot(bot=bot)
    notify_devs(text=f"{get_emoji('info')} *Starting Bot*\n\nBot is starting.")

    persistence = PicklePersistence(filename='persistent_data.pickle')

    # create the EventHandler and pass it the bot's instance
    updater = Updater(bot=bot, use_context=True, persistence=persistence)

    # jobs
    job_queue = updater.job_queue
    job_queue.run_daily(callback=clear_quests,
                        time=time(hour=0, minute=0, second=0))
    job_queue.run_repeating(callback=load_quests, interval=300, first=0)
    job_queue.run_daily(callback=load_shinies,
                        time=time(hour=0, minute=0, second=0))
    job_queue.run_once(callback=load_shinies, when=0)

    # get the dispatcher to register handlers
    dp = updater.dispatcher

    # admin commands
    dp.add_handler(
        CallbackQueryHandler(callback=partial(restart, updater=updater),
                             pattern='^restart_bot$'))
    dp.add_handler(
        CallbackQueryHandler(callback=partial(git_pull, updater=updater),
                             pattern='^git_pull$'))

    # overview
    dp.add_handler(CommandHandler(callback=chat.start, command='start'))
    dp.add_handler(
        CallbackQueryHandler(callback=chat.start, pattern='^overview'))

    # settings
    dp.add_handler(
        CallbackQueryHandler(callback=chat.settings, pattern='^settings'))

    # info section
    dp.add_handler(CallbackQueryHandler(callback=chat.info, pattern='^info'))

    # delete data
    dp.add_handler(
        CallbackQueryHandler(callback=chat.delete_data,
                             pattern='^delete_data'))

    # select area conversation
    conversation_handler_select_area = ConversationHandler(
        entry_points=[
            CallbackQueryHandler(callback=conversation.select_area,
                                 pattern='^select_area$')
        ],
        states={
            # receive location, ask for radius
            conversation.STEP0: [
                MessageHandler(callback=conversation.set_quest_center_point,
                               filters=Filters.all)
            ],
            # receive radius
            conversation.STEP1: [
                MessageHandler(callback=conversation.set_quest_radius,
                               filters=Filters.all)
            ],
            # receive button click, ask for location or radius
            conversation.STEP2: [
                CallbackQueryHandler(callback=conversation.change_center_point,
                                     pattern='^change_center_point'),
                CallbackQueryHandler(callback=conversation.change_radius,
                                     pattern='^change_radius')
            ],
        },
        # fallback to overview
        fallbacks=[
            CallbackQueryHandler(callback=chat.start,
                                 pattern='^back_to_overview')
        ],
        allow_reentry=True,
        persistent=True,
        name="select_area")
    dp.add_handler(conversation_handler_select_area)

    # choose quest conversation
    conversation_handler_choose_quest = ConversationHandler(
        entry_points=[
            CallbackQueryHandler(callback=conversation.choose_quest_type,
                                 pattern="^choose_quest_type$")
        ],
        states={
            # receive quest type, ask for quest
            conversation.STEP0: [
                CallbackQueryHandler(callback=conversation.choose_pokemon,
                                     pattern="^choose_pokemon"),
                CallbackQueryHandler(callback=conversation.choose_item,
                                     pattern="^choose_item"),
                CallbackQueryHandler(callback=conversation.choose_task,
                                     pattern="^choose_task")
            ]
        },
        fallbacks=[
            CallbackQueryHandler(callback=chat.start,
                                 pattern='^back_to_overview',
                                 pass_user_data=True)
        ],
        allow_reentry=True,
        persistent=True,
        name="choose_quest")
    dp.add_handler(conversation_handler_choose_quest)

    # hunt quest conversation
    conversation_handler_start_hunt = ConversationHandler(
        entry_points=[
            CallbackQueryHandler(callback=conversation.start_hunt,
                                 pattern="^start_hunt")
        ],
        states={
            # receive start location, send quest
            conversation.STEP0: [
                CallbackQueryHandler(
                    callback=conversation.continue_previous_hunt,
                    pattern="^continue_previous_hunt"),
                CallbackQueryHandler(callback=conversation.reset_previous_hunt,
                                     pattern="^reset_previous_hunt")
            ],
            conversation.STEP1: [
                MessageHandler(callback=conversation.set_start_location,
                               filters=Filters.all)
            ],
            conversation.STEP2: [
                CallbackQueryHandler(callback=conversation.quest_collected,
                                     pattern="^quest_collected"),
                CallbackQueryHandler(callback=conversation.quest_skip,
                                     pattern="^quest_skip"),
                CallbackQueryHandler(callback=conversation.quest_ignore,
                                     pattern="^quest_ignore"),
                CallbackQueryHandler(callback=conversation.end_hunt,
                                     pattern="^end_hunt"),
                CallbackQueryHandler(callback=conversation.enqueue_skipped,
                                     pattern="^enqueue_skipped"),
                CallbackQueryHandler(callback=conversation.continue_hunt,
                                     pattern="^continue_hunt"),
                CallbackQueryHandler(callback=conversation.process_hint,
                                     pattern="^hint")
            ]
        },
        fallbacks=[
            CallbackQueryHandler(callback=chat.start,
                                 pattern='^back_to_overview')
        ],
        allow_reentry=True,
        persistent=True,
        name="start_hunt")
    dp.add_handler(conversation_handler_start_hunt)

    # catch-all handler that just logs messages
    dp.add_handler(
        MessageHandler(callback=utils.dummy_callback, filters=Filters.all))
    dp.add_handler(
        CallbackQueryHandler(callback=utils.dummy_callback, pattern=".*"))

    # log all errors
    dp.add_error_handler(error)

    # start the bot
    updater.start_polling()

    # Run the bot until you press Ctrl-C or the process receives SIGINT,
    # SIGTERM or SIGABRT. This should be used most of the time, since
    # start_polling() is non-blocking and will stop the bot gracefully.
    updater.idle()