From 2f762025d07c26092e90a2e4f5e45e5d6900364d Mon Sep 17 00:00:00 2001 From: Yee Jia Rong <28086837+fourjr@users.noreply.github.com> Date: Sun, 21 Nov 2021 18:49:41 +0800 Subject: [PATCH] Support LOTTIE images, resolves #3119 --- CHANGELOG.md | 1 + Pipfile | 1 + Pipfile.lock | 167 +++++++++++++++++++++++++++++++++++++++++++++- README.md | 2 +- bot.py | 11 ++- core/changelog.py | 1 + core/thread.py | 58 +++++++++++++--- 7 files changed, 226 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18935f9f23..5db8a4b950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ however, insignificant breaking changes do not guarantee a major version bump, s - Stickers were not working in Modmail. - Large server sizes results in Guild.name == None. ([GH #3088](https://github.com/kyb3r/modmail/issues/3088)) - Attachments did not work on plain replies. ([GH #3102](https://github.com/kyb3r/modmail/issues/3102)) +- Support LOTTIE stickers. ([GH #3119](https://github.com/kyb3r/modmail/issues/3119)) ### Internal diff --git a/Pipfile b/Pipfile index 51569d26e5..a78db95d18 100644 --- a/Pipfile +++ b/Pipfile @@ -21,6 +21,7 @@ pymongo = {extras = ["srv"], version = "*"} # Required by motor python-dateutil = "~=2.8.1" python-dotenv = "~=0.18.0" uvloop = {version = ">=0.15.2", markers = "sys_platform != 'win32'"} +lottie = {version = "==0.6.10", extras = ["pdf"]} [scripts] bot = "python bot.py" diff --git a/Pipfile.lock b/Pipfile.lock index e1a77d86b5..1fa9e5e64a 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "aaa53487befe4f19feda64131fbc5bde140c1ec1a59ff2656d2ad1da2c50b390" + "sha256": "29d6f377cdb874102deb4df1406ff6d0a66834fcb89660f3c9599a2343cda038" }, "pipfile-spec": 6, "requires": { @@ -75,6 +75,76 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==21.2.0" }, + "cairocffi": { + "hashes": [ + "sha256:108a3a7cb09e203bdd8501d9baad91d786d204561bd71e9364e8b34897c47b91" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.0" + }, + "cairosvg": { + "hashes": [ + "sha256:98c276b7e4f0caf01e5c7176765c104ffa1aa1461d63b2053b04ab663cf7052b", + "sha256:b0b9929cf5dba005178d746a8036fcf0025550f498ca54db61873322384783bc" + ], + "markers": "python_version >= '3.5'", + "version": "==2.5.2" + }, + "cffi": { + "hashes": [ + "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3", + "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2", + "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636", + "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20", + "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728", + "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27", + "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66", + "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443", + "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0", + "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7", + "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39", + "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605", + "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a", + "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37", + "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029", + "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139", + "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc", + "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df", + "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14", + "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880", + "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2", + "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a", + "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e", + "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474", + "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024", + "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8", + "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0", + "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e", + "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a", + "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e", + "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032", + "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6", + "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e", + "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b", + "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e", + "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954", + "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962", + "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c", + "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4", + "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55", + "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962", + "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023", + "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c", + "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6", + "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8", + "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382", + "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7", + "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc", + "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997", + "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796" + ], + "version": "==1.15.0" + }, "chardet": { "hashes": [ "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", @@ -91,6 +161,22 @@ "index": "pypi", "version": "==0.4.4" }, + "cssselect2": { + "hashes": [ + "sha256:2f4a9f20965367bae459e3bb42561f7927e0cfe5b7ea1692757cf67ef5d7dace", + "sha256:93fbb9af860e95dd40bf18c3b2b6ed99189a07c0f29ba76f9c5be71344664ec8" + ], + "markers": "python_version >= '3.6'", + "version": "==0.4.1" + }, + "defusedxml": { + "hashes": [ + "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", + "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==0.7.1" + }, "discord-py": { "git": "https://github.com/Rapptz/discord.py.git", "ref": "45d498c1b76deaf3b394d17ccf56112fa691d160" @@ -131,6 +217,16 @@ "index": "pypi", "version": "==0.6.0" }, + "lottie": { + "extras": [ + "pdf" + ], + "hashes": [ + "sha256:d3281d09a8db94da49f60267123b9e0473e3a51c282da2d5e61908d33843f1cc" + ], + "index": "pypi", + "version": "==0.6.10" + }, "motor": { "hashes": [ "sha256:1196db507142ef8f00d953efa2f37b39335ef2d72af6ce4fbccfd870b65c5e9f", @@ -232,6 +328,60 @@ "index": "pypi", "version": "==2.6" }, + "pillow": { + "hashes": [ + "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76", + "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585", + "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b", + "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8", + "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55", + "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc", + "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645", + "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff", + "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc", + "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b", + "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6", + "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20", + "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e", + "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a", + "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779", + "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02", + "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39", + "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f", + "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a", + "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409", + "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c", + "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488", + "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b", + "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d", + "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09", + "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b", + "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153", + "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9", + "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad", + "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df", + "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df", + "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed", + "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed", + "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698", + "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29", + "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649", + "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49", + "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b", + "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2", + "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a", + "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78" + ], + "markers": "python_version >= '3.6'", + "version": "==8.4.0" + }, + "pycparser": { + "hashes": [ + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + ], + "version": "==2.21" + }, "pymongo": { "extras": [ "srv" @@ -372,6 +522,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.16.0" }, + "tinycss2": { + "hashes": [ + "sha256:0353b5234bcaee7b1ac7ca3dea7e02cd338a9f8dcbb8f2dcd32a5795ec1e5f9a", + "sha256:fbdcac3044d60eb85fdb2aa840ece43cf7dbe798e373e6ee0be545d4d134e18a" + ], + "markers": "python_version >= '3.6'", + "version": "==1.1.0" + }, "typing-extensions": { "hashes": [ "sha256:2cdf80e4e04866a9b3689a51869016d36db0814d84b8d8a568d22781d45d27ed", @@ -402,6 +560,13 @@ "markers": "sys_platform != 'win32'", "version": "==0.16.0" }, + "webencodings": { + "hashes": [ + "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", + "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" + ], + "version": "==0.5.1" + }, "yarl": { "hashes": [ "sha256:044daf3012e43d4b3538562da94a88fb12a6490652dbc29fb19adfa02cf72eac", diff --git a/README.md b/README.md index cf62cfe5d6..7492a53116 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@
- +
diff --git a/bot.py b/bot.py index c2e3ee5adf..38bd6423b2 100644 --- a/bot.py +++ b/bot.py @@ -50,9 +50,6 @@ logger = getLogger(__name__) -# # prevent "coroutine noop was never awaited" warning. -# asyncio.coroutines._DEBUG = False # type: ignore - temp_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "temp") if not os.path.exists(temp_dir): @@ -1691,6 +1688,14 @@ def main(): except ImportError: pass + try: + import cairosvg + except OSError: + logger.error( + "Unable to import cairosvg, install GTK Installer for Windows: https://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer/releases/latest" + ) + sys.exit(0) + # check discord version if discord.__version__ != "2.0.0a": logger.error( diff --git a/core/changelog.py b/core/changelog.py index 4ede9f09f1..a4f88ed323 100644 --- a/core/changelog.py +++ b/core/changelog.py @@ -97,6 +97,7 @@ def embed(self) -> Embed: for name, value in self.fields.items(): embed.add_field(name=name, value=truncate(value, 1024), inline=False) embed.set_footer(text=f"Current version: v{self.bot.version}") + embed.set_thumbnail(url=self.bot.user.display_avatar.url) return embed diff --git a/core/thread.py b/core/thread.py index ff2ea791cd..09f8bd3d41 100644 --- a/core/thread.py +++ b/core/thread.py @@ -1,8 +1,11 @@ import asyncio +import base64 import copy +import functools import io import re import time +import traceback import typing import warnings from datetime import timedelta @@ -12,6 +15,8 @@ import discord from discord.ext.commands import MissingRequiredArgument, CommandError +from lottie.importers import importers as l_importers +from lottie.exporters import exporters as l_exporters from core.models import DMDisabled, DummyMessage, getLogger from core.time import human_timedelta @@ -987,16 +992,49 @@ async def send( if is_image_url(url, convert_size=False) ] images.extend(image_urls) - images.extend( - ( - i.url - if i.format in (discord.StickerFormatType.png, discord.StickerFormatType.apng) - else None, - i.name, - True, - ) - for i in message.stickers - ) + + def lottie_to_png(data): + importer = l_importers.get("lottie") + exporter = l_exporters.get("png") + with io.BytesIO() as stream: + stream.write(data) + stream.seek(0) + an = importer.process(stream) + + with io.BytesIO() as stream: + exporter.process(an, stream) + stream.seek(0) + return stream.read() + + for i in message.stickers: + if i.format in (discord.StickerFormatType.png, discord.StickerFormatType.apng): + images.append((i.url, i.name, True)) + elif i.format == discord.StickerFormatType.lottie: + # save the json lottie representation + try: + async with self.bot.session.get(i.url) as resp: + data = await resp.read() + + # convert to a png + img_data = await self.bot.loop.run_in_executor(None, functools.partial(lottie_to_png, data)) + b64_data = base64.b64encode(img_data).decode() + + # upload to imgur + async with self.bot.session.post( + "https://api.imgur.com/3/image", + headers={"Authorization": "Client-ID 50e96145ac5e085"}, + data={"image": b64_data}, + ) as resp: + result = await resp.json() + url = result["data"]["link"] + + except Exception: + traceback.print_exc() + images.append((None, i.name, True)) + else: + images.append((url, i.name, True)) + else: + images.append((None, i.name, True)) embedded_image = False