<commit_before>"""
.. module:: responses
    :platform: Unix, Windows
    :synopsis: A module containing all response classes

.. moduleauthor:: <NAME> <<EMAIL>>

This module contains classes that encapsulate the information returned in API
responses.

"""
import textwrap
import time

from datetime import datetime
from collections import Counter

from .. import config
from ..api import status
from ..api import errors
from ..api import endpoint
from .listers import FilterList, MessagePager
from .attachments import AttachmentFactory


__all__ = ['Recipient', 'Group', 'Member', 'Message', 'Bot', 'User']


class ApiResponse(object):
    """Base class for all API responses.

    .. note::

        All keyword arguments become properties.

    """
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)


class Recipient(ApiResponse):
    """Base class for Group and Member.

    Recipients can post and receive messages.

    :param endpoint: the API endpoint for messages
    :type endpoint: :class:`~groupy.api.endpoint.Endpoint`
    :param str mkey: the :class:`dict` key under which the endpoint returns
        messages
    :param str idkey: the :class:`dict` key whose value represents the key for
        posting and retrieving messages
    """
    def __init__(self, endpoint, mkey, idkey, **kwargs):
        self._endpoint = endpoint
        self._mkey = mkey
        self._idkey = kwargs.get(idkey)
        self.message_count = kwargs.pop('count', 0)
        super().__init__(**kwargs)

    # Splits text into chunks so that each is less than the chunk_size.
    @staticmethod
    def _chunkify(text, chunk_size=1000):
        if not text:
            return [None]
        return textwrap.wrap(text, chunk_size)

    def __len__(self):
        """Return the number of messages in the recipient.
        """
        return self.message_count

    def post(self, text, *attachments):
        """Post a message to the recipient.

        Although the API limits messages to 1000 characters, this method will
        split the text component into as many as necessary and include the
        attachments in the final message. Note that a list of messages sent is
        always returned, even if it contains only one element.

        :param str text: the message text
        :param list attachments: the attachments to include
        :returns: a list of raw API responses (sorry!)
        :rtype: :class:`list`
        """
        if not text and not attachments:
            raise ValueError('must be one attachment or text')
        *chunks, last = self._chunkify(text)
        sent = []
        for chunk in chunks:
            sent.append(self._endpoint.create(self._idkey, chunk))
        sent.append(self._endpoint.create(self._idkey, last, *attachments))
        return sent

    def messages(self, before=None, since=None, after=None, limit=None):
        """Return a page of messages from the recipient.

        .. note::

            Only one of ``before``, ``after``, or ``since`` can be specified in
            a single call.

        :param str before: a reference message ID
        :param str since: a reference message ID
        :param str after: a reference message ID
        :param int limit: maximum number of messages to include in the page
        :returns: a page of messages
        :rtype: :class:`~groupy.object.listers.MessagePager`
        :raises ValueError: if more than one of ``before``, ``after`` or
            ``since`` are specified
        """
        # Check arguments.
        not_None_args = []
        for arg in (before, since, after):
            if arg is not None:
                not_None_args.append(arg)
        if len(not_None_args) > 1:
            raise ValueError("Only one of 'after', 'since', and 'before' can "
                             "be specified in a single call")

        # Messages obtained with the 'after' parameter are in reversed order.
        backward = after is not None
        # Fetch the messages.
        try:
            r = self._endpoint.index(self._idkey, before_id=before,
                                     since_id=since, after_id=after)
        except errors.ApiError as e:
            # NOT_MODIFIED, in this case, means no more messages.
            if e.args[0]['code'] == status.NOT_MODIFIED:
                return None
            raise e
        # Update the message count and grab the messages.
        self.message_count = r['count']
        messages = (Message(self, **m) for m in r[self._mkey])
        return MessagePager(self, messages, backward=backward)


class Group(Recipient):
    """A GroupMe group.
    """
    def __init__(self, **kwargs):
        messages = kwargs.pop('messages', {})
        members = kwargs.pop('members')
        super().__init__(endpoint.Messages, 'messages', 'id', **kwargs)

        self.id = kwargs.get('id')
        self.group_id = kwargs.get('group_id')
        self.name = kwargs.get('name')
        self.type = kwargs.get('type')
        self.description = kwargs.get('description')
        self.image_url = kwargs.get('image_url')
        self.creator_user_id = kwargs.get('creator_user_id')
        self.created_at = datetime.fromtimestamp(kwargs.get('created_at'))
        self.updated_at = datetime.fromtimestamp(kwargs.get('updated_at'))
        ca = messages.get('last_message_created_at')
        if ca is not None and ca >= 0:
            self.last_message_created_at = datetime.fromtimestamp(ca)
            self.last_message_id = messages.get('last_message_id')
        else:
            self.last_message_created_at = None
            self.last_message_id = None<commit_msg>Fixes #20 and fixes #23.<commit_after>"""
.. module:: responses
    :platform: Unix, Windows
    :synopsis: A module containing all response classes

.. moduleauthor:: <NAME> <<EMAIL>>

This module contains classes that encapsulate the information returned in API
responses.

"""
import textwrap
import time

from datetime import datetime
from collections import Counter

from .. import config
from ..api import status
from ..api import errors
from ..api import endpoint
from .listers import FilterList, MessagePager
from .attachments import AttachmentFactory


__all__ = ['Recipient', 'Group', 'Member', 'Message', 'Bot', 'User']


class ApiResponse(object):
    """Base class for all API responses.

    .. note::

        All keyword arguments become properties.

    """
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)


class Recipient(ApiResponse):
    """Base class for Group and Member.

    Recipients can post and receive messages.

    :param endpoint: the API endpoint for messages
    :type endpoint: :class:`~groupy.api.endpoint.Endpoint`
    :param str mkey: the :class:`dict` key under which the endpoint returns
        messages
    :param str idkey: the :class:`dict` key whose value represents the key for
        posting and retrieving messages
    """
    def __init__(self, endpoint, mkey, idkey, **kwargs):
        self._endpoint = endpoint
        self._mkey = mkey
        self._idkey = kwargs.get(idkey)
        self.message_count = kwargs.pop('count', 0)
        super().__init__(**kwargs)

    # Splits text into chunks so that each is less than the chunk_size.
    @staticmethod
    def _chunkify(text, chunk_size=1000):
        if not text:
            return [None]
        return textwrap.wrap(text, chunk_size)

    def __len__(self):
        """Return the number of messages in the recipient.
        """
        return self.message_count

    def post(self, text, *attachments):
        """Post a message to the recipient.

        Although the API limits messages to 1000 characters, this method will
        split the text component into as many as necessary and include the
        attachments in the final message. Note that a list of messages sent is
        always returned, even if it contains only one element.

        :param str text: the message text
        :param list attachments: the attachments to include
        :returns: a list of raw API responses (sorry!)
        :rtype: :class:`list`
        """
        if not text and not attachments:
            raise ValueError('must be one attachment or text')
        *chunks, last = self._chunkify(text)
        sent = []
        for chunk in chunks:
            sent.append(self._endpoint.create(self._idkey, chunk))
        sent.append(self._endpoint.create(self._idkey, last, *attachments))
        return sent

    def messages(self, before=None, since=None, after=None, limit=None):
        """Return a page of messages from the recipient.

        .. note::

            Only one of ``before``, ``after``, or ``since`` can be specified in
            a single call.

        :param str before: a reference message ID
        :param str since: a reference message ID
        :param str after: a reference message ID
        :param int limit: maximum number of messages to include in the page
        :returns: a page of messages
        :rtype: :class:`~groupy.object.listers.MessagePager`
        :raises ValueError: if more than one of ``before``, ``after`` or
            ``since`` are specified
        """
        # Check arguments.
        not_None_args = []
        for arg in (before, since, after):
            if arg is not None:
                not_None_args.append(arg)
        if len(not_None_args) > 1:
            raise ValueError("Only one of 'after', 'since', and 'before' can "
                             "be specified in a single call")

        # Messages obtained with the 'after' parameter are in reversed order.
        backward = after is not None
        # Fetch the messages.
        try:
            r = self._endpoint.index(self._idkey, before_id=before,
                                     since_id=since, after_id=after)
        except errors.ApiError as e:
            # NOT_MODIFIED, in this case, means no more messages. Some versions
            # of the API return the 304 code inside the response envelope, but
            # sometimes there is no JSON response and it returns the 304 in the
            # response http code only.
            try:
                if e.args[0]['code'] == status.NOT_MODIFIED:
                    return None
            except TypeError:
                if e.args[0].status_code == status.NOT_MODIFIED:
                    return None
            raise e
        # Update the message count and grab the messages.
        self.message_count = r['count']
        messages = (Message(self, **m) for m in r[self._mkey])
        return MessagePager(self, messages, backward=backward)


class Group(Recipient):
    """A GroupMe group.
    """
    def __init__(self, **kwargs):
        messages = kwargs.pop('messages', {})
        members = kwargs.pop('members')
        super().__init__(endpoint.Messages, 'messages', 'id', **kwargs)

        self.id = kwargs.get('id')
        self.group_id = kwargs.get('group_id')
        self.name = kwargs.get('name')
        self.type = kwargs.get('type')
        self.description = kwargs.get('description')
        self.image_url = kwargs.get('image_url')
        self.creator_user_id = kwargs.get('creator_user_id')
        self.created_at = datetime.fromtimestamp(kwargs.get('created_at'))
        self.updated_at = datetime.fromtimestamp(kwargs.get('updated_at'))
        ca = messages.get('last_message_created_at')
        if ca is not None and ca >= 0:
            self.last_message_created_at = datetime.fromtimestamp(ca)
            self.last_message_id = messages.get('last_message_id')
        else:
            self.last_message_created_at = None
            self.last_message_id = None
{'fraction_non_alphanumeric': 0.09634862541492893, 'fraction_numerical': 0.0029789769342071664, 'mean_word_length': 2.3832421537575583, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5540075', 'n_tokens_mistral': 3195, 'n_tokens_neox': 3126, 'n_words': 1118}