#!/usr/bin/env python import sys import json import datetime import os import os.path import functools import contextlib import asyncio import concurrent import logging import selectors from mattermostdriver import Driver, Websocket # pip install mattermostdriver import config logging.basicConfig(level='INFO') class MatterLog: __slots__ = ['driver', 'logdir'] def __init__(self, driver, loop, logdir): self.driver = driver self.logdir = logdir @contextlib.contextmanager def channel_file(self, team, channel): current = os.path.join(self.logdir, team, channel, 'current') os.makedirs(os.path.dirname(current), exist_ok=True) with open(current, 'a') as f: yield f async def __call__(self, event): event = json.loads(event) if 'event' not in event: logging.warning('received none-event: ' + str(event)) elif event['event'] in MatterLog.IGNORED_EVENTS: pass elif not hasattr(self, 'handle_' + event['event']): logging.warning('no handler for event: ' + str(event)) else: await getattr(self, 'handle_' + event['event'])(event['data']) IGNORED_EVENTS = { 'emoji_added', 'hello', 'license_changed', 'preference_changed', 'preferences_changed', 'preferences_deleted', 'typing' } async def handle_posted(self, data): post = json.loads(data['post']) with self.channel_file(self.cached_team_name(data['team_id']), data['channel_name']) as f: if post.get('root_id'): print(post['create_at'], post['id'], 'REPLY', post['original_id'], data['sender_name'][1:], post['message'], sep='\t', file=f) else: print(post['create_at'], post['id'], 'MSG', data['sender_name'][1:], post['message'], sep='\t', file=f) @functools.lru_cache def cached_team_name(self, team): return self.driver.teams.get_team(team)['name'] async def messageloop(driver, loop, logdir): selector = selectors.DefaultSelector() for team in driver.teams.get_teams(): for channel in driver.channels.get_channels_for_user(driver.client.userid, team['id']): sayfile = os.path.join(logdir, team['name'], channel['name'], 'say') if os.path.exists(sayfile): os.unlink(sayfile) os.makedirs(os.path.dirname(sayfile), exist_ok=True) os.mkfifo(sayfile) f = os.open(sayfile, os.O_RDONLY | os.O_NONBLOCK) selector.register(f, selectors.EVENT_READ, (sayfile, channel['id'])) while True: events = await loop.run_in_executor(None, lambda: selector.select()) for key, mask in events: selector.unregister(key.fileobj) message = os.read(key.fileobj, 1024) os.close(key.fileobj) if message: driver.posts.create_post(options={ 'channel_id': key.data[1], 'message': message.decode() }) f = os.open(key.data[0], os.O_RDONLY | os.O_NONBLOCK) selector.register(f, selectors.EVENT_READ, key.data) if __name__ == '__main__': driver = Driver(config.driver) driver.login() def listen(driver): websocket = Websocket(driver.options, driver.client.token) loop = asyncio.get_event_loop() loop.set_debug(True) loop.create_task(messageloop(driver, loop, config.logdir)) loop.run_until_complete(websocket.connect(MatterLog(driver, loop, config.logdir)))