discord.py - How to wait for user input that can be different?

I think this is what you're looking for :

@bot.command()
async def generate(ctx):
    options = ["a", "b", "c", "d"]
    already_sent_res = None  # Safe to remove, used for demo

    await ctx.send(
        f"{ctx.author.mention}\n"
        f'Sample question: (**Important**: Type a "!" in front):\n```'
        "> A\n> B\n> C\n> D```"
    )

    def check(m):
        return (
            m.content.startswith("!")
            and m.content.lower()[1:] in options
            and m.channel.id == ctx.channel.id
        )

    while True:
        msg = await bot.wait_for("message", check=check)
        # msg.content now contains the option. You may now proceed to code here
        # The below code from here are used for demonstration and can be safely removed
        if already_sent_res is None:
            sent_message = await ctx.send(
                f"**{str(msg.author.name)}** has chosen option **{str(msg.content).upper()[1:]}**"
            )
            already_sent_res = not None
        else:
            await sent_message.edit(
                f"**{str(msg.author.name)}** has chosen option **{str(msg.content).upper()[1:]}**"
            )

Here I used command handling with bot.command() instead of listening under on_message it is fine, you can use any which you are comfortable with.

The above code will result in the following : result

Remember that the bot will keep on listening for message event in the channel so be sure to have a timeout or an abort command.

Here's the full code I used incase you need it :

import discord
import os
from discord.ext import commands

bot = commands.Bot(command_prefix="!", case_insensitive=True)


@bot.event
async def on_ready():
    print(f"Sucessfully logged in as {bot.user}")


@bot.command()
async def generate(ctx):
    options = ["a", "b", "c", "d"]
    already_sent_res = None  # Safe to remove, used for demo

    await ctx.send(
        f"{ctx.author.mention}\n"
        f'Sample question: (**Important**: Type a "!" in front):\n```'
        "> A\n> B\n> C\n> D```"
    )

    def check(m):
        return (
            m.content.startswith("!")
            and m.content.lower()[1:] in options
            and m.channel.id == ctx.channel.id
        )

    while True:
        msg = await bot.wait_for("message", check=check)
        # msg.content now contains the option. You may now proceed to code here
        # The below code from here are used for demonstration and can be safely removed
        if already_sent_res is None:
            sent_message = await ctx.send(
                f"**{str(msg.author.name)}** has chosen option **{str(msg.content).upper()[1:]}**"
            )
            already_sent_res = not None
        else:
            await sent_message.edit(
                f"**{str(msg.author.name)}** has chosen option **{str(msg.content).upper()[1:]}**"
            )


bot.run(os.getenv("DISCORD_TOKEN"))