def __init__(self): self.updater = Updater(config('BOT_TOKEN'), workers=config('WORKERS', cast=int, default=3)) self.db = MongoBackend() self.jira = JiraBackend() self.AuthData = namedtuple( 'AuthData', 'auth_method jira_host username credentials') for command in self.commands: cb = command(self).command_callback() self.updater.dispatcher.add_handler(cb) self.updater.dispatcher.add_error_handler(self.error_callback)
def __init__(self, status_code, host=None, auth_method=None, credentials=None): super(TelegramError, self).__init__() self.db = MongoBackend() self.message = self.login_error.get(status_code, 'Some problems with login') self.host = host self.credentials = credentials # If handled an error about rejected token and auth method is `oauth` if status_code == 401 and auth_method == 'oauth': self.host_not_verified()
class JiraLoginError(BaseJTBException): """Login error during login into Jira""" login_error = { 401: 'Invalid credentials or token was rejected.\nYou can try the following actions:\n' '1. If you logged in already, please try to use /disconnect command and log in again.\n' '2. Update a previously created Application link (/disconnect, /oauth).\n' '3. If you are connecting with email as a login, please try to use username instead.\n' '4. Try to connect using /oauth', 403: 'Login is denied due to a CAPTCHA requirement, or any other ' 'reason. Please, login (re-login) into Jira via browser ' 'and try again.', 409: 'Login is denied due to an unverified email. ' 'The email must be verified by logging in to JIRA through a ' 'browser and verifying the email.' } def __init__(self, status_code, host=None, auth_method=None, credentials=None): super(TelegramError, self).__init__() self.db = MongoBackend() self.message = self.login_error.get(status_code, 'Some problems with login') self.host = host self.credentials = credentials # If handled an error about rejected token and auth method is `oauth` if status_code == 401 and auth_method == 'oauth': self.host_not_verified() def host_not_verified(self): # Changing `is_confirmed` flag of the host to False # will re-generate a data for the Application link self.db.update_host(self.host, {'is_confirmed': False})
def setup_class(cls): # creates a test database and initialize all test data cls.test_db_name = 'test_' + config('DB_NAME') cls.client = MongoClient('{host}:{port}'.format(host=config('DB_HOST'), port=config('DB_PORT'))) cls.client.admin.authenticate(config('DB_USER'), config('DB_PASS')) cls.client[cls.test_db_name].add_user( config('DB_USER'), config('DB_PASS'), roles=[{'role': 'readWrite', 'db': cls.test_db_name}] ) cls.db = MongoBackend(db_name=cls.test_db_name) # creates a collection and index (TTL with expire after 5 seconds) test_client = cls.db.conn cache_name = config('DB_CACHE_COLLECTION') test_client.create_collection(cache_name) test_client[cache_name].create_index('createdAt', expireAfterSeconds=5, background=True)
class BaseMessage(metaclass=ABCMeta): """Base bot message class. Args: bot (telegram.Bot): telegram bot instance update (telegram.Update): Update instance Keyword arguments: title (str): Title of the message (the text is bold) text (str): Simple text in message (without editing) buttons: any buttons to show in chat items (list): list of strings with any data key (str): key for cached data page (int): number of page that user clicked (at inline keyboard) page_count (int): total count of the pages (for generating a new inline keyboard) raw_items (dict): raw JIRA issue objects, need to format before display """ db = MongoBackend() issues_per_page = 10 callback_paginator_key = 'paginator:{}' def __init__(self, bot, update, **kwargs): self.bot = bot self.update = update self.message_id = kwargs.get('message_id') self.title = kwargs.get('title') self.text = kwargs.get('text') self.buttons = kwargs.get('buttons') self.items = kwargs.get('items') self.key = kwargs.get('key') self.page = kwargs.get('page') self.page_count = kwargs.get('page_count') self.raw_items = kwargs.get('raw_items') @abstractmethod def send(self): """Send message to chat. This method must be implemented in your class. """ pass
import logging from flask import Flask from celery import Celery from celery.signals import after_setup_logger from decouple import config from lib.db import MongoBackend import logger db = MongoBackend() # Flask settings app = Flask(__name__) app.secret_key = config('SECRET_KEY') logger = logger.logger celery = Celery(app.name) celery.conf.broker_url = config("CELERY_BROKER_URL") from .auth import auth as auth_blueprint app.register_blueprint(auth_blueprint, url_prefix='/auth') from .webhooks import webhooks as webhooks_blueprint app.register_blueprint(webhooks_blueprint, url_prefix='/webhook') @after_setup_logger.connect def setup_loggers(logger, *args, **kwargs):
class JTBApp: """Bot to integrate with the JIRA service""" __commands = [ commands.HelpCommand, commands.StartCommand, commands.ListUnresolvedIssuesCommand, commands.ListStatusIssuesCommand, commands.UserStatusIssuesCommand, commands.ProjectStatusIssuesCommand, commands.TimeTrackingCommand, commands.FilterDispatcherCommand, commands.FilterIssuesCommand, commands.WatchDispatcherCommand, commands.CreateWebhookCommand, commands.UnwatchDispatcherCommand, commands.UnsubscribeAllUpdatesCommand, commands.BasicLoginCommand, commands.OAuthLoginCommand, commands.DisconnectMenuCommand, commands.DisconnectCommand, commands.ContentPaginatorCommand, commands.ScheduleCommand, commands.ScheduleCommandListShow, commands.ScheduleCommandList, commands.ScheduleCommandDelete ] def __init__(self): self.updater = Updater(config('BOT_TOKEN'), workers=config('WORKERS', cast=int, default=3)) self.db = MongoBackend() self.jira = JiraBackend() self.AuthData = namedtuple( 'AuthData', 'auth_method jira_host username credentials') for command in self.commands: cb = command(self).command_callback() self.updater.dispatcher.add_handler(cb) self.updater.dispatcher.add_error_handler(self.error_callback) @staticmethod def send(bot, update, **kwargs): message_handler = MessageFactory.get_message_handler(update) if not message_handler: raise SendMessageHandlerError('Unable to get the handler') return message_handler(bot, update, **kwargs).send() def run_scheduler(self): queue = self.updater.job_queue bot = self.updater.bot scheduler = Scheduler(self, bot, queue) self.updater._init_thread(scheduler.run, "scheduler") def start(self): self.updater.start_polling() self.run_scheduler() logger.debug("Jira bot started successfully!") self.updater.idle() @property def commands(self): return self.__commands def authorization(self, telegram_id): """ Gets the user data and tries to log in according to the specified authorization method. Output of messages according to missing information :param telegram_id: user id telegram :return: returns a namedtuple for further authorization or bool and messages TODO: make refactoring in the future """ user_data = self.db.get_user_data(telegram_id) auth_method = user_data.get('auth_method') if not auth_method: raise BotAuthError( 'You are not authorized by any of the methods (user/pass or OAuth)' ) else: if auth_method == 'basic': credentials = (user_data.get('username'), utils.decrypt_password( user_data.get('auth')['basic']['password'])) else: host_data = self.db.get_host_data(user_data.get('host_url')) if not host_data: raise BotAuthError( 'In database there are no data on the {} host'.format( user_data.get('host_url'))) credentials = { 'access_token': user_data.get('auth')['oauth']['access_token'], 'access_token_secret': user_data.get('auth')['oauth']['access_token_secret'], 'consumer_key': host_data.get('consumer_key'), 'key_cert': utils.read_rsa_key(config('PRIVATE_KEY_PATH')) } auth_data = self.AuthData(auth_method, user_data.get('host_url'), user_data.get('username'), credentials) self.jira.check_authorization(auth_data.auth_method, auth_data.jira_host, auth_data.credentials, base_check=True) return auth_data def error_callback(self, bot, update, error): if config("DEBUG", False): traceback.print_exc(file=sys.stdout) try: raise error except (NetworkError, TimedOut, JiraReceivingDataException) as e: if isinstance(update, TelegramUpdate): logger.error( f"User={update.effective_user.username} Message={update.effective_message.text} Error={e.message})" ) self.send( bot, update, text="Something went wrong. Check your request or network." ) self.send(bot, update, text=self.commands[0](self).description) else: logger.error(f"Error={e.message})") except BaseJTBException as e: self.send(bot, update, text=e.message) except Exception as e: logger.critical( f"User={update.effective_user.username} Message={update.effective_message.text} Exception={e})" )