def serve_once(self):
		self.driver = Driver({
			'scheme': self._scheme,
			'url': self.url,
			'port': self._port,
			'verify': not self.insecure,
			'timeout': self.timeout,
			'login_id': self._login,
			'password': self._password,
			'token': self._personal_access_token,
			'mfa_token': self._mfa_token
		})
		self.driver.login()

		self.teamid = self.driver.teams.get_team_by_name(name=self.team)['id']
		userid = self.driver.users.get_user(user_id='me')['id']

		self.token = self.driver.client.token

		self.bot_identifier = MattermostPerson(self.driver, userid=userid, teamid=self.teamid)

		# noinspection PyBroadException
		try:
			loop = self.driver.init_websocket(event_handler=self.mattermost_event_handler)
			self.reset_reconnection_count()
			loop.run_forever()
		except KeyboardInterrupt:
			log.info("Interrupt received, shutting down..")
			return True
		except Exception:
			log.exception("Error reading from RTM stream:")
		finally:
			log.debug("Triggering disconnect callback")
			self.disconnect_callback()
Example #2
0
    def __init__(self, message, filename, format_as_code, user, channel,
                 verbose):
        self.message = self._format_message(message, format_as_code)
        self.filename = filename
        self.user = user
        self.channel = channel
        self.verbose = verbose

        self._check_env_vars()

        self.mm_client = Driver({
            "url": config_vars["MATTERMOST_URL"],
            "login_id": config_vars["MATTERMOST_USERNAME"],
            "password": config_vars["MATTERMOST_PASSWORD"],
            "scheme": "https",
            "port": int(config_vars["MATTERMOST_PORT"]),
            "basepath": "/api/v4",
            "verify": True,
            "timeout": 30,
            "debug": False,
        })
        self.team_name = config_vars["MATTERMOST_TEAM_NAME"]

        try:
            self.mm_client.login()
        except ConnectionError:
            print("Unable to connect to the configured Mattermost server.")
            raise
Example #3
0
def mattermost(reportFile, issueList, target):
    accessToken = configs("mattermost")['accesstoken']
    reportFile = os.path.join(tempfile.gettempdir(), reportFile)
    issueList = '\n'.join('{}: {}'.format(*k) for k in enumerate(issueList, 1))
    mmost = Driver({
        'url': 'mattermost.viidakko.fi',
        'token': accessToken,
        'scheme': 'https',
        'port': 443,
        'basepath': '/api/v4',
        'verify': True,
        'timeout': 30,
        # 'mfa_token': 'TheMfaToken'
    })
    mmost.login()
    channel_id = mmost.api['channels'].get_channel_by_name_and_team_name(
        'development', 'burp-scan-reports')['id']
    file_id = mmost.api['files'].upload_file(
        channel_id=channel_id,
        files={'files': (reportFile, open(reportFile))})['file_infos'][0]['id']

    mmost.api['posts'].create_post(
        options={
            'channel_id': channel_id,
            'message': ':bell: **Scan result for ' + target + '**\n' +
            issueList,
            'file_ids': [file_id]
        })
    print(
        "\033[32m[+]\x1b[0m " +
        "Mattermost message sent, 'Burp Scan Report' channel, report file: \033[32m{}\x1b[0m"
        .format(reportFileName))
Example #4
0
class Client:
    def __init__(self, user_session: UserSession):
        self.user_session = user_session
        self.driver = Driver({
            'login_id': self.user_session.username,
            'password': self.user_session.password,
            'verify': False,
            'scheme': os.getenv('MATTERMOST_SCHEME'),
            'url': os.getenv('MATTERMOST_URL_WITH_PORT')
        })
        self.driver.login()

    def get_channels(self) -> List[Dict]:
        teams_list = self.driver.api['teams'].get_teams()

        def channels_per_team(team: Dict) -> List[Dict]:
            team_id = team.get('id')
            channels_list = \
              self.driver.api['teams'].get_public_channels(team_id)

            def channel_transformer(channel: Dict) -> Dict:
                return {
                    'id': channel.get('id'),
                    'name': channel.get('display_name')
                }

            return _.map_(channels_list, channel_transformer)

        return _.flatten(_.map_(teams_list, channels_per_team))

    def get_channel_messages(self, channel_id: str) -> List[Dict]:
        channel_messages: Dict = \
          self.driver.api['posts'].get_posts_for_channel(channel_id)

        def message_transformer(post_id: str) -> Dict:
            return {
                'userid':
                channel_messages.get('posts', {}).get(post_id,
                                                      {}).get('user_id'),
                'content':
                channel_messages.get('posts', {}).get(post_id,
                                                      {}).get('message'),
                'timestamp':
                datetime.fromtimestamp(
                    float(
                        channel_messages.get('posts', {}).get(
                            post_id, {}).get('create_at')) / 1000)
            }

        return sorted(_.map_(_.reverse(channel_messages.get('order')),
                             message_transformer),
                      key=lambda message: message.get('timestamp'))

    def get_channels_messages(self) -> List[List[Dict]]:
        return _.map_(
            self.get_channels(),
            lambda channel: self.get_channel_messages(channel.get('id')))

    def get_username_by_id(self, id: str) -> str:
        return self.driver.api['users'].get_user(id).get('username')
Example #5
0
def main():
    print("Creating Mattermost Driver...")
    driver_options = {
        'url': config.URL,
        'login_id': config.USERNAME,
        'password': config.PASSWORD,
        'port': config.PORT
    }
    driver = Driver(driver_options)

    print("Authenticating...")
    driver.login()
    driver.users.get_user('me')
    print("Successfully authenticated.")

    print("Retrieving Coffee Buddies participants...")
    team_name = config.TEAM_NAME
    channel_name = config.CHANNEL_NAME
    members = utils.get_channel_members(driver, team_name, channel_name)
    print("Successfully retrieved Coffee Buddies participants.")

    print("Preparing participants database...")
    utils.create_users(members)
    utils.create_pairs(members)
    print("Succesfully prepared participants database.")

    print("Pairing Coffee Buddies participants...")
    pairs = utils.get_pairs(members)
    print("Successfully paired Coffee Buddies participants.")

    print("Messaging paired Coffee Buddies participants...")
    utils.message_pairs(driver, pairs)
    print("Successfully messaged paired Coffee Buddies participants.")
Example #6
0
    def __init__(self, config, opsdroid=None):
        """Create the connector."""
        super().__init__(config, opsdroid=opsdroid)
        _LOGGER.debug(_("Starting Mattermost connector"))
        self.name = config.get("name", "mattermost")
        self.token = config["token"]
        self.url = config["url"]
        self.team_name = config["team-name"]
        self.scheme = config.get("scheme", "https")
        self.port = config.get("port", 8065)
        self.verify = config.get("ssl-verify", True)
        self.timeout = config.get("connect-timeout", 30)
        self.request_timeout = None
        self.mfa_token = None
        self.debug = False
        self.listening = True
        self.bot_id = None

        self.mm_driver = Driver({
            "url": self.url,
            "token": self.token,
            "scheme": self.scheme,
            "port": self.port,
            "verify": self.verify,
            "timeout": self.timeout,
            "request_timeout": self.request_timeout,
            "mfa_token": self.mfa_token,
            "debug": self.debug,
        })
Example #7
0
    def __init__(self,
                 url,
                 username,
                 password,
                 channel_name,
                 team_name,
                 help_text,
                 message_handler,
                 port=8065,
                 scheme='https',
                 debug=False):
        self.username = username
        self.help_text = help_text
        self.message_handler = message_handler
        self.debug = debug

        self.driver = Driver({
            'url': url,
            'port': port,
            'login_id': username,
            'password': password,
            'scheme': scheme,
            'debug': debug,
        })
        self.driver.login()

        # get userid for username since it is not automatically set to driver.client.userid ... for reasons
        res = self.driver.users.get_user_by_username(username)
        self.userid = res['id']

        # get channel id for name
        res = self.driver.channels.get_channel_by_name_and_team_name(
            team_name, channel_name)
        self.channel_id = res['id']
Example #8
0
    def serve_once(self):
        self.driver = Driver({
            'scheme': self._scheme,
            'url': self.url,
            'port': self._port,
            'verify': not self.insecure,
            'timeout': self.timeout,
            'login_id': self._login,
            'password': self._password
        })
        self.driver.login()

        self.userid = self.driver.api['users'].get_user(user_id='me')['id']

        self.token = self.driver.client.token

        try:
            loop = self.driver.init_websocket(
                event_handler=self.mattermost_event_handler)
            loop.run_forever()
            # loop.stop()
        except KeyboardInterrupt:
            log.info("Interrupt received, shutting down..")
            Notify.uninit()
            self.driver.logout()
            return True
        except Exception:
            log.exception("Error reading from RTM stream:")
        finally:
            log.debug("Triggering disconnect callback")
    def __init__(self,
                 url,
                 token,
                 channel_name,
                 team_name,
                 help_text,
                 message_handler,
                 port=8065,
                 scheme='https',
                 debug=False):
        self.help_text = help_text
        self.message_handler = message_handler
        self.debug = debug

        self.driver = Driver({
            'url': url,
            'port': port,
            'token': token,
            'scheme': scheme,
            'debug': debug,
        })
        user_result = self.driver.login()
        self.username = user_result["username"]
        self.userid = user_result["id"]

        # get channel id for name
        res = self.driver.channels.get_channel_by_name_and_team_name(
            team_name, channel_name)
        self.channel_id = res['id']
Example #10
0
 def __init__(self):
     connection_parameters = {
         'url': settings.MATTERMOST_SERVER,
         'port': settings.MATTERMOST_PORT,
         'username': settings.MATTERMOST_LOGIN_ID,
         'token': settings.MATTERMOST_ACCESS_TOKEN
     }
     self.mattermost_connection = Driver(connection_parameters)
Example #11
0
 def create_driver(self):
     self.driver = Driver({
         'url': self.host,
         'login_id': self.username,
         'password': self.passwd,
         'scheme': 'https' if self.https else 'http',
         'port': self.port
     })
Example #12
0
    def initialize(self):
        self.state = "idle"
        self.mm = None
        # login data
        self.username = self.settings.get("username", "")
        self.token = self.settings.get("token", "")
        self.login_id = self.settings.get("login_id", "")
        self.password = self.settings.get("password", "")
        # monitoring
        self.ttl = self.settings.get("ttl", 10) * 60
        self.notify_on_updates = self.settings.get("notify_on_updates", False)
        LOG.debug("username: {}".format(self.username))

        mm_driver_config = {
            'url': self.settings.get("url", "chat.mycroft.ai"),
            'scheme': self.settings.get("scheme", "https"),
            'port': self.settings.get("port", 443),
            'verify': self.settings.get("verify", True)
        }

        if self.token:
            mm_driver_config['token'] = self.token
        elif self.login_id and self.password:
            mm_driver_config['login_id'] = self.login_id
            mm_driver_config['password'] = self.password

        if self.username:
            self.mm = Driver(mm_driver_config)
            try:
                self.mm.login()
                self.userid = \
                    self.mm.users.get_user_by_username(self.username)['id']
                # TODO check if user is member of several teams?
                self.teamid = self.mm.teams.get_team_members_for_user(
                    self.userid)[0]['team_id']
                LOG.debug("userid: {} teamid: {}".format(
                    self.userid, self.teamid))
            except (mme.ResourceNotFound, mme.HTTPError,
                    mme.NoAccessTokenProvided, mme.NotEnoughPermissions,
                    mme.InvalidOrMissingParameters) as e:
                LOG.debug("Exception: {}".format(e))
                self.mm = None
                self.speak_dialog("mattermost.error", {'exception': e})
            if self.mm:
                # info on all subscribed public channels as returned by MM
                self.channel_subscriptions = None
                self.channel_subs_ts = 0
                # basic info of channels
                # channel_id, display_name, (unread) msg_count, mentions
                self.channel_info = None
                self.channel_info_ts = 0
                self.usercache = {}
                self.prev_unread = 0
                self.prev_mentions = 0
                self.monitoring = False

        # Check and then monitor for credential changes
        self.settings.set_changed_callback(self.on_websettings_changed)
Example #13
0
 def __init__(self, user_session: UserSession):
     self.user_session = user_session
     self.driver = Driver({
         'login_id': self.user_session.username,
         'password': self.user_session.password,
         'verify': False,
         'scheme': os.getenv('MATTERMOST_SCHEME'),
         'url': os.getenv('MATTERMOST_URL_WITH_PORT')
     })
     self.driver.login()
Example #14
0
    def upload(src: str) -> None:
        driver = Driver({
            'scheme': 'https',
            'url': MattermostImport._getHost(),
            'token': MattermostImport._getToken(),
            'port': 443,
        })

        driver.login()
        print(driver.emoji.get_custom_emoji_by_name("doge"))
Example #15
0
    def __init__(self, settings):
        assert Connection.instance is None
        self.driver = Driver(settings)
        #self.driver.client.activate_verbose_logging()
        self.bot = Bot(convert_to_mmpy_bot(settings))
        Connection.instance = self

        # workaround for python versions < 3.7
        if sys.version_info.major < 3 or sys.version_info.minor < 7:
            api.load_driver_attributes()
Example #16
0
def connect(host: str, login_token: str = None, username: str = None, password: str = None) -> Driver:
    d = Driver({
        "url": host,
        "port": 443,
        "token": login_token,
        "username": username,
        "password": password
    })
    d.login()
    return d
Example #17
0
 def __init__(self, mail, pswd, url, tags=None, debug=False):
     self.debug = debug
     self.config = {"url": url,
                    "login_id": mail,
                    "password": pswd,
                    "scheme": "https",
                    "port": 443,
                    "verify": True,
                    "debug": debug}
     self.driver = Driver(self.config)
     self.tags = tags or []
Example #18
0
def get_driver():
    my_driver = Driver({
        'url': params['MM_IP'],
        'token': params['PLOT_BOT_TOKEN'],
        'scheme': 'http',
        'port': 8065,
        'basepath': '/api/v4',
        'debug': False
    })
    my_driver.login()
    return my_driver
Example #19
0
 def mattermost_login(self):
     mm = Driver({
         'url': os.environ['MATTERMOST_ADDRESS'],
         'login_id': os.environ['MATTERMOST_LOGIN_ID'],
         'password': os.environ['MATTERMOST_PASSWORD'],
         'scheme': 'http',
         'port': int(os.environ['MATTERMOST_PORT']),
         'basepath': '/api/v3',
         'timeout': 30,
     })
     mm.login()
     return mm
Example #20
0
def login(url, port, username, password, token):
    driver = Driver({
        'url': url,
        'port': int(port),
        'login_id': username,
        'password': password,
        'basepath': '/api/v4',
        'scheme': 'http',
        'token': token
    })

    driver.login()
    return driver
Example #21
0
 def __init__(self, mattermost_setting, username, mattermost_user_setting, selector: MattermostChannelSelect):
     self.driver = Driver({
         'url': mattermost_setting['url'],
         'token': mattermost_user_setting['token'],
         'scheme': mattermost_setting['scheme'],
         'port': mattermost_setting['port'],
         'basepath': mattermost_setting['basepath'],
         'timeout': mattermost_setting['timeout']
     })
     self.username = username
     team_name = mattermost_user_setting['team_name']
     self.driver.login()
     self.selector = selector
     self.team_id = self.driver.teams.get_team_by_name(team_name)['id']
Example #22
0
 def __init__(self, config):
     self.logger = logging.getLogger('lthub.mattermost.sync')
     self.config = config
     self.driver = Driver({
         k: config[k]
         for k in config.keys() & Driver.default_options.keys()
     })
Example #23
0
    def __init__(self):
        with open('settings.yaml', "r") as file:
            self.parsed_data = yaml.safe_load(file)

        self.matt = Driver({
            'url':
            self.parsed_data["Raccooner"]["mattermost"]["general"]["url"],
            'token':
            self.parsed_data["Raccooner"]["mattermost"]["general"]
            ["access_token"],
            'port':
            self.parsed_data["Raccooner"]["mattermost"]["general"]["port"],
            'debug':
            False
        })
        self.matt.login()  # Automatic login
        self.raccoon_stats = {}  # Create an empty dictionary
Example #24
0
def init_mattermost_driver():
    driver = Driver({
        'url': webhook.url,
        'scheme': webhook.scheme,
        'port': webhook.port,
        'verify': webhook.verify
    })
    return driver
Example #25
0
    def __init__(self, username, password, scheme='https', debug=False):
        self.subscriptions = set()

        self.username = username
        self.debug = debug

        self.driver = Driver({
            'url': "192.168.122.254",
            'login_id': username,
            'password': password,
            'scheme': scheme,
            'debug': debug,
        })
        self.driver.login()

        # get userid for username since it is not automatically set to driver.client.userid ... for reasons
        res = self.driver.users.get_user_by_username('bot')
        self.userid = res['id']
Example #26
0
 def __init__(self, **kwargs):
     """Initialize Mattermost class which extends Chat ABC
     Args:
         **kwargs (dict): args to pass to Chat ABC
         team_channel (str): required for posting to channel
         channel_name (str): required for posting to channel
     """
     super().__init__(**kwargs)
     self.inst = Driver({
         'url': self.url,
         'verify': False,
         'token': self.api_key,
         'username': self.username,
         'port': kwargs.get('port', 8065)
     })
     self.team_name = kwargs.get("team_name", None)
     self.channel_name = kwargs.get("channel_name", None)
     self.filepath = kwargs.get("filepath", None)
Example #27
0
def upload_file(connector=Driver(), channel_id="", path=None):
    if path is not None:
        f = connector.files.upload_file(channel_id, {
            "files": open(path, "rb"),
            "filename": path
        })
        return f["file_infos"][0]["id"]
    else:
        return None
Example #28
0
class Connection:
    """
	This class allows the user to connect to a Mattermost server and start the bot.
	
	There should be only one instance per program."""

    instance = None

    def __init__(self, settings):
        assert Connection.instance is None
        self.driver = Driver(settings)
        #self.driver.client.activate_verbose_logging()
        self.bot = Bot(convert_to_mmpy_bot(settings))
        Connection.instance = self

        # workaround for python versions < 3.7
        if sys.version_info.major < 3 or sys.version_info.minor < 7:
            api.load_driver_attributes()

    def __getattr__(self, name):
        return getattr(self.driver, name)

    def login(self):
        logger.info("login...")
        self.driver.login()
        api.me = api.User.by_id(self.driver.client.userid)
        logger.info(f"logged in as {api.me}")

    def start(self):
        # start bot at the end because the method will not return unless an exception occurs
        logger.info("start bot...")
        self.bot.run()
        logger.info("bot started")

    def stop(self):
        self.driver.logout()
        # TODO stop bot

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, type, value, tb):
        self.stop()
Example #29
0
def get_driver():
    return Driver({
        'url': config.get('myvars','HOST'),
        'scheme': 'http',
        'port': 8065,
        'basepath': '/api/v4',
        'verify': True,
        'token': config.get('myvars','BOTTOKEN'),
        'debug': False
    });
Example #30
0
def init(channel, access_token, team_id = 'n1f7ge4biifutrbeq9wdx6sz6y') :
    global mm, channel_id
    # Tuning needed here to switch between GNA and DRW instance may be
    # static override with gna team id to post on gna channels
    # at current time get_team() doesnt return gna team id , only DRW one at index 0 - will need to chat to drw about this.
    # team_id = mm.api['teams'].get_teams()[0]['id']

    url = 'chat.drwholdings.com'
    mm = Driver({
        'url': url,
        'token': access_token,
        'scheme': 'https',
        'port': 443,
        'basepath': '/api/v4',
        'verify': True,
        'timeout': 20
    })
    r = mm.login()
    channel_id = mm.api['channels'].get_channel_by_name(team_id, channel)['id']    
    return r
class MattermostBackend(ErrBot):
	def __init__(self, config):
		super().__init__(config)
		identity = config.BOT_IDENTITY
		self._login = identity.get('login', None)
		self._password = identity.get('password', None)
		self._personal_access_token = identity.get('token', None)
		self._mfa_token = identity.get('mfa_token', None)
		self.team = identity.get('team')
		self._scheme = identity.get('scheme', 'https')
		self._port = identity.get('port', 8065)
		self.cards_hook = identity.get('cards_hook', None)
		self.url = identity.get('server').rstrip('/')
		self.insecure = identity.get('insecure', False)
		self.timeout = identity.get('timeout', DEFAULT_TIMEOUT)
		self.teamid = ''
		self.token = ''
		self.bot_identifier = None
		self.driver = None
		self.md = md()

	@property
	def userid(self):
		return "{}".format(self.bot_identifier.userid)

	@property
	def mode(self):
		return 'mattermost'

	def username_to_userid(self, name):
		"""Converts a name prefixed with @ to the userid"""
		name = name.lstrip('@')
		user = self.driver.users.get_user_by_username(username=name)
		if user is None:
			raise UserDoesNotExistError("Cannot find user {}".format(name))
		return user['id']

	@asyncio.coroutine
	def mattermost_event_handler(self, payload):
		if not payload:
			return

		payload = json.loads(payload)
		if 'event' not in payload:
			log.debug("Message contains no event: {}".format(payload))
			return

		event_handlers = {
			'posted': self._message_event_handler,
			'status_change': self._status_change_event_handler,
			'hello': self._hello_event_handler,
			'user_added': self._room_joined_event_handler,
			'user_removed': self._room_left_event_handler,
		}

		event = payload['event']
		event_handler = event_handlers.get(event)

		if event_handler is None:
			log.debug("No event handler available for {}, ignoring.".format(event))
			return
		# noinspection PyBroadException
		try:
			event_handler(payload)
		except Exception:
			log.exception("{} event handler raised an exception".format(event))

	def _room_joined_event_handler(self, message):
		log.debug('User added to channel')
		if message['data']['user_id'] == self.userid:
			self.callback_room_joined(self)

	def _room_left_event_handler(self, message):
		log.debug('User removed from channel')
		if message['broadcast']['user_id'] == self.userid:
			self.callback_room_left(self)

	def _message_event_handler(self, message):
		log.debug(message)
		data = message['data']

		# In some cases (direct messages) team_id is an empty string
		if data['team_id'] != '' and self.teamid != data['team_id']:
			log.info("Message came from another team ({}), ignoring...".format(data['team_id']))
			return

		broadcast = message['broadcast']

		if 'channel_id' in data:
			channelid = data['channel_id']
		elif 'channel_id' in broadcast:
			channelid = broadcast['channel_id']
		else:
			log.error("Couldn't find a channelid for event {}".format(message))
			return

		channel_type = data['channel_type']

		if channel_type != 'D':
			channel = data['channel_name']
		else:
			channel = channelid

		text = ''
		post_id = ''
		file_ids = None
		userid = None

		if 'post' in data:
			post = json.loads(data['post'])
			text = post['message']
			userid = post['user_id']
			if 'file_ids' in post:
				file_ids = post['file_ids']
			post_id = post['id']
			if 'type' in post and post['type'] == 'system_add_remove':
				log.info("Ignoring message from System")
				return

		if 'user_id' in data:
			userid = data['user_id']

		if not userid:
			log.error('No userid in event {}'.format(message))
			return

		mentions = []
		if 'mentions' in data:
			# TODO: Only user, not channel mentions are in here at the moment
			mentions = self.mentions_build_identifier(json.loads(data['mentions']))

		# Thread root post id
		root_id = post.get('root_id')
		if root_id is '':
			root_id = post_id

		msg = Message(
			text,
			extras={
				'id': post_id,
				'root_id': root_id,
				'mattermost_event': message,
				'url': '{scheme:s}://{domain:s}:{port:s}/{teamname:s}/pl/{postid:s}'.format(
					scheme=self.driver.options['scheme'],
					domain=self.driver.options['url'],
					port=str(self.driver.options['port']),
					teamname=self.team,
					postid=post_id
				)
			}
		)
		if file_ids:
			msg.extras['attachments'] = file_ids

		# TODO: Slack handles bots here, but I am not sure if bot users is a concept in mattermost
		if channel_type == 'D':
			msg.frm = MattermostPerson(self.driver, userid=userid, channelid=channelid, teamid=self.teamid)
			msg.to = MattermostPerson(
				self.driver, userid=self.bot_identifier.userid, channelid=channelid, teamid=self.teamid)
		elif channel_type == 'O' or channel_type == 'P':
			msg.frm = MattermostRoomOccupant(self.driver, userid=userid, channelid=channelid, teamid=self.teamid, bot=self)
			msg.to = MattermostRoom(channel, teamid=self.teamid, bot=self)
		else:
			log.warning('Unknown channel type \'{}\'! Unable to handle {}.'.format(
				channel_type,
				channel
			))
			return

		self.callback_message(msg)

		if mentions:
			self.callback_mention(msg, mentions)

	def _status_change_event_handler(self, message):
		"""Event handler for the 'presence_change' event"""
		idd = MattermostPerson(self.driver, message['data']['user_id'])
		status = message['data']['status']
		if status == 'online':
			status = ONLINE
		elif status == 'away':
			status = AWAY
		else:
			log.error(
				"It appears the Mattermost API changed, I received an unknown status type %s" % status
			)
			status = ONLINE
		self.callback_presence(Presence(identifier=idd, status=status))

	def _hello_event_handler(self, message):
		"""Event handler for the 'hello' event"""
		self.connect_callback()
		self.callback_presence(Presence(identifier=self.bot_identifier, status=ONLINE))

	@lru_cache(1024)
	def get_direct_channel(self, userid, other_user_id):
		"""
		Get the direct channel to another user.
		If it does not exist, it will be created.
		"""
		try:
			return self.driver.channels.create_direct_message_channel(options=[userid, other_user_id])
		except (InvalidOrMissingParameters, NotEnoughPermissions):
			raise RoomDoesNotExistError("Could not find Direct Channel for users with ID {} and {}".format(
				userid, other_user_id
			))

	def build_identifier(self, txtrep):
		"""
		Convert a textual representation into a :class:`~MattermostPerson` or :class:`~MattermostRoom`

		Supports strings with the following formats::

			@username
			~channelname
			channelid
		"""
		txtrep = txtrep.strip()
		if txtrep.startswith('~'):
			# Channel
			channelid = self.channelname_to_channelid(txtrep[1:])
			if channelid is not None:
				return MattermostRoom(channelid=channelid, teamid=self.teamid, bot=self)
		else:
			# Assuming either a channelid or a username
			if txtrep.startswith('@'):
				# Username
				userid = self.username_to_userid(txtrep[1:])
			else:
				# Channelid
				userid = txtrep

			if userid is not None:
				return MattermostPerson(
					self.driver,
					userid=userid,
					channelid=self.get_direct_channel(self.userid, userid)['id'],
					teamid=self.teamid
				)
		raise Exception(
			'Invalid or unsupported Mattermost identifier: %s' % txtrep
		)

	def mentions_build_identifier(self, mentions):
		identifier = []
		for mention in mentions:
			if mention != self.bot_identifier.userid:
				identifier.append(
					self.build_identifier(mention)
				)
		return identifier

	def serve_once(self):
		self.driver = Driver({
			'scheme': self._scheme,
			'url': self.url,
			'port': self._port,
			'verify': not self.insecure,
			'timeout': self.timeout,
			'login_id': self._login,
			'password': self._password,
			'token': self._personal_access_token,
			'mfa_token': self._mfa_token
		})
		self.driver.login()

		self.teamid = self.driver.teams.get_team_by_name(name=self.team)['id']
		userid = self.driver.users.get_user(user_id='me')['id']

		self.token = self.driver.client.token

		self.bot_identifier = MattermostPerson(self.driver, userid=userid, teamid=self.teamid)

		# noinspection PyBroadException
		try:
			loop = self.driver.init_websocket(event_handler=self.mattermost_event_handler)
			self.reset_reconnection_count()
			loop.run_forever()
		except KeyboardInterrupt:
			log.info("Interrupt received, shutting down..")
			return True
		except Exception:
			log.exception("Error reading from RTM stream:")
		finally:
			log.debug("Triggering disconnect callback")
			self.disconnect_callback()

	def _prepare_message(self, message):
		to_name = "<unknown>"
		if message.is_group:
			to_channel_id = message.to.id
			if message.to.name:
				to_name = message.to.name
			else:
				self.channelid_to_channelname(channelid=to_channel_id)
		else:
			to_name = message.to.username

			if isinstance(message.to, RoomOccupant):  # private to a room occupant -> this is a divert to private !
				log.debug("This is a divert to private message, sending it directly to the user.")
				channel = self.get_direct_channel(self.userid, self.username_to_userid(to_name))
				to_channel_id = channel['id']
			else:
				to_channel_id = message.to.channelid
		return to_name, to_channel_id

	def send_message(self, message):
		super().send_message(message)
		try:
			to_name, to_channel_id = self._prepare_message(message)

			message_type = "direct" if message.is_direct else "channel"
			log.debug('Sending %s message to %s (%s)' % (message_type, to_name, to_channel_id))

			body = self.md.convert(message.body)
			log.debug('Message size: %d' % len(body))

			limit = min(self.bot_config.MESSAGE_SIZE_LIMIT, MATTERMOST_MESSAGE_LIMIT)
			parts = self.prepare_message_body(body, limit)

			root_id = None
			if message.parent is not None:
				root_id = message.parent.extras.get('root_id')

			for part in parts:
				self.driver.posts.create_post(options={
					'channel_id': to_channel_id,
					'message': part,
					'root_id': root_id,
				})
		except (InvalidOrMissingParameters, NotEnoughPermissions):
			log.exception(
				"An exception occurred while trying to send the following message "
				"to %s: %s" % (to_name, message.body)
			)

	def send_card(self, card: Card):
		if isinstance(card.to, RoomOccupant):
			card.to = card.to.room

		to_humanreadable, to_channel_id = self._prepare_message(card)

		attachment = {}
		if card.summary:
			attachment['pretext'] = card.summary
		if card.title:
			attachment['title'] = card.title
		if card.link:
			attachment['title_link'] = card.link
		if card.image:
			attachment['image_url'] = card.image
		if card.thumbnail:
			attachment['thumb_url'] = card.thumbnail
		attachment['text'] = card.body

		if card.color:
			attachment['color'] = COLORS[card.color] if card.color in COLORS else card.color

		if card.fields:
			attachment['fields'] = [{'title': key, 'value': value, 'short': True} for key, value in card.fields]

		data = {
			'attachments': [attachment]
		}

		if card.to:
			if isinstance(card.to, MattermostRoom):
				data['channel'] = card.to.name

		try:
			log.debug('Sending data:\n%s', data)
			# We need to send a webhook - mattermost has no api endpoint for attachments/cards
			# For this reason, we need to build our own url, since we need /hooks and not /api/v4
			# Todo: Reminder to check if this is still the case
			self.driver.webhooks.call_webhook(self.cards_hook, options=data)
		except (
					InvalidOrMissingParameters,
					NotEnoughPermissions,
					ContentTooLarge,
					FeatureDisabled,
					NoAccessTokenProvided
				):
			log.exception(
				"An exception occurred while trying to send a card to %s.[%s]" % (to_humanreadable, card)
			)

	def prepare_message_body(self, body, size_limit):
		"""
		Returns the parts of a message chunked and ready for sending.
		This is a staticmethod for easier testing.
		Args:
			body (str)
			size_limit (int): chunk the body into sizes capped at this maximum
		Returns:
			[str]
		"""
		fixed_format = body.startswith('```')  # hack to fix the formatting
		parts = list(split_string_after(body, size_limit))

		if len(parts) == 1:
			# If we've got an open fixed block, close it out
			if parts[0].count('```') % 2 != 0:
				parts[0] += '\n```\n'
		else:
			for i, part in enumerate(parts):
				starts_with_code = part.startswith('```')

				# If we're continuing a fixed block from the last part
				if fixed_format and not starts_with_code:
					parts[i] = '```\n' + part

				# If we've got an open fixed block, close it out
				if parts[i].count('```') % 2 != 0:
					parts[i] += '\n```\n'

		return parts

	def change_presence(self, status: str=ONLINE, message: str=''):
		pass  # Mattermost does not have a request/websocket event to change the presence

	def is_from_self(self, message: Message):
		return self.bot_identifier.userid == message.frm.userid

	def shutdown(self):
		self.driver.logout()
		super().shutdown()

	def query_room(self, room):
		""" Room can either be a name or a channelid """
		return MattermostRoom(room, teamid=self.teamid, bot=self)

	def prefix_groupchat_reply(self, message: Message, identifier):
		super().prefix_groupchat_reply(message, identifier)
		message.body = '@{0}: {1}'.format(identifier.nick, message.body)

	def build_reply(self, message, text=None, private=False, threaded=False):
		response = self.build_message(text)
		response.frm = self.bot_identifier
		if private:
			response.to = message.frm
		else:
			response.to = message.frm.room if isinstance(message.frm, RoomOccupant) else message.frm

		if threaded:
			response.extras['root_id'] = message.extras.get('root_id')
			self.driver.posts.get_post(message.extras.get('root_id'))
			response.parent = message

		return response

	def get_public_channels(self):
		channels = []
		page = 0
		channel_page_limit = 200
		while True:
			channel_list = self.driver.channels.get_public_channels(
				team_id=self.teamid,
				params={'page': page, 'per_page': channel_page_limit}
			)
			if len(channel_list) == 0:
				break
			else:
				channels.extend(channel_list)
			page += 1
		return channels

	def channels(self, joined_only=False):
		channels = []
		channels.extend(self.driver.channels.get_channels_for_user(user_id=self.userid, team_id=self.teamid))
		if not joined_only:
			public_channels = self.get_public_channels()
			for channel in public_channels:
				if channel not in channels:
					channels.append(channel)
		return channels

	def rooms(self):
		"""Return public and private channels, but no direct channels"""
		rooms = self.channels(joined_only=True)
		channels = [channel for channel in rooms if channel['type'] != 'D']
		return [MattermostRoom(channelid=channel['id'], teamid=channel['team_id'], bot=self) for channel in channels]

	def channelid_to_channelname(self, channelid):
		"""Convert the channelid in the current team to the channel name"""
		channel = self.driver.channels.get_channel(channel_id=channelid)
		if 'name' not in channel:
			raise RoomDoesNotExistError("No channel with ID {} exists in team with ID {}".format(
				id, self.teamid
			))
		return channel['name']

	def channelname_to_channelid(self, name):
		"""Convert the channelname in the current team to the channel id"""
		channel = self.driver.channels.get_channel_by_name(team_id=self.teamid, channel_name=name)
		if 'id' not in channel:
			raise RoomDoesNotExistError("No channel with name {} exists in team with ID {}".format(
				name, self.teamid
			))
		return channel['id']

	def __hash__(self):
		return 0  # This is a singleton anyway