Checking some rules before a Telegram bot replies to a message So far, I've created a couple of programs, and the issue that comes up is how ugly and non-scalable they look.

For example, currently I'm working on a telegram bot. Before replying to a message I need to check that the message follows a couple of rules. In order to minimize the number of if/elif statements. I've even created a couple of functions that return True or False if my condition is met, but the code still looks very ugly:

# only if messages are not empty and description has a hyphen
if channel and (description and description[0] == '-'):

    if is_channel(channel) and is_length(description):

        if channel_is_size(channel):
            bot.reply_to(message, replies.success_add.format(channel))
            write_results(' '.join(message_words[1:]))
        else:
            bot.reply_to(message, replies.small_chan.format(min_channel_size))

    else:
        bot.reply_to(message, replies.enter_chan)

elif not description and channel:
    bot.reply_to(message, replies.enter_desc_error)
else:
     bot.reply_to(message, replies.enter_addmessage_error) # if empty


As you can see, these are the conditions for which the user message will be accepted into the database (it's a text file), and this can be seen right under the triple nested if statements.
I've tried to make this code look nicer many times over, but now it seems very messy and if I would want to add more conditions, which I am planning to do, these nested statements will grow out of proportion.
If you want to get rid of your nested code then you should make a function and use guard statements.

If we simplify your code a bit, we can have:

if C and D and D[0] == '-':
    return a  # Ignoring other code, for simplicity
elif C and not D:
    return b
else:
    return c


Instead you can have:

if C:
    if not D:
        return b
    elif D[0] == '-':
        return a
return c


If you use the guard clause then you get:

if not C:
    return c
if not D:
    return b
if D[0] != '-':
    return c
return a


Using the same logic, you can expand all your ifs to guard clauses. And the rest is easier than the above to change.
Which can leave you with:

def gen_response(channel, description, replies, message_words):
    if not channel:
        return replies.enter_addmessage_error

    if not description:
        return replies.enter_desc_error

    if description[0] != '-':
        return replies.enter_addmessage_error

    if not (is_channel(channel) and is_length(description)):
        return replies.enter_chan

    if not channel_is_size(channel):
        return replies.small_chan.format(min_channel_size)

    write_results(' '.join(message_words[1:]))
    return replies.success_add.format(channel)


bot.reply_to(message, gen_response(channel, description, replies, message_words))