From 5ca1711982be1cf5798fcfe251cebb78ca688faa Mon Sep 17 00:00:00 2001 From: adbenitez Date: Mon, 5 Feb 2024 22:46:36 -0500 Subject: [PATCH] port to deltabot-cli --- .github/workflows/python-ci.yml | 15 +- .isort.cfg | 2 - .mypy.ini | 2 - CHANGELOG.rst | 44 ------ MANIFEST.in | 2 - README.md | 37 +++++ README.rst | 44 ------ avatar.png | Bin 0 -> 6231 bytes pyproject.toml | 55 ++++++- requirements/requirements-dev.txt | 9 -- requirements/requirements-test.txt | 2 - requirements/requirements.txt | 8 - setup.py | 65 -------- simplebot_stickers/__init__.py | 107 ------------- simplebot_stickers/telegram.py | 24 --- stickersbot/__init__.py | 11 ++ stickersbot/__main__.py | 5 + stickersbot/hooks.py | 148 ++++++++++++++++++ {simplebot_stickers => stickersbot}/signal.py | 0 {simplebot_stickers => stickersbot}/util.py | 21 +-- tests/test_plugin.py | 15 -- 21 files changed, 258 insertions(+), 358 deletions(-) delete mode 100644 .isort.cfg delete mode 100644 .mypy.ini delete mode 100644 CHANGELOG.rst delete mode 100644 MANIFEST.in create mode 100644 README.md delete mode 100644 README.rst create mode 100644 avatar.png delete mode 100644 requirements/requirements-dev.txt delete mode 100644 requirements/requirements-test.txt delete mode 100644 requirements/requirements.txt delete mode 100644 setup.py delete mode 100644 simplebot_stickers/__init__.py delete mode 100644 simplebot_stickers/telegram.py create mode 100644 stickersbot/__init__.py create mode 100644 stickersbot/__main__.py create mode 100644 stickersbot/hooks.py rename {simplebot_stickers => stickersbot}/signal.py (100%) rename {simplebot_stickers => stickersbot}/util.py (61%) delete mode 100644 tests/test_plugin.py diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 94cc19b..9b2d73c 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.9] + python-version: ['3.8', '3.11'] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} @@ -24,9 +24,6 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install '.[dev]' - - name: Check code with isort - run: | - isort --check . - name: Check code with black run: | black --check . @@ -35,7 +32,7 @@ jobs: pylama - name: Test with pytest run: | - pytest + #pytest deploy: needs: test @@ -57,11 +54,3 @@ jobs: build: true # only upload if a tag is pushed (otherwise just build & check) upload: ${{ github.event_name == 'push' && steps.check-tag.outputs.match == 'true' }} - - name: Create GitHub release - if: ${{ github.event_name == 'push' && steps.check-tag.outputs.match == 'true' }} - uses: Roang-zero1/github-create-release-action@master - with: - version_regex: ^v[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+ - changelog_file: "CHANGELOG.rst" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index 9caba16..0000000 --- a/.isort.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[isort] -profile=black diff --git a/.mypy.ini b/.mypy.ini deleted file mode 100644 index 976ba02..0000000 --- a/.mypy.ini +++ /dev/null @@ -1,2 +0,0 @@ -[mypy] -ignore_missing_imports = True diff --git a/CHANGELOG.rst b/CHANGELOG.rst deleted file mode 100644 index 051c78d..0000000 --- a/CHANGELOG.rst +++ /dev/null @@ -1,44 +0,0 @@ -Changelog -========= - -`Unreleased`_ -------------- - -- allow to set multiple cloud urls in case one fail another cloud will be tried, by default several clouds are set. - -`2.2.0`_ --------- - -- if message has image, don't keep checking for other conditions. -- save cache in the bot account's folder. - -`2.1.0`_ --------- - -- make search case insensitive. -- add ``/info`` command. -- improve HTML search results. -- reduce metadata refresh rate. -- pack that are too big are now uploaded to a cloud instead of sending them directly. -- allow to convert Telegram stickers (``.tgs``) to ``.webp`` - -`2.0.0`_ --------- - -- removed ``/sticker`` command. -- allow to search for sticker packs. -- allow to get a random sticker for an emoji. -- delete zipped pack after it is sent. -- cache stickers and packs metadata. -- limit the size of the zipped pack. - -1.0.0 ------ - -- initial release - - -.. _Unreleased: https://github.com/adbenitez/simplebot_stickers/compare/v2.2.0...HEAD -.. _2.2.0: https://github.com/adbenitez/simplebot_stickers/compare/v2.1.0...v2.2.0 -.. _2.1.0: https://github.com/adbenitez/simplebot_stickers/compare/v2.0.0...v2.1.0 -.. _2.0.0: https://github.com/adbenitez/simplebot_stickers/compare/v1.0.0...v2.0.0 diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 7d61281..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include LICENSE -include *.rst diff --git a/README.md b/README.md new file mode 100644 index 0000000..2039777 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# StickersBot + +[![Latest Release](https://img.shields.io/pypi/v/stickersbot.svg)](https://pypi.org/project/stickersbot) +[![CI](https://github.com/deltachat-bot/stickersbot/actions/workflows/python-ci.yml/badge.svg)](https://github.com/deltachat-bot/stickersbot/actions/workflows/python-ci.yml) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) + +Delta Chat bot that allows users to get stickers packs and convert normal images to stickers. + +## Installation and configuration + +```sh +pip install stickersbot +``` + +Configure the bot: + +```sh +stickersbot init bot@example.com PASSWORD +``` + +Start the bot: + +```sh +stickersbot serve +``` + +Run `stickersbot --help` to see all available options. + +## Usage + +To get sticker packs, browse https://signalstickers.com/ and copy the URL of the pack you want (the link in the "+ Add to Signal" button, an URL starting with ``https://signal.art/addstickers``) and send the pack URL to the bot in private, the bot will send you a zip with the sticker pack. + +To create an sticker from a normal image send the image to the bot and it will send you back the image as sticker. + +Send any text to the bot to search packs matching the given text. + +Send an emoji to the bot to get a random sticker associated with that emoji. diff --git a/README.rst b/README.rst deleted file mode 100644 index 0435983..0000000 --- a/README.rst +++ /dev/null @@ -1,44 +0,0 @@ -"Stickers" SimpleBot plugin -=========================== - -.. image:: https://img.shields.io/pypi/v/simplebot_stickers.svg - :target: https://pypi.org/project/simplebot_stickers - -.. image:: https://img.shields.io/pypi/pyversions/simplebot_stickers.svg - :target: https://pypi.org/project/simplebot_stickers - -.. image:: https://pepy.tech/badge/simplebot_stickers - :target: https://pepy.tech/project/simplebot_stickers - -.. image:: https://img.shields.io/pypi/l/simplebot_stickers.svg - :target: https://pypi.org/project/simplebot_stickers - -.. image:: https://github.com/adbenitez/simplebot_stickers/actions/workflows/python-ci.yml/badge.svg - :target: https://github.com/adbenitez/simplebot_stickers/actions/workflows/python-ci.yml - -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black - -`SimpleBot`_ plugin that allows users to get stickers packs and convert normal images to stickers. - -To get sticker packs, browse https://signalstickers.com/ and copy the URL of the pack you want (the link in the "+ Add to Signal" button, an URL starting with ``https://signal.art/addstickers``) and send the pack URL to the bot in private, the bot will send you a zip with the sticker pack. - -To create an sticker from a normal image send the image to the bot and it will send you back the image as sticker. - -Send any text to the bot to search packs matching the given text. - -Send an emoji to the bot to get a random sticker associated with that emoji. - -By default the bot will upload packs that are too big to some cloud, you can change the cloud server with:: - - simplebot -a bot@example.com db -s simplebot_stickers/cloud "https://example.com https://another-example.com" - -Install -------- - -To install run:: - - pip install simplebot-stickers - - -.. _SimpleBot: https://github.com/simplebot-org/simplebot diff --git a/avatar.png b/avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..3bbeca120847ea1248987954327b1647ce978641 GIT binary patch literal 6231 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 B{kQ-C literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml index 37763cc..0cad1c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,53 @@ [build-system] -requires = [ - "setuptools>=45", - "setuptools-scm>=6.2", - "wheel", -] +requires = ["setuptools>=64", "setuptools_scm>=8"] build-backend = "setuptools.build_meta" +[project] +name = "stickersbot" +description = "Sticker packs for all your Delta Chat needs" +dynamic = ["version"] +readme = "README.md" +requires-python = ">=3.8" +license = {file = "LICENSE"} +keywords = ["deltachat", "bot"] +authors = [ + {name = "adbenitez", email = "adb@merlinux.eu"}, +] +classifiers = [ + "Development Status :: 4 - Beta", + "Programming Language :: Python :: 3", +] +dependencies = [ + "deltabot-cli>=3.0.0,<4.0", + "signalstickers-client>=3.1.0", + "emoji>=1.6.1", + "cachelib>=0.7.0", + "requests>=2.26.0", +] + +[project.optional-dependencies] +dev = [ + "black", + "mypy", + "isort", + "pylint", + "pylama", + "pytest", + "types-emoji", + "types-requests", +] + +[project.scripts] +stickersbot = "stickersbot:main" + [tool.setuptools_scm] -write_to = "simplebot_stickers/_version.py" \ No newline at end of file +# can be empty if no extra settings are needed, presence enables setuptools_scm + +[tool.setuptools] +packages = ["stickersbot"] + +[tool.isort] +profile = "black" + +[tool.mypy] +ignore_missing_imports = "True" diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt deleted file mode 100644 index f807055..0000000 --- a/requirements/requirements-dev.txt +++ /dev/null @@ -1,9 +0,0 @@ --r requirements.txt --r requirements-test.txt -black==22.10.0 -mypy==0.982 -isort==5.10.1 -pylint==2.12.2 -pylama==8.3.6 -types-emoji==2.1.0.1 -types-requests==2.28.11.2 diff --git a/requirements/requirements-test.txt b/requirements/requirements-test.txt deleted file mode 100644 index 19a8976..0000000 --- a/requirements/requirements-test.txt +++ /dev/null @@ -1,2 +0,0 @@ -pytest==7.2.0 - diff --git a/requirements/requirements.txt b/requirements/requirements.txt deleted file mode 100644 index 7a41a6a..0000000 --- a/requirements/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -simplebot>=2.4.0 -signalstickers-client>=3.1.0 -emoji>=1.6.1 -cachelib>=0.7.0 -requests>=2.26.0 -lottie>=0.6.10 -CairoSVG>=2.5.2 -Pillow>=8.4.0 diff --git a/setup.py b/setup.py deleted file mode 100644 index 6ca3e6f..0000000 --- a/setup.py +++ /dev/null @@ -1,65 +0,0 @@ -"""Setup module installation.""" - -import os - -from setuptools import find_packages, setup - - -def load_requirements(path: str) -> list: - """Load requirements from the given relative path.""" - with open(path, encoding="utf-8") as file: - requirements = [] - for line in file.read().split("\n"): - if line.startswith("-r"): - dirname = os.path.dirname(path) - filename = line.split(maxsplit=1)[1] - requirements.extend(load_requirements(os.path.join(dirname, filename))) - elif line and not line.startswith("#"): - requirements.append(line.replace("==", ">=")) - return requirements - - -if __name__ == "__main__": - MODULE_NAME = "simplebot_stickers" - DESC = "Sticker packs for all your Delta Chat needs (SimpleBot plugin)" - - with open("README.rst", encoding="utf-8") as fh: - long_description = fh.read() - - setup( - name=MODULE_NAME, - setup_requires=["setuptools_scm"], - use_scm_version={ - "root": ".", - "relative_to": __file__, - "tag_regex": r"^(?Pv)?(?P[^\+]+)(?P.*)?$", - "git_describe_command": "git describe --dirty --tags --long --match v*.*.*", - }, - description=DESC, - long_description=long_description, - long_description_content_type="text/x-rst", - author="adbenitez", - author_email="adbenitez@nauta.cu", - url=f"https://github.com/adbenitez/{MODULE_NAME}", - keywords="simplebot plugin deltachat stickers", - license="MPL", - classifiers=[ - "Development Status :: 4 - Beta", - "Environment :: Plugins", - "Programming Language :: Python :: 3", - "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", - "Operating System :: OS Independent", - "Topic :: Utilities", - ], - zip_safe=False, - include_package_data=True, - packages=find_packages(), - install_requires=load_requirements("requirements/requirements.txt"), - extras_require={ - "test": load_requirements("requirements/requirements-test.txt"), - "dev": load_requirements("requirements/requirements-dev.txt"), - }, - entry_points={ - "simplebot.plugins": f"{MODULE_NAME} = {MODULE_NAME}", - }, - ) diff --git a/simplebot_stickers/__init__.py b/simplebot_stickers/__init__.py deleted file mode 100644 index da32768..0000000 --- a/simplebot_stickers/__init__.py +++ /dev/null @@ -1,107 +0,0 @@ -"""Plugin's filters and commands definitions.""" - -import io -import os - -import simplebot -from cachelib import FileSystemCache -from deltachat import Message -from emoji import emoji_count -from simplebot.bot import DeltaBot, Replies - -from . import telegram -from .signal import SignalStickers -from .util import getdefault, sizeof_fmt, upload - -DEF_MAX_PACK_SIZE = str(1024**2 * 15) -signal = SignalStickers() - - -@simplebot.hookimpl -def deltabot_init(bot: DeltaBot) -> None: - getdefault( - bot, "cloud", "https://ttm.sh/ https://envs.sh/ https://x0.at/ https://0x0.st/" - ) - getdefault(bot, "max_size", DEF_MAX_PACK_SIZE) - - -@simplebot.hookimpl -def deltabot_start(bot: DeltaBot) -> None: - path = os.path.join(os.path.dirname(bot.account.db_path), __name__) - if not os.path.exists(path): - os.makedirs(path) - signal.cache = FileSystemCache( - path, threshold=5000, default_timeout=60 * 60 * 24 * 60 - ) - - -@simplebot.filter -def filter_messages(bot: DeltaBot, message: Message, replies: Replies) -> None: - """Send me an image or Telegram animated sticker and I will convert it to a Delta Chat sticker for you. - - Send me an emoji to get an sticker representing that emoji. - - Send me a text to search for sticker packs matching that text. - - Also, you can send me an URL of a Signal sticker pack, and I will send you the pack, for example, something like: - sgnl://addstickers/?pack_id=59d338...&pack_key=56af35... - """ - if message.chat.is_multiuser(): - return - - if message.filemime.startswith("image/"): - replies.add(filename=message.filename, viewtype="sticker") - elif telegram.is_sticker(message.filename): - replies.add(filename=telegram.convert(message.filename), viewtype="sticker") - elif signal.is_pack(message.text): - _process_signal_pack(bot, message, replies) - elif emoji_count(message.text): - pack_url, sticker = signal.get_random_sticker(message.text) - if pack_url: - replies.add( - filename=f"{message.text}.webp", - bytefile=io.BytesIO(sticker), - viewtype="sticker", - ) - replies.add(text=pack_url) - else: - replies.add(text=f"❌ No sticker found for: {message.text!r}") - elif message.text: - html = signal.search_html(bot.self_contact.addr, message.text) - if html: - replies.add(text=f"Results for: {message.text!r}", html=html) - else: - replies.add(text=f"❌ No results for: {message.text!r}") - - -@simplebot.command -def info(payload: str, message: Message, replies: Replies) -> None: - """Get pack info. - - Example: - /info sgnl://addstickers/?pack_id=59d338...&pack_key=56af35... - """ - if signal.is_pack(payload): - text, cover = signal.get_pack_metadata(payload) - replies.add( - text=text, filename="cover.webp", bytefile=io.BytesIO(cover), quote=message - ) - else: - replies.add("❌ Unknow pack URL", quote=message) - - -def _process_signal_pack(bot: DeltaBot, message: Message, replies: Replies) -> None: - title, path = signal.download_pack(bot.account.get_blobdir(), message.text) - size = os.stat(path).st_size - if size > int(getdefault(bot, "max_size", DEF_MAX_PACK_SIZE)): - url = upload(bot, path) - if url: - replies.add( - text=f"Name: {title}\nSize: {sizeof_fmt(size)}\nDownload: {url}", - quote=message, - ) - else: - replies.add(text=f"❌ Pack too big ({sizeof_fmt(size)})", quote=message) - os.remove(path) - else: - replies.add(filename=path, quote=message) diff --git a/simplebot_stickers/telegram.py b/simplebot_stickers/telegram.py deleted file mode 100644 index 74bcdaa..0000000 --- a/simplebot_stickers/telegram.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Telegram sticker tools""" - -import os -from tempfile import NamedTemporaryFile - -from lottie.exporters import exporters -from lottie.importers import importers -from lottie.utils.stripper import float_strip - - -def convert(path: str) -> str: - prefix = os.path.join(os.path.dirname(path), "sticker") - with NamedTemporaryFile(prefix=prefix, suffix=".webp", delete=False) as file: - outpath = file.name - anim = importers["lottie"].process(path) - float_strip(anim) - exporters["webp"].process( - anim, outpath, lossless=False, method=0, quality=40, skip_frames=10 - ) - return outpath - - -def is_sticker(path: str) -> bool: - return path.endswith((".json", ".tgs")) diff --git a/stickersbot/__init__.py b/stickersbot/__init__.py new file mode 100644 index 0000000..bd4a494 --- /dev/null +++ b/stickersbot/__init__.py @@ -0,0 +1,11 @@ +"""Stickers bot.""" + +from .hooks import cli + + +def main() -> None: + """Run the application.""" + try: + cli.start() + except KeyboardInterrupt: + pass diff --git a/stickersbot/__main__.py b/stickersbot/__main__.py new file mode 100644 index 0000000..f4a5c4a --- /dev/null +++ b/stickersbot/__main__.py @@ -0,0 +1,5 @@ +"""Support for package execution.""" + +from . import main + +main() diff --git a/stickersbot/hooks.py b/stickersbot/hooks.py new file mode 100644 index 0000000..8fea3d0 --- /dev/null +++ b/stickersbot/hooks.py @@ -0,0 +1,148 @@ +"""Event Hooks""" + +import io +import os +from argparse import Namespace +from tempfile import TemporaryDirectory + +from cachelib import FileSystemCache +from deltabot_cli import ( + AttrDict, + Bot, + BotCli, + ChatType, + EventType, + events, + is_not_known_command, +) +from emoji import emoji_count + +from .signal import SignalStickers +from .util import sizeof_fmt, upload + +DEF_MAX_PACK_SIZE = "15" +DEF_CLOUDS = "https://ttm.sh/ https://envs.sh/ https://x0.at/ https://0x0.st/" +signal = SignalStickers() +cli = BotCli("stickersbot") + + +@cli.on_init +def on_init(bot: Bot, _args: Namespace) -> None: + for accid in bot.rpc.get_all_account_ids(): + if not bot.rpc.get_config(accid, "displayname"): + bot.rpc.set_config(accid, "displayname", "StickersBot") + status = "I am a Delta Chat bot, send me /help for more info" + bot.rpc.set_config(accid, "selfstatus", status) + bot.rpc.set_config(accid, "delete_server_after", "1") + + +@cli.on_start +def on_start(_bot: Bot, args: Namespace) -> None: + path = os.path.join(args.config_dir, "cache") + if not os.path.exists(path): + os.makedirs(path) + signal.cache = FileSystemCache( + path, threshold=5000, default_timeout=60 * 60 * 24 * 60 + ) + + +@cli.on(events.RawEvent) +def log_event(bot: Bot, accid: int, event: AttrDict) -> None: + if event.kind == EventType.INFO: + bot.logger.info(event.msg) + elif event.kind == EventType.WARNING: + bot.logger.warning(event.msg) + elif event.kind == EventType.ERROR: + bot.logger.error(event.msg) + elif event.kind == EventType.SECUREJOIN_INVITER_PROGRESS: + if event.progress == 1000: + bot.logger.debug("QR scanned by contact id=%s", event.contact_id) + chatid = bot.rpc.create_chat_by_contact_id(accid, event.contact_id) + send_help(bot, accid, chatid) + + +@cli.on(events.NewMessage(is_info=False, func=is_not_known_command)) +def on_message(bot: Bot, accid: int, event: AttrDict) -> None: + msg = event.msg + chat = bot.rpc.get_basic_chat_info(accid, msg.chat_id) + if chat.chat_type != ChatType.SINGLE: + return + + bot.rpc.markseen_msgs(accid, [msg.id]) + + if msg.file_mime and msg.file_mime.startswith("image/"): + bot.rpc.send_sticker(accid, msg.chat_id, msg.file) + elif signal.is_pack(msg.text): + process_signal_pack(bot, accid, msg) + elif emoji_count(msg.text): + pack_url, sticker = signal.get_random_sticker(msg.text) + if pack_url: + with TemporaryDirectory() as tmp_dir: + filename = os.path.join(tmp_dir, f"{msg.text}.webp") + with open(filename, mode="wb") as attachment: + attachment.write(sticker) + msg_id = bot.rpc.send_sticker(accid, msg.chat_id, filename) + bot.rpc.send_msg( + accid, msg.chat_id, {"text": pack_url, "quotedMessageId": msg_id} + ) + else: + text = f"❌ No sticker found for: {msg.text!r}" + bot.rpc.send_msg(accid, msg.chat_id, {"text": text}) + elif msg.text: + selfaddr = bot.rpc.get_config(accid, "configured_addr") + html = signal.search_html(selfaddr, msg.text) + if html: + text = f"Results for: {msg.text!r}" + bot.rpc.send_msg(accid, msg.chat_id, {"text": text, "html": html}) + else: + text = f"❌ No results for: {msg.text!r}" + bot.rpc.send_msg(accid, msg.chat_id, {"text": text}) + + +@cli.on(events.NewMessage(command="/help")) +def _help(bot: Bot, accid: int, event: AttrDict) -> None: + send_help(bot, accid, event.msg.chat_id) + + +@cli.on(events.NewMessage(command="/info")) +def _info(bot: Bot, accid: int, event: AttrDict) -> None: + msg = event.msg + if signal.is_pack(event.payload): + text, cover = signal.get_pack_metadata(event.payload) + with TemporaryDirectory() as tmp_dir: + filename = os.path.join(tmp_dir, "cover.webp") + with open(filename, mode="wb") as attachment: + attachment.write(cover) + reply = {"text": text, "file": filename, "quotedMessageId": msg.id} + bot.rpc.send_msg(accid, msg.chat_id, reply) + else: + reply = {"text": "❌ Unknow pack URL", "quotedMessageId": msg.id} + bot.rpc.send_msg(accid, msg.chat_id, reply) + + +def process_signal_pack(bot: Bot, accid: int, msg: AttrDict) -> None: + title, path = signal.download_pack(bot.account.get_blobdir(), msg.text) + size = os.stat(path).st_size + if size > 1024**2 * 15: + url = upload(bot.logger, path) + if url: + text = f"Name: {title}\nSize: {sizeof_fmt(size)}\nDownload: {url}" + else: + text = f"❌ Pack too big ({sizeof_fmt(size)})" + bot.rpc.send_msg(accid, msg.chat_id, {"text": text, "quotedMessageId": msg.id}) + else: + bot.rpc.send_msg(accid, msg.chat_id, {"file": path, "quotedMessageId": msg.id}) + os.remove(path) + + +def send_help(bot: Bot, accid: int, chatid: int) -> None: + lines = [ + "Send me an image and I will convert it to a Delta Chat sticker for you.\n", + "Send me an emoji to get an sticker representing that emoji.\n", + "Send me a text to search for sticker packs matching that text.\n", + "Also, you can send me an URL of a Signal sticker pack, and I will send you the pack, for example, something that looks like:", + "sgnl://addstickers/?pack_id=59d338...&pack_key=56af35..." "", + "**Available commands**", + "/info URL - Get more information about the sticker pack with given URL, example: /info sgnl://addstickers/?pack_id=59d338...&pack_key=56af35...", + ] + bot.rpc.send_msg(accid, chatid, {"text": "\n".join(lines)}) diff --git a/simplebot_stickers/signal.py b/stickersbot/signal.py similarity index 100% rename from simplebot_stickers/signal.py rename to stickersbot/signal.py diff --git a/simplebot_stickers/util.py b/stickersbot/util.py similarity index 61% rename from simplebot_stickers/util.py rename to stickersbot/util.py index 2e4b52a..ac2a029 100644 --- a/simplebot_stickers/util.py +++ b/stickersbot/util.py @@ -1,10 +1,10 @@ -"""Module utilities""" +"""Utilities""" import functools import random +from logging import Logger import requests -from simplebot.bot import DeltaBot session = requests.Session() session.headers.update( @@ -24,24 +24,15 @@ def sizeof_fmt(num: float) -> str: return "%.1f%s%s" % (num, "Yi", suffix) # noqa -def upload(bot: DeltaBot, path: str) -> str: - urls = getdefault(bot, "cloud").split() +def upload(logger: Logger, path: str) -> str: + urls = ["https://ttm.sh/", "https://envs.sh/", "https://x0.at/", "https://0x0.st/"] random.shuffle(urls) for url in urls: try: with open(path, "rb") as file: - with session.post(url, files=dict(file=file)) as resp: + with session.post(url, files={"file": file}) as resp: if 200 <= resp.status_code <= 300: return resp.text.strip() except Exception as ex: - bot.logger.exception(ex) + logger.exception(ex) return "" - - -def getdefault(bot: DeltaBot, key: str, value: str = None) -> str: - scope = __name__.split(".")[0] - val = bot.get(key, scope=scope) - if val is None and value is not None: - bot.set(key, value, scope=scope) - val = value - return val diff --git a/tests/test_plugin.py b/tests/test_plugin.py deleted file mode 100644 index e6680f8..0000000 --- a/tests/test_plugin.py +++ /dev/null @@ -1,15 +0,0 @@ -class TestPlugin: - def test_filter(self, mocker) -> None: - msg = mocker.get_one_reply(filename="image.png") - assert msg.filename - assert msg.is_sticker() - - msg = mocker.get_one_reply( - "https://signal.art/addstickers/#pack_id=59d3387717104e38a67f838e7ad0208c&pack_key=56af35841874d6fe82fa2085e8e5ed74dba5d187af007d3b4d8a3711dd722ad7" - ) - assert msg.filename.endswith("zip") - - msg = mocker.get_one_reply("cat") - assert msg.has_html() - - assert mocker.get_replies("❌") # random behavior