@Kakagawa

Как вызвать одну функцию из другой в discord боте?

Есть две команды

async def ask(channel, question):
    global current_model

    lang = detect(question)
    print(f"Detected language: {lang}")
    data = load_data(data_folder, lang)
    print("Loaded data.")

    if PERSIST and os.path.exists("persist"):
        print("Reusing index...\n")
        vectorstore = Chroma(persist_directory="persist", embedding_function=OpenAIEmbeddings())
        index = VectorStoreIndexWrapper(vectorstore=vectorstore)
        print("Using existing index.")
    else:
        loader = DirectoryLoader(data_folder)
        if PERSIST:
            index = VectorstoreIndexCreator(vectorstore_kwargs={"persist_directory": "persist"}).from_loaders([loader])
        else:
            index = VectorstoreIndexCreator().from_loaders([loader])
        print("Created new index.")

    chain = ConversationalRetrievalChain.from_llm(
        llm=ChatOpenAI(model=current_model),
        retriever=index.vectorstore.as_retriever(search_kwargs={"k": 1}),
    )
    print("Initialized chain.")

    result = chain({"question": question, "chat_history": []})
    
    if result['answer']:
        await channel.send(result['answer'])
        print("Sent answer.")
    else:
        # Если ответ не найден, выполнить команду /search с вопросом пользователя
        await channel.send(f"Извините, не удалось найти ответ на ваш вопрос.")
        search_command = bot.get_command("search")
        if search_command:
            # Создаем контекст для выполнения команды /search с вопросом пользователя
            ctx = await bot.get_context(channel.message)
            ctx.message.content = f"/search {question}"
            await bot.invoke(ctx)
        else:
            await channel.send("Команда /search не найдена.")

    # Проверяем наличие ключевых слов для запроса дополнительной информации
    keywords = ["биография"]
    for keyword in keywords:
        if keyword in question.lower():
            if keyword == "биография":
                bio_file = os.path.join(data_folder, "bio.txt")
                if os.path.exists(bio_file):
                    with open(bio_file, "r", encoding="utf-8") as file:
                        bio_contents = file.read()
                        await channel.send(f"Биография: {bio_contents}")
                else:
                    await channel.send("Биография не найдена.")

@bot.command()
async def search(ctx, *, query):
    try:
        search_results = googlesearch.search(query, num_results=3) 

        if search_results:
            await ctx.send("Результаты поиска в Google:")
            for result in search_results:
                await ctx.send(result)

        else:
            await ctx.send("Ничего не найдено.")

    except Exception as e:
        await ctx.send(f"Произошла ошибка при выполнении поиска: {str(e)}")


Мне нужно, что бы в случае когда бот не находит ответ в локальных файлах, он писал вопрос через команду /search и выдавал ответ.

Я добавил это в команду ask, но бот не ищет в гугле. Как будто игнорирует кусок кода с поиском. С файлов выводит все без проблем. Но если не находит ответ, идет в openai и выдает стандартный ответ об отсутствии актуальных данных.

Если писать через команду /search на прямую - все работает отлично. Как сделать так, что бы работало как надо?) Что я делаю не так?
  • Вопрос задан
  • 115 просмотров
Решения вопроса 1
Vindicar
@Vindicar
RTFM!
Советую не пытаться удалять гланды через противоположный конец тела. А именно:
Вместо того, чтобы пытаться вызвать обработчик команды, и биться об стенку, пытаясь имитировать всё необходимое окружение, следует просто вынести непосредственно операцию поиска в ОТДЕЛЬНУЮ функцию.
Собственно, в твоём случае было бы норм и просто вызвать
search_results = googlesearch.search(query, num_results=3)
внутри ask(), и обработать полученные результаты - невелико повторение. Я бы сделал именно так.

Если прям очень хочется сделать общий код для двух команд, вынеси в отдельную функцию и вывод:
# Messageable - это что-то, чему можно послать текстовое сообщение. Это может быть канал, пользователь, контекст команды...
async def search_and_reply(query: str, ctx: discord.abc.Messageable) -> None:
    try:
        search_results = googlesearch.search(query, num_results=3) 
        if search_results:
            await ctx.send("Результаты поиска в Google:")
            for result in search_results:
                await ctx.send(result)
        else:
            await ctx.send("Ничего не найдено.")
    except Exception as e:
        await ctx.send(f"Произошла ошибка при выполнении поиска: {str(e)}")

И тогда у тебя будет всё проще: search() будет состоять из одного вызова await search_and_reply(query, ctx), но будет брать на себя регистрацию как обработчика команды, проверку ролей и кулдаунов, и прочую мишуру, которой не место внутри search_and_reply(). Тогда ask() может просто сделать аналогичный вызов await search_and_reply(query, channel) в нужный момент, не беспокоясь о специфике дискорд-обработчика, так как search_and_reply() таковым не является.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
fenrir1121
@fenrir1121
Начни с документации
Как вызвать одну функцию из другой в discord боте?
Так же как не в дискорд боте.

Есть две команды
В приложенном коде только одна команда search, а ask просто функция.

# Создаем контекст для выполнения команды /search с вопросом пользователя
ctx = await bot.get_context(channel.message)
ctx.message.content = f"/search {question}"

Зачем вы пытаетесь отправить сообщение и самому на него отреагировать?
Почему вы пытаетесь использовать слеш в команде с префиксом?

По ощущениям код на 80% написан кодогенератором и пишется без понимания что он вообще делает.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы