Пример #1
0

@bot.command(r".+")
def undefined(chat: Chat, match):
    log(chat)


def log(chat: Chat):
    chat_id = chat.message["chat"]["id"]
    text = chat.message["text"].replace("\n", " ")
    print("{}:{:.3f}:{}".format(chat_id, time(), text), flush=True)


async def main():
    global redis
    redis = await aioredis.create_redis(("localhost", 6379), encoding="utf-8")
    await bot.loop()


if __name__ == "__main__":
    asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
    loop = asyncio.get_event_loop()

    # handle Supervisor stop signal
    loop.add_signal_handler(signal.SIGTERM, lambda: bot.stop())

    try:
        loop.run_until_complete(main())
    except KeyboardInterrupt:
        bot.stop()
Пример #2
0
        torinfo = handle.get_torrent_info()
        if not torinfo:
            raise ValueError('magnet2torrent: failed getting torrent info')
        torfile = libtorrent.create_torrent(torinfo)
    except Exception:
        logger.exception('magnet2torrent: failed creating torrent file')
        return

    try:
        torrent_content = libtorrent.bencode(torfile.generate())
        if torrent_content:
            logger.info('magnet2torrent: done [%s]', magnet)
            return torinfo.name() + '.torrent', torrent_content
        else:
            logger.error('magnet2torrent: empty torrent content body [%s]',
                         magnet)
    except Exception:
        logger.exception('magnet2torrent: torrent generating problem [%s]',
                         magnet)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        bots = [t2m_bot.loop(), m2t_bot.loop()]
        loop.run_until_complete(asyncio.wait(bots))
    except KeyboardInterrupt:
        t2m_bot.stop()
        m2t_bot.stop()
        loop.stop()
Пример #3
0
            return await original_api_call(method, **params)
    bot.api_call = api_call_with_handle_exceptions


if __name__ == '__main__':
    logger.setLevel(logging.DEBUG)
    logger.warning('Start RZD telegram bot...')

    # do not get down on aiohttp exceptions
    patch_bot_api_call(bot)

    loop = asyncio.get_event_loop()
    bot_future = asyncio.ensure_future(bot.loop())
    task_future = asyncio.ensure_future(process_queue())
    try:
        loop.run_until_complete(bot_future)
        # bot.run()
    except KeyboardInterrupt:
        bot_future.cancel()
        task_future.cancel()
        loop.run_until_complete(stop_bot())
        bot.stop()
        # raise
    # except:  # noqa
    #     pass
    finally:
        loop.run_until_complete(bot.session.close())
        logger.debug("Closing loop")
    loop.stop()
    loop.close()
Пример #4
0
class TelegramAgent(Agent):
    def __init__(self, name=None):
        super().__init__(name=name)
        self._bot = Bot(api_token=TELEGRAM_BOT_API_TOKEN,
                        name=TELEGRAM_BOT_NAME,
                        api_timeout=10)
        self._bot.default(self.on_incoming_telegram_message)
        self.sent_telegram_messages = {}

    def on_incoming_telegram_message(self, chat, message):
        try:
            text = message['text']
            name = text
            if len(name) > FRAME_NAME_MAX_LENGTH:
                name = name[:FRAME_NAME_MAX_LENGTH - 1] + '…'
            full_name = '{} {}'.format(message['from']['first_name'],
                                       message['from']['last_name'])
            sent_msg = self.message(name,
                                    data={
                                        'text': text,
                                        'message_id': message['message_id'],
                                        'chat_id': message['chat']['id'],
                                        'user_id': message['from']['id'],
                                        'username':
                                        message['from']['username'],
                                        'full_name': full_name,
                                        'session': message['chat']['id'],
                                    })
            self.sent_telegram_messages.update(
                {sent_msg.id: message['chat']['id']})
        except Exception:
            print(traceback.format_exc())
            print(message.name, message.data)

    @on_message('*')
    async def telegram_message_reply(self, message):
        if message.reply_to not in self.sent_telegram_messages:
            return
        text = message.data.text or message.name
        try:
            chat_id = self.sent_telegram_messages[message.reply_to]
            # del self.sent_telegram_messages[message.reply_to]
            await self._bot.send_message(chat_id=chat_id, text=text)
            self.emit('telegram_reply_sent',
                      data={
                          'text': text,
                          'chat_id': chat_id
                      })
        except Exception:
            print(traceback.format_exc())
            print(message.name, message.data)

    @on_event('send_telegram_message')
    async def send_message(self, event):
        text = event.data.text
        chat_id = event.data.session or event.data.chat_id
        if not chat_id:
            self.emit('telegram_message_error',
                      data={'text': 'session or chat_id expected.'})
            return
        try:
            ret_val = await self._bot.send_message(chat_id=chat_id, text=text)
            self.emit('telegram_message_sent',
                      data={
                          'text': text,
                          'chat_id': chat_id,
                          'return': ret_val
                      })
        except Exception:
            print(traceback.format_exc())
            print(event.name, event.data)

    @on_event('*** started')
    def on_started(self, event):
        self.emit('telegram_connecting')
        self.spawn(self._bot.loop())

    @on_event('*** stopping')
    def on_stopping(self, event):
        self._bot.stop()
Пример #5
0
    session.pause()
    try:
        torinfo = handle.get_torrent_info()
        if not torinfo:
            raise ValueError('magnet2torrent: failed getting torrent info')
        torfile = libtorrent.create_torrent(torinfo)
    except Exception:
        logger.exception('magnet2torrent: failed creating torrent file')
        return

    try:
        torrent_content = libtorrent.bencode(torfile.generate())
        if torrent_content:
            logger.info('magnet2torrent: done [%s]', magnet)
            return torinfo.name() + '.torrent', torrent_content
        else:
            logger.error('magnet2torrent: empty torrent content body [%s]', magnet)
    except Exception:
        logger.exception('magnet2torrent: torrent generating problem [%s]', magnet)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        bots = [t2m_bot.loop(), m2t_bot.loop()]
        loop.run_until_complete(asyncio.wait(bots))
    except KeyboardInterrupt:
        t2m_bot.stop()
        m2t_bot.stop()
        loop.stop()
Пример #6
0
class CamBot:
    def __init__(self, agent: 'gphotos.GooglePhotosManager',
                 manager: vkmanager.VKManager):
        self._bot = Bot(conf.bot_token, proxy=conf.tele_proxy)
        self.session = self._bot.session
        self.loop = self._bot.loop
        self.menu_markup = Menu()
        self.init_handlers()
        self.agent = agent
        self.vk_manager = manager

    def init_handlers(self):
        self._bot.add_command(r'/mov (.+) (.+)', self.mov)
        self._bot.add_command(r'/push_vk (.+) (.+)', self.push_vk)
        self._bot.add_command(r'/check (.+) (.+)', self.check_album)
        self._bot.add_command(r'/full_check (.+)', self.full_check)
        self._bot.add_command(r'/clear (.+)', self.clear_command)
        self._bot.add_command(r'/reg', reg)
        self._bot.add_command(r'/ch', self.reg_channel)
        self._bot.add_command(r'/photo_reg', self.reg_photo_channel)
        self._bot.add_command(r'/menu', self.menu)
        self._bot.add_command(r'/all', self.img_all_cams)
        self._bot.add_command(r'/stats (.+)', self.stats_command)
        self._bot.add_command(r'/stats', self.stats_command)
        self._bot.add_command(r'/lstats (.+)', self.lstats_command)
        self._bot.add_command(r'/lstats', self.lstats_command)
        self._bot.add_command(r'/dbdata', self.db_data)
        self._bot.add_command(r'/daily', self.daily_movie_group_command)
        self._bot.add_command(r'/push_on', self.push_vk_on)
        self._bot.add_command(r'/push_off', self.push_vk_off)
        self._bot.add_callback(r'regular (.+)', regular)
        self._bot.add_callback(r'today (.+)', today)
        self._bot.add_callback(r'weekly (.+)', weekly)
        self._bot.add_callback(r'select (.+)', self.select)
        self._bot.add_callback(r'back', self.back)
        self._bot.add_callback(r'img (.+)', self.img_callback)
        self._bot.add_callback(r'choose_cam (.+)', self.choose_cam_callback)
        self._bot.add_callback(r'choose_photo_cam (.+)',
                               self.choose_photo_cam_callback)
        self._bot.add_callback(r'sync (.+)', self.sync_gphotos)
        self._bot.add_callback(r'gsnc (.+)', self.run_sync_gphotos)
        self._bot.add_callback(r'remove (.+)', self.remove_folder)
        self._bot.add_callback(r'post (.+) (.+)', self.post_photo)
        self._bot.add_callback(r'clear_cb (.+)', self.clear_callback)
        self._bot.add_callback(r'check_cb (.+)', self.full_check_callback)
        self._bot.callback(unhandled_callbacks)

    def stop(self):
        self._bot.stop()

    @ThreadSwitcherWithDB.optimized
    async def daily_stats(self):
        await self.stats_request(pendulum.yesterday(), self.notify_admins)

    @ThreadSwitcherWithDB.optimized
    async def daily_movie(self, cam: Cam):
        day = datetime.datetime.now() - datetime.timedelta(days=1)
        day = day.strftime('%d_%m_%Y')
        loop = asyncio.get_event_loop()
        with concurrent.futures.ThreadPoolExecutor() as pool:
            try:
                clip = await loop.run_in_executor(pool,
                                                  lambda: make_movie(cam, day))
            except FileNotFoundError as exc:
                logger.exception(exc)
                await self.notify_admins(
                    f'File {exc.filename} not found for daily movie {cam.name}: {day}'
                )
                return
            except Exception as exc:
                logger.exception(exc)
                await self.notify_admins(
                    f'Error during making daily movie for {cam.name}: {day}')
                return
        if cam.update_channel:
            async with db_in_thread():
                channels = db.query(Channel).filter(
                    Channel.cam == cam.name).all()
            for channel in channels:
                await send_video(Chat(self._bot, channel.chat_id), clip)
        await self.notify_admins(f'Daily movie for {cam.name}: {day} ready!')
        for chat in await self.admin_chats():
            await send_video(Chat(self._bot, chat.chat_id), clip)

    async def push_vk(self, chat, match):
        cam = await get_cam(match.group(1), chat)
        if not cam:
            return
        day = match.group(2)
        path = Path(conf.root_dir
                    ) / 'data' / cam.name / 'regular' / 'clips' / f'{day}.mp4'
        if not path.exists():
            await chat.send_text('Movie file does not exist!')
            return
        try:
            await self.vk_manager.new_post(cam.name, str(path),
                                           day.replace('_', ' '),
                                           day.replace('_', '/'))
        except vkmanager.VKManagerError as exc:
            logger.exception('Error during pushing video to vk')
            await chat.send_text(exc.detail)
        except Exception:
            logger.exception('Unhandled exception during pushing video to vk')
            await chat.send_text('Unhandled error!')
        await chat.send_text('Movie successfully published')

    async def mov(self, chat, match):
        """
        Make movie for specified cam and day. Example: /mov favcam 25_04_2019
        :param chat:
        :param match:
        :return:
        """
        cam = await get_cam(match.group(1), chat)
        if not cam:
            return
        day = match.group(2)
        loop = asyncio.get_event_loop()
        with concurrent.futures.ThreadPoolExecutor() as pool:
            try:
                clip = await loop.run_in_executor(pool,
                                                  lambda: make_movie(cam, day))
            except Exception:
                logger.exception('Error during movie request')
                await self.notify_admins(
                    f'Error during movie request {day} {cam.name}')
                return
        await self.notify_admins(f'Video ready. Uploading..')
        with open(clip.path, 'rb') as clip:
            await chat.send_video(clip)

    async def daily_movie_group(self):
        for cam in sorted(conf.cameras_list, key=lambda k: k.offset):
            if cam.render_daily:
                await self.daily_movie(cam)
        await self.daily_stats()

    async def daily_photo_group(self):
        for cam in conf.cameras_list:
            image = await CamHandler(cam,
                                     self._bot.session).get_img(regular=False)
            if not image:
                await self.notify_admins(
                    f'Error during image request for {cam.name}')
                continue
            path = image.original_path if cam.resize else image.path
            await self._post_photo(cam, path)

    async def daily_movie_group_command(self, chat, match):
        logger.info('Forced daily movie group command')
        await self.daily_movie_group()

    async def push_vk_on(self, chat: Chat, match):
        loop = asyncio.get_event_loop()
        cmd = f'systemctl --user start {conf.vk_service}'.split()
        try:
            await loop.run_in_executor(None, lambda: subprocess_call(cmd))
        except Exception:
            msg = 'Error during starting vk push service'
            logger.exception(msg)
            await chat.send_text(msg)
            return
        await chat.send_text('vk push service started')

    async def push_vk_off(self, chat: Chat, match):
        loop = asyncio.get_event_loop()
        cmd = f'systemctl --user stop {conf.vk_service}'.split()
        try:
            await loop.run_in_executor(None, lambda: subprocess_call(cmd))
        except Exception:
            msg = 'Error during stopping vk push service'
            logger.exception(msg)
            await chat.send_text(msg)
            return
        await chat.send_text('vk push service stopped')

    async def img_all_cams(self, chat: Chat, match):
        for cam in conf.cameras_list:
            await self.img_handler(chat, cam)

    async def img_handler(self, chat: Chat, cam):
        image = await CamHandler(cam, self._bot.session).get_img(regular=False)
        if not image:
            await chat.send_text(f'Error during image request for {cam.name}')
            return
        path = image.original_path if cam.resize else image.path
        markup = Markup([[
            InlineKeyboardButton(text='post',
                                 callback_data=f'post {cam.name} {path.name}')
        ]])
        with open(path, 'rb') as image:
            await chat.send_photo(image, reply_markup=markup.to_json())

    async def img_callback(self, chat, cq, match):
        await cq.answer()
        cam = await get_cam(match.group(1), chat)
        if not cam:
            return
        await self.img_handler(chat, cam)

    @ThreadSwitcherWithDB.optimized
    async def reg_channel(self, chat: Chat, match):
        async with db_in_thread():
            channel = db.query(Channel).filter(
                Channel.chat_id == chat.id).one_or_none()
        if channel:
            await self.notify_admins(f'Channel {chat.id} already registered!')
            return
        await chat.send_text('Choose cam for channel',
                             reply_markup=CamerasChannel().options.to_json())

    @ThreadSwitcherWithDB.optimized
    async def reg_photo_channel(self, chat: Chat, match):
        async with db_in_thread():
            channel = db.query(PhotoChannel).filter(
                PhotoChannel.chat_id == chat.id).one_or_none()
        if channel:
            await self.notify_admins(f'Channel {chat.id} already registered!')
            return
        await chat.send_text(
            'Choose cam for photo channel',
            reply_markup=CamerasChannel('choose_photo_cam').options.to_json())

    @ThreadSwitcherWithDB.optimized
    async def choose_cam_callback(self, chat, cq, match):
        cam = match.group(1)
        async with db_in_thread():
            channel = Channel(chat_id=chat.id, cam=cam)
            db.add(channel)
            db.commit()
        await cq.answer(text=f'Added channel for {cam}')
        await self.notify_admins(text=f'Added channel {chat.id} for {cam}')

    @ThreadSwitcherWithDB.optimized
    async def choose_photo_cam_callback(self, chat, cq, match):
        cam = match.group(1)
        async with db_in_thread():
            channel = PhotoChannel(chat_id=chat.id, cam=cam)
            db.add(channel)
            db.commit()
        await cq.answer(text=f'Added photo channel for {cam}')
        await self.notify_admins(
            text=f'Added photo channel {chat.id} for {cam}')

    async def post_photo(self, chat, cq, match):
        cam = match.group(1)
        photo = match.group(2)
        cam = conf.cameras[cam]
        path = Path(conf.root_dir) / 'data' / cam.name / 'imgs'
        if cam.resize:
            path /= 'original'
        path = path / '_'.join(photo.split('_')[:3]) / photo
        await self._post_photo(cam, path)
        await cq.answer()

    @ThreadSwitcherWithDB.optimized
    async def _post_photo(self, cam: Cam, photo: Path):
        async with db_in_thread():
            channels = db.query(PhotoChannel).filter(
                PhotoChannel.cam == cam.name).all()
        for channel in channels:
            chat = Chat(self._bot, channel.chat_id)
            with open(photo, 'rb') as ph:
                await chat.send_photo(ph)

    @ThreadSwitcherWithDB.optimized
    async def notify_admins(self, text, **options):
        async with db_in_thread():
            admins = db.query(Admin).all()
        for admin in admins:
            await self._bot.send_message(admin.chat_id, text, **options)

    @ThreadSwitcherWithDB.optimized
    async def admin_chats(self):
        async with db_in_thread():
            return db.query(Admin).all()

    async def menu(self, chat, match):
        await chat.send_text('Menu',
                             reply_markup=self.menu_markup.main_menu.to_json())

    async def select(self, chat: Chat, cq, match):
        await cq.answer()
        cam = match.group(1)
        await chat.edit_text(cq.src['message']['message_id'],
                             f'Camera: {cam}',
                             markup=dataclasses.asdict(
                                 self.menu_markup.cam_options[cam].markup))

    async def back(self, chat, cq, match):
        await cq.answer()
        await chat.edit_text(cq.src['message']['message_id'],
                             'Menu',
                             markup=dataclasses.asdict(
                                 self.menu_markup.main_menu))

    async def sync_gphotos(self, chat, cq, match):
        await cq.answer()
        cam = match.group(1)
        await chat.edit_text(cq.src['message']['message_id'],
                             f'Choose folder for {cam}',
                             markup=dataclasses.asdict(
                                 SyncFolders(cam).folders))

    async def run_sync_gphotos(self, chat, cq, match):
        _folder = match.group(1)
        folder = Path(conf.root_dir) / 'data' / _folder
        logger.debug(f'GOING TO SYNC FOLDER {folder}')
        await cq.answer(text=f'GOING TO SYNC FOLDER {folder}')
        await self.notify_admins(f'Started sync {folder}')
        try:
            await GooglePhotosManager().batch_upload(Path(folder))
        except Exception:
            logger.exception('Sync error!')
            await self.notify_admins(f'Error with {folder}!')
            return
        await self.notify_admins(f'{folder} successfully uploaded!')
        markup = Markup([[
            InlineKeyboardButton(text=f'{_folder}',
                                 callback_data=f'remove {_folder}')
        ]])
        await chat.send_text(f'Remove folder {folder.name}',
                             reply_markup=markup.to_json())

    async def remove_folder(self, chat, cq, match):
        await cq.answer(text='Removing folder..')
        folder = match.group(1)
        folder = Path(conf.root_dir) / 'data' / folder
        shutil.rmtree(folder)
        await chat.send_text('Successfully removed!')

    async def stats_command(self, chat: Chat, match):
        try:
            day = pendulum.from_format(match.group(1), 'DD_MM_YYYY')
        except IndexError:
            day = pendulum.today()
        await self.stats_request(day, chat.send_text)

    async def lstats_command(self, chat: Chat, match):
        try:
            day = pendulum.from_format(match.group(1), 'DD_MM_YYYY')
        except IndexError:
            day = pendulum.today()
        await self.stats_request(day, chat.send_text)

    @ThreadSwitcherWithDB.optimized
    async def db_data(self, chat: Chat, match):
        async with db_in_thread():
            md_data = db_data()
        await chat.send_text('\n'.join(md_data), parse_mode='Markdown')

    async def stats_request(self, day: pendulum.DateTime, send_command):
        logger.info(f'Getting stats info for {day}')
        try:
            markdown_result = await self.local_stats_handler(day)
        except Exception:
            logger.exception('Error during stats request')
            await send_command('Error during request stats')
            return
        day = day.format('DD_MM_YYYY')
        markup = Markup([[
            InlineKeyboardButton(text='check', callback_data=f'check_cb {day}')
        ], [
            InlineKeyboardButton(text='clear', callback_data=f'clear_cb {day}')
        ]])
        await send_command('\n'.join(markdown_result),
                           parse_mode='Markdown',
                           reply_markup=markup.to_json())

    async def stats_handler(self, day=None):
        loop = asyncio.get_event_loop()
        result = await loop.run_in_executor(None, lambda: stats(day))
        album_stats = await self.agent.album_stats(day)
        markdown_result = [f'#stats *{day.format("DD/MM/YYYY")}*']
        for d in result['cameras']:
            stat = result['cameras'][d]
            count, size = stat['count'], convert_size(stat['size'])
            if count:
                avg = convert_size(stat['size'] / count)
            else:
                avg = 0
            media_count = album_stats[d]
            markdown_result.append(
                f'*{d}*: {count} - {media_count} - {size} - {avg} ')
        total = convert_size(result['total'])
        markdown_result.append(f'*total*: {total}')
        free = convert_size(result['free'])
        markdown_result.append(f'*free*: {free}')
        return markdown_result

    async def local_stats_handler(self, day=None):
        loop = asyncio.get_event_loop()
        result = await loop.run_in_executor(None, lambda: stats(day))
        markdown_result = [f'#stats *{day.format("DD/MM/YYYY")}*']
        for d in result['cameras']:
            stat = result['cameras'][d]
            count, size = stat['count'], convert_size(stat['size'])
            if count:
                avg = convert_size(stat['size'] / count)
            else:
                avg = 0
            markdown_result.append(f'*{d}*: {count} - {size} - {avg} ')
        total = convert_size(result['total'])
        markdown_result.append(f'*total*: {total}')
        free = convert_size(result['free'])
        markdown_result.append(f'*free*: {free}')
        return markdown_result

    async def check_album(self, chat, match):
        cam = await get_cam(match.group(1), chat)
        if not cam:
            return
        day = match.group(2)
        await self.agent.check_album(cam, day)

    async def full_check_handler(self, chat, day):
        logger.info(f'Going to full check for {day}')
        for cam in conf.cameras_list:
            try:
                await self.agent.check_album(cam, day)
            except Exception:
                logger.exception(
                    f'Error during check and sync {cam.name} -- {day}')
                await chat.send_text(f'Error {cam.name} — {day}')
                continue
            await chat.send_text(f'Finished with {cam.name} — {day}')
        msg = f'Finished full check for {day}'
        logger.info(msg)
        await chat.send_text(msg)

    async def full_check(self, chat, match):
        day = match.group(1)
        await self.full_check_handler(chat, day)

    async def full_check_callback(self, chat, cq, match):
        day = match.group(1)
        await cq.answer(text=f'Running full check for {day}')
        await self.full_check_handler(chat, day)

    async def clear_handler(self, chat, day):
        logger.info(f'Going to clear for {day}')
        loop = asyncio.get_event_loop()
        for cam in conf.cameras_list:
            try:
                await loop.run_in_executor(None,
                                           lambda: clear_cam_storage(day, cam))
            except Exception:
                logger.exception(f'Error during clear {cam.name} -- {day}')
                await chat.send_text(f'Error {cam.name} — {day}')
                continue
            await chat.send_text(f'Finished with {cam.name} — {day}')
        logger.info(f'Finished clear for {day}')

    async def clear_command(self, chat, match):
        day = match.group(1)
        await self.clear_handler(chat, day)

    async def clear_callback(self, chat, cq, match):
        day = match.group(1)
        await cq.answer(text=f'Cleaning for {day}')
        await self.clear_handler(chat, day)
Пример #7
0
class Telegram(Service):

    chats = {}

    class TelegramChat(Chat):
        def __init__(self, service, name, channel):
            super().__init__(service, name)

            self.id = "{0}/{1}/{2}".format(service.id, channel.server.name,
                                           channel.name)
            self.channel = channel

        # async def join(self):
        #     self.service.conn.send("JOIN {}".format(self.name))
        #
        async def send(self, message):
            #self.service.conn.send("PRIVMSG {0} :{1}".format(self.name, message))
            await self.service.conn.send_message(self.channel, message)

        async def receive(self, message):
            # Build context
            text = message.content
            author = message.author
            name = author.name

            if author.id == self.service.conn.user.id:
                logger.debug("{0}: Message is from us - ignoring.".format(
                    self.service.id))
            else:
                for bridge in self.bridges:
                    await bridge.receive(text, self, name)

    def __init__(self, bot, id, name, enabled, token):
        super().__init__()

        self.bot = bot
        self.id = id
        self.name = name
        self.enabled = enabled
        self.token = token

        self.logger.info("Initialising Slack bot {0}".format(id))

    def create(self):
        # Create Slack connection
        self.conn = Bot(api_token=self.token)

        # Event registration
        #self.conn.event(self.on_ready)
        #self.conn.event(self.on_message)
        self.conn.add_command(r'.*', self.on_message)

    def start(self):
        if self.enabled:
            self.create()
            return self.conn.loop()

        else:
            logger.info("{0} is currently disabled".format(self.id))
            return

    async def create_chat(self, channel, discord_channel):
        logger.debug("{0} creating chat for {1}".format(
            self.id, channel['name']))
        chat = self.DiscordChat(self, channel['name'], discord_channel)

        if 'bridges' not in channel:
            bridges = [None]
        else:
            bridges = channel['bridges']

        # Get/create bridges
        for bridge_name in bridges:
            bridge = self.bot.get_bridge(bridge_name)
            bridge.add(chat)
            chat.bridges.append(bridge)

        self.chats[chat.channel.id] = chat
        #await chat.join()
        #await chat.send("Hello {}!".format(chat.name))

    # async def on_join(self, conn, message):
    #     nick = message.prefix.nick
    #     channel = message.parameters[0]
    #     logger.debug("{0}: {1} joined {2}".format(self.id, nick, channel))
    #
    #     if nick == self.nick:
    #         # This is us
    #         logger.debug("{0}: We've joined {1}".format(self.id, channel))
    #         self.chats[channel].joined = True
    #
    # async def log(self, conn, message):
    #     logger.debug("{0}: {1}".format(self.id, message))
    #
    async def on_message(self, chat, match):
        user = chat.sender
        message = chat.message
        chat_details = message['chat']

        logger.debug(
            "{0}: message received: [{1} ({2}, {3})] {4} (@{5}) {6}".format(
                self.id, chat_details['title'], chat.id, chat.type,
                user['first_name'], user['username'], message['text']))
        #channel = message.channel.id

        #if channel in self.chats:
        #    await self.chats[channel].receive(message)
        #else:
        #    logger.debug("{0}: Ignoring message in unconfigured channel.".format(self.id))

    async def quit(self):
        logger.debug("{0}: Quitting...".format(self.id))
        self.conn.stop()
        logger.debug("{0}: Disconnected!".format(self.id))