diff --git a/davtelepot/__init__.py b/davtelepot/__init__.py index 739de9e..8669be3 100644 --- a/davtelepot/__init__.py +++ b/davtelepot/__init__.py @@ -7,7 +7,7 @@ __author__ = "Davide Testa" __email__ = "davide@davte.it" __credits__ = ["Marco Origlia", "Nick Lee @Nickoala"] __license__ = "GNU General Public License v3.0" -__version__ = "2.0.3" +__version__ = "2.0.4" __maintainer__ = "Davide Testa" __contact__ = "t.me/davte" diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..f9cece6 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,2 @@ +secrets.py +bot.db diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/a_simple_bot.py b/examples/a_simple_bot.py new file mode 100644 index 0000000..75928da --- /dev/null +++ b/examples/a_simple_bot.py @@ -0,0 +1,193 @@ +"""This example script shows how to develop a simple bot with davtelepot. + +1. Install davtelepot + ```bash + # Create a new python virtual environment + pip -m venv env + # Activate the virtual environment + source env/bin/activate + # Install davtelepot library in this environment + pip install davtelepot + # Run the current script within this environment + python a_simple_bot.py + ``` + +2. To run your bot, you will need a bot token. You can get up to 20 bot tokens + from https://t.me/botfather + +3. This script will look for your bot token in a gitignored `secrets.py` file + in the same folder as the current script. If no `secrets` module is found, + user will be prompted at runtime for a token, and the entry will be stored + in `secrets.py` for later use. + +4. Standard library, third party and project modules will be imported, and + `logging` preferences set. You are free to edit these settings, e.g. adding + one or more file and error loggers or setting different levels. + +5. `simple_bot` is an instance of `davtelepot.bot.Bot` class. + To instantiate a bot you need to provide at least a Telegram bot API token, + and you may also provide a path to a database (if no path is provided, + a `./bot.db` SQLite database will be used). + +6. `initialize_bot` function is defined and called on `simple_bot`. It assigns + commands, parsers, callback and inline query handlers to the bot. + +7. `Bot.run()` method is called, causing the script to hang while asynchronous + tasks run checking for updates and routing them to get and send replies. + Send a KeyboardInterrupt (ctrl+C) to stop the bot. +""" + +# Standard library modules +import logging +import os +import sys + +# Third party modules +try: + from davtelepot.bot import Bot + from davtelepot.utilities import ( + get_cleaned_text, make_inline_keyboard, make_button + ) +except ImportError: + logging.error( + "Please install davtelepot library.\n" + "The use if a python virtual environment is advised.\n\n" + "```bash\n" + "pip -m venv env\n" + "env/bin/pip install davtelepot\n" + "env/bin/python davtelepot/examples/a_simple_bot.py" + "```" + ) + sys.exit(1) + +# Get path of current script +path = os.path.dirname(__file__) + + +def initialize_bot(bot): + """Take a bot and set commands.""" + bot.set_callback_data_separator('|') + + @bot.command(command='foo', aliases=['Foo'], show_in_keyboard=True, + description="Reply 'bar' to 'foo'", + authorization_level='everybody') + async def foo_command(bot, update, user_record): + return 'Bar!' + + def is_bar_text_message(text): + return text.startswith('bar') + + @bot.parser(condition=is_bar_text_message, + description='Reply Foo to users who write Bar', + authorization_level='everybody', + argument='text') + async def bar_parser(bot, update): + text_except_foo = get_cleaned_text(update, bot, ['bar']) + return f"Foo!\n{text_except_foo}" + + def get_keyboard(chosen_button=-1): + return make_inline_keyboard( + [ + make_button( + prefix='button:///', + delimiter='|', + data=[i], + text=f"{'✅' if chosen_button == i else '☑️'} Button #{i}" + ) + for i in range(1, 13) + ], + 3 + ) + + @bot.command(command='buttons') + async def buttons_command(): + return dict( + text="Press a button!", + reply_markup=get_keyboard() + ) + + @bot.button(prefix='button:///', separator='|', + authorization_level='everybody') + async def buttons_button(bot, update, user_record, data): + button_number = data[0] + return dict( + edit=dict( + text=f"You pressed button #{button_number}", + reply_markup=get_keyboard(button_number) + ) + ) + + def starts_with_a(text): + return text.startswith('a') + + @bot.query( + condition=starts_with_a, + description='Mirror query text if it starts with letter `a`', + authorization_level='everybody' + ) + async def inline_query(bot, update, user_record): + return dict( + type='article', + id=10, + title="Click here to send your query text as a message.", + input_message_content=dict( + message_text=update['query'] + ) + ) + + bot.set_default_inline_query_answer( + dict( + type='article', + id=0, + title="Start query text with `a` to mirror it.", + input_message_content=dict( + message_text="This query does not start with `a`." + ) + ) + ) + + bot.set_unknown_command_message( + "Currently supported features\n\n" + "- /foo (or text starting with `foo`): replies `Bar!`.\n" + "- Text starting with `bar`: replies `Foo!` followed by the rest of " + "text in your bar-starting message.\n" + "- /buttons demonstrates the use of buttons.\n" + "- Inline queries starting with letter `a` will be mirrored as text " + "messages. To use this feature, try writing @{bot.name} " + "your_text_here" + ) + + +def _main(): + # Import or prompt user for bot token + try: + from secrets import simple_bot_token + except ImportError: + simple_bot_token = input("Enter bot token:\t\t") + with open( + f'{path}/secrets.py', + 'w' + ) as secrets_file: + secrets_file.write(f'simple_bot_token = "{simple_bot_token}"') + + # Set logging preferences + log_formatter = logging.Formatter( + "%(asctime)s [%(module)-15s %(levelname)-8s] %(message)s", + style='%' + ) + root_logger = logging.getLogger() + root_logger.setLevel(logging.DEBUG) + consoleHandler = logging.StreamHandler() + consoleHandler.setFormatter(log_formatter) + consoleHandler.setLevel(logging.DEBUG) + root_logger.addHandler(consoleHandler) + + # Instantiate, initialize and make `simple_bot` run. + simple_bot = Bot(token=simple_bot_token, database_url=f"{path}/bot.db") + initialize_bot(simple_bot) + logging.info("Send a KeyboardInterrupt (ctrl+C) to stop bots.") + Bot.run() + + +if __name__ == '__main__': + _main()