diff --git a/disco/types/message.py b/disco/types/message.py index 219dbc8..af5211e 100644 --- a/disco/types/message.py +++ b/disco/types/message.py @@ -2,7 +2,6 @@ import re import six import warnings import functools -import unicodedata from holster.enum import Enum @@ -443,53 +442,26 @@ class Message(SlottedModel): return content -class MessageTable(object): - def __init__(self, sep=' | ', codeblock=True, header_break=True, language=None): - self.header = [] - self.entries = [] - self.size_index = {} - self.sep = sep - self.codeblock = codeblock - self.header_break = header_break - self.language = language - - def recalculate_size_index(self, cols): - for idx, col in enumerate(cols): - size = len(unicodedata.normalize('NFC', col)) - if idx not in self.size_index or size > self.size_index[idx]: - self.size_index[idx] = size - - def set_header(self, *args): - args = list(map(six.text_type, args)) - self.header = args - self.recalculate_size_index(args) - - def add(self, *args): - args = list(map(six.text_type, args)) - self.entries.append(args) - self.recalculate_size_index(args) - - def compile_one(self, cols): - data = self.sep.lstrip() - - for idx, col in enumerate(cols): - padding = ' ' * (self.size_index[idx] - len(col)) - data += col + padding + self.sep - - return data.rstrip() +class Sendable(object): + """ + Base class which implements an entity that can be sent as a message to Discord. + """ def compile(self): - data = [] - if self.header: - data = [self.compile_one(self.header)] - - if self.header and self.header_break: - data.append('-' * (sum(self.size_index.values()) + (len(self.header) * len(self.sep)) + 1)) + """ + Should return a valid (e.g. properly sized, < 2000 length) message that + can be sent to Discord. + """ - for row in self.entries: - data.append(self.compile_one(row)) + @staticmethod + def fit(contents, head='```', tail='```', length=2000): + contents = Sendable.truncate(contents, tail='', length=length - (len(head) + len(tail))) + return u'{}{}{}'.format(head, contents, tail) - if self.codeblock: - return '```{}'.format(self.language if self.language else '') + '\n'.join(data) + '```' + @staticmethod + def truncate(contents, tail='...', length=2000): + if len(contents) <= length: + return contents - return '\n'.join(data) + to_truncate = len(contents) - length + len(tail) + return contents[:-to_truncate] + tail diff --git a/disco/util/message.py b/disco/util/message.py new file mode 100644 index 0000000..840acad --- /dev/null +++ b/disco/util/message.py @@ -0,0 +1,57 @@ +import six +import unicodedata + +from disco.types.message import Sendable + + +class MessageTable(Sendable): + def __init__(self, sep=' | ', codeblock=True, header_break=True, language=None): + self.header = [] + self.entries = [] + self.size_index = {} + self.sep = sep + self.codeblock = codeblock + self.header_break = header_break + self.language = language + + def recalculate_size_index(self, cols): + for idx, col in enumerate(cols): + size = len(unicodedata.normalize('NFC', col)) + if idx not in self.size_index or size > self.size_index[idx]: + self.size_index[idx] = size + + def set_header(self, *args): + args = list(map(six.text_type, args)) + self.header = args + self.recalculate_size_index(args) + + def add(self, *args): + args = list(map(six.text_type, args)) + self.entries.append(args) + self.recalculate_size_index(args) + + def compile_one(self, cols): + data = self.sep.lstrip() + + for idx, col in enumerate(cols): + padding = ' ' * (self.size_index[idx] - len(col)) + data += col + padding + self.sep + + return data.rstrip() + + def compile(self): + data = [] + if self.header: + data = [self.compile_one(self.header)] + + if self.header and self.header_break: + data.append('-' * (sum(self.size_index.values()) + (len(self.header) * len(self.sep)) + 1)) + + for row in self.entries: + data.append(self.compile_one(row)) + + if self.codeblock: + return self.fit((self.lanague if self.language else '') + '\n'.join(data)) + + # TODO: truncate by line + return self.truncate('\n'.join(data), tail='') diff --git a/requirements.txt b/requirements.txt index 4228bdf..9f1683c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ gevent==1.2.2 holster==1.0.15 -requests==2.13.0 +requests==2.18.1 six==1.10.0 -websocket-client==0.40.0 +websocket-client==0.44.0 diff --git a/setup.py b/setup.py index af6c7c4..cc840c5 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ extras_require = { 'voice': ['pynacl==1.1.2'], 'http': ['flask==0.12.2'], 'yaml': ['pyyaml==3.12'], - 'music': ['youtube_dl==2017.4.26'], + 'music': ['youtube_dl==2017.7.2'], 'performance': ['erlpack==0.3.2', 'ujson==1.35'], 'sharding': ['gipc==0.6.0'], 'docs': ['biblio==0.0.2'], diff --git a/tests/test_message.py b/tests/test_message.py new file mode 100644 index 0000000..bd0a451 --- /dev/null +++ b/tests/test_message.py @@ -0,0 +1,17 @@ +from unittest import TestCase + +from disco.types.message import Sendable + + +class TestSendable(TestCase): + def test_sendable_truncate(self): + self.assertEqual(Sendable.truncate('*' * 2001), ('*' * 1997) + '...') + self.assertEqual(Sendable.truncate('*' * 1999), '*' * 1999) + self.assertEqual(Sendable.truncate(u'\U0001F947' * 20), u'\U0001F947' * 20) + self.assertEqual(Sendable.truncate(u'\U0001F947' * 3000, tail=''), u'\U0001F947' * 2000) + + def test_sendable_fit(self): + self.assertEqual(Sendable.fit('*' * 3000), '```{}```'.format('*' * 1994)) + self.assertEqual(Sendable.fit('test'), '```test```') + self.assertEqual(Sendable.fit('test', '`', '`'), '`test`') + self.assertEqual(Sendable.fit(u'\U0001F947'), u'```\U0001F947```')