Exemple #1
0
    def attach(self, amihost):
        """
        attach amihost to a ChannelManager.

        Args:
            amihost (dict): A dictionary containing the connection settings for
                an AMI host.
        """
        # Create Panoramisk asterisk AMI manager.
        amimgr = Manager(loop=self.loop,
                         host=amihost['host'],
                         port=amihost['port'],
                         username=amihost['username'],
                         secret=amihost['password'],
                         ssl=False,
                         encoding='utf8',
                         log=self.logger)

        # Create our own channel manager.
        channel_manager = self.channel_manager(reporter=self.reporter, )

        # Tell Panoramisk to which events we want to listen.
        for event_name in channel_manager.INTERESTING_EVENTS:
            amimgr.register_event(event_name, self.on_event)

        # Record them for later use.
        self.amimgrs[amimgr] = channel_manager

        # Tell asyncio what to work on.
        asyncio.ensure_future(amimgr.connect())
Exemple #2
0
class Bridge:
    def __init__(self, options, filters, push_configs):
        self.loop = asyncio.get_event_loop()

        max_queues = options.pop("max_size", DEFAULT_MAX_QUEUES)
        max_queue_size = options.pop("max_queue_size", DEFAULT_MAX_QUEUE_SIZE)
        self.controller = Controller(self.loop, max_queues, max_queue_size)
        self.controller.load_configs(filters, push_configs)

        options.pop("loop", None)  # discard invalid argument
        self.manager = Manager(loop=self.loop, **options)
        self.manager.log.addHandler(logging.NullHandler())
        self.manager.register_event("*", self.handle_events)

    @asyncio.coroutine
    def handle_events(self, manager, message):
        wrapper = MessageWrapper(message)
        yield from self.controller.handle(wrapper)

    @asyncio.coroutine
    def connect(self):
        yield from self.manager.connect()

    def run(self):
        try:
            self.loop.run_until_complete(self.connect())
            self.loop.run_forever()
        finally:
            self.loop.close()
Exemple #3
0
    def attach(self, asterisk):
        """
        Set up a connection to the specified Asterisk

        Args:
            asterisk (str): A connection string
        """
        ami_host = urlparse(asterisk)

        # Create Panoramisk asterisk AMI manager.
        manager = Manager(loop=self.loop,
                          host=ami_host.hostname,
                          port=ami_host.port,
                          username=ami_host.username,
                          secret=ami_host.password,
                          ssl=ami_host.scheme in ('ssl', 'tls'),
                          encoding='utf8',
                          log=self.logger)

        # Create our own channel manager.
        event_handler = self.event_handler(
            reporter=self.reporter,
            hostname=ami_host.hostname,
            logger=self.logger,
        )

        # Tell Panoramisk to which events we want to listen.
        if event_handler.FILTER_EVENTS:
            for event_name in event_handler.event_handlers().keys():
                manager.register_event(event_name, self.on_event)
        else:
            manager.register_event('*', self.on_event)

        # Record them for later use.
        self.ami_managers[manager] = event_handler

        # Tell asyncio what to work on.
        asyncio.ensure_future(manager.connect())
Exemple #4
0
class Asterisk(object):

    requires = ['irc3.plugins.command']

    def __init__(self, bot):
        self.bot = bot
        self.bot.asterirc = self
        self.config = config = dict(
            host='127.0.0.1',
            port=5038,
            http_port='8088',
            protocol='http',
            debug=True,
        )
        config.update(bot.config.get('asterisk', {}))
        self.log = logging.getLogger('irc3.ast')
        self.ilog = logging.getLogger('irc.asterirc')
        self.ilog.set_irc_targets(bot, self.config['channel'])
        self.log.info('Channel is set to {channel}'.format(**config))
        self.rooms = defaultdict(dict)
        self.http = None
        self.resolver = irc3.utils.maybedotted(self.config['resolver'])
        self.manager = Manager(log=logging.getLogger('irc3.ast.manager'),
                               **config)
        self.manager.register_event('Shutdown', self.handle_shutdown)
        self.manager.register_event('Meet*', self.handle_meetme)
        if config.get('debug'):
            self.manager.register_event('*', self.handle_event)
        if isinstance(self.resolver, type):
            self.resolver = self.resolver(bot)

    def connection_made(self):
        self.bot.loop.call_later(1, self.connect)

    def post_connect(self):
        self.log.debug('post_connect')
        resp = self.send_action({'Action': 'Status'})
        if resp.success:
            self.update_meetme()
        else:  # pragma: no cover
            self.log.error(resp.text)
            self.bot.loop.call_later(1, self.post_connect)

    def update_meetme(self):
        resp = self.send_command('meetme list')
        if resp.success:
            if 'No active MeetMe conferences.' in resp.text:
                self.rooms = defaultdict(dict)
            for line in resp.lines[1:-2]:
                room = line.split(' ', 1)[0]
                if not room or not room.isdigit():
                    continue
                resp = self.send_command('meetme list ' + room)
                if resp.success and resp.iter_lines():
                    room = self.rooms[room]
                    for line in resp.lines:
                        if not line.startswith('User '):
                            continue
                        line = line.split('Channel:')[0]
                        splited = [s for s in line.split() if s][2:]
                        uid = splited.pop(0)
                        caller = ' '.join(splited[1:])
                        if 'external call ' in caller.lower():
                            e, c, n = caller.split(' ')[:4]
                            caller = ' '.join([e, c, n[:6]])
                        room[caller] = uid

    def connect(self):
        if self.manager is not None:
            self.manager.close()
            self.bot.loop.call_later(5, self.post_connect)
        try:
            self.manager.connect()
        except Exception as e:
            self.log.exception(e)
            self.log.info('connect retry in 5s')
            self.bot.loop.call_later(1, self.connect)
            return False
        else:
            return True

    def handle_shutdown(self, event, manager):
        self.manager.close()
        self.bot.loop.call_later(2, self.connect)

    def handle_event(self, event, manager):
        self.log.debug('handle_event %s %s', event, '')  # , event.headers)

    def handle_meetme(self, event, manager):
        self.log.debug('handle_meetme %s %s', event, event.headers)
        lower_header_keys(event)
        name = event.name.lower()
        room = event['meetme']

        if name == 'meetmeend':
            if room in self.rooms:  # pragma: no cover
                del self.rooms[room]
            self.ilog.info('room {} is closed.'.format(room))
            return

        action = None
        caller = event['calleridname']
        if 'external call ' in caller.lower():
            e, c, n = caller.split(' ')[:4]
            caller = ' '.join([e, c, n[:6]])
        elif 'external call' in caller.lower():  # pragma: no cover
            caller += event['calleridnum'][:6]

        if 'join' in name:
            action = 'join'
            self.rooms[room][caller] = event['usernum']
        elif 'leave' in name:
            action = 'leave'
            if caller in self.rooms.get(room, []):
                del self.rooms[room][caller]

        if action:
            # log
            args = dict(caller=caller, action=action,
                        room=room, total=len(self.rooms[room]))
            self.ilog.info((
                '{caller} has {action} room {room} '
                '(total in this room: {total})').format(**args))

    def send_action(self, *args, **kwargs):
        try:
            res = self.manager.send_action(*args, **kwargs)
            return res
        except Exception as e:
            self.log.error('send_action(%r, **%r)', args, kwargs)
            self.log.exception(e)
            return Message('response',
                           'Sorry an error occured. ({})'.format(repr(e)),
                           headers={'Response': 'Failed'})

    def send_command(self, command, debug=False):
        resp = self.send_action({'Action': 'Command', 'Command': command})
        if debug:  # pragma: no cover
            self.log.debug('Command "%s" => Succeed: %s, Data:\n%s',
                           command, resp.success, getattr(resp, 'text', resp))
        if resp.success:
            resp.lines = []
            if resp['response'].lower() == 'follows':
                resp.lines = resp.text.split('\n')
        return resp

    @command(permission='voip')
    def call(self, mask, target, args):
        """Call someone. Destination and from can't contains spaces.

            %%call <destination> [<from>]

        """
        if 'nick' not in args:
            args['nick'] = mask.nick

        callee = self.resolver(mask, args['<destination>'])
        if args['<from>']:
            caller = self.resolver(mask, args['<from>'])
        else:
            caller = self.resolver(mask, mask.nick)

        if caller is None or caller.get('channel') is None:
            return '{nick}: Your caller is invalid.'.format(**args)
        if callee is None or callee.get('exten') is None:
            return '{nick}: Your destination is invalid.'.format(**args)

        action = {
            'Action': 'Originate',
            'Channel': caller['channel'],
            'WaitTime': 20,
            'CallerID': caller.get('fullname', caller['channel']),
            'Exten': callee['exten'],
            'Context': caller.get('context', 'default'),
            'Priority': 1,
        }
        resp = self.send_action(action)
        if resp.success:
            return '{nick}: Call to {<destination>} done.'.format(**args)
        else:
            return resp.text

    @command(permission='voip')
    def room(self, mask, target, args):
        """Invite/kick someone in a room. You can use more than one
        destination. Destinations can't contains spaces.

            %%room (list|invite|kick) [<room>] [<destination>...]

        """
        args['nick'] = mask.nick
        room = args['<room>']
        if args['invite']:

            callees = args['<destination>']
            message = '{nick}: {<from>} has been invited to room {<room>}.'
            if not callees:
                # self invite
                callees = [mask.nick]
                message = '{nick}: You have been invited to room {<room>}.'

            resolved = [self.resolver(mask, c) for c in callees]
            if None in resolved:
                # show invalid arguments and quit
                callees = zip(resolved, callees)
                invalid = [c for r, c in callees if r is None]
                yield (
                    "{0}: I'm not able to resolve {1}. Please fix it!"
                ).format(mask.nick, ', '.join(invalid))
                raise StopIteration()

            for callee in callees:
                # call each
                args['<destination>'] = args['<room>']
                args['<from>'] = callee
                msg = self.call(mask, target, args)
                if 'Call to' in msg:
                    yield message.format(**args)
                else:
                    yield msg

        if room and room not in self.rooms:
            yield 'Invalid room'
            raise StopIteration()

        if args['list']:
            def fmt(room, users):
                amount = len(users)
                users = ', '.join([u for u in sorted(users)])
                return 'Room {0} ({1}): {2}'.format(room, amount, users)

            if room:
                yield fmt(room, self.rooms[room])
            elif self.rooms:
                for room, users in self.rooms.items():
                    yield fmt(room, self.rooms[room])
            else:
                yield 'Nobody here.'

        elif args['kick']:
            users = self.rooms[room]
            commands = []
            for victim in args['<destination>']:
                for user in users:
                    if victim.lower() in user.lower():
                        peer = users[user]
                        commands.append((
                            user,
                            'meetme kick {0} {1}'.format(room, peer)))

            if not commands:
                yield 'No user matching query'
                raise StopIteration()

            for user, cmd in commands:
                resp = self.send_command(cmd)
                if resp.success:
                    del self.rooms[room][user]
                    if not self.rooms[room]:
                        del self.rooms[room]
                    yield '{0} kicked from {1}.'.format(user, room)
                else:  # pragma: no cover
                    yield 'Failed to kick {0} from {1}.'.format(user, room)

    @command(permission='voip')
    def asterisk(self, mask, target, args):
        """Show voip status

            %%asterisk status [<id>]
        """
        self.log.info('voip %s %s %s', mask, target, args)
        args['nick'] = mask.nick
        if args['<id>']:
            peer = self.resolver(mask.nick, args['<id>'])
        else:
            peer = self.resolver(mask, mask.nick)
        if peer is None:
            return '{nick}: Your id is invalid.'.format(**args)
        action = {'Action': 'SIPshowpeer', 'peer': peer['login']}
        resp = self.send_action(action)
        if resp.success:
            print(resp.lheaders)
            return ('{nick}: Your VoIP phone is registered. '
                    '(User Agent: {sip-useragent} on {address-ip})'
                    ).format(nick=mask.nick, **resp.lheaders)

    @command(permission='admin', venusian_category='irc3.debug')
    def asterisk_command(self, mask, target, args):  # pragma: no cover
        """Send a raw command to asterisk. Use "help" to list core commands.

            %%asterisk_command <command>...
        """
        cmd = ' '.join(args['<command>'])
        cmd = dict(
            help='core show help',
        ).get(cmd, cmd)
        resp = self.send_command(cmd, debug=True)
        return resp.success

    @command(permission='admin', venusian_category='irc3.debug')
    def asterisk_originate(self, mask, target, args):  # pragma: no cover
        """Send raw originate

            %%asterisk_originate <Channel> <CallerID> <Exten> <Context>
        """
        action = {
            'Action': 'Originate',
            'WaitTime': 20,
            'Priority': 1,
        }
        for k, v in list(args.items()):
            if k.startswith('<'):
                action[k.strip('<>')] = v
        self.send_action(action)
        return 'Action {} sent'.format(repr(action))

    def SIGINT(self):
        if self.manager is not None:
            self.manager.close()
        self.log.info('SIGINT: connection closed')
Exemple #5
0
class Asterisk(object):

    requires = ['irc3.plugins.command']

    def __init__(self, bot):
        self.bot = bot
        self.bot.asterirc = self
        self.config = config = dict(
            host='127.0.0.1',
            port=5038,
            http_port='8088',
            protocol='http',
            debug=True,
        )
        config.update(bot.config.get('asterisk', {}))
        self.log = logging.getLogger('irc3.ast')
        self.ilog = logging.getLogger('irc.asterirc')
        self.ilog.set_irc_targets(bot, self.config['channel'])
        self.log.info('Channel is set to {channel}'.format(**config))
        self.rooms = defaultdict(dict)
        self.http = None
        self.resolver = irc3.utils.maybedotted(self.config['resolver'])
        self.manager = Manager(log=logging.getLogger('irc3.ast.manager'),
                               **config)
        self.manager.register_event('Shutdown', self.handle_shutdown)
        self.manager.register_event('Meet*', self.handle_meetme)
        if config.get('debug'):
            self.manager.register_event('*', self.handle_event)
        if isinstance(self.resolver, type):
            self.resolver = self.resolver(bot)

    def connection_made(self):
        self.bot.loop.call_later(1, self.connect)

    def post_connect(self):
        self.log.debug('post_connect')
        resp = self.send_action({'Action': 'Status'})
        if resp.success:
            self.update_meetme()
        else:  # pragma: no cover
            self.log.error(resp.text)
            self.bot.loop.call_later(1, self.post_connect)

    def update_meetme(self):
        resp = self.send_command('meetme list')
        if resp.success:
            if 'No active MeetMe conferences.' in resp.text:
                self.rooms = defaultdict(dict)
            for line in resp.lines[1:-2]:
                room = line.split(' ', 1)[0]
                if not room or not room.isdigit():
                    continue
                resp = self.send_command('meetme list ' + room)
                if resp.success and resp.iter_lines():
                    room = self.rooms[room]
                    for line in resp.lines:
                        if not line.startswith('User '):
                            continue
                        line = line.split('Channel:')[0]
                        splited = [s for s in line.split() if s][2:]
                        uid = splited.pop(0)
                        caller = ' '.join(splited[1:])
                        if 'external call ' in caller.lower():
                            e, c, n = caller.split(' ')[:4]
                            caller = ' '.join([e, c, n[:6]])
                        room[caller] = uid

    def connect(self):
        if self.manager is not None:
            self.manager.close()
            self.bot.loop.call_later(5, self.post_connect)
        try:
            self.manager.connect()
        except Exception as e:
            self.log.exception(e)
            self.log.info('connect retry in 5s')
            self.bot.loop.call_later(1, self.connect)
            return False
        else:
            return True

    def handle_shutdown(self, event, manager):
        self.manager.close()
        self.bot.loop.call_later(2, self.connect)

    def handle_event(self, event, manager):
        self.log.debug('handle_event %s %s', event, '')  # , event.headers)

    def handle_meetme(self, event, manager):
        self.log.debug('handle_meetme %s %s', event, event.headers)
        lower_header_keys(event)
        name = event.name.lower()
        room = event['meetme']

        if name == 'meetmeend':
            if room in self.rooms:  # pragma: no cover
                del self.rooms[room]
            self.ilog.info('room {} is closed.'.format(room))
            return

        action = None
        caller = event['calleridname']
        if 'external call ' in caller.lower():
            e, c, n = caller.split(' ')[:4]
            caller = ' '.join([e, c, n[:6]])
        elif 'external call' in caller.lower():  # pragma: no cover
            caller += event['calleridnum'][:6]

        if 'join' in name:
            action = 'join'
            self.rooms[room][caller] = event['usernum']
        elif 'leave' in name:
            action = 'leave'
            if caller in self.rooms.get(room, []):
                del self.rooms[room][caller]

        if action:
            # log
            args = dict(caller=caller,
                        action=action,
                        room=room,
                        total=len(self.rooms[room]))
            self.ilog.info(('{caller} has {action} room {room} '
                            '(total in this room: {total})').format(**args))

    def send_action(self, *args, **kwargs):
        try:
            res = self.manager.send_action(*args, **kwargs)
            return res
        except Exception as e:
            self.log.error('send_action(%r, **%r)', args, kwargs)
            self.log.exception(e)
            return Message('response',
                           'Sorry an error occured. ({})'.format(repr(e)),
                           headers={'Response': 'Failed'})

    def send_command(self, command, debug=False):
        resp = self.send_action({'Action': 'Command', 'Command': command})
        if debug:  # pragma: no cover
            self.log.debug('Command "%s" => Succeed: %s, Data:\n%s', command,
                           resp.success, getattr(resp, 'text', resp))
        if resp.success:
            resp.lines = []
            if resp['response'].lower() == 'follows':
                resp.lines = resp.text.split('\n')
        return resp

    @command(permission='voip')
    def call(self, mask, target, args):
        """Call someone. Destination and from can't contains spaces.

            %%call <destination> [<from>]

        """
        if 'nick' not in args:
            args['nick'] = mask.nick

        callee = self.resolver(mask, args['<destination>'])
        if args['<from>']:
            caller = self.resolver(mask, args['<from>'])
        else:
            caller = self.resolver(mask, mask.nick)

        if caller is None or caller.get('channel') is None:
            return '{nick}: Your caller is invalid.'.format(**args)
        if callee is None or callee.get('exten') is None:
            return '{nick}: Your destination is invalid.'.format(**args)

        action = {
            'Action': 'Originate',
            'Channel': caller['channel'],
            'WaitTime': 20,
            'CallerID': caller.get('fullname', caller['channel']),
            'Exten': callee['exten'],
            'Context': caller.get('context', 'default'),
            'Priority': 1,
        }
        resp = self.send_action(action)
        if resp.success:
            return '{nick}: Call to {<destination>} done.'.format(**args)
        else:
            return resp.text

    @command(permission='voip')
    def room(self, mask, target, args):
        """Invite/kick someone in a room. You can use more than one
        destination. Destinations can't contains spaces.

            %%room (list|invite|kick) [<room>] [<destination>...]

        """
        args['nick'] = mask.nick
        room = args['<room>']
        if args['invite']:

            callees = args['<destination>']
            message = '{nick}: {<from>} has been invited to room {<room>}.'
            if not callees:
                # self invite
                callees = [mask.nick]
                message = '{nick}: You have been invited to room {<room>}.'

            resolved = [self.resolver(mask, c) for c in callees]
            if None in resolved:
                # show invalid arguments and quit
                callees = zip(resolved, callees)
                invalid = [c for r, c in callees if r is None]
                yield (
                    "{0}: I'm not able to resolve {1}. Please fix it!").format(
                        mask.nick, ', '.join(invalid))
                raise StopIteration()

            for callee in callees:
                # call each
                args['<destination>'] = args['<room>']
                args['<from>'] = callee
                msg = self.call(mask, target, args)
                if 'Call to' in msg:
                    yield message.format(**args)
                else:
                    yield msg

        if room and room not in self.rooms:
            yield 'Invalid room'
            raise StopIteration()

        if args['list']:

            def fmt(room, users):
                amount = len(users)
                users = ', '.join([u for u in sorted(users)])
                return 'Room {0} ({1}): {2}'.format(room, amount, users)

            if room:
                yield fmt(room, self.rooms[room])
            elif self.rooms:
                for room, users in self.rooms.items():
                    yield fmt(room, self.rooms[room])
            else:
                yield 'Nobody here.'

        elif args['kick']:
            users = self.rooms[room]
            commands = []
            for victim in args['<destination>']:
                for user in users:
                    if victim.lower() in user.lower():
                        peer = users[user]
                        commands.append(
                            (user, 'meetme kick {0} {1}'.format(room, peer)))

            if not commands:
                yield 'No user matching query'
                raise StopIteration()

            for user, command in commands:
                resp = self.send_command(command)
                if resp.success:
                    del self.rooms[room][user]
                    if not self.rooms[room]:
                        del self.rooms[room]
                    yield '{0} kicked from {1}.'.format(user, room)
                else:  # pragma: no cover
                    yield 'Failed to kick {0} from {1}.'.format(user, room)

    @command(permission='voip')
    def asterisk(self, mask, target, args):
        """Show voip status

            %%asterisk status [<id>]
        """
        self.log.info('voip %s %s %s', mask, target, args)
        args['nick'] = mask.nick
        if args['<id>']:
            peer = self.resolver(mask.nick, args['<id>'])
        else:
            peer = self.resolver(mask, mask.nick)
        if peer is None:
            return '{nick}: Your id is invalid.'.format(**args)
        action = {'Action': 'SIPshowpeer', 'peer': peer['login']}
        resp = self.send_action(action)
        if resp.success:
            print(resp.lheaders)
            return ('{nick}: Your VoIP phone is registered. '
                    '(User Agent: {sip-useragent} on {address-ip})').format(
                        nick=mask.nick, **resp.lheaders)

    @command(permission='admin', venusian_category='irc3.debug')
    def asterisk_command(self, mask, target, args):  # pragma: no cover
        """Send a raw command to asterisk. Use "help" to list core commands.

            %%asterisk_command <command>...
        """
        cmd = ' '.join(args['<command>'])
        cmd = dict(help='core show help', ).get(cmd, cmd)
        resp = self.send_command(cmd, debug=True)
        return resp.success

    @command(permission='admin', venusian_category='irc3.debug')
    def asterisk_originate(self, mask, target, args):  # pragma: no cover
        """Send raw originate

            %%asterisk_originate <Channel> <CallerID> <Exten> <Context>
        """
        action = {
            'Action': 'Originate',
            'WaitTime': 20,
            'Priority': 1,
        }
        for k, v in list(args.items()):
            if k.startswith('<'):
                action[k.strip('<>')] = v
        self.send_action(action)
        return 'Action {} sent'.format(repr(action))

    def SIGINT(self):
        if self.manager is not None:
            self.manager.close()
        self.log.info('SIGINT: connection closed')
Exemple #6
0
class AsteriskClient:
    def __init__(self, loop):
        self.manager = Manager(loop=loop,
                               host='178.248.87.116',
                               port=1709,
                               username='******',
                               secret='100')
        self.callManager = CallBaseManager()
        self.httpclient = HttpClientTornado()

        self.manager.register_event('FullyBooted', self.FullyBooted)
        self.manager.register_event('BridgeEnter', self.BridgeEnter)
        self.manager.register_event('Hangup', self.Hangup)

        self.manager.connect()
        try:
            self.manager.loop.run_forever()
        except KeyboardInterrupt:
            self.manager.loop.close()

    def FullyBooted(self, manager, message):
        print(message)
        resp = yield from manager.send_action({'Action': 'SIPpeers'})
        self.callManager.FillSIPpeers(resp)
        resp = yield from manager.send_action({'Action': 'QueueStatus'})
        pprint(resp)
        result = self.httpclient.QueueStatus(resp, method='connect')
        self.httpclient.SendRequest(result)

    def BridgeEnter(self, manager, message):
        # global isDial
        # isDial=False
        print(message)
        Bridge = self.callManager.CallBegin(
            CallerNumber=message.CallerIDNum,
            ConnectedNumber=message.ConnectedLineNum)
        if isinstance(Bridge, dict):
            resp = yield from manager.send_action({'Action': 'QueueStatus'})
            pprint(resp)
            result = self.httpclient.QueueStatus(resp,
                                                 method='call_begin',
                                                 call=Bridge)
            self.httpclient.SendRequest(result)

    def Hangup(self, manager, message):
        print(message)
        Call = self.callManager.CallEnd(CallerNumber=message.CallerIDNum)
        if isinstance(Call, dict):
            resp = yield from manager.send_action({'Action': 'QueueStatus'})
            pprint(resp)
            result = self.httpclient.QueueStatus(resp,
                                                 method='call_end',
                                                 call=Call)
            self.httpclient.SendRequest(result)

    def ChanSpy(self, channel_from: str, channel_to: str, type: str):
        call = yield from self.manager.send_action({
            'Action':
            'Originate',
            'Channel':
            'SIP/' + channel_from,
            'Application':
            'ChanSpy',
            'Data':
            'SIP/' + channel_to + ',' + type,
            'Priority':
            1,
            'Callerid':
            'Spy-{%s} <{%s}>'.format(channel_to),
            'Variable':
            'SIPADDHEADER="Call-Info:\;answer-after=0"',
        })
        pprint(call)

    def PickUp(self, channel: str):
        call = yield from self.manager.send_action({
            'Action':
            'Originate',
            'Channel':
            'SIP/' + channel,
            'Application':
            'PickupChan',
            'Data':
            'SIP/' + channel,
            'Priority':
            1,
            'Callerid':
            channel,
            'Variable':
            'SIPADDHEADER="Call-Info:\;answer-after=0"',
        })
        pprint(call)

    def Originate(self, channel_from, channel_to):
        call = yield from self.manager.send_action({
            'Action':
            'Originate',
            'Channel':
            'SIP/' + channel_from,
            'Exten':
            channel_to,
            'Priority':
            1,
            'Callerid':
            channel_from,
            'Variable':
            'SIPADDHEADER="Call-Info:\;answer-after=0"',
        })
        pprint(call)

    def Redirect(self, channel_from, channel_to):
        call = yield from self.manager.send_action({
            'Action': 'Redirect',
            # 'Channel': 'SIP/' + channel_from,
            # 'Exten': channel_to,
            'Priority': 1,
            'Context': 'from-internal',
        })
        pprint(call)

    def QueueAdd(self, channel):
        call = yield from self.manager.send_action({
            'Action': 'QueueAdd',
            'Queue': 'operator',
            'Interface': 'SIP/' + channel,
            'Penalty': 0,
        })
        pprint(call)

    def QueueRemove(self, channel):
        call = yield from self.manager.send_action({
            'Action':
            'QueueRemove',
            'Queue':
            'operator',
            'Interface':
            'SIP/' + channel,
        })
        pprint(call)

    def Queue(self, worker: str, action: int):
        if action == 1:
            self.QueueAdd(worker)
        else:
            self.QueueRemove(worker)

    def Parse(self, message: dict):
        method = message['method']
        if method == 'call_abonent':
            self.Originate(message['from'], message['to'])
        elif method == 'transfer_call':
            self.Redirect(message['from'], message['to'])
        elif method == 'connect_with_abonent':
            self.ChanSpy(message['from'], message['to'], 'qBx')
        elif method == 'connect_without_abonent':
            self.ChanSpy(message['from'], message['to'], 'wx')
        elif method == 'connect_without_microphone':
            self.ChanSpy(message['from'], message['to'], 'qx')
        elif method == 'queue':
            self.Queue(message['worker'], message['action'])


# if __name__ == '__main__':
#     main()
class AmiClient:
    events_map = []

    async def start(self):
        # Set process name
        salt.utils.process.appendproctitle(self.__class__.__name__)
        manager_disconnected = asyncio.Event()
        # Ok let's connect to Asterisk and process events.
        self.loop = asyncio.get_event_loop()
        # Create event loop to receive actions as events.
        self.loop.create_task(self.action_event_loop())
        host = __salt__['config.get']('ami_host', 'localhost')
        port = int(__salt__['config.get']('ami_port', '5038'))
        login = __salt__['config.get']('ami_login', 'salt')
        self.manager = Manager(
            loop=self.loop,
            host=host,
            port=port,
            username=login,
            secret=__salt__['config.get']('ami_secret', 'stack'),
            forgetable_actions=('ping', 'login'),
        )
        log.info('AMI connecting to %s@%s:%s...', login, host, port)
        # Register events
        for ev_name in __salt__['config.get']('ami_register_events', []):
            log.info('Registering for AMI event %s', ev_name)
            self.manager.register_event(ev_name, self.on_asterisk_event)
        try:
            await self.manager.connect()
            log.info('Connected to AMI.')
        except Exception as e:
            log.error('Cannot connect to Asterisk AMI: %s', e)
        await manager_disconnected.wait()

    async def on_asterisk_event(self, manager, event):
        event = dict(event)
        trace_events = __opts__.get('ami_trace_events')
        if trace_events:
            if isinstance(trace_events, bool):
                log.info('AMI event: %s', json.dumps(event, indent=2))
            elif isinstance(trace_events,
                            list) and event['Event'] in trace_events:
                log.info('AMI event: %s', json.dumps(event, indent=2))
        # Inject system name in every message
        event['SystemName'] = __grains__['id']
        # Send event to Salt's event map
        __salt__['event.fire'](event, 'AMI/{}'.format(event['Event']))

    async def action_event_loop(self):
        log.debug('AMI action event loop started.')
        event = salt.utils.event.MinionEvent(__opts__)
        trace_actions = __opts__.get('ami_trace_actions')
        while True:
            try:
                evdata = event.get_event(tag='ami_action', no_block=True)
                # TODO: Connect to Salt's tornado eventloop.
                try:
                    await asyncio.sleep(0.01)
                except concurrent.futures._base.CancelledError:
                    return
                if evdata:
                    # Remove salt's event item
                    evdata.pop('_stamp', False)
                    reply_channel = evdata.pop('reply_channel', False)
                    # Trace the request if set.
                    if trace_actions and isinstance(trace_actions, bool):
                        log.info('Action request: %s', evdata)
                    elif isinstance(
                            trace_actions,
                            list) and evdata['Action'] in trace_actions:
                        log.info('Action request: %s', evdata)
                    else:
                        log.debug('Action request: %s', evdata)
                    try:
                        # Send action and get action result.
                        res = await asyncio.wait_for(
                            self.manager.send_action(evdata), timeout=1.0)
                        if trace_actions and isinstance(trace_actions, bool):
                            log.info('Action result: %s', res)
                        elif isinstance(
                                trace_actions,
                                list) and evdata['Action'] in trace_actions:
                            log.info('Action result: %s', res)
                        else:
                            log.debug('Action result: %s', res)
                    except asyncio.TimeoutError:
                        log.error('Send action timeout: %s', evdata)
                        res = {
                            'Message': 'Action Timeout',
                            'Response': 'Error'
                        }
                    except concurrent.futures._base.CancelledError:
                        log.info('AMI action event loop quitting.')
                        return
                    except Exception as e:
                        log.exception('Send action error:')
                        res = str(e)
                    # Make a list of results to unify.
                    if not isinstance(res, list):
                        res = [res]
                    if reply_channel:
                        # Send back the result.
                        __salt__['event.fire']({
                            'Reply': [dict(k) for k in res]
                        }, 'ami_reply/{}'.format(reply_channel))

            except Exception as e:
                if "'int' object is not callable" in str(e):
                    # Reaction on CTRL+C :-)
                    log.info('AMI events action lister quitting.')
                    return
                else:
                    log.exception('AMI action event loop error:')
                    await asyncio.sleep(1)  # Protect log flood.
Exemple #8
0
def register(manager: Manager, events: Tuple[str]):
    for event in events:
        manager.register_event(event, capture_message)