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 __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
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))
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')
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.")
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, })
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']
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']
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)
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 })
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)
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 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"))
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 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
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 []
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
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
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
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']
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() })
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
def init_mattermost_driver(): driver = Driver({ 'url': webhook.url, 'scheme': webhook.scheme, 'port': webhook.port, 'verify': webhook.verify }) return driver
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']
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)
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
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()
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 });
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