def test_create_group_with_valid_symbols(client: BotIntegrationClient): client.send_message(text="test_create_group_with_valid_symbols", chat_id=config('BOT_NAME')) group_name = create_group_command(client) assert group_is_existing( client, group_name), "Cannot find created group in the list"
def test_delete_existing_group(client: BotIntegrationClient): client.send_message(text="test_delete_existing_group", chat_id=config('BOT_NAME')) group_name = create_group_command(client) menu = enter_group(client, group_name) delete_group_command(client, group_name, menu)
def test_create_group_abort(client: BotIntegrationClient): client.send_message(text="test_create_group_abort", chat_id=config('BOT_NAME')) response = client.send_command_await("/create", num_expected=1) assert response.num_messages == 1 assert response.full_text.startswith("Ok, send the name of the group") response = client.send_command_await("/cancel", num_expected=1) assert response.num_messages == 1 assert response.full_text.startswith("Sure, what now?")
def test_categories(client: BotIntegrationClient): # Responses implement __eq__ assert (client.send_command_await("categories") == client.send_message_await(captions.CATEGORIES)) cat = client.send_command_await("categories", num_expected=1) social = cat.inline_keyboards[0].press_button_await(pattern=r'.*Social') ilkb = social.inline_keyboards[0] assert ilkb.num_buttons > 3 share_btn = ilkb.find_button(r'Share') assert 'Social' in share_btn.switch_inline_query
def create_group_command(client: BotIntegrationClient, generated_name: str = None): if not generated_name: generated_name = generate_name() client.send_command_await('/create', num_expected=1) response = client.send_message_await(generated_name) assert response.full_text.startswith('Saved'), \ "Correctly saved responses start with 'Saved.'" return generated_name
def test_explore_button(client: BotIntegrationClient): res = client.send_command_await("/start", num_expected=3) btn = next(x for x in res.keyboard_buttons if 'explore' in x.lower()) explore = client.send_message_await(btn) assert explore.num_messages == 1 count = 10 while "explored all the bots" not in explore.full_text: if count == 0: break # ok explore = explore.inline_keyboards[0].press_button_await( pattern=r'.*🔄') # emoji count -= 1
def test_other(client: BotIntegrationClient): test = ["contributing", "rules", "examples"] for t in test: res = client.get_inline_bot_results(client.peer_id, t) assert res.find_results(title_pattern=re.compile( r'.*{}.*'.format(t), re.IGNORECASE)), "{} did not work".format(t)
def test_help(client: BotIntegrationClient): # Send /help and wait for one message res = client.send_command_await("/help", num_expected=1) # Make some assertions about the response assert not res.empty, "Bot did not respond to /help command" assert 'reliable and unbiased bot catalog' in res.full_text.lower() keyboard = res.inline_keyboards[0] assert len(keyboard.rows[0]) == 3 # 3 buttons in first row assert len(keyboard.rows[1]) == 1 # 1 button in second row # Click the inline button that says "Contributing" contributing = res.inline_keyboards[0].press_button_await(pattern=r'.*Contributing') assert not contributing.empty, 'Pressing "Contributing" button had no effect.' assert "to contribute to the botlist" in contributing.full_text.lower() # Click the inline button that says "Help" help_ = res.inline_keyboards[0].press_button_await(pattern=r'.*Help') assert not contributing.empty, 'Pressing "Help" button had no effect.' assert "first steps" in help_.full_text.lower() # Click the inline button that says "Examples" examples = res.inline_keyboards[0].press_button_await(pattern=r'.*Examples') assert not examples.empty, 'Pressing "Examples" button had no effect.' assert "examples for contributing to the botlist:" in examples.full_text.lower()
def test_commands(client: BotIntegrationClient): for c in client.command_list: print("Sending {} ({})".format(c.command, c.description)) res = client.send_command_await(c.command) assert not res.empty
def enter_group(client: BotIntegrationClient, name: str): response = client.send_command_await('/groups', num_expected=1) pattern = r'.*{} | .+ member(s)'.format(name) menu = response.inline_keyboards[0].press_button_await(pattern=pattern) assert not menu.empty, 'Pressing "group_name" button had no effect.' assert name.lower() in menu.full_text.lower() return menu
def group_is_existing(client: BotIntegrationClient, group_name: str): response = client.send_command_await("/groups", num_expected=1) for row in response.inline_keyboards[0].rows: assert len(row) == 1, "Each row should be of length 1" if row[0].text.startswith(group_name): return True else: return False
def test_new(client: BotIntegrationClient): uname = '@test__bot' try: try: b = Bot.get(username=uname) b.delete_instance() except Bot.DoesNotExist: pass res = client.send_command_await("new", [uname]) if client.get_me().id in settings.MODERATORS: assert 'is currently pending' in res.full_text.lower() assert res.inline_keyboards[0].find_button(r'.*Accept.*') else: assert re.search('you submitted.*for approval', res.full_text, re.IGNORECASE) finally: Bot.delete().where(Bot.username == uname)
def test_search(client: BotIntegrationClient, bots): for username in bots: # First send the username in private chat to get target description of the bot res = client.send_message_await(username, num_expected=1) assert not res.empty, "Bot did not yield a response for username {}.".format( username) full_expected = res.full_text res = client.get_inline_bot_results(client.peer_id, username) results = res.find_results(title_pattern=re.compile( r'{}\b.*'.format(username), re.IGNORECASE)) assert len(results) == 1, "Not exactly one result for {}".format( username) # Description of bot should be the same in inline query message and private message assert full_expected in results.pop().result.send_message.message, \ "Message texts did not match."
def client(): integration_client = BotIntegrationClient( bot_under_test=config('BOT_NAME'), session_name='test_account', api_id=config('API_ID'), api_hash=config('API_HASH'), max_wait_response=20, min_wait_consecutive=20, global_action_delay=3) integration_client.start() integration_client.send_command(command='/cancel', bot=config('BOT_NAME')) try: yield integration_client except Exception: integration_client.send_command(command='/cancel', bot=config('BOT_NAME')) integration_client.stop()
def rename_group_command(client: BotIntegrationClient, menu): new_name = generate_name() menu.inline_keyboards[0].press_button_await(pattern=r'.*Rename group', min_wait_consecutive=5) response = client.send_message_await(new_name, num_expected=2) assert response[0].text.startswith('Saved') assert response[1].text.startswith('Choose an action') assert group_is_existing(client, new_name) return new_name
def __init__(self, session_name, log_level=logging.INFO): self.purchase_balance = None self.withdrawal_balance = None self.diamonds = None self.menu = None # type: ReplyKeyboard self.logger = logging.getLogger(self.__class__.__name__) self.logger.setLevel(log_level) self.client = BotIntegrationClient(session_name=session_name, bot_under_test='@DinoParkBot', max_wait_response=20, min_wait_consecutive=2.0, global_action_delay=1.0, config_file=os.path.join( examples_dir, 'config.ini')) self.client.start() self._update_keyboard() self.update_balance()
def client(): # Setup print('Initializing BotIntegrationClient') examples_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) c = BotIntegrationClient( session_name='my_account', bot_under_test='@BotListBot', # We're going to test the @BotListBot max_wait_response=10, # Wait a max of 10 seconds for responses, ... raise_no_response= False, # ... then check for response.empty instead of raising min_wait_consecutive= 2.0, # Wait at least 2 seconds to collect more than one message global_action_delay=1.8, # Space out all messages by 1.8 seconds workdir=examples_dir # Load configuration from parent folder ) print("Starting integration test client...") c.start() print("Client ready.") yield c # py.test sugar to separate setup from teardown # Teardown c.stop()
def client(): # Setup print('Initializing BotController') examples_dir = Path(__file__).parent.parent c = BotIntegrationClient( session_name='tgintegration_examples', bot_under_test='@BotListBot', # We're going to test the @BotListBot max_wait_response=10, # Wait a max of 10 seconds for responses, ... raise_no_response= False, # ... then check for response.empty instead of raising min_wait_consecutive= 2.0, # Wait at least 2 seconds to collect more than one message global_action_delay=1.8, # Space out all messages by 1.8 seconds workdir=examples_dir, config_file=str(examples_dir / 'config.ini')) print("Starting integration test service...") c.start() print("Client ready.") yield c # py.test sugar to separate setup from teardown # Teardown c.stop()
def test_search(client: BotIntegrationClient, bots): for bot in bots: res = client.get_inline_bot_results(client.peer_id, bot.username) results = res.find_results(title_pattern=re.compile( r'{}\b.*'.format(bot.username), re.IGNORECASE)) try: results.pop() # pop first, make sure now it's empty except KeyError: print(results) raise KeyError("No result found for {}".format(bot.username)) assert len( results ) == 0, "More than one article in inline query results for {}".format( bot.username)
def test_add_members_to_existing_group(client: BotIntegrationClient): client.send_message(text="test_add_members_to_existing_group", chat_id=config('BOT_NAME')) group_name = create_group_command(client) group_name = enter_group(client, group_name) button = group_name.inline_keyboards[0].press_button_await( pattern=r'.*Add members', min_wait_consecutive=5) assert not button.empty, 'Pressing "Add members" button had no effect.' assert button.full_text.startswith( 'Ok, send'), "Adding users' message has been changed." user_name = generate_name() response = client.send_message_await(user_name, num_expected=1) assert response.full_text.startswith("Added") response = client.send_command_await("/done", num_expected=2) assert response[0].text.startswith('Saved new members') members = response[1].text.split('\n')[3:] members = [item.split()[1] for item in members] assert user_name in members, "User hasn't been added"
def setUp(self): """ Sets up the environment""" API_ID = os.environ.get("API_ID") API_HASH = os.environ.get("API_HASH") TEST_BOT_NAME = os.environ.get("TEST_BOT_NAME") if None in [API_HASH, API_ID, TEST_BOT_NAME]: print("API_ID, API_HASH, TEST_BOT_NAME not set") raise ValueError() self.TEST_BOT_NAME = TEST_BOT_NAME client = BotIntegrationClient( bot_under_test=TEST_BOT_NAME, session_name= './session/my_account', # Arbitrary file path to the Pyrogram session file api_id=API_ID, # See "Requirements" above, ... api_hash=API_HASH, # alternatively use a `config.ini` file max_wait_response=15, # Maximum timeout for bot responses min_wait_consecutive= 2 # Minimum time to wait for consecutive messages ) client.start() #client.clear_chat() self.client = client
def test_help(client: BotIntegrationClient): res = client.send_command_await("/help", num_expected=1) assert 'reliable and unbiased bot catalog' in res.full_text.lower() kb = res[0].reply_markup.inline_keyboard assert len(kb[0]) == 3 assert len(kb[1]) == 1 contributing = res.inline_keyboards[0].press_button_await( pattern=r'.*Contributing') assert "to contribute to the botlist" in contributing.full_text.lower() help_ = res.inline_keyboards[0].press_button_await(pattern=r'.*Help') assert "first steps" in help_.full_text.lower() examples = res.inline_keyboards[0].press_button_await( pattern=r'.*Examples') assert "Examples for contributing to the BotList:" in examples.full_text
def client(): # setup print('Initializing integration test client') c = BotIntegrationClient(bot_under_test=settings.BOT_UNDER_TEST, max_wait_response=8, min_wait_consecutive=1.5, global_action_delay=1.5, session_name=settings.TEST_USERBOT_SESSION, api_id=settings.API_ID, api_hash=settings.API_HASH, phone_number=settings.TEST_USERBOT_PHONE) print("Starting client...") c.start() if c.peer_id in settings.MODERATORS: print("Restarting bot...") c.send_command_await("r", num_expected=2) # restart bot # c.clear_chat() yield c # teardown c.stop()
def test_explore_button(client: BotIntegrationClient): # Send /start to bot and wait for 3 messages start = client.send_command_await("/start", num_expected=3) # Click the "Explore" keyboard button explore = start.reply_keyboard.press_button_await(pattern=r'.*Explore') assert not explore.empty, 'Pressing the "Explore" button had no effect.' assert explore.inline_keyboards, 'The "Explore" message had no inline keyboard.' # Click the "Explore" inline keyboard button 10 times or until it says that # all bots have been explored count = 10 while "explored all the bots" not in explore.full_text: if count == 0: break # ok # Pressing an inline button also makes the BotController listen for edit events. explore = explore.inline_keyboards[0].press_button_await(index=2) assert not explore.empty, 'Pressing the "Explore" button had no effect.' count -= 1
def test_commands(client: BotIntegrationClient): # The BotController automatically loads the available commands and we test them all here for c in client.command_list: res = client.send_command_await(c.command) assert not res.empty, "Bot did not respond to command /{}.".format( c.command)
def test_start(client: BotIntegrationClient): # Send /start and wait for 3 messages res = client.send_command_await("/start", num_expected=3) assert res.num_messages == 3 assert res[0].sticker # First message is a sticker
""" Full version of the GitHub README. """ from tgintegration import BotIntegrationClient print("Initializing service...") # This example uses the configuration of `config.ini` (see examples/README) client = BotIntegrationClient( bot_under_test='@BotListBot', session_name= 'tgintegration_examples', # Arbitrary file path to the Pyrogram session file max_wait_response=8, # Maximum timeout for bot responses min_wait_consecutive=2, # Minimum time to wait for consecutive messages raise_no_response= True # Raise `InvalidResponseError` when no response received ) print("Starting...") client.start() print("Clearing chat to start with a blank screen...") client.clear_chat() print( "Send the /start command to the bot_under_test and 'await' exactly three messages..." ) response = client.send_command_await("start", num_expected=3) assert response.num_messages == 3 print("Three messages received.")
import os import time import traceback from tgintegration import BotIntegrationClient from tgintegration.response import Response from typing import Dict examples_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # This example uses the configuration of `config.ini` (see examples/README) client = BotIntegrationClient( session_name='my_account', bot_under_test='@IdleTownBot', max_wait_response=15, # Maximum time in seconds to wait for a response from the bot min_wait_consecutive=None, # Do not wait for more than one message global_action_delay=2.3, # The @IdleTownBot has a spam limit of about 1.9s workdir=examples_dir, # Load configuration from parent folder config_file=os.path.join(examples_dir, 'config.ini') ) client.load_config() client.start() def ascii_chars(text): # type: (str) -> str return ''.join(x for x in text if str.isalpha(x) or str.isdigit(x)).strip() def get_buttons(response):
def test_start(client: BotIntegrationClient): client.send_message(text="test_start", chat_id=config('BOT_NAME')) response = client.send_command_await("/start", num_expected=1) assert response.num_messages == 1 assert response.full_text.startswith("Hello")
class DinoParkGame: VALUE_PATTERN = re.compile(r'^.*?\s*(\w+): ([\d ]+).*$', re.MULTILINE) NUMBERS_ONLY_PATTERN = re.compile(r'\b(\d[\d ]+)\b') def __init__(self, session_name, log_level=logging.INFO): self.purchase_balance = None self.withdrawal_balance = None self.diamonds = None self.menu = None # type: ReplyKeyboard self.logger = logging.getLogger(self.__class__.__name__) self.logger.setLevel(log_level) self.client = BotIntegrationClient(session_name=session_name, bot_under_test='@DinoParkBot', max_wait_response=20, min_wait_consecutive=2.0, global_action_delay=1.0, config_file=os.path.join( examples_dir, 'config.ini')) self.client.start() self._update_keyboard() self.update_balance() def _update_keyboard(self): start = self.client.send_command_await("start") self.menu = start.reply_keyboard def _extract_values(self, text): groups = self.VALUE_PATTERN.findall(text) try: return {g[0].lower(): str_to_int(g[1]) for g in groups} except KeyError: return {} def update_balance(self): balance = self.menu.press_button_await(r'.*Balance') values = self._extract_values(balance.full_text) self.purchase_balance = values['purchases'] self.withdrawal_balance = values['withdrawals'] self.diamonds = values['storehouse'] self.logger.debug( "Balance updated: +{} for purchases, +{} for withdrawals, +{} diamonds." .format(self.purchase_balance, self.withdrawal_balance, self.diamonds)) def collect_diamonds(self): farm = self.menu.press_button_await(".*Farm") collected = farm.inline_keyboards[0].press_button_await( ".*Collect diamonds") num_collected = self._extract_values(collected.full_text).get( 'collected', 0) self.diamonds += num_collected self.logger.info("{} diamonds collected.".format( num_collected if num_collected > 0 else 'No')) def sell_diamonds(self): market = self.menu.press_button_await(r'.*Marketplace') if not market.inline_keyboards: self.logger.debug("No selling available at the moment.") return sold_msg = market.inline_keyboards[0].press_button_await( r'Sell diamonds.*') values = self.VALUE_PATTERN.findall(sold_msg.full_text) sold = str_to_int(values[0][1]) plus_purchase = str_to_int(values[1][1]) plus_withdrawal = str_to_int(values[2][1]) self.diamonds -= sold self.purchase_balance += plus_purchase self.withdrawal_balance += plus_withdrawal self.logger.info( "{} diamonds sold, +{} to purchase balance, +{} to withdrawal balance." .format(sold, plus_purchase, plus_withdrawal)) def buy_dinosaurs(self): """ Buy the best affordable dinosaurs """ dinos = self.menu.press_button_await(r'.*Dinosaurs').inline_keyboards[ 0].press_button_await(r'.*Buy dinosaurs') # Build up a list of cost per dino dino_cost_sequence = [] for msg in dinos.messages: # "Worth" in the message has no colon (:) before the number, therefore we use a numbers # only pattern values = self.NUMBERS_ONLY_PATTERN.findall(msg.caption) cost = str_to_int(values[0]) dino_cost_sequence.append(cost) while True: try: can_afford_id = next( x[0] for x in reversed(list(enumerate(dino_cost_sequence))) if x[1] <= self.purchase_balance) except StopIteration: self.logger.debug("Can't afford any dinosaurs.") # Can not afford any break bought = dinos.inline_keyboards[can_afford_id].press_button_await( r'.*Buy') self.logger.info("Bought dinosaur: " + bought.full_text) def play_lucky_number(self): lucky_number = self.menu.press_button_await( r'.*Games').reply_keyboard.press_button_await(r'.*Lucky number') bet = lucky_number.reply_keyboard.press_button_await( r'.*Place your bet') if 'only place one bet per' in bet.full_text.lower(): self.logger.debug("Already betted in this round") # Clean up self.client.delete_messages( self.client.peer_id, [bet.messages[0].message_id, bet.action_result.message_id]) return self.client.send_message_await(str(random.randint(1, 30))) self.logger.debug("Bet placed.")