<commit_before># -*- test-case-name: vumi.transports.integrat.tests.test_integrat -*-

from twisted.internet.defer import inlineCallbacks
from twisted.web import http
from twisted.web.resource import Resource

from vumi.utils import http_request, normalize_msisdn
from vumi.message import TransportUserMessage
from vumi.transports.integrat.utils import HigateXMLParser
from vumi.transports import Transport

hxg = HigateXMLParser()


class IntegratHttpResource(Resource):
    isLeaf = True

    # map events to session event types
    EVENT_TYPE_MAP = {
        'open': TransportUserMessage.SESSION_NEW,
        'close': TransportUserMessage.SESSION_CLOSE,
        'resume': TransportUserMessage.SESSION_RESUME,
        }

    # Integrat sends both 'new' and 'open' events but
    # we only pass 'open' events on ('open' is the more
    # complete and reliable of the two in Integrat's case).
    EVENTS_TO_SKIP = set(['new'])

    def __init__(self, publish_message):
        self.publish_message = publish_message

    def render(self, request):
        request.setResponseCode(http.OK)
        request.setHeader('Content-Type', 'text/plain')
        hxg_msg = hxg.parse(request.content.read())

        if hxg_msg.get('Type') != 'OnUSSEvent':
            # TODO: add support for non-USSD messages
            return ''

        text = hxg_msg.get('USSText', '').strip()

        if hxg_msg['EventType'] == 'Request':
            if text == 'REQ':
                session_event = TransportUserMessage.SESSION_NEW
            else:
                session_event = TransportUserMessage.SESSION_RESUME
        else:
            event_type = hxg_msg['EventType'].lower()
            if event_type in self.EVENTS_TO_SKIP:
                return ''
            session_event = self.EVENT_TYPE_MAP.get(event_type,
                    TransportUserMessage.SESSION_RESUME)

        if session_event != TransportUserMessage.SESSION_RESUME:
            text = None

        transport_metadata = {
            'session_id': hxg_msg['SessionID'],
            }
        self.publish_message(
            from_addr=normalize_msisdn(hxg_msg['MSISDN']),
            to_addr=hxg_msg['ConnStr'],
            session_event=session_event,
            content=text,
            transport_metadata=transport_metadata,
            )
        return ''


class HealthResource(Resource):
    isLeaf = True

    def render(self, request):
        request.setResponseCode(http.OK)
        return 'OK'


class IntegratTransport(Transport):

    def validate_config(self):
        """
        Transport-specific config validation happens in here.
        """
        self.web_path = self.config['web_path']
        self.web_port = self.config['web_port']
        self.integrat_url = self.config['url']
        self.integrat_username = self.config['username']
        self.integrat_password = self.config['password']

    def setup_transport(self):
        """
        All transport_specific setup should happen in here.
        """
        self.web_resource = yield self.start_web_resources(
            [
                (IntegratHttpResource(self.publish_message), self.web_path),
                (HealthResource(), 'health'),
            ],
            self.web_port,
        )

    @inlineCallbacks
    def handle_outbound_message(self, message):
        text = message['content']
        if text is None:
            text = ''
        flags = '0'
        if message['session_event'] == message.SESSION_CLOSE:
            flags = '1'
        session_id = message['transport_metadata']['session_id']
        yield http_request(self.integrat_url, hxg.build({
            'Flags': flags,
            'SessionID': session_id,
            'Type': 'USSReply',
            'USSText': text,
            'Password': <PASSWORD>,
            'UserID': self.integrat_username,
        }), headers={
            'Content-Type': ['text/xml; charset=utf-8']
        })
<commit_msg>Add transport_name to inbound messages, teardown web resources when transport is stopped, wrapping handle_outbound_message with inlineCallbacks.<commit_after># -*- test-case-name: vumi.transports.integrat.tests.test_integrat -*-

from twisted.internet.defer import inlineCallbacks
from twisted.web import http
from twisted.web.resource import Resource

from vumi.utils import http_request, normalize_msisdn
from vumi.message import TransportUserMessage
from vumi.transports.integrat.utils import HigateXMLParser
from vumi.transports import Transport

hxg = HigateXMLParser()


class IntegratHttpResource(Resource):
    isLeaf = True

    # map events to session event types
    EVENT_TYPE_MAP = {
        'open': TransportUserMessage.SESSION_NEW,
        'close': TransportUserMessage.SESSION_CLOSE,
        'resume': TransportUserMessage.SESSION_RESUME,
        }

    # Integrat sends both 'new' and 'open' events but
    # we only pass 'open' events on ('open' is the more
    # complete and reliable of the two in Integrat's case).
    EVENTS_TO_SKIP = set(['new'])

    def __init__(self, transport_name, publish_message):
        self.transport_name = transport_name
        self.publish_message = publish_message

    def render(self, request):
        request.setResponseCode(http.OK)
        request.setHeader('Content-Type', 'text/plain')
        hxg_msg = hxg.parse(request.content.read())

        if hxg_msg.get('Type') != 'OnUSSEvent':
            # TODO: add support for non-USSD messages
            return ''

        text = hxg_msg.get('USSText', '').strip()

        if hxg_msg['EventType'] == 'Request':
            if text == 'REQ':
                session_event = TransportUserMessage.SESSION_NEW
            else:
                session_event = TransportUserMessage.SESSION_RESUME
        else:
            event_type = hxg_msg['EventType'].lower()
            if event_type in self.EVENTS_TO_SKIP:
                return ''
            session_event = self.EVENT_TYPE_MAP.get(event_type,
                    TransportUserMessage.SESSION_RESUME)

        if session_event != TransportUserMessage.SESSION_RESUME:
            text = None

        transport_metadata = {
            'session_id': hxg_msg['SessionID'],
            }
        self.publish_message(
            from_addr=normalize_msisdn(hxg_msg['MSISDN']),
            to_addr=hxg_msg['ConnStr'],
            session_event=session_event,
            content=text,
            transport_name=self.transport_name,
            transport_type="ussd",
            transport_metadata=transport_metadata,
            )
        return ''


class HealthResource(Resource):
    isLeaf = True

    def render(self, request):
        request.setResponseCode(http.OK)
        return 'OK'


class IntegratTransport(Transport):

    def validate_config(self):
        """
        Transport-specific config validation happens in here.
        """
        self.web_path = self.config['web_path']
        self.web_port = int(self.config['web_port'])
        self.integrat_url = self.config['url']
        self.integrat_username = self.config['username']
        self.integrat_password = self.config['password']

    @inlineCallbacks
    def setup_transport(self):
        """
        All transport_specific setup should happen in here.
        """
        self.web_resource = yield self.start_web_resources(
            [
                (IntegratHttpResource(self.transport_name,
                                      self.publish_message), self.web_path),
                (HealthResource(), 'health'),
            ],
            self.web_port,
        )

    @inlineCallbacks
    def teardown_transport(self):
        yield self.web_resource.loseConnection()

    @inlineCallbacks
    def handle_outbound_message(self, message):
        text = message['content']
        if text is None:
            text = ''
        flags = '0'
        if message['session_event'] == message.SESSION_CLOSE:
            flags = '1'
        session_id = message['transport_metadata']['session_id']
        yield http_request(self.integrat_url, hxg.build({
            'Flags': flags,
            'SessionID': session_id,
            'Type': 'USSReply',
            'USSText': text,
            'Password': <PASSWORD>,
            'UserID': self.integrat_username,
        }), headers={
            'Content-Type': ['text/xml; charset=utf-8']
        })

{'fraction_non_alphanumeric': 0.10039535162333772, 'fraction_numerical': 0.0007188211333413202, 'mean_word_length': 2.3365307753796962, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7470319', 'n_tokens_mistral': 2364, 'n_tokens_neox': 2308, 'n_words': 485}