diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index c73a96f..0000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,9 +0,0 @@ -# See https://help.github.com/articles/about-codeowners/ for valid syntax for entries. -# Default owners for everything in the repo, unless a later match takes precedence - -# Phoenix owns the files under blacklist thingies -/SaitamaRobot/modules/blacklistusers.py @rsktg -/SaitamaRobot/modules/sql/blacklistusers_sql.py @rsktg -#pep8-ing the code doesn't change maker -/SaitamaRobot/modules/paste.py @DragSama -/SaitamaRobot/modules/speed_test.py @DragSama diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 108a3a6..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,12 +0,0 @@ -# These are supported funding model platforms - -github: [TR-TECH-GUIDE] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index d7bdcc4..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: 'Bug report ' -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots(if any)** -If applicable, add screenshots to help explain your problem. - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 11fc491..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/query.md b/.github/ISSUE_TEMPLATE/query.md deleted file mode 100644 index 4e37984..0000000 --- a/.github/ISSUE_TEMPLATE/query.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: Query -about: Ask us a question -title: '' -labels: question -assignees: '' - ---- - -**I wanted to ask that...** -Just type your question here [...] diff --git a/.github/disabled/docker_disabled.yml b/.github/disabled/docker_disabled.yml deleted file mode 100644 index 1358dbb..0000000 --- a/.github/disabled/docker_disabled.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: BuildDocker - -on: - - push: - - branches: [ shiken ] - - pull_request: - - branches: [ shiken ] - -jobs: - - build: - - runs-on: ubuntu-20.04 - - steps: - - - uses: actions/checkout@master - - run: bash exp.sh - - uses: elgohr/Publish-Docker-Github-Action@master - - with: - - name: animekaizoku/saitamarobot - - username: ${{ secrets.DOCKER_USERNAME }} - - password: ${{ secrets.DOCKER_PASSWORD }} - - dockerfile: Dockerfile - - buildoptions: --force-rm --compress --no-cache --squash - - tags: "shiken" diff --git a/.github/disabled/main.yml b/.github/disabled/main.yml deleted file mode 100644 index fdea9eb..0000000 --- a/.github/disabled/main.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Deploy - -on: - push: - branches: - - shiken - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: akhileshns/heroku-deploy@v3.5.6 # This is the action - with: - heroku_api_key: ${{secrets.HEROKU_API_KEY}} - heroku_app_name: ${{secrets.HEROKU_APP_NAME}} #Must be unique in Heroku - heroku_email: ${{secrets.HEROKU_EMAIL}} diff --git a/.github/workflows/Readme.md b/.github/workflows/Readme.md deleted file mode 100644 index 0647fd1..0000000 --- a/.github/workflows/Readme.md +++ /dev/null @@ -1 +0,0 @@ -# Here we auto rename SaitamaRobot to SnowGirl and check pylint diff --git a/.github/workflows/sed.yml b/.github/workflows/sed.yml deleted file mode 100644 index dfb5516..0000000 --- a/.github/workflows/sed.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Sed-replacer -on: push -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Find and Replace - uses: jacobtomlinson/gha-find-replace@master - with: - find: "Amanda" - replace: "Amanda" - - name: Pull All Updates - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: 'Welcome to Amanda' - commit_options: '--no-verify' - repository: . - commit_user_name: TR-TECH-GUIDE - commit_user_email: tharukrenujatechguide@gmail.com - commit_author: TR-TECH-GUIDE diff --git a/.github/workflows/yapf.yml b/.github/workflows/yapf.yml deleted file mode 100644 index dc62b41..0000000 --- a/.github/workflows/yapf.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Format Python code -on: - push: - branches: [shiken] - pull_request: - branches: [shiken] -jobs: - Autoyapf: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@master - - name: Auto yapf - uses: Accipiter7/yapf-action@master - - name: Create Pull Request - uses: peter-evans/create-pull-request@master - with: - commit-message: Automated code formatting. - committer: Accipiter7 - title: Automated code formatting. - body: This is an automated code formatting pull request. - labels: Code Formatting - reviewers: Accipiter7 - branch: autofix diff --git a/.gitignore b/.gitignore index 78ab858..510c73d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,114 @@ -SaitamaRobot/config.py -SaitamaRobot/*.session -SaitamaRobot/*.session-journal -SaitamaRobot/config.ini -log.txt -*.pyc -.idea/ -.project -.pydevproject -.directory -.vscode -SaitamaRobot/modules/helper_funcs/temp.txt -SaitamaRobot/elevated_users.json -start-3.6.bat -*.session -kangsticker.png -venv -env -saitama.session-journal -updates.txt -*.exe -suzuyaPyro -str +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/Amanda/Addons/ImageEditor/edit_1.py b/Amanda/Addons/ImageEditor/edit_1.py new file mode 100644 index 0000000..91ea91a --- /dev/null +++ b/Amanda/Addons/ImageEditor/edit_1.py @@ -0,0 +1,247 @@ +# By @TroJanzHEX +import os +import shutil + +import cv2 +from PIL import Image, ImageEnhance, ImageFilter + + +async def bright(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "brightness.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + image = Image.open(a) + brightness = ImageEnhance.Brightness(image) + brightness.enhance(1.5).save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("bright-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def mix(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "mix.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + image = Image.open(a) + red, green, blue = image.split() + new_image = Image.merge("RGB", (green, red, blue)) + new_image.save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("mix-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def black_white(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "black_white.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + image_file = cv2.imread(a) + grayImage = cv2.cvtColor(image_file, cv2.COLOR_BGR2GRAY) + cv2.imwrite(edit_img_loc, grayImage) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("black_white-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def normal_blur(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "BlurImage.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + OriImage = Image.open(a) + blurImage = OriImage.filter(ImageFilter.BLUR) + blurImage.save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("normal_blur-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def g_blur(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "gaussian_blur.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + im1 = Image.open(a) + im2 = im1.filter(ImageFilter.GaussianBlur(radius=5)) + im2.save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("g_blur-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def box_blur(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "box_blur.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + im1 = Image.open(a) + im2 = im1.filter(ImageFilter.BoxBlur(0)) + im2.save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("box_blur-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return diff --git a/Amanda/Addons/ImageEditor/edit_2.py b/Amanda/Addons/ImageEditor/edit_2.py new file mode 100644 index 0000000..d331fa3 --- /dev/null +++ b/Amanda/Addons/ImageEditor/edit_2.py @@ -0,0 +1,399 @@ +# By @TroJanzHEX +import os +import shutil + +import cv2 +import numpy as np +from PIL import Image, ImageDraw, ImageEnhance + + +async def circle_with_bg(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "circle.png" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + img = Image.open(a).convert("RGB") + npImage = np.array(img) + h, w = img.size + alpha = Image.new("L", img.size, 0) + draw = ImageDraw.Draw(alpha) + draw.pieslice([0, 0, h, w], 0, 360, fill=255) + npAlpha = np.array(alpha) + npImage = np.dstack((npImage, npAlpha)) + Image.fromarray(npImage).save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("circle_with_bg-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def circle_without_bg(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "circle.png" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + img = Image.open(a).convert("RGB") + npImage = np.array(img) + h, w = img.size + alpha = Image.new("L", img.size, 0) + draw = ImageDraw.Draw(alpha) + draw.pieslice([0, 0, h, w], 0, 360, fill=255) + npAlpha = np.array(alpha) + npImage = np.dstack((npImage, npAlpha)) + Image.fromarray(npImage).save(edit_img_loc) + await message.reply_chat_action("upload_document") + await message.reply_to_message.reply_document(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("circle_without_bg-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def sticker(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "sticker.webp" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + os.rename(a, edit_img_loc) + await message.reply_to_message.reply_sticker(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("sticker-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +def add_corners(im, rad): + circle = Image.new("L", (rad * 2, rad * 2), 0) + draw = ImageDraw.Draw(circle) + draw.ellipse((0, 0, rad * 2, rad * 2), fill=255) + alpha = Image.new("L", im.size, 255) + w, h = im.size + alpha.paste(circle.crop((0, 0, rad, rad)), (0, 0)) + alpha.paste(circle.crop((0, rad, rad, rad * 2)), (0, h - rad)) + alpha.paste(circle.crop((rad, 0, rad * 2, rad)), (w - rad, 0)) + alpha.paste(circle.crop((rad, rad, rad * 2, rad * 2)), (w - rad, h - rad)) + im.putalpha(alpha) + return im + + +async def edge_curved(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "edge_curved.webp" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + im = Image.open(a) + im = add_corners(im, 100) + im.save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_sticker(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("edge_curved-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def contrast(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "contrast.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + image = Image.open(a) + contrast = ImageEnhance.Contrast(image) + contrast.enhance(1.5).save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("contrast-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +def sepia(img): + width, height = img.size + new_img = img.copy() + for x in range(width): + for y in range(height): + red, green, blue = img.getpixel((x, y)) + new_val = 0.3 * red + 0.59 * green + 0.11 * blue + new_red = int(new_val * 2) + if new_red > 255: + new_red = 255 + new_green = int(new_val * 1.5) + if new_green > 255: + new_green = 255 + new_blue = int(new_val) + if new_blue > 255: + new_blue = 255 + + new_img.putpixel((x, y), (new_red, new_green, new_blue)) + + return new_img + + +async def sepia_mode(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "sepia.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + image = Image.open(a) + new_img = sepia(image) + new_img.save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("sepia_mode-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +def dodgeV2(x, y): + return cv2.divide(x, 255 - y, scale=256) + + +async def pencil(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "pencil.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + img = cv2.imread(a) + img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + img_invert = cv2.bitwise_not(img_gray) + img_smoothing = cv2.GaussianBlur(img_invert, (21, 21), sigmaX=0, sigmaY=0) + final_img = dodgeV2(img_gray, img_smoothing) + cv2.imwrite(edit_img_loc, final_img) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("pencil-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +def color_quantization(img, k): + data = np.float32(img).reshape((-1, 3)) + criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0) + _, label, center = cv2.kmeans( + data, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS + ) + center = np.uint8(center) + result = center[label.flatten()] + result = result.reshape(img.shape) + return result + + +async def cartoon(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "kang.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + img = cv2.imread(a) + edges = cv2.Canny(img, 100, 200) + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + edges = cv2.adaptiveThreshold( + gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 5 + ) + color = cv2.bilateralFilter(img, d=9, sigmaColor=200, sigmaSpace=200) + + cv2.bitwise_and(color, color, mask=edges) + img_1 = color_quantization(img, 7) + cv2.imwrite(edit_img_loc, img_1) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("cartoon-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return diff --git a/Amanda/Addons/ImageEditor/edit_3.py b/Amanda/Addons/ImageEditor/edit_3.py new file mode 100644 index 0000000..880a7b5 --- /dev/null +++ b/Amanda/Addons/ImageEditor/edit_3.py @@ -0,0 +1,165 @@ +# By @TroJanzHEX +import os +import shutil + +from PIL import Image, ImageOps + + +async def black_border(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "imaged-black-border.png" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + img = Image.open(a) + img_with_border = ImageOps.expand(img, border=100, fill="black") + img_with_border.save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("black_border-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def green_border(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "imaged-green-border.png" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + img = Image.open(a) + img_with_border = ImageOps.expand(img, border=100, fill="green") + img_with_border.save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("green_border-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def blue_border(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "imaged-blue-border.png" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + img = Image.open(a) + img_with_border = ImageOps.expand(img, border=100, fill="blue") + img_with_border.save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("blue_border-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def red_border(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "imaged-red-border.png" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + img = Image.open(a) + img_with_border = ImageOps.expand(img, border=100, fill="red") + img_with_border.save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("red_border-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return diff --git a/Amanda/Addons/ImageEditor/edit_4.py b/Amanda/Addons/ImageEditor/edit_4.py new file mode 100644 index 0000000..7a82381 --- /dev/null +++ b/Amanda/Addons/ImageEditor/edit_4.py @@ -0,0 +1,412 @@ +# By @TroJanzHEX +import io +import os +import shutil + +import cv2 +import numpy as np +import requests +from PIL import Image, ImageDraw, ImageOps + +from Amanda.config import get_str_key + +RemoveBG_API = get_str_key("REM_BG_API_KEY", required=False) +#Fixed + +async def rotate_90(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "rotate_90.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + src = cv2.imread(a) + image = cv2.rotate(src, cv2.cv2.ROTATE_90_CLOCKWISE) + cv2.imwrite(edit_img_loc, image) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("rotate_90-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def rotate_180(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "rotate_180.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + src = cv2.imread(a) + image = cv2.rotate(src, cv2.ROTATE_180) + cv2.imwrite(edit_img_loc, image) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("rotate_180-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def rotate_270(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "rotate_270.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + src = cv2.imread(a) + image = cv2.rotate(src, cv2.ROTATE_90_COUNTERCLOCKWISE) + cv2.imwrite(edit_img_loc, image) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("rotate_270-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +def resize_photo(photo: str, userid: str) -> io.BytesIO: + image = Image.open(photo) + maxsize = 512 + scale = maxsize / max(image.width, image.height) + new_size = (int(image.width * scale), int(image.height * scale)) + image = image.resize(new_size, Image.LANCZOS) + resized_photo = io.BytesIO() + resized_photo.name = "./DOWNLOADS" + "/" + userid + "resized.png" + image.save(resized_photo, "PNG") + return resized_photo + + +async def round_sticker(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "rounded.webp" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + resized = resize_photo(a, userid) + img = Image.open(resized).convert("RGB") + npImage = np.array(img) + h, w = img.size + alpha = Image.new("L", img.size, 0) + draw = ImageDraw.Draw(alpha) + draw.pieslice([0, 0, h, w], 0, 360, fill=255) + npAlpha = np.array(alpha) + npImage = np.dstack((npImage, npAlpha)) + Image.fromarray(npImage).save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_sticker(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("round_sticker-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def inverted(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "inverted.png" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + a = await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + image = Image.open(a) + inverted_image = ImageOps.invert(image) + inverted_image.save(edit_img_loc) + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("inverted-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def removebg_plain(client, message): + try: + if not (RemoveBG_API == ""): + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "nobgplain.png" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + + response = requests.post( + "https://api.remove.bg/v1.0/removebg", + files={"image_file": open(download_location, "rb")}, + data={"size": "auto"}, + headers={"X-Api-Key": RemoveBG_API}, + ) + if response.status_code == 200: + with open(f"{edit_img_loc}", "wb") as out: + out.write(response.content) + else: + await message.reply_to_message.reply_text( + "Check if your api is correct", quote=True + ) + return + + await message.reply_chat_action("upload_document") + await message.reply_to_message.reply_document(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + else: + await message.reply_to_message.reply_text( + "Get the api from https://www.remove.bg/b/background-removal-api and add in Config Var", + quote=True, + disable_web_page_preview=True, + ) + except Exception as e: + print("removebg_plain-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def removebg_white(client, message): + try: + if not (RemoveBG_API == ""): + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "nobgwhite.png" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + + response = requests.post( + "https://api.remove.bg/v1.0/removebg", + files={"image_file": open(download_location, "rb")}, + data={"size": "auto"}, + headers={"X-Api-Key": Config.RemoveBG_API}, + ) + if response.status_code == 200: + with open(f"{edit_img_loc}", "wb") as out: + out.write(response.content) + else: + await message.reply_to_message.reply_text( + "Check if your api is correct", quote=True + ) + return + + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + else: + await message.reply_to_message.reply_text( + "Get the api from https://www.remove.bg/b/background-removal-api and add in Config Var", + quote=True, + disable_web_page_preview=True, + ) + except Exception as e: + print("removebg_white-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def removebg_sticker(client, message): + try: + if not (RemoveBG_API == ""): + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "nobgsticker.webp" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + + response = requests.post( + "https://api.remove.bg/v1.0/removebg", + files={"image_file": open(download_location, "rb")}, + data={"size": "auto"}, + headers={"X-Api-Key": RemoveBG_API}, + ) + if response.status_code == 200: + with open(f"{edit_img_loc}", "wb") as out: + out.write(response.content) + else: + await message.reply_to_message.reply_text( + "Check if your api is correct", quote=True + ) + return + + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_sticker(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + else: + await message.reply_to_message.reply_text( + "Get the api from https://www.remove.bg/b/background-removal-api and add in Config Var", + quote=True, + disable_web_page_preview=True, + ) + except Exception as e: + print("removebg_sticker-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return diff --git a/Amanda/Addons/ImageEditor/edit_5.py b/Amanda/Addons/ImageEditor/edit_5.py new file mode 100644 index 0000000..76eda47 --- /dev/null +++ b/Amanda/Addons/ImageEditor/edit_5.py @@ -0,0 +1,424 @@ +# By @TroJanzHEX +import asyncio +import os +import shutil + + +async def normalglitch_1(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "normalglitch_1.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + cd = ["glitch_this", "-c", "-o", edit_img_loc, download_location, "1"] + process = await asyncio.create_subprocess_exec( + *cd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + await process.communicate() + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("normalglitch_1-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def normalglitch_2(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "normalglitch_2.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + cd = ["glitch_this", "-c", "-o", edit_img_loc, download_location, "2"] + process = await asyncio.create_subprocess_exec( + *cd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + await process.communicate() + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("normalglitch_2-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def normalglitch_3(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "normalglitch_3.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + cd = ["glitch_this", "-c", "-o", edit_img_loc, download_location, "3"] + process = await asyncio.create_subprocess_exec( + *cd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + await process.communicate() + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("normalglitch_3-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def normalglitch_4(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "normalglitch_4.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + cd = ["glitch_this", "-c", "-o", edit_img_loc, download_location, "4"] + process = await asyncio.create_subprocess_exec( + *cd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + await process.communicate() + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("normalglitch_4-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def normalglitch_5(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "normalglitch_5.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + cd = ["glitch_this", "-c", "-o", edit_img_loc, download_location, "5"] + process = await asyncio.create_subprocess_exec( + *cd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + await process.communicate() + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("normalglitch_5-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def scanlineglitch_1(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "scanlineglitch_1.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + cd = ["glitch_this", "-c", "-s", "-o", edit_img_loc, download_location, "1"] + process = await asyncio.create_subprocess_exec( + *cd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + await process.communicate() + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("scanlineglitch_1-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def scanlineglitch_2(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "scanlineglitch_2.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + cd = ["glitch_this", "-c", "-s", "-o", edit_img_loc, download_location, "2"] + process = await asyncio.create_subprocess_exec( + *cd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + await process.communicate() + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("scanlineglitch_2-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def scanlineglitch_3(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "scanlineglitch_3.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + cd = ["glitch_this", "-c", "-s", "-o", edit_img_loc, download_location, "3"] + process = await asyncio.create_subprocess_exec( + *cd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + await process.communicate() + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("scanlineglitch_3-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def scanlineglitch_4(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "scanlineglitch_4.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + cd = ["glitch_this", "-c", "-s", "-o", edit_img_loc, download_location, "4"] + process = await asyncio.create_subprocess_exec( + *cd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + await process.communicate() + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("scanlineglitch_4-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return + + +async def scanlineglitch_5(client, message): + try: + userid = str(message.chat.id) + if not os.path.isdir(f"./DOWNLOADS/{userid}"): + os.makedirs(f"./DOWNLOADS/{userid}") + download_location = "./DOWNLOADS" + "/" + userid + "/" + userid + ".jpg" + edit_img_loc = "./DOWNLOADS" + "/" + userid + "/" + "scanlineglitch_5.jpg" + if not message.reply_to_message.empty: + msg = await message.reply_to_message.reply_text( + "Downloading image", quote=True + ) + await client.download_media( + message=message.reply_to_message, file_name=download_location + ) + await msg.edit("Processing Image...") + cd = ["glitch_this", "-c", "-s", "-o", edit_img_loc, download_location, "5"] + process = await asyncio.create_subprocess_exec( + *cd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + await process.communicate() + await message.reply_chat_action("upload_photo") + await message.reply_to_message.reply_photo(edit_img_loc, quote=True) + await msg.delete() + else: + await message.reply_text("Why did you delete that??") + try: + shutil.rmtree(f"./DOWNLOADS/{userid}") + except Exception: + pass + except Exception as e: + print("scanlineglitch_5-error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_to_message.reply_text( + "Something went wrong!", quote=True + ) + except Exception: + return diff --git a/Amanda/AmandaBestTools/dark.py b/Amanda/AmandaBestTools/dark.py new file mode 100644 index 0000000..ec36056 --- /dev/null +++ b/Amanda/AmandaBestTools/dark.py @@ -0,0 +1,7 @@ +def get_arg(message): + msg = message.text + msg = msg.replace(" ", "", 1) if msg[1] == " " else msg + split = msg[1:].replace("\n", " \n").split(" ") + if " ".join(split[1:]).strip() == "": + return "" + return " ".join(split[1:]) diff --git a/Amanda/AmandaBestTools/errors.py b/Amanda/AmandaBestTools/errors.py new file mode 100644 index 0000000..44fe150 --- /dev/null +++ b/Amanda/AmandaBestTools/errors.py @@ -0,0 +1,50 @@ +import sys +import traceback +from functools import wraps + +from Amanda import pbot +from Amanda import SUPPORT_CHAT + +def split_limits(text): + if len(text) < 2048: + return [text] + + lines = text.splitlines(True) + small_msg = '' + result = [] + for line in lines: + if len(small_msg) + len(line) < 2048: + small_msg += line + else: + result.append(small_msg) + small_msg = line + else: + result.append(small_msg) + + return result + +def capture_err(func): + @wraps(func) + async def capture(client, message, *args, **kwargs): + try: + return await func(client, message, *args, **kwargs) + except Exception as err: + exc_type, exc_obj, exc_tb = sys.exc_info() + errors = traceback.format_exception( + etype=exc_type, value=exc_obj, tb=exc_tb, + ) + error_feedback = split_limits( + '**ERROR** | `{}` | `{}`\n\n```{}```\n\n```{}```\n'.format( + 0 if not message.from_user else message.from_user.id, + 0 if not message.chat else message.chat.id, + message.text or message.caption, + ''.join(errors), + ), + ) + for x in error_feedback: + await pbot.send_message( + SUPPORT_CHAT, + x + ) + raise err + return capture diff --git a/Amanda/AmandaBestTools/pluginshelper.py b/Amanda/AmandaBestTools/pluginshelper.py new file mode 100644 index 0000000..259ca44 --- /dev/null +++ b/Amanda/AmandaBestTools/pluginshelper.py @@ -0,0 +1,353 @@ +import asyncio +import math +import shlex +import sys +import time +import traceback +from functools import wraps +from typing import Callable, Coroutine, Dict, List, Tuple, Union + +from PIL import Image +from pyrogram import Client +from pyrogram.errors import FloodWait, MessageNotModified +from pyrogram.types import Chat, Message, User + +from Amanda import OWNER_ID, SUPPORT_CHAT +from Amanda import pbot + + +def get_user(message: Message, text: str) -> [int, str, None]: + if text is None: + asplit = None + else: + asplit = text.split(" ", 1) + user_s = None + reason_ = None + if message.reply_to_message: + user_s = message.reply_to_message.from_user.id + reason_ = text if text else None + elif asplit is None: + return None, None + elif len(asplit[0]) > 0: + user_s = int(asplit[0]) if asplit[0].isdigit() else asplit[0] + if len(asplit) == 2: + reason_ = asplit[1] + return user_s, reason_ + + +def get_readable_time(seconds: int) -> int: + count = 0 + ping_time = "" + time_list = [] + time_suffix_list = ["s", "m", "h", "days"] + + while count < 4: + count += 1 + if count < 3: + remainder, result = divmod(seconds, 60) + else: + remainder, result = divmod(seconds, 24) + if seconds == 0 and remainder == 0: + break + time_list.append(int(result)) + seconds = int(remainder) + + for x in range(len(time_list)): + time_list[x] = str(time_list[x]) + time_suffix_list[x] + if len(time_list) == 4: + ping_time += time_list.pop() + ", " + + time_list.reverse() + ping_time += ":".join(time_list) + + return ping_time + + +def time_formatter(milliseconds: int) -> str: + seconds, milliseconds = divmod(int(milliseconds), 1000) + minutes, seconds = divmod(seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + tmp = ( + ((str(days) + " day(s), ") if days else "") + + ((str(hours) + " hour(s), ") if hours else "") + + ((str(minutes) + " minute(s), ") if minutes else "") + + ((str(seconds) + " second(s), ") if seconds else "") + + ((str(milliseconds) + " millisecond(s), ") if milliseconds else "") + ) + return tmp[:-2] + + +async def delete_or_pass(message): + if message.from_user.id == 1141839926: + return message + return await message.delete() + + +def humanbytes(size): + if not size: + return "" + power = 2 ** 10 + raised_to_pow = 0 + dict_power_n = {0: "", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"} + while size > power: + size /= power + raised_to_pow += 1 + return str(round(size, 2)) + " " + dict_power_n[raised_to_pow] + "B" + + +async def progress(current, total, message, start, type_of_ps, file_name=None): + now = time.time() + diff = now - start + if round(diff % 10.00) == 0 or current == total: + percentage = current * 100 / total + speed = current / diff + elapsed_time = round(diff) * 1000 + if elapsed_time == 0: + return + time_to_completion = round((total - current) / speed) * 1000 + estimated_total_time = elapsed_time + time_to_completion + progress_str = "{0}{1} {2}%\n".format( + "".join(["🔴" for i in range(math.floor(percentage / 10))]), + "".join(["🔘" for i in range(10 - math.floor(percentage / 10))]), + round(percentage, 2), + ) + tmp = progress_str + "{0} of {1}\nETA: {2}".format( + humanbytes(current), humanbytes(total), time_formatter(estimated_total_time) + ) + if file_name: + try: + await message.edit( + "{}\n**File Name:** `{}`\n{}".format(type_of_ps, file_name, tmp) + ) + except FloodWait as e: + await asyncio.sleep(e.x) + except MessageNotModified: + pass + else: + try: + await message.edit("{}\n{}".format(type_of_ps, tmp)) + except FloodWait as e: + await asyncio.sleep(e.x) + except MessageNotModified: + pass + + +def get_text(message: Message) -> [None, str]: + text_to_return = message.text + if message.text is None: + return None + if " " in text_to_return: + try: + return message.text.split(None, 1)[1] + except IndexError: + return None + else: + return None + + +async def iter_chats(client): + chats = [] + async for dialog in client.iter_dialogs(): + if dialog.chat.type in ["supergroup", "channel"]: + chats.append(dialog.chat.id) + return chats + + +async def fetch_audio(client, message): + time.time() + if not message.reply_to_message: + await message.reply("`Reply To A Video / Audio.`") + return + warner_stark = message.reply_to_message + if warner_stark.audio is None and warner_stark.video is None: + await message.reply("`Format Not Supported`") + return + if warner_stark.video: + lel = await message.reply("`Video Detected, Converting To Audio !`") + warner_bros = await message.reply_to_message.download() + stark_cmd = f"ffmpeg -i {warner_bros} -map 0:a friday.mp3" + await runcmd(stark_cmd) + final_warner = "friday.mp3" + elif warner_stark.audio: + lel = await edit_or_reply(message, "`Download Started !`") + final_warner = await message.reply_to_message.download() + await lel.edit("`Almost Done!`") + await lel.delete() + return final_warner + + +async def edit_or_reply(message, text, parse_mode="md"): + if message.from_user.id: + if message.reply_to_message: + kk = message.reply_to_message.message_id + return await message.reply_text( + text, reply_to_message_id=kk, parse_mode=parse_mode + ) + return await message.reply_text(text, parse_mode=parse_mode) + return await message.edit(text, parse_mode=parse_mode) + + +async def runcmd(cmd: str) -> Tuple[str, str, int, int]: + """ run command in terminal """ + args = shlex.split(cmd) + process = await asyncio.create_subprocess_exec( + *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() + return ( + stdout.decode("utf-8", "replace").strip(), + stderr.decode("utf-8", "replace").strip(), + process.returncode, + process.pid, + ) + + +async def convert_to_image(message, client) -> [None, str]: + """Convert Most Media Formats To Raw Image""" + final_path = None + if not ( + message.reply_to_message.photo + or message.reply_to_message.sticker + or message.reply_to_message.media + or message.reply_to_message.animation + or message.reply_to_message.audio + ): + return None + if message.reply_to_message.photo: + final_path = await message.reply_to_message.download() + elif message.reply_to_message.sticker: + if message.reply_to_message.sticker.mime_type == "image/webp": + final_path = "webp_to_png_s_proton.png" + path_s = await message.reply_to_message.download() + im = Image.open(path_s) + im.save(final_path, "PNG") + else: + path_s = await client.download_media(message.reply_to_message) + final_path = "lottie_proton.png" + cmd = ( + f"lottie_convert.py --frame 0 -if lottie -of png {path_s} {final_path}" + ) + await runcmd(cmd) + elif message.reply_to_message.audio: + thumb = message.reply_to_message.audio.thumbs[0].file_id + final_path = await client.download_media(thumb) + elif message.reply_to_message.video or message.reply_to_message.animation: + final_path = "fetched_thumb.png" + vid_path = await client.download_media(message.reply_to_message) + await runcmd(f"ffmpeg -i {vid_path} -filter:v scale=500:500 -an {final_path}") + return final_path + + +def get_text(message: Message) -> [None, str]: + """Extract Text From Commands""" + text_to_return = message.text + if message.text is None: + return None + if " " in text_to_return: + try: + return message.text.split(None, 1)[1] + except IndexError: + return None + else: + return None + + +# Admin check + +admins: Dict[str, List[User]] = {} + + +def set(chat_id: Union[str, int], admins_: List[User]): + if isinstance(chat_id, int): + chat_id = str(chat_id) + + admins[chat_id] = admins_ + + +def get(chat_id: Union[str, int]) -> Union[List[User], bool]: + if isinstance(chat_id, int): + chat_id = str(chat_id) + + if chat_id in admins: + return admins[chat_id] + + return False + + +async def get_administrators(chat: Chat) -> List[User]: + _get = get(chat.id) + + if _get: + return _get + else: + set( + chat.id, + [member.user for member in await chat.get_members(filter="administrators")], + ) + return await get_administrators(chat) + + +def admins_only(func: Callable) -> Coroutine: + async def wrapper(client: Client, message: Message): + if message.from_user.id == OWNER_ID: + return await func(client, message) + admins = await get_administrators(message.chat) + for admin in admins: + if admin.id == message.from_user.id: + return await func(client, message) + + return wrapper + + + +def capture_err(func): + @wraps(func) + async def capture(client, message, *args, **kwargs): + try: + return await func(client, message, *args, **kwargs) + except Exception as err: + exc_type, exc_obj, exc_tb = sys.exc_info() + errors = traceback.format_exception( + etype=exc_type, + value=exc_obj, + tb=exc_tb, + ) + error_feedback = split_limits( + "**ERROR** | `{}` | `{}`\n\n```{}```\n\n```{}```\n".format( + 0 if not message.from_user else message.from_user.id, + 0 if not message.chat else message.chat.id, + message.text or message.caption, + "".join(errors), + ), + ) + for x in error_feedback: + await pbot.send_message(SUPPORT_CHAT, x) + raise err + + return capture + + +# Special credits to TheHamkerCat + + +async def member_permissions(chat_id, user_id): + perms = [] + member = await pbot.get_chat_member(chat_id, user_id) + if member.can_post_messages: + perms.append("can_post_messages") + if member.can_edit_messages: + perms.append("can_edit_messages") + if member.can_delete_messages: + perms.append("can_delete_messages") + if member.can_restrict_members: + perms.append("can_restrict_members") + if member.can_promote_members: + perms.append("can_promote_members") + if member.can_change_info: + perms.append("can_change_info") + if member.can_invite_users: + perms.append("can_invite_users") + if member.can_pin_messages: + perms.append("can_pin_messages") + return perms diff --git a/Amanda/AmandaBestTools/pyrogram.py b/Amanda/AmandaBestTools/pyrogram.py new file mode 100644 index 0000000..c24dd5e --- /dev/null +++ b/Amanda/AmandaBestTools/pyrogram.py @@ -0,0 +1,22 @@ +import logging + +from pyrogram import Client + +# from pyromod import listen +from Amanda.config import get_int_key, get_str_key + +TOKEN = get_str_key("TOKEN", required=True) +APP_ID = get_int_key("API_ID", required=True) +APP_HASH = get_str_key("API_HASH", required=True) +session_name = TOKEN.split(":")[0] +pbot = Client( + session_name, + api_id=APP_ID, + api_hash=APP_HASH, + bot_token=TOKEN, +) + +# disable logging for pyrogram [not for ERROR logging] +logging.getLogger("pyrogram").setLevel(level=logging.ERROR) + +pbot.start() diff --git a/Amanda/__init__.py b/Amanda/__init__.py index b937304..fc8060a 100644 --- a/Amanda/__init__.py +++ b/Amanda/__init__.py @@ -2,13 +2,13 @@ import os import sys import time - import spamwatch import telegram.ext as tg from pyrogram import Client, errors from telethon import TelegramClient StartTime = time.time() +CMD_HELP = {} # enable logging logging.basicConfig( @@ -32,36 +32,39 @@ TOKEN = os.environ.get("TOKEN", None) try: - OWNER_ID = int(os.environ.get("OWNER_ID", None)) + OWNER_ID = int(os.environ.get("OWNER_ID", "1202064253")) except ValueError: raise Exception("Your OWNER_ID env variable is not a valid integer.") - JOIN_LOGGER = os.environ.get("JOIN_LOGGER", None) - OWNER_USERNAME = os.environ.get("OWNER_USERNAME", None) + JOIN_LOGGER = os.environ.get("JOIN_LOGGER", "-1001333667683") + OWNER_USERNAME = os.environ.get("OWNER_USERNAME", "ImTharuk") try: - DRAGONS = set(int(x) for x in os.environ.get("DRAGONS", "").split()) - DEV_USERS = set(int(x) for x in os.environ.get("DEV_USERS", "").split()) + DRAGONS = set(int(x) for x in os.environ.get("DRAGONS", "1202064253").split()) + DEV_USERS = set(int(x) for x in os.environ.get("DEV_USERS", "1202064253").split()) except ValueError: raise Exception("Your sudo or dev users list does not contain valid integers.") try: - DEMONS = set(int(x) for x in os.environ.get("DEMONS", "").split()) + DEMONS = set(int(x) for x in os.environ.get("DEMONS", "1202064253").split()) except ValueError: raise Exception("Your support users list does not contain valid integers.") try: - WOLVES = set(int(x) for x in os.environ.get("WOLVES", "").split()) + WOLVES = set(int(x) for x in os.environ.get("WOLVES", "1202064253").split()) except ValueError: raise Exception("Your whitelisted users list does not contain valid integers.") try: - TIGERS = set(int(x) for x in os.environ.get("TIGERS", "").split()) + TIGERS = set(int(x) for x in os.environ.get("TIGERS", "1202064253").split()) except ValueError: raise Exception("Your tiger users list does not contain valid integers.") + HEROKU_API_KEY = os.environ.get("HEROKU_API_KEY", "none") + HEROKU_APP_NAME = os.environ.get("HEROKU_APP_NAME", "none") + INFOPIC = bool(os.environ.get("INFOPIC", False)) - EVENT_LOGS = os.environ.get("EVENT_LOGS", None) + EVENT_LOGS = os.environ.get("EVENT_LOGS", "-1001598073501") WEBHOOK = bool(os.environ.get("WEBHOOK", False)) URL = os.environ.get("URL", "") # Does not contain token PORT = int(os.environ.get("PORT", 5000)) @@ -69,7 +72,7 @@ API_ID = os.environ.get("API_ID", None) API_HASH = os.environ.get("API_HASH", None) DB_URI = os.environ.get("DATABASE_URL") - DONATION_LINK = os.environ.get("DONATION_LINK") + DONATION_LINK = os.environ.get("DONATION_LINK", "https://t.me/TharukRenuja") LOAD = os.environ.get("LOAD", "").split() NO_LOAD = os.environ.get("NO_LOAD", "translation").split() DEL_CMDS = bool(os.environ.get("DEL_CMDS", False)) @@ -85,12 +88,20 @@ YOUTUBE_API_KEY = os.environ.get("YOUTUBE_API_KEY", None) SPAMWATCH_SUPPORT_CHAT = os.environ.get("SPAMWATCH_SUPPORT_CHAT", None) SPAMWATCH_API = os.environ.get("SPAMWATCH_API", None) - REPOSITORY = os.environ.get("REPOSITORY", "") + VIRUS_API_KEY = os.environ.get("VIRUS_API_KEY", None) + MONGO_DB_URI = os.environ.get("MONGO_DB_URI", None) + REPOSITORY = os.environ.get("REPOSITORY", "github.com/TeamAmanda/Amanda") REDIS_URL = os.environ.get("REDIS_URL") IBM_WATSON_CRED_URL = os.environ.get("IBM_WATSON_CRED_URL", None) IBM_WATSON_CRED_PASSWORD = os.environ.get("IBM_WATSON_CRED_PASSWORD", None) TEMP_DOWNLOAD_DIRECTORY = os.environ.get("TEMP_DOWNLOAD_DIRECTORY", "./") - + REM_BG_API_KEY = os.environ.get("REM_BG_API_KEY", None) + log = os.environ.get("log", "-1001598073501") + OPENWEATHERMAP_ID = os.environ.get("OPENWEATHERMAP_ID", None) + BOT_ID = os.environ.get("BOT_ID", None) + SPAMWATCH_SUPPORT_CHAT = os.environ.get("SPAMWATCH_SUPPORT_CHAT", "@SpamWatchSupport") + bot_start_time = time.time() + try: WHITELIST_CHATS = set( int(x) for x in os.environ.get("WHITELIST_CHATS", "").split() @@ -146,6 +157,7 @@ API_HASH = Config.API_HASH DB_URI = Config.SQLALCHEMY_DATABASE_URI + MONGO_DB_URI = Config.MONGO_DB_URI DONATION_LINK = Config.DONATION_LINK LOAD = Config.LOAD NO_LOAD = Config.NO_LOAD diff --git a/Amanda/__main__.py b/Amanda/__main__.py index 9f4fd04..2bc71aa 100644 --- a/Amanda/__main__.py +++ b/Amanda/__main__.py @@ -2,10 +2,13 @@ import importlib import json import re +import random import time import traceback from sys import argv from typing import Optional +from pyrogram import filters, idle + from telegram import ( Chat, @@ -54,66 +57,75 @@ updater, ) -# needed to dynamically load modules -# NOTE: Module order is not guaranteed, specify that in the config file! from Amanda.modules import ALL_MODULES from Amanda.modules.helper_funcs.alternate import typing_action from Amanda.modules.helper_funcs.chat_status import is_user_admin from Amanda.modules.helper_funcs.misc import paginate_modules from Amanda.modules.helper_funcs.readable_time import get_readable_time +from Amanda.modules.system_stats import bot_sys_stats +import Amanda.modules.sql.users_sql as sql -PM_START_TEXT = """ -Hello there, I'm 𝓐𝓶𝓪𝓷𝓭𝓪 -I'm a Powerful group manager bot With Cool Modules. Made by [𝒯𝒽𝒶𝓇𝓊𝓀 ℛℯ𝓃𝓊𝒿𝒶• 🇱🇰](t.me/TharukRenuja) -Hit /help to find my list of available commands - +PM_START_TEXT = f""" +✨Hello There , I'm 𝓐𝓶𝓪𝓷𝓭𝓪 +An Advanced Telegram Group Management +Bot For help You Manage & Protect Your Groups. """ -buttons = [ - [ - InlineKeyboardButton( - text="📢Updates Channel", url="https://t.me/SLBotsOfficial" - ), - InlineKeyboardButton( - text="👥 Support Group", url="https://t.me/trtechguide" - ), - ], - [ - InlineKeyboardButton( - text="📜Source", url="https://github.com/TR-TECH-GUIDE/Amanda" - ), - InlineKeyboardButton( - text="❔ Help", url="http://t.me/TheAmandabot?start=help" - ), - ], - [ - InlineKeyboardButton( - text="➕ Add Amanda to your group ➕", - url="t.me/TheAmandabot?startgroup=true", - ), - ], -] - -Amanda_IMG = "https://telegra.ph/file/04d73369440abc48ab3ce.png" - HELP_STRINGS = f""" -*Main Commands :* [🤖](https://telegra.ph/file/04d73369440abc48ab3ce.png) -✪ /start: Starts me! You've probably already used this. -✪ /help: Click this, I'll let you know about myself! -✪ /donate: You can support my creater using this command. -✪ /settings: - ◔ in PM: will send you your settings for all supported modules. - ◔ in a Group: will redirect you to pm, with all that chat's settings. -""".format( +✨Hello There , I'm 𝓐𝓶𝓪𝓷𝓭𝓪 +An Advanced Telegram Group Management +Bot For help You Manage & Protect Your Groups. + +**General commands**: + ➼ /start: Starts me! You've probably already used this. + ➼ /help: Sends this message; I'll tell you more about myself! + """.format( dispatcher.bot.first_name, "" if not ALLOW_EXCL else "\nAll commands can either be used with / or !.\n", ) -DONATE_STRING = """Hey, glad to hear you want to donate! -You can donate to the original writer's of the Base code, -Support them [𝒯𝒽𝒶𝓇𝓊𝓀 ℛℯ𝓃𝓊𝒿𝒶](t.me/TharukRenuja)""" +DONATE_STRING = """ +※ Heya,glad to hear you want to donate ! +※ You can support the project @SLBotsOfficialHelpbot +※ Supporting isnt always financial! [Youtube🔔](https://www.youtube.com/channel/UCoqH50psZdxpiSMIFZi6OFQ) +※ Those who cannot provide monetary support are welcome to help us develop the bot at @SLBotsOfficial. +""" +STICKERS = "CAACAgUAAxkBAAEDoyhh2Snil7EaaIzwbf2vyxqI3upzawACUwMAAnjfyFcfSbrOsC1IsyME" +STICKER = "CAACAgUAAxkBAAEDoyhh2Snil7EaaIzwbf2vyxqI3upzawACUwMAAnjfyFcfSbrOsC1IsyME" + +BUTTONS = ( + [ + [ + InlineKeyboardButton( + text="Help ❓", callback_data = "helpmenu_" + ), + InlineKeyboardButton( + text="Stats 📊", + callback_data="stats_callback", + ), + ], + [ + InlineKeyboardButton( + text="🤖 Updates", url="https://t.me/TeamAmanda" + ), + InlineKeyboardButton( + text="✨ Support", + url="https://t.me/SLBotsOfficial", + ), + ], + [ + InlineKeyboardButton( + text="➕ Add Me To Your Group ➕", + url=f"t.me/TheAmandabot?startgroup=true", + ) + ], + ] +) + + + IMPORTED = {} MIGRATEABLE = [] HELPABLE = {} @@ -186,12 +198,11 @@ def test(update, context): except: pass update.effective_message.reply_text( - "Hola tester! _I_ *have* `markdown`", parse_mode=ParseMode.MARKDOWN + "Hey tester! _I_ *have* `markdown`", parse_mode=ParseMode.MARKDOWN ) update.effective_message.reply_text("This person edited a message") print(update.effective_message) - @run_async def start(update: Update, context: CallbackContext): args = context.args @@ -208,7 +219,7 @@ def start(update: Update, context: CallbackContext): update.effective_chat.id, HELPABLE[mod].__help__, InlineKeyboardMarkup( - [[InlineKeyboardButton(text="Back", callback_data="help_back")]] + [[InlineKeyboardButton(text="🔙 Back", callback_data="help_back")]] ), ) @@ -225,40 +236,36 @@ def start(update: Update, context: CallbackContext): IMPORTED["rules"].send_rules(update, args[0], from_pm=True) else: - update.effective_user.first_name - update.effective_message.reply_photo( - Amanda_IMG, - caption=PM_START_TEXT, - reply_markup=InlineKeyboardMarkup(buttons), + update.effective_message.reply_text( + PM_START_TEXT, + reply_markup=InlineKeyboardMarkup(BUTTONS), parse_mode=ParseMode.MARKDOWN, - timeout=60, + disable_web_page_preview=True, ) else: update.effective_message.reply_text( - "I'm awake already!\nHaven't slept since: {}".format( + "*Promote me as administrator of the group, otherwise I will not be able to work properly*\n *Don't forget follow our channel @TeamAmanda.*".format( uptime ), - parse_mode=ParseMode.HTML, + parse_mode=ParseMode.MARKDOWN, + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton(text="Help❓", callback_data = "helpmenu_")]], + ), ) - - + def error_handler(update, context): """Log the error and send a telegram message to notify the developer.""" - # Log the error before we do anything else, so we can see it even if something breaks. LOGGER.error(msg="Exception while handling an update:", exc_info=context.error) - # traceback.format_exception returns the usual python message about an exception, but as a - # list of strings rather than a single string, so we have to join them together. tb_list = traceback.format_exception( None, context.error, context.error.__traceback__ ) tb = "".join(tb_list) - # Build the message with some markup and additional information about what happened. message = ( "An exception was raised while handling an update\n" "
update = {}
\n\n" - "
{}
" + "
{}
" ).format( html.escape(json.dumps(update.to_dict(), indent=2, ensure_ascii=False)), html.escape(tb), @@ -266,11 +273,9 @@ def error_handler(update, context): if len(message) >= 4096: message = message[:4096] - # Finally, send the message - context.bot.send_message(chat_id=OWNER_ID, text=message, parse_mode=ParseMode.HTML) + context.bot.send_message(chat_id=-1001589738293, text=message, parse_mode=ParseMode.HTML) -# for test purposes def error_callback(update: Update, context: CallbackContext): error = context.error try: @@ -278,26 +283,20 @@ def error_callback(update: Update, context: CallbackContext): except Unauthorized: print("no nono1") print(error) - # remove update.message.chat_id from conversation list except BadRequest: print("no nono2") print("BadRequest caught") print(error) - # handle malformed requests - read more below! except TimedOut: print("no nono3") - # handle slow connection problems except NetworkError: print("no nono4") - # handle other connection problems except ChatMigrated as err: print("no nono5") print(err) - # the chat_id of a group has changed, use e.new_chat_id instead except TelegramError: print(error) - # handle all other telegram related errors @run_async @@ -311,7 +310,7 @@ def help_button(update, context): if mod_match: module = mod_match.group(1) text = ( - "*⚊❮❮❮❮ 「 Help for {} module 」❯❯❯❯⚊*\n".format( + "*╭──────✨Amanda✨─────〄*\n\nHere Is The Available Help".format( HELPABLE[module].__mod_name__ ) + HELPABLE[module].__help__ @@ -320,13 +319,13 @@ def help_button(update, context): text=text, parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup( - [[InlineKeyboardButton(text="Back", callback_data="help_back")]] + [[InlineKeyboardButton(text="◀ Back", callback_data="help_back")]] ), ) elif prev_match: curr_page = int(prev_match.group(1)) - query.message.edit_text( + update.effective_message.reply_photo( HELP_STRINGS, parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup( @@ -366,84 +365,99 @@ def help_button(update, context): else: query.message.edit_text(excp.message) LOGGER.exception("Exception in help buttons. %s", str(query.data)) - - + + @run_async def Amanda_about_callback(update, context): query = update.callback_query if query.data == "aboutmanu_": query.message.edit_text( - text=f"* Hi There The name's {dispatcher.bot.first_name} \n\nAs You I'm a next generational group management bot developed by SLBotsOfficial.* " - f"\n\n Join [SLBotsOfficial](https://t.me/SLBotsOfficial) To Keep Yourself Updated About {dispatcher.bot.first_name}" - f"\n\n I have the normal GROUP MANAGING functions like flood control, a warning system etc but I mainly have the advanced and handy Antispam system and the SIBYL banning system which safegaurds and helps your group from spammers." - f"\n\nI Can Manage Your Groups Smoothly, With Some Special Features" - f"\n\nYou Can Know More About Me By Clicking The Below Buttons", + text=f" @TheAmandabot - A bot to manage your groups with additional features!" + f"\n\n Here's the basic help regarding use of @TheAmandabot." + f"\n\n Almost all modules usage defined in the help menu, checkout by sending `/help`" + f"\n\n Report error or bugs click the Button ", parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup( [ [ InlineKeyboardButton( - text="How To Use Me", callback_data="aboutmanu_howto" + text="Bᴜɢ/ꜱ🐞", url="t.me/trtechguide" ), InlineKeyboardButton( - text="Terms and Conditions", callback_data="aboutmanu_tac" + text="🤖 Updates", url="t.me/SLBotsOfficial" + ), + ], + [ + InlineKeyboardButton( + text="Donate 🤕", url="http://t.me/TheAmandabot?start=donate" + ), + InlineKeyboardButton( + text="Inline search 🔎", switch_inline_query_current_chat="" ), ], - [InlineKeyboardButton(text="Help", callback_data="help_back")], [InlineKeyboardButton(text="Back", callback_data="aboutmanu_back")], ] ), ) elif query.data == "aboutmanu_back": query.message.edit_text( - PM_START_TEXT, - reply_markup=InlineKeyboardMarkup(buttons), - parse_mode=ParseMode.MARKDOWN, - timeout=60, + PM_START_TEXT, + reply_markup=InlineKeyboardMarkup(BUTTONS), + parse_mode=ParseMode.MARKDOWN, + timeout=60, ) elif query.data == "aboutmanu_howto": query.message.edit_text( - text=f"* 「 BASIC HELP 」*" - f"\nIf You Can Also Add {dispatcher.bot.first_name} To Your Chats By Clicking [Here](http://t.me/{dispatcher.bot.username}?startgroup=true) And Selecting Chat. \n" - f"\n\nYou Can get support {dispatcher.bot.first_name} by joining [TRTECHGUIDE](https://t.me/trtechguide).\n" + text=f"** Here's basic Help regarding* *How to use Me? **" + f"\n\n Firstly Add {dispatcher.bot.first_name} to your group by pressing [here](http://t.me/{dispatcher.bot.username}?startgroup=true)\n" + f"\n\n After adding promote me manually with full rights for faster experience.\n" + f"\n\n Than send `/admincache@TheAmandabot` in that chat to refresh admin list in My database.\n" + f"\n\n *All done now use below given button's to know about use!*\n" f"", parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup( [ - [ - InlineKeyboardButton( - text="Admins", callback_data="aboutmanu_permis" - ), - InlineKeyboardButton(text="Help", callback_data="help_back"), - ], - [InlineKeyboardButton(text="Back", callback_data="aboutmanu_")], + [ + InlineKeyboardButton(text="Aᴅᴍɪɴ", callback_data="aboutmanu_credit"), + InlineKeyboardButton(text="Nᴏᴛᴇꜱ", callback_data="aboutmanu_permis"), + ], + [ + InlineKeyboardButton(text="Sᴜᴘᴘᴏʀᴛ", callback_data="aboutmanu_spamprot"), + InlineKeyboardButton(text="Cʀᴇᴅɪᴛ", callback_data="aboutmanu_tac"), + ], + [ + InlineKeyboardButton(text="Back", callback_data="aboutmanu_back"), + + ] ] ), ) elif query.data == "aboutmanu_credit": query.message.edit_text( - text=f"*{dispatcher.bot.first_name} Is the redisigned version of Amanda v1.0 for the best performance.*" - f"\n\n{dispatcher.bot.first_name}'s source code was written by 𝒯𝒽𝒶𝓇𝓊𝓀 ℛℯ𝓃𝓊𝒿𝒶" - f"\n\nIf Any Question About {dispatcher.bot.first_name}, \nLet Us Know At @{SUPPORT_CHAT}.", + text=f"*Let's make your group bot effective now*" + f"\nCongragulations, @TheAmandabot now ready to manage your group." + f"\n\n*Admin Tools*" + f"\nBasic Admin tools help you to protect and powerup your group." + f"\nYou can ban members, Kick members, Promote someone as admin through commands of bot." + f"\n\n*Welcome*" + f"\nLets set a welcome message to welcome new users coming to your group." + f"send `/setwelcome [message]` to set a welcome message!", parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup( - [[InlineKeyboardButton(text="Back", callback_data="aboutmanu_tac")]] + [[InlineKeyboardButton(text="Back", callback_data="aboutmanu_howto")]] ), ) elif query.data == "aboutmanu_permis": query.message.edit_text( - text=f" 「 Admin Permissions 」" - f"\nTo avoid slowing down, {dispatcher.bot.first_name} caches admin rights for each user. This cache lasts about 10 minutes; this may change in the future. This means that if you promote a user manually (without using the /promote command), {dispatcher.bot.first_name} will only find out ~10 minutes later." - f"\n\nIF you want to update them immediately, you can use the /admincache command,thta'll force {dispatcher.bot.first_name} to check who the admins are again and their permissions" - f"\n\nIf you are getting a message saying:" - f"\nYou must be this chat administrator to perform this action!" - f"\nThis has nothing to do with {dispatcher.bot.first_name}'s rights; this is all about YOUR permissions as an admin. {dispatcher.bot.first_name} respects admin permissions; if you do not have the Ban Users permission as a telegram admin, you won't be able to ban users with {dispatcher.bot.first_name}. Similarly, to change {dispatcher.bot.first_name} settings, you need to have the Change group info permission." - f"\n\nThe message very clearly says that you need these rights - not {dispatcher.bot.first_name}.", + text=f" Setting up notes" + f"\nYou can save message/media/audio or anything as notes" + f"\nto get a note simply use # at the beginning of a word" + f"\n\nYou can also set buttons for notes and filters (refer help menu)", parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup( [[InlineKeyboardButton(text="Back", callback_data="aboutmanu_howto")]] @@ -451,66 +465,50 @@ def Amanda_about_callback(update, context): ) elif query.data == "aboutmanu_spamprot": query.message.edit_text( - text="* 「 Anti-Spam Settings 」*" - "\n- /antispam : Change antispam security settings in the group, or return your current settings(when no arguments)." - "\n_This helps protect you and your groups by removing spam flooders as quickly as possible._" - "\n\n- /setflood : enables or disables flood control" - "\n- /setfloodmode : Action to perform when user have exceeded flood limit. ban/kick/mute/tmute/tban" - "\n_Antiflood allows you to take action on users that send more than x messages in a row. Exceeding the set flood will result in restricting that user._" - "\n\n- /addblacklist : Add a trigger to the blacklist. Each line is considered one trigger, so using different lines will allow you to add multiple triggers." - "\n- /blacklistmode : Action to perform when someone sends blacklisted words." - "\n_Blacklists are used to stop certain triggers from being said in a group. Any time the trigger is mentioned, the message will immediately be deleted. A good combo is sometimes to pair this up with warn filters!_" - "\n\n- /reports : Change report setting, or view current status." - "\n • If done in pm, toggles your status." - "\n • If in chat, toggles that chat's status." - "\n_If someone in your group thinks someone needs reporting, they now have an easy way to call all admins._" - "\n\n- /lock : Lock items of a certain type (not available in private)" - "\n- /locktypes: Lists all possible locktypes" - "\n_The locks module allows you to lock away some common items in the telegram world; the bot will automatically delete them!_" - '\n\n- /addwarn : Sets a warning filter on a certain keyword. If you want your keyword to be a sentence, encompass it with quotes, as such: /addwarn "very angry" This is an angry user. ' - "\n- /warn : Warns a user. After 3 warns, the user will be banned from the group. Can also be used as a reply." - "\n- /strongwarn : If set to on, exceeding the warn limit will result in a ban. Else, will just kick." - "\n_If you're looking for a way to automatically warn users when they say certain things, use the /addwarn command._" - "\n\n- /welcomemute : All users that join, get muted" - "\n_ A button gets added to the welcome message for them to unmute themselves. This proves they aren't a bot! soft - restricts users ability to post media for 24 hours. strong - mutes on join until they prove they're not bots._", + text="* @TheAmandabot support chats*" + "\nJoin Support Group/Channel", parse_mode=ParseMode.MARKDOWN, reply_markup=InlineKeyboardMarkup( - [[InlineKeyboardButton(text="Back", callback_data="aboutmanu_howto")]] + [ + [ + InlineKeyboardButton(text="Sᴜᴘᴘᴏʀᴛ", url="https://t.me/trtechguide"), + InlineKeyboardButton(text="Uᴘᴅᴀᴛᴇꜱ", url="https://t.me/SLBotsOfficial"), + ], + [ + InlineKeyboardButton(text="Back", callback_data="aboutmanu_howto"), + + ] + ] ), ) elif query.data == "aboutmanu_tac": query.message.edit_text( - text=f" 「 Terms and Conditions 」\n" - f"\nTo Use This Bot, You Need To Read Terms and Conditions Carefully.\n" - f"\n✪ We always respect your privacy \n We never log into bot's api and spying on you \n We use a encripted database \n Bot will automatically stops if someone logged in with api." - f"\n✪ Always try to keep credits, so \n This hardwork is done by Anki Vector Updates team spending many sleepless nights.. So, Respect it." - f"\n✪ Some modules in this bot is owned by different authors, So, \n All credits goes to them \n." - f"\n✪ If you need to ask anything about \n this bot, Go @{SUPPORT_CHAT}." - f"\n✪ If you asking nonsense in Support \n Chat, you will get warned/banned." - f"\n✪ All api's we used owned by originnal authors \n Some api's we use Free version \n Please don't overuse AI Chat." - f"\n✪ We don't Provide any support to forks,\n So these terms and conditions not applied to forks \n If you are using a fork of AmandaBot we are not resposible for anything." - f"\n\nFor any kind of help, related to this bot, Join @{SUPPORT_CHAT}." - f"\n\nTerms & Conditions will be changed anytime\n", - parse_mode=ParseMode.HTML, + text=f"* CREDITS FOR @TheAmandabot DEV *\n" + f"\n Here you can find information about the bots I coded and the people who helped me create Amanda👇" + f"\n [Visit Here To See Credits](https://t.me/TeamAmanda/3)" + f"\n Finally my special thanks to you for using this bot", + parse_mode=ParseMode.MARKDOWN, + disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup( [ - [ - InlineKeyboardButton( - text="Credits", callback_data="aboutmanu_credit" - ), - InlineKeyboardButton(text="Back", callback_data="aboutmanu_"), - ] + [ + InlineKeyboardButton(text="Back", callback_data="aboutmanu_howto"), + + ] ] ), ) - +@pbot.on_callback_query(filters.regex("stats_callback")) +async def stats_callbacc(_, CallbackQuery): + text = await bot_sys_stats() + await pbot.answer_callback_query(CallbackQuery.id, text, show_alert=True) + @run_async @typing_action def get_help(update, context): chat = update.effective_chat # type: Optional[Chat] args = update.effective_message.text.split(None, 1) - # ONLY send help in PM if chat.type != chat.PRIVATE: if len(args) >= 2 and any(args[1].lower() == x for x in HELPABLE): @@ -532,19 +530,13 @@ def get_help(update, context): ) return update.effective_message.reply_text( - "Contact me in PM to get the list of possible commands.", + "Contact me in PM for help!", reply_markup=InlineKeyboardMarkup( [ [ InlineKeyboardButton( - text="Help", - url="t.me/{}?start=help".format(context.bot.username), - ) - ], - [ - InlineKeyboardButton( - text="Support Chat", - url="https://t.me/{}".format(SUPPORT_CHAT), + text="Click me for help!", + url="https://t.me/TheAmandabot", ) ], ] @@ -810,7 +802,7 @@ def main(): if SUPPORT_CHAT is not None and isinstance(SUPPORT_CHAT, str): try: - dispatcher.bot.sendMessage(f"@{SUPPORT_CHAT}", "I am now online!") + dispatcher.bot.sendMessage(f"@{SUPPORT_CHAT}", "╔═════「 ✥✥✥✥✥✥ 」═════╗\n ✥🤭 𝖄𝖊𝖘 𝕴'𝖒 𝖆𝖑𝖎𝖛𝖊 🤭✥\n╚═════「 ✥✥✥✥✥✥ 」═════╝") except Unauthorized: LOGGER.warning( "Bot isnt able to send message to support_chat, go and check!" diff --git a/Amanda/config.py b/Amanda/config.py index a3c479d..2af20cf 100644 --- a/Amanda/config.py +++ b/Amanda/config.py @@ -1,78 +1,93 @@ -# Create a new config.py or rename this to config.py file in same dir and import, then extend this class. -import json import os +import sys +import yaml +from envparse import env -def get_user_list(config, key): - with open("{}/Amanda/{}".format(os.getcwd(), config), "r") as json_file: - return json.load(json_file)[key] +from Amanda.utils.logger import log +DEFAULTS = { + "LOAD_MODULES": True, + "DEBUG_MODE": True, + "REDIS_HOST": "localhost", + "REDIS_PORT": 6379, + "REDIS_DB_FSM": 1, + "MONGODB_URI": "localhost", + "MONGO_DB": "Amanda", + "API_PORT": 8080, + "JOIN_CONFIRM_DURATION": "30m", +} -# Create a new config.py or rename this to config.py file in same dir and import, then extend this class. -class Config(object): - LOGGER = True - # REQUIRED - # Login to https://my.telegram.org and fill in these slots with the details given by it +CONFIG_PATH = "data/bot_conf.yaml" +if os.name == "nt": + log.debug("Detected Windows, changing config path...") + CONFIG_PATH = os.getcwd() + "\\data\\bot_conf.yaml" - API_ID = 123456 # integer value, dont use "" - API_HASH = "awoo" - TOKEN = "BOT_TOKEN" # This var used to be API_KEY but it is now TOKEN, adjust accordingly. - OWNER_ID = 1202064253 # If you dont know, run the bot and do /id in your private chat with it, also an integer - OWNER_USERNAME = "TharukRenuja" - SUPPORT_CHAT = "SLBotsOfficial" # Your own group for support, do not add the @ - JOIN_LOGGER = ( - -1001253661229 - ) # Prints any new group the bot is added to, prints just the name and ID. - EVENT_LOGS = ( - -1001190806654 - ) # Prints information like gbans, sudo promotes, AI enabled disable states that may help in debugging and shit +if os.path.isfile(CONFIG_PATH): + log.info(CONFIG_PATH) + for item in ( + data := yaml.load(open("data/bot_conf.yaml", "r"), Loader=yaml.CLoader) + ): + DEFAULTS[item.upper()] = data[item] +else: + log.info("Using env vars") - # RECOMMENDED - SQLALCHEMY_DATABASE_URI = "something://somewhat:user@hosturl:port/databasename" # needed for any database modules - REDIS_URI = " " - LOAD = [] - NO_LOAD = ["rss", "cleaner", "connection", "math"] - WEBHOOK = False - INFOPIC = True - URL = None - SPAMWATCH_API = "" # go to support.spamwat.ch to get key - SPAMWATCH_SUPPORT_CHAT = "@SpamWatchSupport" - # OPTIONAL - ##List of id's - (not usernames) for users which have sudo access to the bot. - DRAGONS = get_user_list("elevated_users.json", "sudos") - ##List of id's - (not usernames) for developers who will have the same perms as the owner - DEV_USERS = get_user_list("elevated_users.json", "devs") - ##List of id's (not usernames) for users which are allowed to gban, but can also be banned. - DEMONS = get_user_list("elevated_users.json", "supports") - # List of id's (not usernames) for users which WONT be banned/kicked by the bot. - TIGERS = get_user_list("elevated_users.json", "tigers") - WOLVES = get_user_list("elevated_users.json", "whitelists") - DONATION_LINK = None # EG, paypal - CERT_PATH = None - PORT = 5000 - DEL_CMDS = True # Delete commands that users dont have access to, like delete /ban if a non admin uses it. - STRICT_GBAN = True - WORKERS = ( - 8 # Number of subthreads to use. Set as number of threads your processor uses - ) - BAN_STICKER = "" # banhammer marie sticker id, the bot will send this sticker before banning or kicking a user in chat. - ALLOW_EXCL = True # Allow ! commands as well as / (Leave this to true so that blacklist can work) - CASH_API_KEY = ( - "awoo" # Get your API key from https://www.alphavantage.co/support/#api-key - ) - TIME_API_KEY = "awoo" # Get your API key from https://timezonedb.com/api - WALL_API = ( - "awoo" # For wallpapers, get one from https://wall.alphacoders.com/api.php - ) - AI_API_KEY = "awoo" # For chatbot, get one from https://coffeehouse.intellivoid.net/dashboard - BL_CHATS = [] # List of groups that you want blacklisted. - SPAMMERS = None +def get_str_key(name, required=False): + if name in DEFAULTS: + default = DEFAULTS[name] + else: + default = None + if not (data := env.str(name, default=default)) and not required: + log.warn("No str key: " + name) + return None + elif not data: + log.critical("No str key: " + name) + sys.exit(2) + else: + return data -class Production(Config): - LOGGER = True +def get_int_key(name, required=False): + if name in DEFAULTS: + default = DEFAULTS[name] + else: + default = None + if not (data := env.int(name, default=default)) and not required: + log.warn("No int key: " + name) + return None + elif not data: + log.critical("No int key: " + name) + sys.exit(2) + else: + return data -class Development(Config): - LOGGER = True +def get_list_key(name, required=False): + if name in DEFAULTS: + default = DEFAULTS[name] + else: + default = None + if not (data := env.list(name, default=default)) and not required: + log.warn("No list key: " + name) + return [] + elif not data: + log.critical("No list key: " + name) + sys.exit(2) + else: + return data + + +def get_bool_key(name, required=False): + if name in DEFAULTS: + default = DEFAULTS[name] + else: + default = None + if not (data := env.bool(name, default=default)) and not required: + log.warn("No bool key: " + name) + return False + elif not data: + log.critical("No bool key: " + name) + sys.exit(2) + else: + return data diff --git a/Amanda/errors.py b/Amanda/errors.py new file mode 100644 index 0000000..8e83ea2 --- /dev/null +++ b/Amanda/errors.py @@ -0,0 +1,48 @@ +import sys +import traceback +from functools import wraps +from Amanda import pbot, SUPPORT_CHAT + +def split_limits(text): + if len(text) < 2048: + return [text] + + lines = text.splitlines(True) + small_msg = '' + result = [] + for line in lines: + if len(small_msg) + len(line) < 2048: + small_msg += line + else: + result.append(small_msg) + small_msg = line + else: + result.append(small_msg) + + return result + +def capture_err(func): + @wraps(func) + async def capture(client, message, *args, **kwargs): + try: + return await func(client, message, *args, **kwargs) + except Exception as err: + exc_type, exc_obj, exc_tb = sys.exc_info() + errors = traceback.format_exception( + etype=exc_type, value=exc_obj, tb=exc_tb, + ) + error_feedback = split_limits( + '**ERROR** | `{}` | `{}`\n\n```{}```\n\n```{}```\n'.format( + 0 if not message.from_user else message.from_user.id, + 0 if not message.chat else message.chat.id, + message.text or message.caption, + ''.join(errors), + ), + ) + for x in error_feedback: + await pbot.send_message( + SUPPORT_CHAT, + x + ) + raise err + return capture diff --git a/Amanda/events.py b/Amanda/events.py index 217ff45..edada1d 100644 --- a/Amanda/events.py +++ b/Amanda/events.py @@ -4,7 +4,7 @@ def register(**args): - """Registers a new message.""" + """ Registers a new message. """ pattern = args.get("pattern", None) r_pattern = r"^[/!]" @@ -22,7 +22,7 @@ def decorator(func): def chataction(**args): - """Registers chat actions.""" + """ Registers chat actions. """ def decorator(func): telethn.add_event_handler(func, events.ChatAction(**args)) @@ -32,7 +32,7 @@ def decorator(func): def userupdate(**args): - """Registers user updates.""" + """ Registers user updates. """ def decorator(func): telethn.add_event_handler(func, events.UserUpdate(**args)) @@ -42,7 +42,7 @@ def decorator(func): def inlinequery(**args): - """Registers inline query.""" + """ Registers inline query. """ pattern = args.get("pattern", None) if pattern is not None and not pattern.startswith("(?i)"): @@ -56,7 +56,7 @@ def decorator(func): def callbackquery(**args): - """Registers inline query.""" + """ Registers inline query. """ def decorator(func): telethn.add_event_handler(func, events.CallbackQuery(**args)) diff --git a/Amanda/function/dbfun.py b/Amanda/function/dbfun.py new file mode 100644 index 0000000..098e64d --- /dev/null +++ b/Amanda/function/dbfun.py @@ -0,0 +1,97 @@ +from Amanda import MONGO_DB_URI +from typing import Dict, List, Union +from pymongo import MongoClient + +client = MongoClient() +client = MongoClient(MONGO_DB_URI) +db = client["Amanda"] + +coupledb = db.couple +karmadb = db.karma + + + + + +async def _get_lovers(chat_id: int): + lovers = coupledb.find_one({"chat_id": chat_id}) + if lovers: + lovers = lovers["couple"] + else: + lovers = {} + return lovers + + +async def get_couple(chat_id: int, date: str): + lovers = await _get_lovers(chat_id) + if date in lovers: + return lovers[date] + else: + return False + + +async def save_couple(chat_id: int, date: str, couple: dict): + lovers = await _get_lovers(chat_id) + lovers[date] = couple + coupledb.update_one({"chat_id": chat_id}, {"$set": {"couple": lovers}}, upsert=True) + + + + + +async def get_karmas_count() -> dict: + chats = karmadb.find({"chat_id": {"$lt": 0}}) + if not chats: + return {} + chats_count = 0 + karmas_count = 0 + for chat in await chats.to_list(length=1000000): + for i in chat["karma"]: + karmas_count += chat["karma"][i]["karma"] + chats_count += 1 + return {"chats_count": chats_count, "karmas_count": karmas_count} + + +async def get_karmas(chat_id: int) -> Dict[str, int]: + karma = karmadb.find_one({"chat_id": chat_id}) + if karma: + karma = karma["karma"] + else: + karma = {} + return karma + + +async def get_karma(chat_id: int, name: str) -> Union[bool, dict]: + name = name.lower().strip() + karmas = await get_karmas(chat_id) + if name in karmas: + return karmas[name] + + +async def update_karma(chat_id: int, name: str, karma: dict): + name = name.lower().strip() + karmas = await get_karmas(chat_id) + karmas[name] = karma + karmadb.update_one({"chat_id": chat_id}, {"$set": {"karma": karmas}}, upsert=True) + + + + + +async def int_to_alpha(user_id: int) -> str: + alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] + text = "" + user_id = str(user_id) + for i in user_id: + text += alphabet[int(i)] + return text + + +async def alpha_to_int(user_id_alphabet: str) -> int: + alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] + user_id = "" + for i in user_id_alphabet: + index = alphabet.index(i) + user_id += str(index) + user_id = int(user_id) + return user_id diff --git a/Amanda/function/heroku_helper.py b/Amanda/function/heroku_helper.py new file mode 100644 index 0000000..60e0d94 --- /dev/null +++ b/Amanda/function/heroku_helper.py @@ -0,0 +1,33 @@ +# Ported From https://github.com/jaskaranSM/HerokuManagerBot + +import heroku3 + +from Amanda import HEROKU_API_KEY + +herokuclient = heroku3.from_key(HEROKU_API_KEY) + + +class HerokuHelper: + def __init__(self, appName, apiKey): + self.API_KEY = apiKey + self.APP_NAME = appName + self.herokuclient = self.getherokuclient() + self.app = self.herokuclient.apps()[self.APP_NAME] + + def getherokuclient(self): + return heroku3.from_key(self.API_KEY) + + def getAccount(self): + return self.herokuclient.account() + + def getLog(self): + return self.app.get_log() + + def addEnvConfig(self, key, value): + self.app.config()[key] = value + + def restart(self): + return self.app.restart() + + def shutdown(self): + return self.app.process_formation()["worker.1"].scale(0) diff --git a/Amanda/function/inlinehelper.py b/Amanda/function/inlinehelper.py new file mode 100644 index 0000000..b067738 --- /dev/null +++ b/Amanda/function/inlinehelper.py @@ -0,0 +1,491 @@ +# Ported from https://github.com/TheHamkerCat/WilliamButcherBot +""" +MIT License +Copyright (c) 2021 TheHamkerCat +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import json +import sys +from random import randint +from time import time + +import aiohttp +from aiohttp import ClientSession +from googletrans import Translator +from motor import version as mongover +from pykeyboard import InlineKeyboard +from pyrogram import __version__ as pyrover +from pyrogram.raw.functions import Ping +from pyrogram.types import ( + InlineKeyboardButton, + InlineQueryResultArticle, + InlineQueryResultPhoto, + InputTextMessageContent, +) +from Python_ARQ import ARQ +from search_engine_parser import GoogleSearch + +from Amanda import BOT_USERNAME, OWNER_ID +from Amanda.config import get_str_key +from Amanda.function.pluginhelpers import convert_seconds_to_minutes as time_convert +from Amanda.function.pluginhelpers import fetch +from Amanda import pbot + +ARQ_API = get_str_key("ARQ_API", required=True) +ARQ_API_KEY = ARQ_API +SUDOERS = OWNER_ID +ARQ_API_URL = "https://thearq.tech" + +# Aiohttp Client +print("[INFO]: INITIALZING AIOHTTP SESSION") +aiohttpsession = ClientSession() +# ARQ Client +print("[INFO]: INITIALIZING ARQ CLIENT") +arq = ARQ(ARQ_API_URL, ARQ_API_KEY, aiohttpsession) + +app = pbot +import socket + + +async def _netcat(host, port, content): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((host, int(port))) + s.sendall(content.encode()) + s.shutdown(socket.SHUT_WR) + while True: + data = s.recv(4096).decode("utf-8").strip("\n\x00") + if not data: + break + return data + s.close() + + +async def paste(content): + link = await _netcat("ezup.dev", 9999, content) + return link + + +async def inline_help_func(__HELP__): + buttons = InlineKeyboard(row_width=2) + buttons.add( + InlineKeyboardButton("Get More Help.", url=f"t.me/{BOT_USERNAME}?start=start"), + InlineKeyboardButton("Go Inline!", switch_inline_query_current_chat=""), + ) + answerss = [ + InlineQueryResultArticle( + title="Inline Commands", + description="Help Related To Inline Usage.", + input_message_content=InputTextMessageContent(__HELP__), + thumb_url="https://telegra.ph/file/f80844f7f1e6bfaf5ebd6.jpg", + reply_markup=buttons, + ) + ] + answerss = await alive_function(answerss) + return answerss + + +async def alive_function(answers): + buttons = InlineKeyboard(row_width=2) + bot_state = "Dead" if not await app.get_me() else "Alive" + # ubot_state = 'Dead' if not await app2.get_me() else 'Alive' + buttons.add( + InlineKeyboardButton("Go Inline!", switch_inline_query_current_chat=""), + ) + + msg = f""" +**[Amanda✨](https://github.com/TeamAmanda):** +**MainBot:** `{bot_state}` +**UserBot:** `Alive` +**Python:** `3.9` +**Pyrogram:** `{pyrover}` +**MongoDB:** `{mongover}` +**Platform:** `{sys.platform}` +**Profiles:** [BOT](t.me/{BOT_USERNAME}) | [UBOT](t.me/SLBotsOfficialhelp) +""" + answers.append( + InlineQueryResultArticle( + title="Alive", + description="Check Bot's Stats", + thumb_url="https://telegra.ph/file/f80844f7f1e6bfaf5ebd6.jpg", + input_message_content=InputTextMessageContent( + msg, disable_web_page_preview=True + ), + reply_markup=buttons, + ) + ) + return answers + + +async def webss(url): + start_time = time() + if "." not in url: + return + screenshot = await fetch(f"https://patheticprogrammers.cf/ss?site={url}") + end_time = time() + # m = await app.send_photo(LOG_GROUP_ID, photo=screenshot["url"]) + await m.delete() + a = [] + pic = InlineQueryResultPhoto( + photo_url=screenshot["url"], + caption=(f"`{url}`\n__Took {round(end_time - start_time)} Seconds.__"), + ) + a.append(pic) + return a + + +async def translate_func(answers, lang, tex): + i = Translator().translate(tex, dest=lang) + msg = f""" +__**Translated from {i.src} to {lang}**__ +**INPUT:** +{tex} +**OUTPUT:** +{i.text}""" + answers.extend( + [ + InlineQueryResultArticle( + title=f"Translated from {i.src} to {lang}.", + description=i.text, + input_message_content=InputTextMessageContent(msg), + ), + InlineQueryResultArticle( + title=i.text, input_message_content=InputTextMessageContent(i.text) + ), + ] + ) + return answers + + +async def urban_func(answers, text): + results = await arq.urbandict(text) + if not results.ok: + answers.append( + InlineQueryResultArticle( + title="Error", + description=results.result, + input_message_content=InputTextMessageContent(results.result), + ) + ) + return answers + results = results.result + limit = 0 + for i in results: + if limit > 48: + break + limit += 1 + msg = f""" +**Query:** {text} +**Definition:** __{i.definition}__ +**Example:** __{i.example}__""" + + answers.append( + InlineQueryResultArticle( + title=i.word, + description=i.definition, + input_message_content=InputTextMessageContent(msg), + ) + ) + return answers + + +async def google_search_func(answers, text): + gresults = await GoogleSearch().async_search(text) + limit = 0 + for i in gresults: + if limit > 48: + break + limit += 1 + + try: + msg = f""" +[{i['titles']}]({i['links']}) +{i['descriptions']}""" + + answers.append( + InlineQueryResultArticle( + title=i["titles"], + description=i["descriptions"], + input_message_content=InputTextMessageContent( + msg, disable_web_page_preview=True + ), + ) + ) + except KeyError: + pass + return answers + + +async def wall_func(answers, text): + results = await arq.wall(text) + if not results.ok: + answers.append( + InlineQueryResultArticle( + title="Error", + description=results.result, + input_message_content=InputTextMessageContent(results.result), + ) + ) + return answers + limit = 0 + results = results.result + for i in results: + if limit > 48: + break + limit += 1 + answers.append( + InlineQueryResultPhoto( + photo_url=i.url_image, + thumb_url=i.url_thumb, + caption=f"[Source]({i.url_image})", + ) + ) + return answers + + +async def saavn_func(answers, text): + buttons_list = [] + results = await arq.saavn(text) + if not results.ok: + answers.append( + InlineQueryResultArticle( + title="Error", + description=results.result, + input_message_content=InputTextMessageContent(results.result), + ) + ) + return answers + results = results.result + for count, i in enumerate(results): + buttons = InlineKeyboard(row_width=1) + buttons.add(InlineKeyboardButton("Download | Play", url=i.media_url)) + buttons_list.append(buttons) + duration = await time_convert(i.duration) + caption = f""" +**Title:** {i.song} +**Album:** {i.album} +**Duration:** {duration} +**Release:** {i.year} +**Singers:** {i.singers}""" + description = f"{i.album} | {duration} " + f"| {i.singers} ({i.year})" + answers.append( + InlineQueryResultArticle( + title=i.song, + input_message_content=InputTextMessageContent( + caption, disable_web_page_preview=True + ), + description=description, + thumb_url=i.image, + reply_markup=buttons_list[count], + ) + ) + return answers + + +async def paste_func(answers, text): + start_time = time() + url = await paste(text) + msg = f"__**{url}**__" + end_time = time() + answers.append( + InlineQueryResultArticle( + title=f"Pasted In {round(end_time - start_time)} Seconds.", + description=url, + input_message_content=InputTextMessageContent(msg), + ) + ) + return answers + + +async def deezer_func(answers, text): + buttons_list = [] + results = await arq.deezer(text, 5) + if not results.ok: + answers.append( + InlineQueryResultArticle( + title="Error", + description=results.result, + input_message_content=InputTextMessageContent(results.result), + ) + ) + return answers + results = results.result + for count, i in enumerate(results): + buttons = InlineKeyboard(row_width=1) + buttons.add(InlineKeyboardButton("Download | Play", url=i.url)) + buttons_list.append(buttons) + duration = await time_convert(i.duration) + caption = f""" +**Title:** {i.title} +**Artist:** {i.artist} +**Duration:** {duration} +**Source:** [Deezer]({i.source})""" + description = f"{i.artist} | {duration}" + answers.append( + InlineQueryResultArticle( + title=i.title, + thumb_url=i.thumbnail, + description=description, + input_message_content=InputTextMessageContent( + caption, disable_web_page_preview=True + ), + reply_markup=buttons_list[count], + ) + ) + return answers + + +# Used my api key here, don't fuck with it +async def shortify(url): + if "." not in url: + return + header = { + "Authorization": "Bearer ad39983fa42d0b19e4534f33671629a4940298dc", + "Content-Type": "application/json", + } + payload = {"long_url": f"{url}"} + payload = json.dumps(payload) + async with aiohttp.ClientSession() as session: + async with session.post( + "https://api-ssl.bitly.com/v4/shorten", headers=header, data=payload + ) as resp: + data = await resp.json() + msg = data["link"] + a = [] + b = InlineQueryResultArticle( + title="Link Shortened!", + description=data["link"], + input_message_content=InputTextMessageContent( + msg, disable_web_page_preview=True + ), + ) + a.append(b) + return a + + +async def torrent_func(answers, text): + results = await arq.torrent(text) + if not results.ok: + answers.append( + InlineQueryResultArticle( + title="Error", + description=results.result, + input_message_content=InputTextMessageContent(results.result), + ) + ) + return answers + limit = 0 + results = results.result + for i in results: + if limit > 48: + break + title = i.name + size = i.size + seeds = i.seeds + leechs = i.leechs + upload_date = i.uploaded + " Ago" + magnet = i.magnet + caption = f""" +**Title:** __{title}__ +**Size:** __{size}__ +**Seeds:** __{seeds}__ +**Leechs:** __{leechs}__ +**Uploaded:** __{upload_date}__ +**Magnet:** `{magnet}`""" + + description = f"{size} | {upload_date} | Seeds: {seeds}" + answers.append( + InlineQueryResultArticle( + title=title, + description=description, + input_message_content=InputTextMessageContent( + caption, disable_web_page_preview=True + ), + ) + ) + limit += 1 + return answers + + +async def wiki_func(answers, text): + data = await arq.wiki(text) + if not data.ok: + answers.append( + InlineQueryResultArticle( + title="Error", + description=data.result, + input_message_content=InputTextMessageContent(data.result), + ) + ) + return answers + data = data.result + msg = f""" +**QUERY:** +{data.title} +**ANSWER:** +__{data.answer}__""" + answers.append( + InlineQueryResultArticle( + title=data.title, + description=data.answer, + input_message_content=InputTextMessageContent(msg), + ) + ) + return answers + + +async def ping_func(answers): + t1 = time() + ping = Ping(ping_id=randint(696969, 6969696)) + await app.send(ping) + t2 = time() + ping = f"{str(round((t2 - t1), 2))} Seconds" + answers.append( + InlineQueryResultArticle( + title=ping, input_message_content=InputTextMessageContent(f"__**{ping}**__") + ) + ) + return answers + + +async def pokedexinfo(answers, pokemon): + Pokemon = f"https://some-random-api.ml/pokedex?pokemon={pokemon}" + result = await fetch(Pokemon) + buttons = InlineKeyboard(row_width=1) + buttons.add( + InlineKeyboardButton("Pokedex", switch_inline_query_current_chat="pokedex") + ) + caption = f""" +**Pokemon:** `{result['name']}` +**Pokedex:** `{result['id']}` +**Type:** `{result['type']}` +**Abilities:** `{result['abilities']}` +**Height:** `{result['height']}` +**Weight:** `{result['weight']}` +**Gender:** `{result['gender']}` +**Stats:** `{result['stats']}` +**Description:** `{result['description']}`""" + answers.append( + InlineQueryResultPhoto( + photo_url=f"https://img.pokemondb.net/artwork/large/{pokemon}.jpg", + title=result["name"], + description=result["description"], + caption=caption, + reply_markup=buttons, + ) + ) + return answers diff --git a/Amanda/function/pluginhelpers.py b/Amanda/function/pluginhelpers.py new file mode 100644 index 0000000..b27acfe --- /dev/null +++ b/Amanda/function/pluginhelpers.py @@ -0,0 +1,474 @@ +import asyncio +import math +import shlex +import sys +import time +import traceback +from functools import wraps +from typing import Callable, Coroutine, Dict, List, Tuple, Union + +import aiohttp +from PIL import Image +from pyrogram import Client +from pyrogram.errors import FloodWait, MessageNotModified +from pyrogram.types import Chat, Message, User + +from Amanda import OWNER_ID, SUPPORT_CHAT +from Amanda import pbot + + +def get_user(message: Message, text: str) -> [int, str, None]: + if text is None: + asplit = None + else: + asplit = text.split(" ", 1) + user_s = None + reason_ = None + if message.reply_to_message: + user_s = message.reply_to_message.from_user.id + reason_ = text if text else None + elif asplit is None: + return None, None + elif len(asplit[0]) > 0: + user_s = int(asplit[0]) if asplit[0].isdigit() else asplit[0] + if len(asplit) == 2: + reason_ = asplit[1] + return user_s, reason_ + + +def get_readable_time(seconds: int) -> int: + count = 0 + ping_time = "" + time_list = [] + time_suffix_list = ["s", "m", "h", "days"] + + while count < 4: + count += 1 + if count < 3: + remainder, result = divmod(seconds, 60) + else: + remainder, result = divmod(seconds, 24) + if seconds == 0 and remainder == 0: + break + time_list.append(int(result)) + seconds = int(remainder) + + for x in range(len(time_list)): + time_list[x] = str(time_list[x]) + time_suffix_list[x] + if len(time_list) == 4: + ping_time += time_list.pop() + ", " + + time_list.reverse() + ping_time += ":".join(time_list) + + return ping_time + + +def time_formatter(milliseconds: int) -> str: + seconds, milliseconds = divmod(int(milliseconds), 1000) + minutes, seconds = divmod(seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + tmp = ( + ((str(days) + " day(s), ") if days else "") + + ((str(hours) + " hour(s), ") if hours else "") + + ((str(minutes) + " minute(s), ") if minutes else "") + + ((str(seconds) + " second(s), ") if seconds else "") + + ((str(milliseconds) + " millisecond(s), ") if milliseconds else "") + ) + return tmp[:-2] + + +async def delete_or_pass(message): + if message.from_user.id == 1141839926: + return message + return await message.delete() + + +def humanbytes(size): + if not size: + return "" + power = 2 ** 10 + raised_to_pow = 0 + dict_power_n = {0: "", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"} + while size > power: + size /= power + raised_to_pow += 1 + return str(round(size, 2)) + " " + dict_power_n[raised_to_pow] + "B" + + +async def progress(current, total, message, start, type_of_ps, file_name=None): + now = time.time() + diff = now - start + if round(diff % 10.00) == 0 or current == total: + percentage = current * 100 / total + speed = current / diff + elapsed_time = round(diff) * 1000 + if elapsed_time == 0: + return + time_to_completion = round((total - current) / speed) * 1000 + estimated_total_time = elapsed_time + time_to_completion + progress_str = "{0}{1} {2}%\n".format( + "".join(["🔴" for i in range(math.floor(percentage / 10))]), + "".join(["🔘" for i in range(10 - math.floor(percentage / 10))]), + round(percentage, 2), + ) + tmp = progress_str + "{0} of {1}\nETA: {2}".format( + humanbytes(current), humanbytes(total), time_formatter(estimated_total_time) + ) + if file_name: + try: + await message.edit( + "{}\n**File Name:** `{}`\n{}".format(type_of_ps, file_name, tmp) + ) + except FloodWait as e: + await asyncio.sleep(e.x) + except MessageNotModified: + pass + else: + try: + await message.edit("{}\n{}".format(type_of_ps, tmp)) + except FloodWait as e: + await asyncio.sleep(e.x) + except MessageNotModified: + pass + + +def get_text(message: Message) -> [None, str]: + text_to_return = message.text + if message.text is None: + return None + if " " in text_to_return: + try: + return message.text.split(None, 1)[1] + except IndexError: + return None + else: + return None + + +async def iter_chats(client): + chats = [] + async for dialog in client.iter_dialogs(): + if dialog.chat.type in ["supergroup", "channel"]: + chats.append(dialog.chat.id) + return chats + + +async def fetch_audio(client, message): + time.time() + if not message.reply_to_message: + await message.reply("`Reply To A Video / Audio.`") + return + warner_stark = message.reply_to_message + if warner_stark.audio is None and warner_stark.video is None: + await message.reply("`Format Not Supported`") + return + if warner_stark.video: + lel = await message.reply("`Video Detected, Converting To Audio !`") + warner_bros = await message.reply_to_message.download() + stark_cmd = f"ffmpeg -i {warner_bros} -map 0:a friday.mp3" + await runcmd(stark_cmd) + final_warner = "friday.mp3" + elif warner_stark.audio: + lel = await edit_or_reply(message, "`Download Started !`") + final_warner = await message.reply_to_message.download() + await lel.edit("`Almost Done!`") + await lel.delete() + return final_warner + + +async def edit_or_reply(message, text, parse_mode="md"): + if message.from_user.id: + if message.reply_to_message: + kk = message.reply_to_message.message_id + return await message.reply_text( + text, reply_to_message_id=kk, parse_mode=parse_mode + ) + return await message.reply_text(text, parse_mode=parse_mode) + return await message.edit(text, parse_mode=parse_mode) + + +async def runcmd(cmd: str) -> Tuple[str, str, int, int]: + """run command in terminal""" + args = shlex.split(cmd) + process = await asyncio.create_subprocess_exec( + *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await process.communicate() + return ( + stdout.decode("utf-8", "replace").strip(), + stderr.decode("utf-8", "replace").strip(), + process.returncode, + process.pid, + ) + + +async def convert_to_image(message, client) -> [None, str]: + """Convert Most Media Formats To Raw Image""" + final_path = None + if not ( + message.reply_to_message.photo + or message.reply_to_message.sticker + or message.reply_to_message.media + or message.reply_to_message.animation + or message.reply_to_message.audio + ): + return None + if message.reply_to_message.photo: + final_path = await message.reply_to_message.download() + elif message.reply_to_message.sticker: + if message.reply_to_message.sticker.mime_type == "image/webp": + final_path = "webp_to_png_s_proton.png" + path_s = await message.reply_to_message.download() + im = Image.open(path_s) + im.save(final_path, "PNG") + else: + path_s = await client.download_media(message.reply_to_message) + final_path = "lottie_proton.png" + cmd = ( + f"lottie_convert.py --frame 0 -if lottie -of png {path_s} {final_path}" + ) + await runcmd(cmd) + elif message.reply_to_message.audio: + thumb = message.reply_to_message.audio.thumbs[0].file_id + final_path = await client.download_media(thumb) + elif message.reply_to_message.video or message.reply_to_message.animation: + final_path = "fetched_thumb.png" + vid_path = await client.download_media(message.reply_to_message) + await runcmd(f"ffmpeg -i {vid_path} -filter:v scale=500:500 -an {final_path}") + return final_path + + +def get_text(message: Message) -> [None, str]: + """Extract Text From Commands""" + text_to_return = message.text + if message.text is None: + return None + if " " in text_to_return: + try: + return message.text.split(None, 1)[1] + except IndexError: + return None + else: + return None + + +# Admin check + +admins: Dict[str, List[User]] = {} + + +def set(chat_id: Union[str, int], admins_: List[User]): + if isinstance(chat_id, int): + chat_id = str(chat_id) + + admins[chat_id] = admins_ + + +def get(chat_id: Union[str, int]) -> Union[List[User], bool]: + if isinstance(chat_id, int): + chat_id = str(chat_id) + + if chat_id in admins: + return admins[chat_id] + + return False + + +async def get_administrators(chat: Chat) -> List[User]: + _get = get(chat.id) + + if _get: + return _get + else: + set( + chat.id, + [member.user for member in await chat.get_members(filter="administrators")], + ) + return await get_administrators(chat) + + +def admins_only(func: Callable) -> Coroutine: + async def wrapper(client: Client, message: Message): + if message.from_user.id == OWNER_ID: + return await func(client, message) + admins = await get_administrators(message.chat) + for admin in admins: + if admin.id == message.from_user.id: + return await func(client, message) + + return wrapper + + +# @Mr_Dark_Prince +def capture_err(func): + @wraps(func) + async def capture(client, message, *args, **kwargs): + try: + return await func(client, message, *args, **kwargs) + except Exception as err: + exc_type, exc_obj, exc_tb = sys.exc_info() + errors = traceback.format_exception( + etype=exc_type, + value=exc_obj, + tb=exc_tb, + ) + error_feedback = split_limits( + "**ERROR** | `{}` | `{}`\n\n```{}```\n\n```{}```\n".format( + 0 if not message.from_user else message.from_user.id, + 0 if not message.chat else message.chat.id, + message.text or message.caption, + "".join(errors), + ), + ) + for x in error_feedback: + await pbot.send_message(SUPPORT_CHAT, x) + raise err + + return capture + + +# Ported from https://github.com/TheHamkerCat/WilliamButcherBot +""" +MIT License +Copyright (c) 2021 TheHamkerCat +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + + +async def member_permissions(chat_id, user_id): + perms = [] + member = await pbot.get_chat_member(chat_id, user_id) + if member.can_post_messages: + perms.append("can_post_messages") + if member.can_edit_messages: + perms.append("can_edit_messages") + if member.can_delete_messages: + perms.append("can_delete_messages") + if member.can_restrict_members: + perms.append("can_restrict_members") + if member.can_promote_members: + perms.append("can_promote_members") + if member.can_change_info: + perms.append("can_change_info") + if member.can_invite_users: + perms.append("can_invite_users") + if member.can_pin_messages: + perms.append("can_pin_messages") + return perms + + +async def current_chat_permissions(chat_id): + perms = [] + perm = (await pbot.get_chat(chat_id)).permissions + if perm.can_send_messages: + perms.append("can_send_messages") + if perm.can_send_media_messages: + perms.append("can_send_media_messages") + if perm.can_send_stickers: + perms.append("can_send_stickers") + if perm.can_send_animations: + perms.append("can_send_animations") + if perm.can_send_games: + perms.append("can_send_games") + if perm.can_use_inline_bots: + perms.append("can_use_inline_bots") + if perm.can_add_web_page_previews: + perms.append("can_add_web_page_previews") + if perm.can_send_polls: + perms.append("can_send_polls") + if perm.can_change_info: + perms.append("can_change_info") + if perm.can_invite_users: + perms.append("can_invite_users") + if perm.can_pin_messages: + perms.append("can_pin_messages") + + return perms + + +# URL LOCK + + +def get_url(message_1: Message) -> Union[str, None]: + messages = [message_1] + + if message_1.reply_to_message: + messages.append(message_1.reply_to_message) + + text = "" + offset = None + length = None + + for message in messages: + if offset: + break + + if message.entities: + for entity in message.entities: + if entity.type == "url": + text = message.text or message.caption + offset, length = entity.offset, entity.length + break + + if offset in (None,): + return None + + return text[offset : offset + length] + + +async def fetch(url): + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + try: + data = await resp.json() + except Exception: + data = await resp.text() + return data + + +async def convert_seconds_to_minutes(seconds: int): + seconds = int(seconds) + seconds = seconds % (24 * 3600) + seconds %= 3600 + minutes = seconds // 60 + seconds %= 60 + return "%02d:%02d" % (minutes, seconds) + + +async def json_object_prettify(objecc): + dicc = objecc.__dict__ + output = "" + for key, value in dicc.items(): + if key == "pinned_message" or key == "photo" or key == "_" or key == "_client": + continue + output += f"**{key}:** `{value}`\n" + return output + + +async def json_prettify(data): + output = "" + try: + for key, value in data.items(): + output += f"**{key}:** `{value}`\n" + except Exception: + for datas in data: + for key, value in datas.items(): + output += f"**{key}:** `{value}`\n" + output += "------------------------\n" + return output diff --git "a/Amanda/modules/Anti-Virus_\360\237\220\236.py" "b/Amanda/modules/Anti-Virus_\360\237\220\236.py" new file mode 100644 index 0000000..aed847e --- /dev/null +++ "b/Amanda/modules/Anti-Virus_\360\237\220\236.py" @@ -0,0 +1,116 @@ +# Copyright (C) 2021 TeamDaisyX + + +# This file is part of Daisy (Telegram Bot) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +import os + +import cloudmersive_virus_api_client +from telethon.tl import functions, types +from telethon.tl.types import DocumentAttributeFilename, MessageMediaDocument + +from Amanda.config import get_str_key +from Amanda.events import register +from Amanda import telethn as tbot + + +async def is_register_admin(chat, user): + if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): + return isinstance( + ( + await tbot(functions.channels.GetParticipantRequest(chat, user)) + ).participant, + (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), + ) + if isinstance(chat, types.InputPeerUser): + return True + + +VIRUS_API_KEY = get_str_key("VIRUS_API_KEY", required=False) +configuration = cloudmersive_virus_api_client.Configuration() +configuration.api_key["Apikey"] = VIRUS_API_KEY +api_instance = cloudmersive_virus_api_client.ScanApi( + cloudmersive_virus_api_client.ApiClient(configuration) +) +allow_executables = True +allow_invalid_files = True +allow_scripts = True +allow_password_protected_files = True + + +@register(pattern="^/scanit$") +async def virusscan(event): + if event.fwd_from: + return + if event.is_group: + if await is_register_admin(event.input_chat, event.message.sender_id): + pass + else: + return + if not event.reply_to_msg_id: + await event.reply("Reply to a file to scan it.") + return + + c = await event.get_reply_message() + try: + c.media.document + except Exception: + await event.reply("💁🏼‍♂️ This bot accepts only files.\n💬support chat👉 @slbotzone") + return + h = c.media + try: + k = h.document.attributes + except Exception: + await event.reply("💁🏼‍♂️ This bot accepts only files.\n💬support chat👉 @slbotzone") + return + if not isinstance(h, MessageMediaDocument): + await event.reply("💁🏼‍♂️ This bot accepts only files.\n💬support chat👉 @slbotzone") + return + if not isinstance(k[0], DocumentAttributeFilename): + await event.reply("💁🏼‍♂️ This bot accepts only files.\n💬support chat👉 @slbotzone") + return + try: + virus = c.file.name + await event.client.download_file(c, virus) + gg = await event.reply("🚀 ** File initialized**.\n✅ File downloaded.\n✅ File uploaded to telegram.") + fsize = c.file.size + if not fsize <= 3145700: # MAX = 3MB + await gg.edit("🔗 This file size is not supported. File size exceeds 3MB") + return + api_response = api_instance.scan_file_advanced( + c.file.name, + allow_executables=allow_executables, + allow_invalid_files=allow_invalid_files, + allow_scripts=allow_scripts, + allow_password_protected_files=allow_password_protected_files, + ) + if api_response.clean_result is True: + await gg.edit("This file is safe ✅\n🧬 `Detections: 0 / 57`\n[⚜️ Link to VirusTotal ](https://www.virustotal.com)\n💬support chat👉 @slbotzone") + else: + await gg.edit("This file is Dangerous ☠️️\nVirus detected ❌\n[⚜️ Link to VirusTotal ](https://www.virustotal.com)\n💬support chat👉 @slbotzone") + os.remove(virus) + except Exception as e: + print(e) + os.remove(virus) + await gg.edit("Some error occurred.") + return + +__help__ = """ +@TheAmandabot + ❍ /scanit: Scan a file for virus (MAX SIZE = 3MB) +""" +__mod_name__ = "ᴠɪʀᴜꜱ🦠" diff --git a/Amanda/modules/ForceSubscribe.py b/Amanda/modules/ForceSubscribe.py index 9dcbdc4..969a544 100644 --- a/Amanda/modules/ForceSubscribe.py +++ b/Amanda/modules/ForceSubscribe.py @@ -14,6 +14,7 @@ from Amanda import pbot from Amanda.modules.sql import forceSubscribe_sql as sql + logging.basicConfig(level=logging.INFO) static_data_filter = filters.create( @@ -35,8 +36,6 @@ def _onUnMuteRequest(client, cb): client.get_chat_member(channel, user_id) client.unban_chat_member(chat_id, user_id) cb.message.delete() - # if cb.message.reply_to_message.from_user.id == user_id: - # cb.message.delete() except UserNotParticipant: client.answer_callback_query( cb.id, @@ -62,7 +61,7 @@ def _onUnMuteRequest(client, cb): else: client.answer_callback_query( cb.id, - text="❗ Warning! Don't press the button when you cn talk.", + text="❗ Warning! Don't press the button when you can talk 🤷‍♂️.", show_alert=True, ) @@ -84,7 +83,7 @@ def _check_member(client, message): except UserNotParticipant: try: sent_message = message.reply_text( - "Welcome {} 🙏 \n **You havent joined our @{} Channel yet** 😭 \n \nPlease Join [Our Channel](https://t.me/{}) and hit the **UNMUTE ME** Button.".format( + "Welcome {} 🙏 \n **You havent joined our @{} Channel yet** 😭 \n \nPlease Join [Our Channel](https://t.me/{}) and hit the **UNMUTE ME** Button. \n \n ".format( message.from_user.mention, channel, channel ), disable_web_page_preview=True, @@ -109,7 +108,7 @@ def _check_member(client, message): ) except ChatAdminRequired: sent_message.edit( - "❗ **Amanda is not admin here..**\n__Give me ban permissions and retry.. \n#Ending FSub...__" + "❗ **RoseBot is not admin here..**\n__Give me ban permissions and retry.. \n#Ending FSub...__" ) except ChatAdminRequired: @@ -117,8 +116,7 @@ def _check_member(client, message): chat_id, text=f"❗ **I not an admin of @{channel} channel.**\n__Give me admin of that channel and retry.\n#Ending FSub...__", ) - - + @pbot.on_message(filters.command(["forcesubscribe", "fsub"]) & ~filters.private) def config(client, message): user = client.get_chat_member(message.chat.id, message.from_user.id) @@ -151,7 +149,7 @@ def config(client, message): client.get_chat_member(input_str, "me") sql.add_channel(chat_id, input_str) message.reply_text( - f"✅ **Force Subscribe is Enabled**\n__Force Subscribe is enabled, all the group members have to subscribe this [channel](https://t.me/{input_str}) in order to send messages in this group.__", + f"✅ **Force Subscribe is Enabled in your chat**\n__Force Subscribe is enabled, all the group members have to subscribe this [channel](https://t.me/{input_str}) in order to send messages in this group.__", disable_web_page_preview=True, ) except UserNotParticipant: @@ -166,7 +164,7 @@ def config(client, message): else: if sql.fs_settings(chat_id): message.reply_text( - f"✅ **Force Subscribe is enabled in this chat.**\n__For this [Channel](https://t.me/{sql.fs_settings(chat_id).channel})__", + f"✅ **Force Subscribe is enabled in your chat.**\n__For this [Channel](https://t.me/{sql.fs_settings(chat_id).channel})__", disable_web_page_preview=True, ) else: @@ -176,25 +174,24 @@ def config(client, message): "❗ **Group Creator Required**\n__You have to be the group creator to do that.__" ) - __help__ = """ +@TheAmandabot *ForceSubscribe:* - -❂ Amanda can mute members who are not subscribed your channel until they subscribe -❂ When enabled I will mute unsubscribed members and show them a unmute button. When they pressed the button I will unmute them +❍ RoseBot can mute members who are not subscribed your channel until they subscribe. +❍ When enabled I will mute unsubscribed members and show them a unmute button. When they pressed the button I will unmute them. *Setup* + 1) First of all add me in the group as admin with ban users permission and in the channel as admin. Note: Only creator of the group can setup me and i will not allow force subscribe again if not done so. *Commmands* -❂ /ForceSubscribe - To get the current settings. -❂ /ForceSubscribe no/off/disable - To turn of ForceSubscribe. -❂ /ForceSubscribe {channel username} - To turn on and setup the channel. -❂ /ForceSubscribe clear - To unmute all members who muted by me. +❍ /ForceSubscribe - To get the current settings. +❍ /ForceSubscribe no/off/disable - To turn of ForceSubscribe. +❍ /ForceSubscribe {channel username} - To turn on and setup the channel. +❍ /ForceSubscribe clear - To unmute all members who muted by me. -Note: /FSub is an alias of /ForceSubscribe +⚠️ Note: /FSub is an alias of /ForceSubscribe - """ -__mod_name__ = "Force Subscribe" +__mod_name__ = "ꜰ-ꜱᴜʙ🗻" diff --git a/Amanda/modules/MoreTools.py b/Amanda/modules/MoreTools.py index 90a10c9..165c9d9 100644 --- a/Amanda/modules/MoreTools.py +++ b/Amanda/modules/MoreTools.py @@ -1,31 +1,30 @@ __help__ = """ +@TheAmandabot ** GPS ** - - /gps : Show Location on a map + ❍ /gps : Show Location on a map ** Blue Cleaner ** - - /cleanblue on : Turn bluetext cleaner on - - /cleanblue off : Turn bluetext cleaner off + ❍ /cleanblue on : Turn bluetext cleaner on + ❍ /cleanblue off : Turn bluetext cleaner off ** Send ** - - /snd : Message like the bot + ❍ /snd : Message like the bot ** Grammer ** - - /t : Show grammer corrected text + ❍ /t : Show grammer corrected text ** Image Tools** - - /img : perform a image search - - /getqr : Read QR code - - /makeqr : Make QR code + ❍ /img : perform a image search + ❍ /getqr : Read QR code + ❍ /makeqr : Make QR code ** Style Text ** - - /weebify : Weebify Text - - /square : square Text - - /blue : Blues text - -** Telegraph ** -- /telegraph : upload to telegraph. - + ❍ /weebify : Weebify Text + ❍ /square : square Text + ❍ /blue : Blues text + ** More ** - - /phone : Track Phone no + ❍ /phone : Track Phone no + """ -__mod_name__ = "More Tools" +__mod_name__ = "ᴛᴏᴏʟꜱ🛠️" diff --git "a/Amanda/modules/RMBG_\342\234\217\357\270\217.py" "b/Amanda/modules/RMBG_\342\234\217\357\270\217.py" new file mode 100644 index 0000000..99573da --- /dev/null +++ "b/Amanda/modules/RMBG_\342\234\217\357\270\217.py" @@ -0,0 +1,104 @@ +import io +import os +from datetime import datetime + +import requests +from telethon import types +from telethon.tl import functions + +from Amanda.config import get_str_key +from Amanda.events import register +from Amanda import telethn as tbot + +REM_BG_API_KEY = get_str_key("REM_BG_API_KEY", required=False) +TEMP_DOWNLOAD_DIRECTORY = "./" + + +async def is_register_admin(chat, user): + if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): + return isinstance( + ( + await tbot(functions.channels.GetParticipantRequest(chat, user)) + ).participant, + (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), + ) + if isinstance(chat, types.InputPeerUser): + return True + + +@register(pattern="^/rmbg") +async def _(event): + HELP_STR = "use `/rmbg` as reply to a media.\nJoin my updates channel 👉 @sl_bot_zone " + if event.fwd_from: + return + if event.is_group: + if await is_register_admin(event.input_chat, event.message.sender_id): + pass + else: + return + if REM_BG_API_KEY is None: + await event.reply("You need API token from remove.bg to use this plugin.") + return False + start = datetime.now() + message_id = event.message.id + if event.reply_to_msg_id: + message_id = event.reply_to_msg_id + reply_message = await event.get_reply_message() + gg = await event.reply("🔄 ** Please wait Processing Your image ...**") + try: + downloaded_file_name = await tbot.download_media( + reply_message, TEMP_DOWNLOAD_DIRECTORY + ) + except Exception as e: + await event.reply(str(e)) + return + else: + output_file_name = ReTrieveFile(downloaded_file_name) + os.remove(downloaded_file_name) + else: + await event.reply(HELP_STR) + return + contentType = output_file_name.headers.get("content-type") + if "image" in contentType: + with io.BytesIO(output_file_name.content) as remove_bg_image: + remove_bg_image.name = "rmbg.png" + await tbot.send_file( + event.chat_id, + remove_bg_image, + force_document=True, + supports_streaming=False, + allow_cache=False, + reply_to=message_id, + ) + end = datetime.now() + ms = (end - start).seconds + await gg.edit("🤗** Background Removed in `{}` seconds **\nPowered by @TheAmandabot \nUpdates channel 👉 @sl_bot_zone ".format(ms)) + else: + await gg.edit( + "remove.bg API returned Errors. Please report to @slbotzone\n`{}`\nor join 👉 @sl_bot_zone ".format( + output_file_name.content.decode("UTF-8") + ) + ) + + +def ReTrieveFile(input_file_name): + headers = { + "X-API-Key": REM_BG_API_KEY, + } + files = { + "image_file": (input_file_name, open(input_file_name, "rb")), + } + r = requests.post( + "https://api.remove.bg/v1.0/removebg", + headers=headers, + files=files, + allow_redirects=True, + stream=True, + ) + return r + +__help__ = """ +@TheAmandabot + ❍ /rmbg: Type in reply to a media to remove it's background +""" +__mod_name__ = "ʀᴍʙɢ💹" diff --git a/Amanda/modules/Telegraph.py b/Amanda/modules/Telegraph.py new file mode 100644 index 0000000..d22ef40 --- /dev/null +++ b/Amanda/modules/Telegraph.py @@ -0,0 +1,80 @@ + +from Amanda.events import register +from Amanda import telethn as tbot +TMP_DOWNLOAD_DIRECTORY = "./" +from telethon import events +import os +from PIL import Image +from datetime import datetime +from telegraph import Telegraph, upload_file, exceptions +Hero = "Amanda" +telegraph = Telegraph() +r = telegraph.create_account(short_name=Hero) +auth_url = r["auth_url"] + + +@register(pattern="^/t(m|xt) ?(.*)") +async def _(event): + if event.fwd_from: + return + optional_title = event.pattern_match.group(2) + if event.reply_to_msg_id: + start = datetime.now() + r_message = await event.get_reply_message() + input_str = event.pattern_match.group(1) + if input_str == "m": + downloaded_file_name = await tbot.download_media( + r_message, + TMP_DOWNLOAD_DIRECTORY + ) + end = datetime.now() + ms = (end - start).seconds + h = await event.reply("Downloaded to {} in {} seconds.".format(downloaded_file_name, ms)) + if downloaded_file_name.endswith((".webp")): + resize_image(downloaded_file_name) + try: + start = datetime.now() + media_urls = upload_file(downloaded_file_name) + except exceptions.TelegraphException as exc: + await h.edit("ERROR: " + str(exc)) + os.remove(downloaded_file_name) + else: + end = datetime.now() + ms_two = (end - start).seconds + os.remove(downloaded_file_name) + await h.edit("**Uploaded To Telegraph!\n\n👉 https://telegra.ph{}\n\nUploaded by @TheAmandabot**".format(media_urls[0]),link_preview=True) + elif input_str == "xt": + user_object = await tbot.get_entity(r_message.sender_id) + title_of_page = user_object.first_name # + " " + user_object.last_name + # apparently, all Users do not have last_name field + if optional_title: + title_of_page = optional_title + page_content = r_message.message + if r_message.media: + if page_content != "": + title_of_page = page_content + downloaded_file_name = await tbot.download_media( + r_message, + TMP_DOWNLOAD_DIRECTORY + ) + m_list = None + with open(downloaded_file_name, "rb") as fd: + m_list = fd.readlines() + for m in m_list: + page_content += m.decode("UTF-8") + "\n" + os.remove(downloaded_file_name) + page_content = page_content.replace("\n", "
") + response = telegraph.create_page( + title_of_page, + html_content=page_content + ) + end = datetime.now() + ms = (end - start).seconds + await event.reply("** Uploaded To Telegraph!\n\n👉https://telegra.ph/{} in {} seconds.\n\nUploaded by @TheAmandabot**".format(response["path"], ms), link_preview=True) + else: + await event.reply("Reply to a message to get a permanent telegra.ph link.") + + +def resize_image(image): + im = Image.open(image) + im.save(image, "PNG") diff --git a/Amanda/modules/__Directlink.py b/Amanda/modules/__Directlink.py new file mode 100644 index 0000000..30829c5 --- /dev/null +++ b/Amanda/modules/__Directlink.py @@ -0,0 +1,92 @@ +# Hitsuki (A telegram bot project) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +import re +from random import choice + +import requests +from bs4 import BeautifulSoup +from Amanda import pbot +from pyrogram import Client, filters +from pyrogram.types import Update + + +@pbot.on_message(filters.command("direct")) +async def direct_link_generator(c: Client, update: Update): + if not len(update.command) == 2: + m = "Usage: `/direct `" + await update.reply_text( + parse_mode="md", + text=m) + return + + text = update.command[1] + if text: + links = re.findall(r'\bhttps?://.*\.\S+', text) + else: + return + reply = [] + if not links: + await update.reply_text("No links found!") + return + for link in links: + if 'sourceforge.net' in link: + reply.append(sourceforge(link)) + else: + reply.append(re.findall( + r"\bhttps?://(.*?[^/]+)", link)[0] + ' is not supported') + + await update.reply_text("\n".join(reply)) + + +def sourceforge(url: str) -> str: + try: + link = re.findall(r'\bhttps?://.*sourceforge\.net\S+', url)[0] + except IndexError: + reply = "No SourceForge links found\n" + return reply + file_path = re.findall(r'/files(.*)/download', link) + if not file_path: + file_path = re.findall(r'/files(.*)', link) + file_path = file_path[0] + reply = f"Mirrors for {file_path.split('/')[-1]}\n" + project = re.findall(r'projects?/(.*?)/files', link)[0] + mirrors = f'https://sourceforge.net/settings/mirror_choices?' \ + f'projectname={project}&filename={file_path}' + page = BeautifulSoup(requests.get(mirrors).content, 'lxml') + info = page.find('ul', {'id': 'mirrorList'}).findAll('li') + for mirror in info[1:]: + name = re.findall(r'\((.*)\)', mirror.text.strip())[0] + dl_url = f'https://{mirror["id"]}.dl.sourceforge.net/project/{project}/{file_path}' + reply += f'{name} ' + return reply + + +def useragent(): + useragents = BeautifulSoup( + requests.get( + 'https://developers.whatismybrowser.com/' + 'useragents/explore/operating_system_name/android/').content, + 'lxml').findAll('td', {'class': 'useragent'}) + user_agent = choice(useragents) + return user_agent.text + + +__help__ = """ +@TheAmandabot + ❍ /direct - get any file useing link +""" +__mod_name__ = "ᴅ-ʟɪɴᴋ🖇️" diff --git a/Amanda/modules/__couple.py b/Amanda/modules/__couple.py new file mode 100644 index 0000000..be86475 --- /dev/null +++ b/Amanda/modules/__couple.py @@ -0,0 +1,94 @@ +""" +MIT License +Copyright (c) 2021 TheHamkerCat +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +import random +from datetime import datetime + +from pyrogram import filters + +from Amanda import pbot +from Amanda.function.dbfun import get_couple, save_couple + +# Date and time +def dt(): + now = datetime.now() + dt_string = now.strftime("%d/%m/%Y %H:%M") + dt_list = dt_string.split(" ") + return dt_list + + +def dt_tom(): + a = ( + str(int(dt()[0].split("/")[0]) + 1) + + "/" + + dt()[0].split("/")[1] + + "/" + + dt()[0].split("/")[2] + ) + return a + + +today = str(dt()[0]) +tomorrow = str(dt_tom()) + + +@pbot.on_message(filters.command("couple") & ~filters.edited) +async def couple(_, message): + if message.chat.type == "private": + return await message.reply_text("This command only works in groups.") + try: + chat_id = message.chat.id + is_selected = await get_couple(chat_id, today) + if not is_selected: + list_of_users = [] + async for i in pbot.iter_chat_members(message.chat.id): + if not i.user.is_bot: + list_of_users.append(i.user.id) + if len(list_of_users) < 2: + return await message.reply_text("Not enough users") + c1_id = random.choice(list_of_users) + c2_id = random.choice(list_of_users) + while c1_id == c2_id: + c1_id = random.choice(list_of_users) + c1_mention = (await pbot.get_users(c1_id)).mention + c2_mention = (await pbot.get_users(c2_id)).mention + + couple_selection_message = f"""**Couple of the day:** +{c1_mention} + {c2_mention} = ❤️ +__New couple of the day may be chosen at 12AM {tomorrow}__""" + await pbot.send_message( + message.chat.id, text=couple_selection_message + ) + couple = {"c1_id": c1_id, "c2_id": c2_id} + await save_couple(chat_id, today, couple) + + elif is_selected: + c1_id = int(is_selected["c1_id"]) + c2_id = int(is_selected["c2_id"]) + c1_name = (await pbot.get_users(c1_id)).first_name + c2_name = (await pbot.get_users(c2_id)).first_name + couple_selection_message = f"""Couple of the day: +[{c1_name}](tg://openmessage?user_id={c1_id}) + [{c2_name}](tg://openmessage?user_id={c2_id}) = ❤️ +__New couple of the day may be chosen at 12AM {tomorrow}__""" + await pbot.send_message( + message.chat.id, text=couple_selection_message + ) + except Exception as e: + print(e) + await message.reply_text(e) diff --git a/Amanda/modules/__json.py b/Amanda/modules/__json.py new file mode 100644 index 0000000..b9bc1cc --- /dev/null +++ b/Amanda/modules/__json.py @@ -0,0 +1,60 @@ +import io +from Amanda.events import register +from Amanda import telethn as borg +from Amanda import telethn as tbot +from telethon import types +from telethon import events +from telethon.tl import functions, types +from telethon.tl.types import * + + +async def is_register_admin(chat, user): + if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): + return isinstance( + ( + await tbot(functions.channels.GetParticipantRequest(chat, user)) + ).participant, + (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), + ) + if isinstance(chat, types.InputPeerUser): + return True + + +@register(pattern="^/json$") +async def _(event): + if event.fwd_from: + return + if event.is_group: + if not (await is_register_admin(event.input_chat, event.message.sender_id)): + await event.reply("🚨 Need Admin Pewer.. You can't use this command.. But you can use in my pm") + return + + the_real_message = None + reply_to_id = None + if event.reply_to_msg_id: + previous_message = await event.get_reply_message() + the_real_message = previous_message.stringify() + reply_to_id = event.reply_to_msg_id + else: + the_real_message = event.stringify() + reply_to_id = event.message.id + if len(the_real_message) > 4095: + with io.BytesIO(str.encode(the_real_message)) as out_file: + out_file.name = "json.text" + await borg.send_file( + event.chat_id, + out_file, + force_document=True, + allow_cache=False, + reply_to=reply_to_id, + ) + await event.delete() + else: + await event.reply("`{}`".format(the_real_message)) + + +__help__ = """ + ❍ /json*:* Get Detailed info about any message +""" + +__mod_name__ = "ᴊꜱᴏɴ⚖️" diff --git a/Amanda/modules/__nightmode.py b/Amanda/modules/__nightmode.py new file mode 100644 index 0000000..f3fcb74 --- /dev/null +++ b/Amanda/modules/__nightmode.py @@ -0,0 +1,177 @@ +import os + +from Amanda.modules.sql.night_mode_sql import add_nightmode, rmnightmode, get_all_chat_id, is_nightmode_indb +from telethon.tl.types import ChatBannedRights +from apscheduler.schedulers.asyncio import AsyncIOScheduler +from telethon import functions +from Amanda.events import register +from Amanda import OWNER_ID +from Amanda import telethn as tbot +from telethon import * +from telethon import Button, custom, events + +hehes = ChatBannedRights( + until_date=None, + send_messages=True, + send_media=True, + send_stickers=True, + send_gifs=True, + send_games=True, + send_inline=True, + send_polls=True, + invite_users=True, + pin_messages=True, + change_info=True, +) + +openhehe = ChatBannedRights( + until_date=None, + send_messages=False, + send_media=False, + send_stickers=False, + send_gifs=False, + send_games=False, + send_inline=False, + send_polls=False, + invite_users=True, + pin_messages=True, + change_info=True, +) + +from telethon.tl.types import ( + ChannelParticipantsAdmins, + ChatAdminRights, + MessageEntityMentionName, + MessageMediaPhoto, +) + +from telethon.tl.functions.channels import ( + EditAdminRequest, + EditBannedRequest, + EditPhotoRequest, +) + +async def is_register_admin(chat, user): + if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): + return isinstance( + ( + await tbot(functions.channels.GetParticipantRequest(chat, user)) + ).participant, + (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), + ) + if isinstance(chat, types.InputPeerUser): + return True + +async def can_change_info(message): + result = await tbot( + functions.channels.GetParticipantRequest( + channel=message.chat_id, + user_id=message.sender_id, + ) + ) + p = result.participant + return isinstance(p, types.ChannelParticipantCreator) or ( + isinstance(p, types.ChannelParticipantAdmin) and p.admin_rights.change_info + ) + +@register(pattern="^/(nightmode|Nightmode|NightMode) ?(.*)") +async def profanity(event): + if event.fwd_from: + return + if event.is_private: + return + input = event.pattern_match.group(2) + if not event.sender_id == OWNER_ID: + if not await is_register_admin(event.input_chat, event.sender_id): + await event.reply("Only admins can execute this command!") + return + else: + if not await can_change_info(message=dmod): + await event.reply("You are missing the following rights to use this command:CanChangeinfo") + return + if not input: + if is_nightmode_indb(str(event.chat_id)): + await event.reply( + "Currently NightMode is Enabled for this Chat" + ) + return + await event.reply( + "Currently NightMode is Disabled for this Chat" + ) + return + if "on" in input: + if event.is_group: + if is_nightmode_indb(str(event.chat_id)): + await event.reply( + "Night Mode is Already Turned ON for this Chat" + ) + return + add_nightmode(str(event.chat_id)) + await event.reply("NightMode turned on for this chat.") + if "off" in input: + if event.is_group: + if not is_nightmode_indb(str(event.chat_id)): + await event.reply( + "Night Mode is Already Off for this Chat" + ) + return + rmnightmode(str(event.chat_id)) + await event.reply("NightMode Disabled!") + if not "off" in input and not "on" in input: + await event.reply("Please Specify On or Off!") + return + + +async def job_close(): + chats = get_all_chat_id() + if len(chats) == 0: + return + for pro in chats: + try: + await tbot.send_message( + int(pro.chat_id), "12:00 Am, Group Is Closing Till 6 Am. Night Mode Started ! \n**Powered By @TheAmandabot**" + ) + await tbot( + functions.messages.EditChatDefaultBannedRightsRequest( + peer=int(pro.chat_id), banned_rights=hehes + ) + ) + except Exception as e: + logger.info(f"Unable To Close Group {chat} - {e}") + +#Run everyday at 12am +scheduler = AsyncIOScheduler(timezone="Asia/Kolkata") +scheduler.add_job(job_close, trigger="cron", hour=23, minute=59) +scheduler.start() + +async def job_open(): + chats = get_all_chat_id() + if len(chats) == 0: + return + for pro in chats: + try: + await tbot.send_message( + int(pro.chat_id), "Good morning!06:00 Am, Group Is Opening.\n**Powered By @TheAmandabot**" + ) + await tbot( + functions.messages.EditChatDefaultBannedRightsRequest( + peer=int(pro.chat_id), banned_rights=openhehe + ) + ) + except Exception as e: + logger.info(f"Unable To Open Group {pro.chat_id} - {e}") + +# Run everyday at 06 +scheduler = AsyncIOScheduler(timezone="Asia/Kolkata") +scheduler.add_job(job_open, trigger="cron", hour=5, minute=58) +scheduler.start() + + +__help__ = """ +@TheAmandabot + ❍ /nightmode on/off +**Note:** Night Mode chats get Automatically closed at 12pm(IST) +and Automatically openned at 6am(IST) To Prevent Night Spams. +""" + +__mod_name__ = "ɴ-ᴍᴏᴅᴇ🌌" diff --git a/Amanda/modules/__timerytext b/Amanda/modules/__timerytext new file mode 100644 index 0000000..9b6f3e5 --- /dev/null +++ b/Amanda/modules/__timerytext @@ -0,0 +1,101 @@ +#Copyright ©️ 2021 TeLe TiPs. All Rights Reserved +#You are free to use this code in any of your project, but you MUST include the following in your README.md (Copy & paste) +# ##Credits - [Countdown Timer Telegram bot by TeLe TiPs] (https://github.com/teletips/CountdownTimer-TeLeTiPs) + +# Changing the code is not allowed! Read GNU AFFERO GENERAL PUBLIC LICENSE: https://github.com/teletips/CountdownTimer-TeLeTiPs/blob/main/LICENSE +# convert as plugin for group manager bots by me youtubeslgeekshow + +import asyncio +from pyrogram import filters +from pyrogram.errors import FloodWait +from Amanda import pbot as bot + + +stoptimer = False + +@bot.on_message(filters.command('settime')) +async def set_timer(client, message): + global stoptimer + try: + if message.chat.id>0: + return await message.reply('⛔️ Try this command in a **group chat**.') + elif not (await client.get_chat_member(message.chat.id,message.from_user.id)).can_manage_chat: + return await message.reply('👮🏻‍♂️ Sorry, **only admins** can execute this command.') + elif len(message.command)<3: + return await message.reply('❌ **Incorrect format.**\n\n✅ Format should be like,\n /set seconds "event"\n\n**Example**:\n /set 86400 "TIME LEFT UNTIL NEW YEAR"') + else: + user_input_time = int(message.command[1]) + user_input_event = str(message.command[2]) + get_user_input_time = await bot.send_message(message.chat.id, user_input_time) + await get_user_input_time.pin() + if stoptimer: stoptimer = False + if 00 and not stoptimer: + s=user_input_time%60 + rose_text='{}\n\n⏳ {:02d}**s**\n\n**s**\n\nBy @TheAmandabot'.format(user_input_event, s) + finish_countdown = await get_user_input_time.edit(rose_text) + await asyncio.sleep(3) + user_input_time -=3 + await finish_countdown.edit("🚨 Beep! Beep!! **TIME'S UP!!!**") + elif 60<=user_input_time<3600: + while user_input_time>0 and not stoptimer: + m=user_input_time%3600//60 + s=user_input_time%60 + rose_text='{}\n\n⏳ {:02d}**m** : {:02d}**s**\n\nBy @TheAmandabot'.format(user_input_event, m, s) + finish_countdown = await get_user_input_time.edit(rose_text) + await asyncio.sleep(3) + user_input_time -=3 + await finish_countdown.edit("🚨 Beep! Beep!! **TIME'S UP!!!**") + elif 3600<=user_input_time<86400: + while user_input_time>0 and not stoptimer: + h=user_input_time%(3600*24)//3600 + m=user_input_time%3600//60 + s=user_input_time%60 + rose_text='{}\n\n⏳ {:02d}**h** : {:02d}**m** : {:02d}**s**\n\nBy @TheAmandabot'.format(user_input_event, h, m, s) + finish_countdown = await get_user_input_time.edit(rose_text) + await asyncio.sleep(7) + user_input_time -=7 + await finish_countdown.edit("🚨 Beep! Beep!! **TIME'S UP!!!**") + elif user_input_time>=86400: + while user_input_time>0 and not stoptimer: + d=user_input_time//(3600*24) + h=user_input_time%(3600*24)//3600 + m=user_input_time%3600//60 + s=user_input_time%60 + rose_text='{}\n\n⏳ {:02d}**d** : {:02d}**h** : {:02d}**m** : {:02d}**s**\n\nBy @TheAmandabot'.format(user_input_event, d, h, m, s) + finish_countdown = await get_user_input_time.edit(rose_text) + await asyncio.sleep(9) + user_input_time -=9 + await finish_countdown.edit("🚨 Beep! Beep!! **TIME'S UP!!!**") + else: + await get_user_input_time.edit(f"🤷🏻‍♂️ I can't countdown from {user_input_time}") + await get_user_input_time.unpin() + except FloodWait as e: + await asyncio.sleep(e.x) + +@bot.on_message(filters.command('stopc')) +async def stop_timer(Client, message): + global stoptimer + try: + if (await bot.get_chat_member(message.chat.id,message.from_user.id)).can_manage_chat: + stoptimer = True + await message.reply(' Countdown stopped.') + else: + await message.reply('👮🏻‍♂️ Sorry, **only admins** can execute this command.') + except FloodWait as e: + await asyncio.sleep(e.x) + +__help__ = """ +@TheAmandabot +❍ /settime [time in seconds] "Title": To use Count Down Timer +❍ /stopc : To stop all countdown timers +""" +__mod_name__ = "Timer" diff --git a/Amanda/modules/__video.py b/Amanda/modules/__video.py new file mode 100644 index 0000000..8db5c79 --- /dev/null +++ b/Amanda/modules/__video.py @@ -0,0 +1,73 @@ +# Copyright (C) 2021 By VeezMusicProject +# thank you i get your codes for my TheAmandabot + +from __future__ import unicode_literals + +import asyncio +import math +import os +import time +import wget +from random import randint +from urllib.parse import urlparse + +import aiofiles +import aiohttp +import requests +import youtube_dl +from yt_dlp import YoutubeDL +from pyrogram import Client, filters +from pyrogram.errors import FloodWait, MessageNotModified +from pyrogram.types import * +from youtube_search import YoutubeSearch + +from Amanda.config import get_str_key +from Amanda import pbot + +@pbot.on_message(filters.command(["video"])) +async def vsong(pbot, message): + ydl_opts = { + 'format':'best', + 'keepvideo':True, + 'prefer_ffmpeg':False, + 'geo_bypass':True, + 'outtmpl':'%(title)s.%(ext)s', + 'quite':True + } + query = " ".join(message.command[1:]) + try: + results = YoutubeSearch(query, max_results=1).to_dict() + link = f"https://youtube.com{results[0]['url_suffix']}" + title = results[0]["title"][:40] + thumbnail = results[0]["thumbnails"][0] + thumb_name = f"thumb{title}.jpg" + thumb = requests.get(thumbnail, allow_redirects=True) + open(thumb_name, "wb").write(thumb.content) + duration = results[0]["duration"] + results[0]["url_suffix"] + results[0]["views"] + rby = message.from_user.mention + except Exception as e: + print(e) + try: + msg = await message.reply("📥 **downloading video...**") + with YoutubeDL(ydl_opts) as ytdl: + ytdl_data = ytdl.extract_info(link, download=True) + file_name = ytdl.prepare_filename(ytdl_data) + except Exception as e: + return await msg.edit(f"❌**YouTube Download Error !*** {str(e)}\n\n Go support chat👉 @slbotzone") + preview = wget.download(thumbnail) + await msg.edit("📤 **uploading video...**") + await message.reply_video( + file_name, + duration=int(ytdl_data["duration"]), + thumb=preview, + caption=ytdl_data['title'], + reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Join updates", url=f"https://t.me/szroseupdates")]])) + try: + os.remove(file_name) + await msg.delete() + except Exception as e: + print(e) + + diff --git a/Amanda/modules/_karma.py b/Amanda/modules/_karma.py new file mode 100644 index 0000000..7695053 --- /dev/null +++ b/Amanda/modules/_karma.py @@ -0,0 +1,143 @@ +from Amanda import pbot +import random +from Amanda.function.dbfun import ( + update_karma, + get_karma, + get_karmas, + int_to_alpha, + alpha_to_int, +) +from Amanda.utils.filter_groups import karma_positive_group, karma_negative_group +from pyrogram import filters + + +regex_upvote = r"^((?i)\+|\+\+|\+1|thx|tnx|ty|thank you|thanx|thanks|pro|cool|good|👍)$" +regex_downvote = r"^(\-|\-\-|\-1|👎)$" + + +@pbot.on_message( + filters.text + & filters.group + & filters.incoming + & filters.reply + & filters.regex(regex_upvote) + & ~filters.via_bot + & ~filters.bot + & ~filters.edited, + group=karma_positive_group, +) +async def upvote(_, message): + if message.reply_to_message.from_user.id == message.from_user.id: + return + chat_id = message.chat.id + user_id = message.reply_to_message.from_user.id + user_mention = message.reply_to_message.from_user.mention + current_karma = await get_karma(chat_id, await int_to_alpha(user_id)) + if current_karma: + current_karma = current_karma["karma"] + karma = current_karma + 1 + new_karma = {"karma": karma} + await update_karma(chat_id, await int_to_alpha(user_id), new_karma) + else: + karma = 1 + new_karma = {"karma": karma} + await update_karma(chat_id, await int_to_alpha(user_id), new_karma) + await message.reply_text( + f"Incremented Karma of {user_mention} By 1 \nTotal Points: {karma}" + ) + + +@pbot.on_message( + filters.text + & filters.group + & filters.incoming + & filters.reply + & filters.regex(regex_downvote) + & ~filters.via_bot + & ~filters.bot + & ~filters.edited, + group=karma_negative_group, +) +async def downvote(_, message): + if message.reply_to_message.from_user.id == message.from_user.id: + return + chat_id = message.chat.id + user_id = message.reply_to_message.from_user.id + user_mention = message.reply_to_message.from_user.mention + current_karma = await get_karma(chat_id, await int_to_alpha(user_id)) + if current_karma: + current_karma = current_karma["karma"] + karma = current_karma - 1 + new_karma = {"karma": karma} + await update_karma(chat_id, await int_to_alpha(user_id), new_karma) + else: + karma = 1 + new_karma = {"karma": karma} + await update_karma(chat_id, await int_to_alpha(user_id), new_karma) + await message.reply_text( + f"Decremented Karma Of {user_mention} By 1 \nTotal Points: {karma}" + ) + + +@pbot.on_message(filters.command("karma") & filters.group) +async def command_karma(_, message): + chat_id = message.chat.id + + if not message.reply_to_message: + m = await message.reply_text("Analyzing Karma...") + karma = await get_karmas(chat_id) + if not karma: + return await m.edit("No karma in DB for this chat.") + msg = f"Karma list of {message.chat.title}" + limit = 0 + karma_dicc = {} + for i in karma: + user_id = await alpha_to_int(i) + user_karma = karma[i]["karma"] + karma_dicc[str(user_id)] = user_karma + karma_arranged = dict( + sorted( + karma_dicc.items(), + key=lambda item: item[1], + reverse=True, + ) + ) + if not karma_dicc: + return await m.edit("No karma in DB for this chat.") + userdb = await get_user_id_and_usernames(pbot) + karma = {} + for user_idd, karma_count in karma_arranged.items(): + if limit > 15: + break + if int(user_idd) not in list(userdb.keys()): + continue + username = userdb[int(user_idd)] + karma[karma_count] = ["@" + username] + limit += 1 + await m.edit(section(msg, karma)) + else: + if not message.reply_to_message.from_user: + return await message.reply("Anon user hash no karma.") + + user_id = message.reply_to_message.from_user.id + karma = await get_karma(chat_id, await int_to_alpha(user_id)) + if karma: + karma = karma["karma"] + await message.reply_text(f"**Total Points**: __{karma}__") + else: + karma = 0 + await message.reply_text(f"**Total Points**: __{karma}__") + + +__help__ = """ +@TheAmandabot +[UPVOTE] - Use upvote keywords like "+", "+1", "thanks" etc to upvote a cb.message. +[DOWNVOTE] - Use downvote keywords like "-", "-1", etc to downvote a cb.message. + +❍ /karma[ON/OFF]: Enable/Disable karma in group. (don't give space) +❍ /karma [Reply to a message]: Check user's karma +❍ /karma: Chek karma list of top 10 users + +""" +__mod_name__ = "ᴋᴀʀᴍᴀ🎪" + diff --git a/Amanda/modules/admin.py b/Amanda/modules/admin.py index ed3f02f..943a00e 100644 --- a/Amanda/modules/admin.py +++ b/Amanda/modules/admin.py @@ -16,10 +16,7 @@ connection_status, user_admin, ) -from Amanda.modules.helper_funcs.extraction import ( - extract_user, - extract_user_and_text, -) +from Amanda.modules.helper_funcs.extraction import extract_user, extract_user_and_text from Amanda.modules.log_channel import loggable @@ -465,16 +462,19 @@ def adminlist(update, context): __help__ = """ - ✪ /admins*:* list of admins in the chat +@TheAmandabot + ❍ /admins*:* list of admins in the chat + *Admins only:* - ✪ /pin*:* silently pins the message replied to - add `'loud'` or `'notify'` to give notifs to users - ✪ /unpin*:* unpins the currently pinned message - ✪ /invitelink*:* gets invitelink - ✪ /promote*:* promotes the user replied to - ✪ /demote*:* demotes the user replied to - ✪ /title *:* sets a custom title for an admin that the bot promoted - ✪ /admincache*:* force refresh the admins list - ✪ /zombies*:* scan and clean zombies + ❍ /pin*:* silently pins the message replied to - add `'loud'` or `'notify'` to give notifs to users + ❍ /unpin*:* unpins the currently pinned message + ❍ /invitelink*:* gets invitelink + ❍ /promote*:* promotes the user replied to + ❍ /demote*:* demotes the user replied to + ❍ /title <title here>*:* sets a custom title for an admin that the bot promoted + ❍ /admincache*:* force refresh the admins list + ❍ /zombies*:* scan and clean zombies + """ ADMINLIST_HANDLER = DisableAbleCommandHandler("admins", adminlist) @@ -501,7 +501,7 @@ def adminlist(update, context): dispatcher.add_handler(SET_TITLE_HANDLER) dispatcher.add_handler(ADMIN_REFRESH_HANDLER) -__mod_name__ = "Admin" +__mod_name__ = "ᴀᴅᴍɪɴ🎖️" __command_list__ = [ "adminlist", "admins", diff --git a/Amanda/modules/afk.py b/Amanda/modules/afk.py index b56aa9d..a40bbf2 100644 --- a/Amanda/modules/afk.py +++ b/Amanda/modules/afk.py @@ -6,10 +6,7 @@ from telegram.ext import CallbackContext, Filters, MessageHandler, run_async from Amanda import dispatcher -from Amanda.modules.disable import ( - DisableAbleCommandHandler, - DisableAbleMessageHandler, -) +from Amanda.modules.disable import DisableAbleCommandHandler, DisableAbleMessageHandler from Amanda.modules.sql import afk_sql as sql from Amanda.modules.users import get_user_id @@ -150,9 +147,12 @@ def check_afk(update, context, user_id, fst_name, userc_id): __help__ = """ - • `/afk <reason>`*:* mark yourself as AFK(away from keyboard). - • `brb <reason>`*:* same as the afk command - but not a command. -When marked as AFK, any mentions will be replied to with a message to say you're not available! +@TheAmandabot + ❍ /afk <reason> *:* mark yourself as AFK(away from keyboard). + ❍ /brb <reason> *:* same as the afk command - but not a command. + + When marked as AFK, any mentions will be replied to with a message to say you're not available! + """ AFK_HANDLER = DisableAbleCommandHandler("afk", afk) @@ -167,7 +167,7 @@ def check_afk(update, context, user_id, fst_name, userc_id): dispatcher.add_handler(NO_AFK_HANDLER, AFK_GROUP) dispatcher.add_handler(AFK_REPLY_HANDLER, AFK_REPLY_GROUP) -__mod_name__ = "AFK" +__mod_name__ = "ᴀꜰᴋ🥡" __command_list__ = ["afk"] __handlers__ = [ (AFK_HANDLER, AFK_GROUP), diff --git a/Amanda/modules/animation.py b/Amanda/modules/animation.py new file mode 100644 index 0000000..2d2df9a --- /dev/null +++ b/Amanda/modules/animation.py @@ -0,0 +1,178 @@ +import time +from typing import List + +from telegram import Update +from telegram.ext import run_async,CallbackContext + +from Amanda import dispatcher +from Amanda.modules.disable import DisableAbleCommandHandler +from Amanda.modules.helper_funcs.chat_status import user_admin + + + + +EDIT_SLEEP = 1 + +EDIT_TIMES = 10 + + + + + + +EDIT_SLEEP = 1 + +EDIT_TIMES = 9 + + + + + + + + +EDIT_SLEEP = 1 + +EDIT_TIMES = 10 + + + + +EDIT_SLEEP = 1 + +EDIT_TIMES = 12 + + + + + +kill_you = [ + "Fiiiiire", + "( ・ิω・ิ)︻デ═一-->", + "---->____________⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠", + "------>__________⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠", + "-------->⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠_________", + "---------->⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠_______", + "------------>⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠_____", + "-------------->⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠⁠____", + "------------------>", + "------>;(^。^)ノ", + "( ̄ー ̄) DED", + "**Target killed successfully" +] + + + + +love_siren = [ + "❤️❤️❤️🧡🧡🧡💚💚💚\n💙💙💙💜💜💜🖤🖤🖤", + "🖤🖤🖤💜💜💜💙💙💙\n❤️❤️❤️🧡🧡🧡💚💚💚", + "💛💛💛💙💙💙❤️❤️❤️\n💜💜💜❤️❤️❤️🧡🧡🧡", + "❤️❤️❤️🧡🧡🧡💚💚💚\n💙💙💙💜💜💜🖤🖤🖤", + "🖤🖤🖤💜💜💜💙💙💙\n❤️❤️❤️🧡🧡🧡💚💚💚", + "💛💛💛💙💙💙❤️❤️❤️\n💜💜💜❤️❤️❤️🧡🧡🧡", + "❤️❤️❤️🧡🧡🧡💚💚💚\n💙💙💙💜💜💜🖤🖤🖤", + "🖤🖤🖤💜💜💜💙💙💙\n❤️❤️❤️🧡🧡🧡💚💚💚", + "💛💛💛💙💙💙❤️❤️❤️\n💜💜💜❤️❤️❤️🧡🧡🧡" +] + + +hack_you = [ + "Looking for TG databases in targeted person...", + " User online: True\nTelegram access: True\nRead Storage: True ", + "Hacking... 20.63%\n[███░░░░░░░░░░░░░░░░░]", + "Hacking... 86.21%\n[███████████████░░░░░]", + "Hacking... 93.50%\n[█████████████████░░░]", + "hacking.... 100%\n[████████████████████]", +] + + + + +bomb_ettu = [ + "▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️", + "💣💣💣💣\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️", + "▪️▪️▪️▪️\n💣💣💣💣\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️", + "▪️▪️▪️▪️\n▪️▪️▪️▪️\n💣💣💣💣\n▪️▪️▪️▪️\n▪️▪️▪️▪️", + "▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n💣💣💣💣\n▪️▪️▪️▪️", + "▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n💣💣💣💣", + "▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n💥💥💥💥", + "▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n💥💥💥💥\n💥💥💥💥", + "▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n▪️▪️▪️▪️\n😵😵😵😵", +] + + + + +@user_admin +@run_async +def bombs(update: Update, context: CallbackContext): + bot,args = context.bot, context.args + msg = update.effective_message.reply_text('💣') + for x in range(EDIT_TIMES): + msg.edit_text(bomb_ettu[x%9]) + time.sleep(EDIT_SLEEP) + msg.edit_text('BOOOM...BOO') + + + +@user_admin +@run_async +def hack(update: Update, context: CallbackContext): + bot, args = context.bot, context.args + msg = update.effective_message.reply_text('Target selected') + for x in range(EDIT_TIMES): + msg.edit_text(hack_you[x%5]) + time.sleep(EDIT_SLEEP) + msg.edit_text('successful hacked all data send on my Database') + + + + + +@user_admin +@run_async +def love(update: Update, context: CallbackContext): + bot, args = context.bot, context.args + msg = update.effective_message.reply_text('❣️') + for x in range(EDIT_TIMES): + msg.edit_text(love_siren[x%5]) + time.sleep(EDIT_SLEEP) + msg.edit_text('True Love💞') + + + + +@user_admin +@run_async +def kill(update: Update, context: CallbackContext): + bot, args = context.bot, context.args + msg = update.effective_message.reply_text('🔫') + for x in range(EDIT_TIMES): + msg.edit_text(kill_you[x%12]) + time.sleep(EDIT_SLEEP) + msg.edit_text('⚰') + +__help__ = """ +@TheAmandabot + ❍ /kill + ❍ /bombs + ❍ /hack + ❍ /love +""" +__mod_name__ = "ᴀɴɪᴍᴀᴛɪᴏɴ🎞️" + +KILL_HANDLER = DisableAbleCommandHandler("kill",kill) +LOVE_HANDLER = DisableAbleCommandHandler("love", love) +HACK_HANDLER = DisableAbleCommandHandler("hack", hack) +BOMBS_HANDLER = DisableAbleCommandHandler("bombs",bombs) +dispatcher.add_handler(KILL_HANDLER) +dispatcher.add_handler(LOVE_HANDLER) +dispatcher.add_handler(HACK_HANDLER) +dispatcher.add_handler(BOMBS_HANDLER) + +__command_list__ = ["love", "hack", "bombs", "kill"] + +__command_list__ = ["love", "hack", "bombs","kill"] + +__handlers__ = [LOVE_HANDLER, HACK_HANDLER, BOMBS_HANDLER, KILL_HANDLER] diff --git a/Amanda/modules/anime.py b/Amanda/modules/anime.py index 8e6dad6..6972314 100644 --- a/Amanda/modules/anime.py +++ b/Amanda/modules/anime.py @@ -570,17 +570,19 @@ def kayo(update: Update, context: CallbackContext): __help__ = """ +@TheAmandabot Get information about anime, manga or characters from [AniList](anilist.co). *Available commands:* - • `/anime <anime>`*:* returns information about the anime. - • `/character <character>`*:* returns information about the character. - • `/animequote` *:* Get random Anime qoute. - • `/manga <manga>`*:* returns information about the manga. - • `/user <user>`*:* returns information about a MyAnimeList user. - • `/upcoming`*:* returns a list of new anime in the upcoming seasons. - • `/kaizoku <anime>`*:* search an anime on animekaizoku.com - • `/kayo <anime>`*:* search an anime on animekayo.com - • `/airing <anime>`*:* returns anime airing info. + ❍ `/anime <anime>`*:* returns information about the anime. + ❍ `/character <character>`*:* returns information about the character. + ❍ `/animequote` *:* Get random Anime qoute. + ❍ `/manga <manga>`*:* returns information about the manga. + ❍ `/user <user>`*:* returns information about a MyAnimeList user. + ❍ `/upcoming`*:* returns a list of new anime in the upcoming seasons. + ❍ `/kaizoku <anime>`*:* search an anime on animekaizoku.com + ❍ `/kayo <anime>`*:* search an anime on animekayo.com + ❍ `/airing <anime>`*:* returns anime airing info. + """ ANIME_HANDLER = DisableAbleCommandHandler("anime", anime) @@ -625,4 +627,4 @@ def kayo(update: Update, context: CallbackContext): BUTTON_HANDLER, AIRING_HANDLER, ] -__mod_name__ = "Anime" +__mod_name__ = "ᴀɴɪᴍᴇ✨" diff --git a/Amanda/modules/animequotes.py b/Amanda/modules/animequotes.py index ec7ae55..178b004 100644 --- a/Amanda/modules/animequotes.py +++ b/Amanda/modules/animequotes.py @@ -1,4 +1,4 @@ -# Made By @TharukRenuja On Telegram & Github Id TR-TECH-GUIDE +# Made By @Madepranav On Telegram & Github Id Superboyfan import random from telegram import Update diff --git a/Amanda/modules/antiflood.py b/Amanda/modules/antiflood.py index f40371d..e8a4e67 100644 --- a/Amanda/modules/antiflood.py +++ b/Amanda/modules/antiflood.py @@ -395,13 +395,14 @@ def __chat_settings__(chat_id, user_id): __help__ = """ -You know how sometimes, people join, send 100 messages, and ruin your chat? With antiflood, that happens no more! -Antiflood allows you to take action on users that send more than x messages in a row. Exceeding the set flood \ -will result in restricting that user. - ✪ /flood*:* Get the current flood control setting -*Admin only:* - ✪ /setflood <int/'no'/'off'>: enables or disables flood control - ✪ /setfloodmode <ban/kick/mute/tban/tmute> <value>: Action to perform when user have exceeded flood limit. ban/kick/mute/tmute/tban +@TheAmandabot + You know how sometimes, people join, send 100 messages, and ruin your chat? With antiflood, that happens no more! + Antiflood allows you to take action on users that send more than x messages in a row. Exceeding the set flood \ + will result in restricting that user. + ❍ /flood*:* Get the current flood control setting + *Admin only:* + ❍ /setflood <int/'no'/'off'>: enables or disables flood control + ❍ /setfloodmode <ban/kick/mute/tban/tmute> <value>: Action to perform when user have exceeded flood limit. ban/kick/mute/tmute/tban Note: - Value must be filled for tban and tmute! It can be: @@ -409,9 +410,11 @@ def __chat_settings__(chat_id, user_id): 6h = 6 hours 3d = 3 days 1w = 1 week + + """ -__mod_name__ = "Antiflood" +__mod_name__ = "ᴀɴᴛɪꜰʟᴏᴏᴅ📡" FLOOD_BAN_HANDLER = MessageHandler( Filters.all & ~Filters.status_update & Filters.group, check_flood diff --git a/Amanda/modules/approve.py b/Amanda/modules/approve.py index ad26556..cea9470 100644 --- a/Amanda/modules/approve.py +++ b/Amanda/modules/approve.py @@ -198,15 +198,19 @@ def unapproveall_btn(update: Update, context: CallbackContext): __help__ = """ +@TheAmandabot Sometimes, you might trust a user not to send unwanted content. Maybe not enough to make them admin, but you might be ok with locks, blacklists, and antiflood not applying to them. + That's what approvals are for - approve of trustworthy users to allow them to send + *Admin commands:* -- `/approval`*:* Check a user's approval status in this chat. -- `/approve`*:* Approve of a user. Locks, blacklists, and antiflood won't apply to them anymore. -- `/unapprove`*:* Unapprove of a user. They will now be subject to locks, blacklists, and antiflood again. -- `/approved`*:* List all approved users. -- `/unapproveall`*:* Unapprove *ALL* users in a chat. This cannot be undone. +❍ `/approval`*:* Check a user's approval status in this chat. +❍ `/approve`*:* Approve of a user. Locks, blacklists, and antiflood won't apply to them anymore. +❍ `/unapprove`*:* Unapprove of a user. They will now be subject to locks, blacklists, and antiflood again. +❍ `/approved`*:* List all approved users. +❍ `/unapproveall`*:* Unapprove *ALL* users in a chat. This cannot be undone. + """ APPROVE = DisableAbleCommandHandler("approve", approve) @@ -223,6 +227,6 @@ def unapproveall_btn(update: Update, context: CallbackContext): dispatcher.add_handler(UNAPPROVEALL) dispatcher.add_handler(UNAPPROVEALL_BTN) -__mod_name__ = "Approval" +__mod_name__ = "ᴀᴘᴘʀᴏᴠᴀʟ🥡" __command_list__ = ["approve", "unapprove", "approved", "approval"] __handlers__ = [APPROVE, DISAPPROVE, APPROVED, APPROVAL] diff --git a/Amanda/modules/attendance.py b/Amanda/modules/attendance.py new file mode 100644 index 0000000..fdef0b2 --- /dev/null +++ b/Amanda/modules/attendance.py @@ -0,0 +1,145 @@ +# ©youtubesgeekshow TheAmandabot only + +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ParseMode +from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, Filters +from telegram.utils.helpers import mention_markdown, escape_markdown + +from Amanda import dispatcher +from Amanda.modules.disable import DisableAbleCommandHandler +from Amanda.modules.helper_funcs.chat_status import user_admin, user_admin_no_reply + + +@user_admin +def start_attendance(update, context): + if ('flag' in context.chat_data) and (context.chat_data['flag'] == 1): + update.message.reply_text( + "Please close the current attendance first", + ) + elif ('flag' not in context.chat_data) or (context.chat_data['flag'] == 0): + context.chat_data['flag'] = 1 + context.chat_data['attendees'] = {} + context.chat_data['id'] = update.effective_chat.id + keyboard = [ + [ + InlineKeyboardButton( + "Present", + callback_data='present', + ), + ], + [ + InlineKeyboardButton( + "End Attendance ", + callback_data='end_attendance', + ), + ], + ] + reply_markup = InlineKeyboardMarkup(keyboard) + context.chat_data['message'] = update.message.reply_text( + "Please mark your attendance 👮‍♀️", reply_markup=reply_markup, + ) + + +def mark_attendance(update, context): + query = update.callback_query + if ( + str(update.effective_user.id) not in + context.chat_data['attendees'].keys() + ): + context.chat_data['attendees'][ + update.effective_user.id + ] = f'{escape_markdown(update.effective_user.full_name)}' + context.bot.answer_callback_query( + callback_query_id=query.id, + text="Your attendance has been marked📝", + show_alert=True, + ) + else: + context.bot.answer_callback_query( + callback_query_id=query.id, + text="Your attendance is already marked🔐", + show_alert=True, + ) + query.answer() + + +@user_admin_no_reply +def end_attendance(update, context): + query = update.callback_query + query.answer() + if (context.chat_data['id'] != update.effective_chat.id): + return + if len(context.chat_data['attendees'].items()) > 0: + attendee_list = "\n- ".join([ + mention_markdown(id, name) + for id, name in context.chat_data['attendees'].items() + ]) + context.bot.edit_message_text( + text="Attendance is over. " + + str(len(context.chat_data['attendees'])) + + " member(s) marked attendance.\n" + + "Here is the list:\n- " + attendee_list, + chat_id=context.chat_data['message'].chat_id, + message_id=context.chat_data['message'].message_id, + parse_mode=ParseMode.MARKDOWN, + ) + else: + context.bot.edit_message_text( + text="Attendance is over. No one was present.", + chat_id=context.chat_data['message'].chat_id, + message_id=context.chat_data['message'].message_id, + ) + context.chat_data['flag'] = 0 + context.chat_data['attendees'].clear() + +@user_admin +def end_attendance_cmd(update, context): + if ('flag' not in context.chat_data) and (context.chat_data['flag'] != 1): + update.message.reply_text( + "No Attendance is goind on.", + ) + else: + if (context.chat_data['id'] != update.effective_chat.id): + return + if len(context.chat_data['attendees'].items()) > 0: + attendee_list = "\n- ".join([ + mention_markdown(id, name) + for id, name in context.chat_data['attendees'].items() + ]) + context.bot.edit_message_text( + text="Attendance is over. " + + str(len(context.chat_data['attendees'])) + + " members marked attendance.\n" + + "Here is the list:\n- " + attendee_list, + chat_id=context.chat_data['message'].chat_id, + message_id=context.chat_data['message'].message_id, + parse_mode=ParseMode.MARKDOWN, + ) + else: + context.bot.edit_message_text( + text="Attendance is over. No one was present.", + chat_id=context.chat_data['message'].chat_id, + message_id=context.chat_data['message'].message_id, + ) + context.chat_data['flag'] = 0 + context.chat_data['attendees'].clear() + +__help__ = """ +@TheAmandabot + ❍ /attendance :Start the attendance + ❍ /end_attendance : End the attendance +""" +__mod_name__ = "ᴀᴛᴛᴇɴᴅᴀɴᴄᴇ🧲" + +START_ATTENDANCE = DisableAbleCommandHandler("attendance", start_attendance) +MARK_ATTENDANCE = CallbackQueryHandler(mark_attendance, pattern="present") +END_ATTENDANCE = CallbackQueryHandler(end_attendance, pattern="end_attendance") +END_ATTENDANCE_CMD = DisableAbleCommandHandler("end_attendance", end_attendance_cmd) + +dispatcher.add_handler(START_ATTENDANCE) +dispatcher.add_handler(MARK_ATTENDANCE) +dispatcher.add_handler(END_ATTENDANCE) +dispatcher.add_handler(END_ATTENDANCE_CMD) + + +__command_list__ = ["attendance", "end_attendance"] +__handlers__ = [START_ATTENDANCE, END_ATTENDANCE, END_ATTENDANCE_CMD] diff --git a/Amanda/modules/backups.py b/Amanda/modules/backups.py index 1b273e3..40e9ff2 100644 --- a/Amanda/modules/backups.py +++ b/Amanda/modules/backups.py @@ -103,7 +103,7 @@ def import_data(update, context): ) LOGGER.exception( - "Import for the chat %s with the name %s failed.", + "Imprt for the chat %s with the name %s failed.", str(chat.id), str(chat.title), ) @@ -372,16 +372,17 @@ def get_chat(chat_id, chat_data): return {"status": False, "value": False} -__mod_name__ = "Backups" +__mod_name__ = "ʙᴀᴄᴋᴜᴘ📩" __help__ = """ +@TheAmandabot *Only for group owner:* - ✪ `/import`: Reply to the backup file for the butler / emilia group to import as much as possible, making transfers very easy! \ + ❍ `/import`: Reply to the backup file for the butler / emilia group to import as much as possible, making transfers very easy! \ Note that files / photos cannot be imported due to telegram restrictions. - ✪ `/export`: Export group data, which will be exported are: rules, notes (documents, images, music, video, audio, voice, text, text buttons) \ - + ❍ `/export`: Export group data, which will be exported are: rules, notes (documents, images, music, video, audio, voice, text, text buttons) \ + """ IMPORT_HANDLER = CommandHandler("import", import_data) diff --git a/Amanda/modules/bans.py b/Amanda/modules/bans.py index d3f9adb..a73fcf6 100644 --- a/Amanda/modules/bans.py +++ b/Amanda/modules/bans.py @@ -48,25 +48,25 @@ def ban(update: Update, context: CallbackContext) -> str: user_id, reason = extract_user_and_text(message, args) if not user_id: - message.reply_text("I doubt that's a user.") + message.reply_text("You don't seem to be referring to a user.") return log_message try: member = chat.get_member(user_id) except BadRequest as excp: if excp.message == "User not found": - message.reply_text("Can't seem to find this person.") + message.reply_text("Can't seem to find this user.") return log_message else: raise if user_id == bot.id: - message.reply_text("Oh yeah, ban myself, noob!") + message.reply_text("I'm not gonna BAN myself, are you crazy?") return log_message if is_user_ban_protected(chat, user_id, member) and user not in DEV_USERS: if user_id == OWNER_ID: - message.reply_text("Trying to put me against a God level disaster huh?") + message.reply_text("Are you mad supun is my owner I will fuck you 😈") return log_message elif user_id in DEV_USERS: message.reply_text("I can't act against our own.") @@ -96,8 +96,8 @@ def ban(update: Update, context: CallbackContext) -> str: log = ( f"<b>{html.escape(chat.title)}:</b>\n" f"#BANNED\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - f"<b>User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" + f"<b>•Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" + f"<b>•User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" ) if reason: log += "\n<b>Reason:</b> {}".format(reason) @@ -106,11 +106,11 @@ def ban(update: Update, context: CallbackContext) -> str: chat.kick_member(user_id) # bot.send_sticker(chat.id, BAN_STICKER) # banhammer marie sticker reply = ( - f"<code>❕</code><b>Ban Event</b>\n" - f"<code> </code><b>• User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" + f"<code>❕</code><b>Ban Event</b> wow 🤣\n" + f"<code> </code><b> 😏User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))} By me @TheAmandabot" ) if reason: - reply += f"\n<code> </code><b>• Reason:</b> \n{html.escape(reason)}" + reply += f"\n<code> </code><b>😕 Reason:</b> \n{html.escape(reason)} @TheAmandabot" bot.sendMessage(chat.id, reply, parse_mode=ParseMode.HTML, quote=False) return log @@ -172,8 +172,8 @@ def sban(update: Update, context: CallbackContext) -> str: log = ( f"<b>{html.escape(chat.title)}:</b>\n" f"#SBANNED\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - f"<b>User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" + f"<b>•Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" + f"<b>•User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}" ) if reason: log += "\n<b>Reason:</b> {}".format(reason) @@ -253,9 +253,9 @@ def temp_ban(update: Update, context: CallbackContext) -> str: log = ( f"<b>{html.escape(chat.title)}:</b>\n" "#TEMP BANNED\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - f"<b>User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}\n" - f"<b>Time:</b> {time_val}" + f"<b>•Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" + f"<b>•User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}\n" + f"<b>•Time:</b> {time_val}" ) if reason: log += "\n<b>Reason:</b> {}".format(reason) @@ -344,9 +344,9 @@ def stemp_ban(update: Update, context: CallbackContext) -> str: log = ( f"<b>{html.escape(chat.title)}:</b>\n" "#STEMP BANNED\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - f"<b>User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}\n" - f"<b>Time:</b> {time_val}" + f"<b>•Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" + f"<b>•User:</b> {mention_html(member.user.id, html.escape(member.user.first_name))}\n" + f"<b>•Time:</b> {time_val}" ) if reason: log += "\n<b>Reason:</b> {}".format(reason) @@ -594,20 +594,22 @@ def selfunban(context: CallbackContext, update: Update) -> str: __help__ = """ +@TheAmandabot *Kicks:* - ✪ /kick <userhandle>*:* Kicks a user out of the group, (via handle, or reply) - ✪ /skick <userhandle>*:* Silently kicks a user out of the group, (via handle, or reply) - ✪ /kickme*:* Kicks the user who used the command. + ❍ /kick <userhandle>*:* Kicks a user out of the group, (via handle, or reply) + ❍ /skick <userhandle>*:* Silently kicks a user out of the group, (via handle, or reply) + ❍ /kickme*:* Kicks the user who used the command. *Bans:* - ✪ /ban <userhandle>*:* Bans a user. (via handle, or reply) - ✪ /sban <userhandle>*:* Silently bans a user without leaving any message. (via handle, or reply) - ✪ /tban <userhandle> x(m/h/d)*:* Bans a user for `x` time. (via handle, or reply). `m` = `minutes`, `h` = `hours`, `d` = `days`. - ✪ /stban <userhandle> x(m/h/d)*:* Silently bans a user for `x` time. (via handle, or reply). `m` = `minutes`, `h` = `hours`, `d` = `days`. - ✪ /unban <userhandle>*:* Unbans a user. (via handle, or reply) + ❍ /ban <userhandle>*:* Bans a user. (via handle, or reply) + ❍ /sban <userhandle>*:* Silently bans a user without leaving any message. (via handle, or reply) + ❍ /tban <userhandle> x(m/h/d)*:* Bans a user for `x` time. (via handle, or reply). `m` = `minutes`, `h` = `hours`, `d` = `days`. + ❍ /stban <userhandle> x(m/h/d)*:* Silently bans a user for `x` time. (via handle, or reply). `m` = `minutes`, `h` = `hours`, `d` = `days`. + ❍ /unban <userhandle>*:* Unbans a user. (via handle, or reply) _NOTE:_ If you set Log Channels, you will get logs of Silent kick and bans. Check *Logger* module to know more about Log Channel. + """ BAN_HANDLER = CommandHandler("ban", ban) @@ -630,7 +632,7 @@ def selfunban(context: CallbackContext, update: Update) -> str: dispatcher.add_handler(KICKME_HANDLER) dispatcher.add_handler(SBAN_HANDLER) -__mod_name__ = "Bans" +__mod_name__ = "ʙᴀɴ🚫" __handlers__ = [ BAN_HANDLER, TEMPBAN_HANDLER, diff --git a/Amanda/modules/blacklist.py b/Amanda/modules/blacklist.py index 471a646..f79168c 100644 --- a/Amanda/modules/blacklist.py +++ b/Amanda/modules/blacklist.py @@ -449,16 +449,18 @@ def __stats__(): ) -__mod_name__ = "Blacklists" +__mod_name__ = "ʙʟᴀᴄᴋʟɪꜱᴛꜱ⛔" __help__ = """ +@TheAmandabot Blacklists are used to stop certain triggers from being said in a group. Any time the trigger is mentioned, the message will immediately be deleted. A good combo is sometimes to pair this up with warn filters! *NOTE*: Blacklists do not affect group admins. - • `/blacklist`*:* View the current blacklisted words. + ❍ `/blacklist`*:* View the current blacklisted words. Admin only: - • `/addblacklist <triggers>`*:* Add a trigger to the blacklist. Each line is considered one trigger, so using different lines will allow you to add multiple triggers. - • `/unblacklist <triggers>`*:* Remove triggers from the blacklist. Same newline logic applies here, so you can remove multiple triggers at once. - • `/blacklistmode <off/del/warn/ban/kick/mute/tban/tmute>`*:* Action to perform when someone sends blacklisted words. + ❍ `/addblacklist <triggers>`*:* Add a trigger to the blacklist. Each line is considered one trigger, so using different lines will allow you to add multiple triggers. + ❍ `/unblacklist <triggers>`*:* Remove triggers from the blacklist. Same newline logic applies here, so you can remove multiple triggers at once. + ❍ `/blacklistmode <off/del/warn/ban/kick/mute/tban/tmute>`*:* Action to perform when someone sends blacklisted words. + """ BLACKLIST_HANDLER = DisableAbleCommandHandler( "blacklist", blacklist, pass_args=True, admin_ok=True diff --git a/Amanda/modules/blacklist_stickers.py b/Amanda/modules/blacklist_stickers.py index b7d478f..afa755b 100644 --- a/Amanda/modules/blacklist_stickers.py +++ b/Amanda/modules/blacklist_stickers.py @@ -502,19 +502,21 @@ def __stats__(): __help__ = """ +@TheAmandabot Blacklist sticker is used to stop certain stickers. Whenever a sticker is sent, the message will be deleted immediately. *NOTE:* Blacklist stickers do not affect the group admin - • `/blsticker`*:* See current blacklisted sticker + ❍ `/blsticker`*:* See current blacklisted sticker *Only admin:* - • `/addblsticker <sticker link>`*:* Add the sticker trigger to the black list. Can be added via reply sticker - • `/unblsticker <sticker link>`*:* Remove triggers from blacklist. The same newline logic applies here, so you can delete multiple triggers at once - • `/rmblsticker <sticker link>`*:* Same as above - • `/blstickermode <ban/tban/mute/tmute>`*:* sets up a default action on what to do if users use blacklisted stickers + ❍ `/addblsticker <sticker link>`*:* Add the sticker trigger to the black list. Can be added via reply sticker + ❍ `/unblsticker <sticker link>`*:* Remove triggers from blacklist. The same newline logic applies here, so you can delete multiple triggers at once + ❍ `/rmblsticker <sticker link>`*:* Same as above + ❍ `/blstickermode <ban/tban/mute/tmute>`*:* sets up a default action on what to do if users use blacklisted stickers Note: - • `<sticker link>` can be `https://t.me/addstickers/<sticker>` or just `<sticker>` or reply to the sticker message + ❍ `<sticker link>` can be `https://t.me/addstickers/<sticker>` or just `<sticker>` or reply to the sticker message + """ -__mod_name__ = "Stickers Blacklist" +__mod_name__ = "ʙ-ꜱᴛɪᴄᴋᴇʀꜱ📵" BLACKLIST_STICKER_HANDLER = DisableAbleCommandHandler( "blsticker", blackliststicker, admin_ok=True diff --git a/Amanda/modules/blacklistusers.py b/Amanda/modules/blacklistusers.py index b77026d..e50ea40 100644 --- a/Amanda/modules/blacklistusers.py +++ b/Amanda/modules/blacklistusers.py @@ -1,4 +1,4 @@ -# Module to blacklist users and prevent them from using commands by @TheRayaRobot +# Module to blacklist users and prevent them from using commands by @TheRealPhoenix import html from telegram import ParseMode, Update @@ -9,10 +9,7 @@ import Amanda.modules.sql.blacklistusers_sql as sql from Amanda import DEMONS, DEV_USERS, DRAGONS, OWNER_ID, TIGERS, WOLVES, dispatcher from Amanda.modules.helper_funcs.chat_status import dev_plus -from Amanda.modules.helper_funcs.extraction import ( - extract_user, - extract_user_and_text, -) +from Amanda.modules.helper_funcs.extraction import extract_user, extract_user_and_text from Amanda.modules.log_channel import gloggable BLACKLISTWHITELIST = [OWNER_ID] + DEV_USERS + DRAGONS + WOLVES + DEMONS diff --git a/Amanda/modules/boo.py b/Amanda/modules/boo.py new file mode 100644 index 0000000..346e0ed --- /dev/null +++ b/Amanda/modules/boo.py @@ -0,0 +1,6 @@ +__help__ = """ +@TheAmandabot +** book ** + ❍ /book <book name > : Search any book useing this bot +""" +__mod_name__ = "ʙᴏᴏᴋꜱ📚" diff --git a/Amanda/modules/book.py b/Amanda/modules/book.py new file mode 100644 index 0000000..b891c30 --- /dev/null +++ b/Amanda/modules/book.py @@ -0,0 +1,77 @@ +# Copyright (C) 2020 DevsExpo +# Copyright (C) 2021 Inuka Asith +# Copyright (C) 2021 TeamDaisyX + +# This file is part of Daisy (Telegram Bot) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +import os +import re + +import requests +from bs4 import BeautifulSoup +from telethon import events + +from Amanda import telethn as tbot + +@tbot.on(events.NewMessage(pattern="^/book (.*)")) +async def _(event): + if event.fwd_from: + return + input_str = event.pattern_match.group(1) + lool = 0 + KkK = await event.reply("`searching for the book...`") + lin = "https://b-ok.cc/s/" + text = input_str + link = lin + text + + headers = [ + "User-Agent", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0", + ] + page = requests.get(link) + soup = BeautifulSoup(page.content, "html.parser") + f = open("book.txt", "w") + total = soup.find(class_="totalCounter") + for nb in total.descendants: + nbx = nb.replace("(", "").replace(")", "") + if nbx == "0": + await event.reply("No Books Found with that name.") + else: + + for tr in soup.find_all("td"): + for td in tr.find_all("h3"): + for ts in td.find_all("a"): + title = ts.get_text() + lool = lool + 1 + for ts in td.find_all("a", attrs={"href": re.compile("^/book/")}): + ref = ts.get("href") + link = "https://b-ok.cc" + ref + + f.write("\n" + title) + f.write("\nBook link:- " + link + "\n\n") + + f.write("By @TheAmandabot.") + f.close() + caption = "A collabration with Friday.\n Join Support @slbotzone" + + await tbot.send_file( + event.chat_id, + "book.txt", + caption=f"**BOOKS GATHERED SUCCESSFULLY!**\n\nBY @TheAmandabot.\n\n JOIN THE UPDATE 👉 @szroseupdates.", + ) + os.remove("book.txt") + await KkK.delete() diff --git a/Amanda/modules/callback.py b/Amanda/modules/callback.py index 24d4aef..4799038 100644 --- a/Amanda/modules/callback.py +++ b/Amanda/modules/callback.py @@ -1,5 +1,6 @@ from pyrogram import filters +# Credits @Pokurt def callback_data(data): diff --git a/Amanda/modules/caption_editor.py b/Amanda/modules/caption_editor.py new file mode 100644 index 0000000..3d810a0 --- /dev/null +++ b/Amanda/modules/caption_editor.py @@ -0,0 +1,41 @@ +# Copyright (C) 2021 TeamDaisyX + + +# This file is part of Daisy (Telegram Bot) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from pyrogram import filters +from pyrogram.errors import RPCError + +from Amanda.function.pluginhelpers import admins_only, get_text +from Amanda.services.pyrogram import pbot + + +@pbot.on_message( + filters.command("cedit") & ~filters.edited & ~filters.bot & ~filters.private +) +@admins_only +async def loltime(client, message): + lol = await message.reply("Processing please wait") + cap = get_text(message) + if not message.reply_to_message: + await lol.edit("reply to any message to edit caption") + reply = message.reply_to_message + try: + await reply.copy(message.chat.id, caption=cap) + await lol.delete() + except RPCError as i: + await lol.edit(i) + return diff --git a/Amanda/modules/chatbot.py b/Amanda/modules/chatbot.py index 27dc9de..4b848de 100644 --- a/Amanda/modules/chatbot.py +++ b/Amanda/modules/chatbot.py @@ -1,178 +1,116 @@ -import html - -# AI module using Intellivoid's Coffeehouse API by @TheRayaRobot -from time import sleep, time - -from coffeehouse.api import API -from coffeehouse.exception import CoffeeHouseError as CFError -from coffeehouse.lydia import LydiaAI -from telegram import Update -from telegram.error import BadRequest, RetryAfter, Unauthorized -from telegram.ext import ( - CallbackContext, - CommandHandler, - Filters, - MessageHandler, - run_async, -) -from telegram.utils.helpers import mention_html - -import Amanda.modules.sql.chatbot_sql as sql -from Amanda import AI_API_KEY, SUPPORT_CHAT, dispatcher -from Amanda.modules.helper_funcs.chat_status import user_admin -from Amanda.modules.helper_funcs.filters import CustomFilters -from Amanda.modules.log_channel import gloggable - -CoffeeHouseAPI = API(AI_API_KEY) -api_client = LydiaAI(CoffeeHouseAPI) - - -@run_async -@user_admin -@gloggable -def add_chat(update: Update, context: CallbackContext): - global api_client - chat = update.effective_chat - msg = update.effective_message - user = update.effective_user - is_chat = sql.is_chat(chat.id) - if chat.type == "private": - msg.reply_text("You can't enable AI in PM.") - return - - if not is_chat: - ses = api_client.create_session() - ses_id = str(ses.id) - expires = str(ses.expires) - sql.set_ses(chat.id, ses_id, expires) - msg.reply_text("AI successfully enabled for this chat!") - message = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#AI_ENABLED\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - ) - return message - else: - msg.reply_text("AI is already enabled for this chat!") - return "" - - -@run_async -@user_admin -@gloggable -def remove_chat(update: Update, context: CallbackContext): - msg = update.effective_message - chat = update.effective_chat - user = update.effective_user - is_chat = sql.is_chat(chat.id) - if not is_chat: - msg.reply_text("AI isn't enabled here in the first place!") - return "" - else: - sql.rem_chat(chat.id) - msg.reply_text("AI disabled successfully!") - message = ( - f"<b>{html.escape(chat.title)}:</b>\n" - f"#AI_DISABLED\n" - f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n" - ) - return message - - -def check_message(context: CallbackContext, message): - reply_msg = message.reply_to_message +import requests, functools +from Amanda import pbot, BOT_ID +from pyrogram import filters +from Amanda.modules.sql.chatbot_sql import is_chatbot_indb, addchatbot, rmchatbot +from googletrans import Translator + +tr = Translator() + +def is_admin(func): + @functools.wraps(func) + async def oops(client,message): + is_admin = False + try: + user = await message.chat.get_member(message.from_user.id) + admin_strings = ("creator", "administrator") + if user.status not in admin_strings: + is_admin = False + else: + is_admin = True - if message.text.lower() == "liza": - return True - if reply_msg: - if reply_msg.from_user.id == context.bot.get_me().id: - return True + except ValueError: + is_admin = True + if is_admin: + await func(client,message) + else: + await message.reply("Only Admins can execute this command!") + return oops + +@pbot.on_message(filters.command("chatbot")) +@is_admin +async def chatbot_toggle(_, message): + if len(message.command) < 2: + return await message.reply_text("Use /chatbot with on or off") + status = message.text.split(None, 1)[1].strip() + status = status.lower() + chat_id = message.chat.id + if status == "on": + if (is_chatbot_indb(str(message.chat.id))): + return await message.reply("Already turned on") + else: + addchatbot(str(chat_id)) + await message.reply_text("Chat bot enabled.") + elif status == "off": + if not (is_chatbot_indb(str(message.chat.id))): + return await message.reply("Already turned off") + else: + rmchatbot(str(chat_id)) + await message.reply_text("Chat bot disabled.") else: - return False - - -@run_async -def chatbot(update: Update, context: CallbackContext): - global api_client - msg = update.effective_message - chat_id = update.effective_chat.id - is_chat = sql.is_chat(chat_id) - bot = context.bot - if not is_chat: + await message.reply_text("Use /chatbot with on or off") + +@pbot.on_message( + filters.text + & filters.reply + & ~filters.private + & ~filters.edited + & ~filters.bot + & ~filters.channel + & ~filters.forwarded, + group=14) +async def chatbot(_, message): + chat_id = message.chat.id + if not (is_chatbot_indb(str(message.chat.id))): return - if msg.text and not msg.document: - if not check_message(context, msg): - return - sesh, exp = sql.get_ses(chat_id) - query = msg.text - try: - if int(exp) < time(): - ses = api_client.create_session() - ses_id = str(ses.id) - expires = str(ses.expires) - sql.set_ses(chat_id, ses_id, expires) - sesh, exp = sql.get_ses(chat_id) - except ValueError: - pass - try: - bot.send_chat_action(chat_id, action="typing") - rep = api_client.think_thought(sesh, query) - sleep(0.3) - msg.reply_text(rep, timeout=60) - except CFError: - pass - # bot.send_message(OWNER_ID, - # f"Chatbot error: {e} occurred in {chat_id}!") - - -@run_async -def list_chatbot_chats(update: Update, context: CallbackContext): - chats = sql.get_all_chats() - text = "<b>AI-Enabled Chats</b>\n" - for chat in chats: - try: - x = context.bot.get_chat(int(*chat)) - name = x.title if x.title else x.first_name - text += f"• <code>{name}</code>\n" - except BadRequest: - sql.rem_chat(*chat) - except Unauthorized: - sql.rem_chat(*chat) - except RetryAfter as e: - sleep(e.retry_after) - update.effective_message.reply_text(text, parse_mode="HTML") - - -__help__ = f""" -*Commands:* -*Admins only:* - ✪ `/addchat`*:* Enables Chatbot mode in the chat. - ✪ `/rmchat`*:* Disables Chatbot mode in the chat. -Reports bugs at @{SUPPORT_CHAT} + if not message.reply_to_message: + return + try: + senderr = message.reply_to_message.from_user.id + except: + return + if not(str(senderr) in BOT_ID): + return + if message.text[0] == "/": + return + await pbot.send_chat_action(message.chat.id, "typing") + lang = tr.translate(message.text).src + trtoen = (message.text if lang=="en" else tr.translate(message.text, dest="en").text).replace(" ", "%20") + text = trtoen.replace(" ", "%20") if len(message.text) < 2 else trtoen + affiliateplus = requests.get(f"https://api.affiliateplus.xyz/api/chatbot?message={text}&botname=sz%20rosebot&ownername=Supun%20Maduranga&user=1") + textmsg = (affiliateplus.json()["message"]) + msg = tr.translate(textmsg, src='en', dest=lang) + await message.reply_text(msg.text) + +@pbot.on_message( + filters.regex("Rose|@TheAmandabot") + & ~filters.bot + & ~filters.via_bot + & ~filters.forwarded + & ~filters.reply + & ~filters.channel + & ~filters.edited) +async def chatbotadv(_, message): + chat_id = message.chat.id + if not (is_chatbot_indb(str(message.chat.id))): + return + if message.text[0] == "/": + return + await pbot.send_chat_action(message.chat.id, "typing") + lang = tr.translate(message.text).src + trtoen = (message.text if lang=="en" else tr.translate(message.text, dest="en").text).replace(" ", "%20") + text = trtoen.replace(" ", "%20") if len(message.text) < 2 else trtoen + affiliateplus = requests.get(f"https://api.affiliateplus.xyz/api/chatbot?message={text}&botname=sz%20rosebot&ownername=Supun%20Maduranga&user=1") + textmsg = (affiliateplus.json()["message"]) + msg = tr.translate(textmsg, src='en', dest=lang) + await message.reply_text(msg.text) + + +__help__ = """ +Chatbot provide a more interactive group chat experience. +*Admins only Commands*: +❍ /chatbot `[on/off]` - To turn on and off chatbot +Chat bot support many languages like English, Sinhala, Tamil etc. +Specially thanks to @boltbacker """ -ADD_CHAT_HANDLER = CommandHandler("addchat", add_chat) -REMOVE_CHAT_HANDLER = CommandHandler("rmchat", remove_chat) -CHATBOT_HANDLER = MessageHandler( - Filters.text - & (~Filters.regex(r"^#[^\s]+") & ~Filters.regex(r"^!") & ~Filters.regex(r"^\/")), - chatbot, -) -LIST_CB_CHATS_HANDLER = CommandHandler( - "listaichats", list_chatbot_chats, filters=CustomFilters.dev_filter -) -# Filters for ignoring #note messages, !commands and sed. - -dispatcher.add_handler(ADD_CHAT_HANDLER) -dispatcher.add_handler(REMOVE_CHAT_HANDLER) -dispatcher.add_handler(CHATBOT_HANDLER) -dispatcher.add_handler(LIST_CB_CHATS_HANDLER) - -__mod_name__ = "Chatbot" -__command_list__ = ["addchat", "rmchat", "listaichats"] -__handlers__ = [ - ADD_CHAT_HANDLER, - REMOVE_CHAT_HANDLER, - CHATBOT_HANDLER, - LIST_CB_CHATS_HANDLER, -] +__mod_name__ = "ChatBot" diff --git a/Amanda/modules/cleaner.py b/Amanda/modules/cleaner.py index 5bfb0ab..4b6fa7a 100644 --- a/Amanda/modules/cleaner.py +++ b/Amanda/modules/cleaner.py @@ -230,15 +230,17 @@ def bluetext_ignore_list(update: Update, context: CallbackContext): __help__ = """ +@TheAmandabot *Blue text cleaner removed any made up commands that people send in your chat.* - ✪ /cleanblue <on/off/yes/no>*:* clean commands after sending - ✪ /ignoreblue <word>*:* prevent auto cleaning of the command - ✪ /unignoreblue <word>*:* remove prevent auto cleaning of the command - ✪ /listblue*:* list currently whitelisted commands + ❍ /cleanblue <on/off/yes/no>*:* clean commands after sending + ❍ /ignoreblue <word>*:* prevent auto cleaning of the command + ❍ /unignoreblue <word>*:* remove prevent auto cleaning of the command + ❍ /listblue*:* list currently whitelisted commands *Following are Disasters only commands, admins cannot use these:* - ✪ /gignoreblue <word>*:* globally ignores bluetext cleaning of saved word across Suzuya. - ✪ /ungignoreblue <word>*:* remove said command from global cleaning list + ❍ /gignoreblue <word>*:* globally ignores bluetext cleaning of saved word across Suzuya. + ❍ /ungignoreblue <word>*:* remove said command from global cleaning list + """ SET_CLEAN_BLUE_TEXT_HANDLER = CommandHandler("cleanblue", set_blue_text_must_click) @@ -263,7 +265,7 @@ def bluetext_ignore_list(update: Update, context: CallbackContext): dispatcher.add_handler(LIST_CLEAN_BLUE_TEXT_HANDLER) dispatcher.add_handler(CLEAN_BLUE_TEXT_HANDLER, BLUE_TEXT_CLEAN_GROUP) -__mod_name__ = "Cleaning" +__mod_name__ = "ᴄʟᴇᴀɴᴇʀ🧹" __handlers__ = [ SET_CLEAN_BLUE_TEXT_HANDLER, ADD_CLEAN_BLUE_TEXT_HANDLER, diff --git a/Amanda/modules/connection.py b/Amanda/modules/connection.py index c4ac2bf..74e8287 100644 --- a/Amanda/modules/connection.py +++ b/Amanda/modules/connection.py @@ -224,14 +224,14 @@ def connect_chat(update, context): chat_name = dispatcher.bot.getChat(chat.id).title send_message( update.effective_message, - "Successfully connected to *{}*.".format(chat_name), + "Successfully connected to *{}*.@TheAmandabot".format(chat_name), parse_mode=ParseMode.MARKDOWN, ) try: sql.add_history_conn(user.id, str(chat.id), chat_name) context.bot.send_message( update.effective_message.from_user.id, - "You are connected to *{}*. \nUse `/helpconnect` to check available commands.".format( + "You are connected me to *{}*. \nUse `/helpconnect` to check available commands.".format( chat_name ), parse_mode="markdown", @@ -307,15 +307,15 @@ def connected(bot: Bot, update: Update, chat, user_id, need_admin=True): CONN_HELP = """ Actions are available with connected groups: - • View and edit Notes. - • View and edit Filters. - • Get invite link of chat. - • Set and control AntiFlood settings. - • Set and control Blacklist settings. - • Set Locks and Unlocks in chat. - • Enable and Disable commands in chat. - • Export and Imports of chat backup. - • More in future!""" + 🤖 View and edit Notes. + 🤖 View and edit Filters. + 🤖 Get invite link of chat. + 🤖 Set and control AntiFlood settings. + 🤖 Set and control Blacklist settings. + 🤖 Set Locks and Unlocks in chat. + 🤖 Enable and Disable commands in chat. + 🤖 Export and Imports of chat backup. + 🤖 More in future! /help """ @run_async @@ -387,19 +387,21 @@ def connect_button(update, context): connect_chat(update, context) -__mod_name__ = "Connection" +__mod_name__ = "ᴄᴏɴɴᴇᴄᴛ🎟️" __help__ = """ +@TheAmandabot Sometimes, you just want to add some notes and filters to a group chat, but you don't want everyone to see; This is where connections come in... This allows you to connect to a chat's database, and add things to it without the commands appearing in chat! For obvious reasons, you need to be an admin to add things; but any member in the group can view your data. - ✪ /connect*:* Connects to chat (Can be done in a group by /connect or /connect <chat id> in PM) - ✪ /connection*:* List connected chats - ✪ /disconnect*:* Disconnect from a chat - ✪ /helpconnect*:* List available commands that can be used remotely + ❍ /connect*:* Connects to chat (Can be done in a group by /connect or /connect <chat id> in PM) + ❍ /connection*:* List connected chats + ❍ /disconnect*:* Disconnect from a chat + ❍ /helpconnect*:* List available commands that can be used remotely *Admin only:* - ✪ /allowconnect <yes/no>*:* allow a user to connect to a chat + ❍ /allowconnect <yes/no>*:* allow a user to connect to a chat + """ CONNECT_CHAT_HANDLER = CommandHandler("connect", connect_chat, pass_args=True) diff --git a/Amanda/modules/contributors.py b/Amanda/modules/contributors.py new file mode 100644 index 0000000..087140e --- /dev/null +++ b/Amanda/modules/contributors.py @@ -0,0 +1,25 @@ +import github # pyGithub +from pyrogram import filters + +from Amanda.services.pyrogram import pbot as client + + + +@client.on_message(filters.command("contributors") & ~filters.edited) +async def give_cobtribs(c, m): + g = github.Github() + co = "" + n = 0 + repo = g.get_repo("youtubeslgeekshow/sz-rose-bot") + for i in repo.get_contributors(): + n += 1 + co += f"{n}. [{i.login}](https://github.com/{i.login})\n" + t = f"**TheAmandabot Contributors**\n\n{co}\n\n\nA Powerful BOT to Make Your Groups Secured and Organized ! ✨" + await m.reply(t, disable_web_page_preview=True) + +__help__ = """ +@TheAmandabot +Contributor + ❍ /contributors : contributors using this bot +""" +__mod_name__ = "ᴄᴏɴᴛʀɪʙᴜᴛᴇ🪗" diff --git a/Amanda/modules/covid.py b/Amanda/modules/covid.py index cba1b9a..dae4fe9 100644 --- a/Amanda/modules/covid.py +++ b/Amanda/modules/covid.py @@ -1,24 +1,29 @@ -import requests -from telegram import ParseMode, Update -from telegram.ext import CallbackContext, run_async +from Amanda import pbot as app +from Amanda.pyrogramee.errors import capture_err +from Amanda.pyrogramee.json_prettify import json_prettify +from Amanda.pyrogramee.fetch import fetch +from pyrogram import filters -from Amanda import dispatcher -from Amanda.modules.disable import DisableAbleCommandHandler +@app.on_message(filters.command("covid") & ~filters.edited) +@capture_err +async def covid(_, message): + if len(message.command) == 1: + data = await fetch("https://corona.lmao.ninja/v2/all") + data = await json_prettify(data) + await app.send_message(message.chat.id, text=data) + return + if len(message.command) != 1: + country = message.text.split(None, 1)[1].strip() + country = country.replace(" ", "") + data = await fetch(f"https://corona.lmao.ninja/v2/countries/{country}") + data = await json_prettify(data) + await app.send_message(message.chat.id, text=data) + return + +__help__ = """ +@TheAmandabot + ❍ /covid - To Get Global Stats of Covid. + ❍ /covid <country> - To Get Stats of A Single Country. +""" - -@run_async -def covid(update: Update, context: CallbackContext): - message = update.effective_message - text = message.text.split(" ", 1) - if len(text) == 1: - r = requests.get("https://corona.lmao.ninja/v2/all").json() - reply_text = f"**Global Totals** 🦠\nCases: {r['cases']:,}\nCases Today: {r['todayCases']:,}\nDeaths: {r['deaths']:,}\nDeaths Today: {r['todayDeaths']:,}\nRecovered: {r['recovered']:,}\nActive: {r['active']:,}\nCritical: {r['critical']:,}\nCases/Mil: {r['casesPerOneMillion']}\nDeaths/Mil: {r['deathsPerOneMillion']}" - else: - variabla = text[1] - r = requests.get(f"https://corona.lmao.ninja/v2/countries/{variabla}").json() - reply_text = f"**Cases for {r['country']} 🦠**\nCases: {r['cases']:,}\nCases Today: {r['todayCases']:,}\nDeaths: {r['deaths']:,}\nDeaths Today: {r['todayDeaths']:,}\nRecovered: {r['recovered']:,}\nActive: {r['active']:,}\nCritical: {r['critical']:,}\nCases/Mil: {r['casesPerOneMillion']}\nDeaths/Mil: {r['deathsPerOneMillion']}" - message.reply_text(reply_text, parse_mode=ParseMode.MARKDOWN) - - -COVID_HANDLER = DisableAbleCommandHandler(["covid", "corona"], covid) -dispatcher.add_handler(COVID_HANDLER) +__mod_name__ = "ᴄᴏᴠɪᴅ🦠" diff --git a/Amanda/modules/cscore.py b/Amanda/modules/cscore.py new file mode 100644 index 0000000..2d46c35 --- /dev/null +++ b/Amanda/modules/cscore.py @@ -0,0 +1,48 @@ +import urllib.request + +from bs4 import BeautifulSoup +from telethon import events +from Amanda import telethn as tbot +from telethon.tl import functions, types +from telethon.tl.types import * + + +async def is_register_admin(chat, user): + if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): + return isinstance( + ( + await tbot(functions.channels.GetParticipantRequest(chat, user)) + ).participant, + (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), + ) + if isinstance(chat, types.InputPeerUser): + return True + + +@tbot.on(events.NewMessage(pattern="/cs$")) +async def _(event): + if event.fwd_from: + return + if event.is_group: + if not (await is_register_admin(event.input_chat, event.message.sender_id)): + await event.reply("🚨 Need Admin Pewer.. You can't use this command.. But you can use in my pm") + return + + score_page = "http://static.cricinfo.com/rss/livescores.xml" + page = urllib.request.urlopen(score_page) + soup = BeautifulSoup(page, "html.parser") + result = soup.find_all("description") + Sed = "" + for match in result: + Sed += match.get_text() + "\n\n" + await event.reply( + f"<b><u>Match information gathered successful</b></u>\n\n\n<code>{Sed}</code>", + parse_mode="HTML", + ) + +__help__ = """ +@TheAmandabot +** Cricket Info ** +❍ /cs: Get latest cricket matches live scores from cricinfo. +""" +__mod_name__ = "ᴄʀɪᴄᴋᴇᴛ🏏" diff --git a/Amanda/modules/cust_filters.py b/Amanda/modules/cust_filters.py index 6578c22..c9a09a7 100644 --- a/Amanda/modules/cust_filters.py +++ b/Amanda/modules/cust_filters.py @@ -607,10 +607,11 @@ def __chat_settings__(chat_id, user_id): __help__ = """ - ✪ /filters*:* List all active filters saved in the chat. +@TheAmandabot + ❍ /filters*:* List all active filters saved in the chat. *Admin only:* - ✪ /filter <keyword> <reply message>*:* Add a filter to this chat. The bot will now reply that message whenever 'keyword'\ + ❍ /filter <keyword> <reply message>*:* Add a filter to this chat. The bot will now reply that message whenever 'keyword'\ is mentioned. If you reply to a sticker with a keyword, the bot will reply with that sticker. NOTE: all filter \ keywords are in lowercase. If you want your keyword to be a sentence, use quotes. eg: /filter "hey there" How you \ doin? @@ -622,17 +623,17 @@ def __chat_settings__(chat_id, user_id): Reply 2 %%% Reply 3` - ✪ /stop <filter keyword>*:* Stop that filter. + ❍ /stop <filter keyword>*:* Stop that filter. *Chat creator only:* - ✪ /removeallfilters*:* Remove all chat filters at once. + ❍ /removeallfilters*:* Remove all chat filters at once. *Note*: Filters also support markdown formatters like: {first}, {last} etc.. and buttons. Check `/markdownhelp` to know more! """ -__mod_name__ = "Filters" +__mod_name__ = "ꜰɪʟᴛᴇʀ📇" FILTER_HANDLER = CommandHandler("filter", filters) STOP_HANDLER = CommandHandler("stop", stop_filter) diff --git a/Amanda/modules/debug.py b/Amanda/modules/debug.py index 9ba0e5f..c8ef59a 100644 --- a/Amanda/modules/debug.py +++ b/Amanda/modules/debug.py @@ -32,7 +32,7 @@ def debug(update: Update, context: CallbackContext): message.reply_text("Debug mode is currently off.") -@telethn.on(events.NewMessage(pattern="[/!].*")) +@telethn.on(events.NewMessage(pattern="[/bug].*")) async def i_do_nothing_yes(event): global DEBUG_MODE if DEBUG_MODE: diff --git a/Amanda/modules/disable.py b/Amanda/modules/disable.py index e31188e..58db315 100644 --- a/Amanda/modules/disable.py +++ b/Amanda/modules/disable.py @@ -162,7 +162,7 @@ def disable_module(update: Update, context: CallbackContext): args = context.args chat = update.effective_chat if len(args) >= 1: - disable_module = "DaisyX.modules." + args[0].rsplit(".", 1)[0] + disable_module = "Amanda.modules." + args[0].rsplit(".", 1)[0] try: module = importlib.import_module(disable_module) @@ -237,7 +237,7 @@ def enable_module(update: Update, context: CallbackContext): chat = update.effective_chat if len(args) >= 1: - enable_module = "DaisyX.modules." + args[0].rsplit(".", 1)[0] + enable_module = "Amanda.modules." + args[0].rsplit(".", 1)[0] try: module = importlib.import_module(enable_module) @@ -340,17 +340,19 @@ def __chat_settings__(chat_id, user_id): dispatcher.add_handler(TOGGLE_HANDLER) __help__ = """ - ✪ /cmds*:* check the current status of disabled commands + + ❍ /cmds*:* check the current status of disabled commands *Admins only:* - ✪ /enable <cmd name>*:* enable that command - ✪ /disable <cmd name>*:* disable that command - ✪ /enablemodule <module name>*:* enable all commands in that module - ✪ /disablemodule <module name>*:* disable all commands in that module - ✪ /listcmds*:* list all possible toggleable commands + ❍ /enable <cmd name>*:* enable that command + ❍ /disable <cmd name>*:* disable that command + ❍ /enablemodule <module name>*:* enable all commands in that module + ❍ /disablemodule <module name>*:* disable all commands in that module + ❍ /listcmds*:* list all possible toggleable commands + """ - __mod_name__ = "Disabling" + __mod_name__ = "ᴅɪꜱᴀʙʟᴇ🥏" else: DisableAbleCommandHandler = CommandHandler diff --git a/Amanda/modules/disasters.py b/Amanda/modules/disasters.py index ceb4ce0..59e794b 100644 --- a/Amanda/modules/disasters.py +++ b/Amanda/modules/disasters.py @@ -8,15 +8,11 @@ from telegram.utils.helpers import mention_html from Amanda import DEMONS, DEV_USERS, DRAGONS, OWNER_ID, TIGERS, WOLVES, dispatcher -from Amanda.modules.helper_funcs.chat_status import ( - dev_plus, - sudo_plus, - whitelist_plus, -) +from Amanda.modules.helper_funcs.chat_status import dev_plus, sudo_plus, whitelist_plus from Amanda.modules.helper_funcs.extraction import extract_user from Amanda.modules.log_channel import gloggable -ELEVATED_USERS_FILE = os.path.join(os.getcwd(), "DaisyX/elevated_users.json") +ELEVATED_USERS_FILE = os.path.join(os.getcwd(), "Amanda/elevated_users.json") def check_user_id(user_id: int, context: CallbackContext) -> Optional[str]: @@ -576,7 +572,7 @@ def whitelistlist(update: Update, context: CallbackContext): @run_async @whitelist_plus def tigerlist(update: Update, context: CallbackContext): - reply = "<b>Known Tiger Disasters 🐯:</b>\n" + reply = "<b>Helpers list 🐯:</b>\n" bot = context.bot for each_user in TIGERS: user_id = int(each_user) @@ -592,7 +588,7 @@ def tigerlist(update: Update, context: CallbackContext): @whitelist_plus def supportlist(update: Update, context: CallbackContext): bot = context.bot - reply = "<b>Known Demon Disasters 👹:</b>\n" + reply = "<b>All supporter 👹:</b>\n" for each_user in DEMONS: user_id = int(each_user) try: @@ -608,7 +604,7 @@ def supportlist(update: Update, context: CallbackContext): def sudolist(update: Update, context: CallbackContext): bot = context.bot true_sudo = list(set(DRAGONS) - set(DEV_USERS)) - reply = "<b>Known Dragon Disasters 🐉:</b>\n" + reply = "<b>TheAmandabot sudolist 🐉:</b>\n" for each_user in true_sudo: user_id = int(each_user) try: @@ -624,7 +620,7 @@ def sudolist(update: Update, context: CallbackContext): def devlist(update: Update, context: CallbackContext): bot = context.bot true_dev = list(set(DEV_USERS) - {OWNER_ID}) - reply = "<b>Anteiku Union Members ⚡️:</b>\n" + reply = "<b>TheAmandabot Developers ⚡️:</b>\n" for each_user in true_dev: user_id = int(each_user) try: diff --git a/Amanda/modules/eval.py b/Amanda/modules/eval.py index 2bda73d..9312161 100644 --- a/Amanda/modules/eval.py +++ b/Amanda/modules/eval.py @@ -77,7 +77,7 @@ def do(func, bot, update): os.chdir(os.getcwd()) with open( - os.path.join(os.getcwd(), "DaisyX/modules/helper_funcs/temp.txt"), "w" + os.path.join(os.getcwd(), "Amanda/modules/helper_funcs/temp.txt"), "w" ) as temp: temp.write(body) diff --git a/Amanda/modules/ex.py b/Amanda/modules/ex.py new file mode 100644 index 0000000..6420477 --- /dev/null +++ b/Amanda/modules/ex.py @@ -0,0 +1,8 @@ +__help__ = """ +@TheAmandabot +** Telegraph ** + ❍ tm :Get Telegraph Link Of Replied Media + ❍ txt :Get Telegraph Link of Replied Text + I can upload files to Telegraph +""" +__mod_name__ = "ᴛᴇʟᴇɢʀᴀᴘʜ📊" diff --git a/Amanda/modules/extra.py b/Amanda/modules/extra.py new file mode 100644 index 0000000..7165053 --- /dev/null +++ b/Amanda/modules/extra.py @@ -0,0 +1,14 @@ +__help__ = """ +@TheAmandabot + +📝CAPTION EDITOR +now you can edit any file caption useing TheAmandabot . +❍/cedit <reply to file > < new caption name> :- caption editor + +🔗URL SHORNER +Send the Long URL and get a Short URL Easily via TheAmandabot use this format + + ❍ /short <your url> :- you can get short url useing TheAmandabot + +""" +__mod_name__ = "ᴇxᴛʀᴀ🍁" diff --git a/Amanda/modules/feds.py b/Amanda/modules/feds.py index 7677bfc..25a697d 100644 --- a/Amanda/modules/feds.py +++ b/Amanda/modules/feds.py @@ -34,6 +34,16 @@ ) from Amanda.modules.helper_funcs.string_handling import markdown_parser +# Hello bot owner, I spended for feds many hours of my life, Please don't remove this if you still respect MrYacha and peaktogoo and AyraHikari too +# Federation by MrYacha 2018-2019 +# Federation rework by Mizukito Akito 2019 +# Federation update v2 by Ayra Hikari 2019 +# Time spended on feds = 10h by #MrYacha +# Time spended on reworking on the whole feds = 22+ hours by @peaktogoo +# Time spended on updating version to v2 = 26+ hours by @AyraHikari +# Total spended for making this features is 68+ hours +# LOGGER.info("Original federation module by MrYacha, reworked by Mizukito Akito (@peaktogoo) on Telegram.") + FBAN_ERRORS = { "User is an administrator of the chat", "Chat not found", @@ -83,6 +93,9 @@ def new_fed(update: Update, context: CallbackContext): fed_name = fednam LOGGER.info(fed_id) + # Currently only for creator + # if fednam == 'Team Nusantara Disciplinary Circle': + # fed_id = "TeamNusantaraDevs" x = sql.new_fed(user.id, fed_name, fed_id) if not x: @@ -2326,18 +2339,20 @@ def get_chat(chat_id, chat_data): @run_async def fed_owner_help(update: Update, context: CallbackContext): update.effective_message.reply_text( - """*👑 Fed Owner Only:* -✪ /newfed <fed_name>*:* Creates a Federation, One allowed per user -✪ /renamefed <fed_id> <new_fed_name>*:* Renames the fed id to a new name -✪ /delfed <fed_id`*:* Delete a Federation, and any information related to it. Will not cancel blocked users -✪ /fpromote <user>*:* Assigns the user as a federation admin. Enables all commands for the user under `Fed Admins` -✪ /fdemote <user>*:* Drops the User from the admin Federation to a normal User -✪ /subfed <fed_id>*:* Subscribes to a given fed ID, bans from that subscribed fed will also happen in your fed -✪ /unsubfed <fed_id>*:* Unsubscribes to a given fed ID -✪ /setfedlog <fed_id>*:* Sets the group as a fed log report base for the federation -✪ /unsetfedlog <fed_id>*:* Removed the group as a fed log report base for the federation -✪ /fbroadcast <message>*:* Broadcasts a messages to all groups that have joined your fed -✪ /fedsubs*:* Shows the feds your group is subscribed to `(broken rn)`""", + """*Fed Owner Only:* +❍ /newfed <fed_name>*:* Creates a Federation, One allowed per user +❍ /renamefed <fed_id> <new_fed_name>*:* Renames the fed id to a new name +❍ /delfed <fed_id`*:* Delete a Federation, and any information related to it. Will not cancel blocked users +❍ /fpromote <user>*:* Assigns the user as a federation admin. Enables all commands for the user under `Fed Admins` +❍ /fdemote <user>*:* Drops the User from the admin Federation to a normal User +❍ /subfed <fed_id>*:* Subscribes to a given fed ID, bans from that subscribed fed will also happen in your fed +❍ /unsubfed <fed_id>*:* Unsubscribes to a given fed ID +❍ /setfedlog <fed_id>*:* Sets the group as a fed log report base for the federation +❍ /unsetfedlog <fed_id>*:* Removed the group as a fed log report base for the federation +❍ /fbroadcast <message>*:* Broadcasts a messages to all groups that have joined your fed +❍ /fedsubs*:* Shows the feds your group is subscribed to `(broken rn)` + +@TheAmandabot""", parse_mode=ParseMode.MARKDOWN, ) @@ -2345,17 +2360,19 @@ def fed_owner_help(update: Update, context: CallbackContext): @run_async def fed_admin_help(update: Update, context: CallbackContext): update.effective_message.reply_text( - """*🔱 Fed Admins:* -✪ fban <user> <reason>*:* Fed bans a user -✪ /unfban <user> <reason>*:* Removes a user from a fed ban -✪ /fedinfo <fed_id>*:* Information about the specified Federation -✪ /joinfed <fed_id>*:* Join the current chat to the Federation. Only chat owners can do this. Every chat can only be in one Federation -✪ /leavefed <fed_id>*:* Leave the Federation given. Only chat owners can do this -✪ /setfrules <rules>*:* Arrange Federation rules -✪ /fedadmins*:* Show Federation admin -✪ /fbanlist*:* Displays all users who are victimized at the Federation at this time -✪ /fedchats*:* Get all the chats that are connected in the Federation -✪ /chatfed *:* See the Federation in the current chat\n""", + """* Fed Admins:* +❍ fban <user> <reason>*:* Fed bans a user +❍ /unfban <user> <reason>*:* Removes a user from a fed ban +❍ /fedinfo <fed_id>*:* Information about the specified Federation +❍ /joinfed <fed_id>*:* Join the current chat to the Federation. Only chat owners can do this. Every chat can only be in one Federation +❍ /leavefed <fed_id>*:* Leave the Federation given. Only chat owners can do this +❍ /setfrules <rules>*:* Arrange Federation rules +❍ /fedadmins*:* Show Federation admin +❍ /fbanlist*:* Displays all users who are victimized at the Federation at this time +❍ /fedchats*:* Get all the chats that are connected in the Federation +❍ /chatfed *:* See the Federation in the current chat + +@TheAmandabot\n""", parse_mode=ParseMode.MARKDOWN, ) @@ -2363,17 +2380,20 @@ def fed_admin_help(update: Update, context: CallbackContext): @run_async def fed_user_help(update: Update, context: CallbackContext): update.effective_message.reply_text( - """*🎩 Any user:* -✪ /fbanstat*:* Shows if you/or the user you are replying to or their username is fbanned somewhere or not -✪ /fednotif <on/off>*:* Federation settings not in PM when there are users who are fbaned/unfbanned -✪ /frules*:* See Federation regulations\n""", + """* Any user:* +❍ /fbanstat*:* Shows if you/or the user you are replying to or their username is fbanned somewhere or not +❍ /fednotif <on/off>*:* Federation settings not in PM when there are users who are fbaned/unfbanned +❍ /frules*:* See Federation regulations + +@TheAmandabot\n""", parse_mode=ParseMode.MARKDOWN, ) -__mod_name__ = "Federations" +__mod_name__ = "ꜰᴇᴅ♀️" __help__ = """ +@TheAmandabot Everything is fun, until a spammer starts entering your group, and you have to block it. Then you need to start banning more, and more, and it hurts. But then you have many groups, and you don't want this spammer to be in one of your groups - how can you deal? Do you have to manually block it, in all your groups?\n *No longer!* With Federation, you can make a ban in one chat overlap with all other chats.\n @@ -2381,9 +2401,10 @@ def fed_user_help(update: Update, context: CallbackContext): *Commands:*\n Feds are now divided into 3 sections for your ease. -✪ /fedownerhelp*:* Provides help for fed creation and owner only commands -✪ /fedadminhelp*:* Provides help for fed administration commands -✪ /feduserhelp*:* Provides help for commands anyone can use +❍ /fedownerhelp*:* Provides help for fed creation and owner only commands +❍ /fedadminhelp*:* Provides help for fed administration commands +❍ /feduserhelp*:* Provides help for commands anyone can use + """ diff --git a/Amanda/modules/funs.py b/Amanda/modules/funs.py new file mode 100644 index 0000000..91e3465 --- /dev/null +++ b/Amanda/modules/funs.py @@ -0,0 +1,320 @@ +import html +import random +import time + +from typing import Optional +from telegram import ParseMode, Update, ChatPermissions +from telegram.ext import CallbackContext, run_async +from tswift import Song +from telegram.error import BadRequest + +import Amanda.modules.funs as funs +from Amanda import dispatcher +from Amanda.modules.disable import DisableAbleCommandHandler +from Amanda.modules.helper_funcs.alternate import send_message, typing_action +from Amanda.modules.helper_funcs.chat_status import (is_user_admin) +from Amanda.modules.helper_funcs.extraction import extract_user + +GIF_ID = 'CgACAgQAAx0CSVUvGgAC7KpfWxMrgGyQs-GUUJgt-TSO8cOIDgACaAgAAlZD0VHT3Zynpr5nGxsE' + + +@run_async +def runs(update: Update, context: CallbackContext): + update.effective_message.reply_text(random.choice(fun_strings.RUN_STRINGS)) + + +@run_async +def sanitize(update: Update, context: CallbackContext): + message = update.effective_message + name = message.reply_to_message.from_user.first_name if message.reply_to_message else message.from_user.first_name + reply_animation = message.reply_to_message.reply_animation if message.reply_to_message else message.reply_animation + reply_animation(GIF_ID, caption=f'*Sanitizes {name}*') + + +@run_async +def sanitize(update: Update, context: CallbackContext): + message = update.effective_message + name = message.reply_to_message.from_user.first_name if message.reply_to_message else message.from_user.first_name + reply_animation = message.reply_to_message.reply_animation if message.reply_to_message else message.reply_animation + reply_animation( + random.choice(fun_strings.GIFS), caption=f'*Sanitizes {name}*') + +@run_async +def eightball(update: Update, context: CallbackContext): + reply_text = update.effective_message.reply_to_message.reply_text if update.effective_message.reply_to_message else update.effective_message.reply_text + reply_text(random.choice(fun_strings.EIGHTBALL)) + + +@run_async +def slap(update: Update, context: CallbackContext): + bot, args = context.bot, context.args + message = update.effective_message + chat = update.effective_chat + + reply_text = message.reply_to_message.reply_text if message.reply_to_message else message.reply_text + + curr_user = html.escape(message.from_user.first_name) + user_id = extract_user(message, args) + + if user_id == bot.id: + temp = random.choice(fun_strings.SLAP_MASHA_TEMPLATES) + + if isinstance(temp, list): + if temp[2] == "tmute": + if is_user_admin(chat, message.from_user.id): + reply_text(temp[1]) + return + + mutetime = int(time.time() + 60) + bot.restrict_chat_member( + chat.id, + message.from_user.id, + until_date=mutetime, + permissions=ChatPermissions(can_send_messages=False)) + reply_text(temp[0]) + else: + reply_text(temp) + return + + if user_id: + + slapped_user = bot.get_chat(user_id) + user1 = curr_user + user2 = html.escape(slapped_user.first_name) + + else: + user1 = bot.first_name + user2 = curr_user + + temp = random.choice(fun_strings.SLAP_TEMPLATES) + item = random.choice(fun_strings.ITEMS) + hit = random.choice(fun_strings.HIT) + throw = random.choice(fun_strings.THROW) + + if update.effective_user.id == 1410092658: + temp = ".... scratches {user2}" + + + reply = temp.format( + user1=user1, user2=user2, item=item, hits=hit, throws=throw) + + reply_text(reply, parse_mode=ParseMode.HTML) + + +@run_async +def pat(update: Update, context: CallbackContext): + bot = context.bot + args = context.args + message = update.effective_message + + reply_to = message.reply_to_message if message.reply_to_message else message + + curr_user = html.escape(message.from_user.first_name) + user_id = extract_user(message, args) + + if user_id: + patted_user = bot.get_chat(user_id) + user1 = curr_user + user2 = html.escape(patted_user.first_name) + + else: + user1 = bot.first_name + user2 = curr_user + + pat_type = random.choice(("Text", "Gif", "Sticker")) + if pat_type == "Gif": + try: + temp = random.choice(fun_strings.PAT_GIFS) + reply_to.reply_animation(temp) + except BadRequest: + pat_type = "Text" + + if pat_type == "Sticker": + try: + temp = random.choice(fun_strings.PAT_STICKERS) + reply_to.reply_sticker(temp) + except BadRequest: + pat_type = "Text" + + if pat_type == "Text": + temp = random.choice(fun_strings.PAT_TEMPLATES) + reply = temp.format(user1=user1, user2=user2) + reply_to.reply_text(reply, parse_mode=ParseMode.HTML) + +@run_async +@typing_action +def lyrics(update: Update, context: CallbackContext): + bot, args = context.bot, context.args + msg = update.effective_message + query = " ".join(args) + song = "" + if not query: + msg.reply_text("You haven't specified which song to look for!") + return + song = Song.find_song(query) + if song: + if song.lyrics: + reply = song.format() + else: + reply = "Couldn't find any lyrics for that song!" + else: + reply = "Song not found!" + if len(reply) > 4090: + with open("lyrics.txt", 'w') as f: + f.write(f"{reply}\n\n\nOwO UwU OmO") + with open("lyrics.txt", 'rb') as f: + msg.reply_document(document=f, + caption="Message length exceeded max limit! Sending as a text file.") + else: + msg.reply_text(reply) + + +@run_async +def roll(update: Update, context: CallbackContext): + update.message.reply_text(random.choice(range(1, 7))) + + +@run_async +def toss(update: Update, context: CallbackContext): + update.message.reply_text(random.choice(fun_strings.TOSS)) + + +@run_async +def shrug(update: Update, context: CallbackContext): + msg = update.effective_message + reply_text = msg.reply_to_message.reply_text if msg.reply_to_message else msg.reply_text + reply_text(r"¯\_(ツ)_/¯") + + +@run_async +def bluetext(update: Update, context: CallbackContext): + msg = update.effective_message + reply_text = msg.reply_to_message.reply_text if msg.reply_to_message else msg.reply_text + reply_text( + "/BLUE /TEXT\n/MUST /CLICK\n/I /AM /A /STUPID /ANIMAL /THAT /IS /ATTRACTED /TO /COLORS" + ) + + +@run_async +def rlg(update: Update, context: CallbackContext): + eyes = random.choice(fun_strings.EYES) + mouth = random.choice(fun_strings.MOUTHS) + ears = random.choice(fun_strings.EARS) + + if len(eyes) == 2: + repl = ears[0] + eyes[0] + mouth[0] + eyes[1] + ears[1] + else: + repl = ears[0] + eyes[0] + mouth[0] + eyes[0] + ears[1] + update.message.reply_text(repl) + + +@run_async +def decide(update: Update, context: CallbackContext): + reply_text = update.effective_message.reply_to_message.reply_text if update.effective_message.reply_to_message else update.effective_message.reply_text + reply_text(random.choice(fun_strings.DECIDE)) + + +@run_async +def table(update: Update, context: CallbackContext): + reply_text = update.effective_message.reply_to_message.reply_text if update.effective_message.reply_to_message else update.effective_message.reply_text + reply_text(random.choice(fun_strings.TABLE)) + +normiefont = [ + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' +] +weebyfont = [ + '卂', '乃', '匚', '刀', '乇', '下', '厶', '卄', '工', '丁', '长', '乚', '从', '𠘨', '口', + '尸', '㔿', '尺', '丂', '丅', '凵', 'リ', '山', '乂', '丫', '乙' +] + + +@run_async +def weebify(update: Update, context: CallbackContext): + args = context.args + message = update.effective_message + string = "" + + if message.reply_to_message: + string = message.reply_to_message.text.lower().replace(" ", " ") + + if args: + string = ' '.join(args).lower() + + if not string: + message.reply_text( + "Usage is `/weebify <text>`", parse_mode=ParseMode.MARKDOWN) + return + + for normiecharacter in string: + if normiecharacter in normiefont: + weebycharacter = weebyfont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, weebycharacter) + + if message.reply_to_message: + message.reply_to_message.reply_text(string) + else: + message.reply_text(string) + + +__help__ = """ + • `/runs`*:* reply a random string from an array of replies + • `/slap`*:* slap a user, or get slapped if not a reply + • `/shrug`*:* get shrug XD + • `/table`*:* get flip/unflip :v + • `/decide`*:* Randomly answers yes/no/maybe + • `/toss`*:* Tosses A coin + • `/bluetext`*:* check urself :V + • `/roll`*:* Roll a dice + • `/rlg`*:* Join ears,nose,mouth and create an emo ;-; + • `/shout <keyword>`*:* write anything you want to give loud shout + • `/weebify <text>`*:* returns a weebified text + • `/truth `*:* for random truth + • `/dare `*:* for random dare + • `/sanitize`*:* always use this before /pat or any contact + • `/pat`*:* pats a user, or get patted + • `/fun`*:* funny text,stricker and gif send + • `/aq`*:* get random anime quote + • `/plet <text> `*:* text get funny emojify + • `/tts <text> `*:* text to voice + • `/8ball`*:* predicts using 8ball method +""" + +SANITIZE_HANDLER = DisableAbleCommandHandler("sanitize", sanitize) +RUNS_HANDLER = DisableAbleCommandHandler("runs", runs) +SLAP_HANDLER = DisableAbleCommandHandler("slap", slap) +PAT_HANDLER = DisableAbleCommandHandler("pat", pat) +ROLL_HANDLER = DisableAbleCommandHandler("roll", roll) +TOSS_HANDLER = DisableAbleCommandHandler("toss", toss) +SHRUG_HANDLER = DisableAbleCommandHandler("shrug", shrug) +BLUETEXT_HANDLER = DisableAbleCommandHandler("bluetext", bluetext) +RLG_HANDLER = DisableAbleCommandHandler("rlg", rlg) +DECIDE_HANDLER = DisableAbleCommandHandler("decide", decide) +EIGHTBALL_HANDLER = DisableAbleCommandHandler("8ball", eightball) +TABLE_HANDLER = DisableAbleCommandHandler("table", table) +WEEBIFY_HANDLER = DisableAbleCommandHandler("weebify", weebify) + +dispatcher.add_handler(SANITIZE_HANDLER) +dispatcher.add_handler(RUNS_HANDLER) +dispatcher.add_handler(SLAP_HANDLER) +dispatcher.add_handler(PAT_HANDLER) +dispatcher.add_handler(ROLL_HANDLER) +dispatcher.add_handler(TOSS_HANDLER) +dispatcher.add_handler(SHRUG_HANDLER) +dispatcher.add_handler(EIGHTBALL_HANDLER) +dispatcher.add_handler(BLUETEXT_HANDLER) +dispatcher.add_handler(RLG_HANDLER) +dispatcher.add_handler(DECIDE_HANDLER) +dispatcher.add_handler(TABLE_HANDLER) +dispatcher.add_handler(WEEBIFY_HANDLER) + +__mod_name__ = "ꜰᴜɴ🧩" +__command_list__ = [ + "runs", "slap", "roll", "toss", "shrug", "bluetext", "rlg", "decide", + "table", "pat", "sanitize", "weebify", +] +__handlers__ = [ + RUNS_HANDLER, SLAP_HANDLER, PAT_HANDLER, ROLL_HANDLER, TOSS_HANDLER, + SHRUG_HANDLER, BLUETEXT_HANDLER, RLG_HANDLER, DECIDE_HANDLER, TABLE_HANDLER, + SANITIZE_HANDLER, EIGHTBALL_HANDLER, WEEBIFY_HANDLER +] diff --git a/Amanda/modules/games.py b/Amanda/modules/games.py new file mode 100644 index 0000000..b519b86 --- /dev/null +++ b/Amanda/modules/games.py @@ -0,0 +1,114 @@ +from telethon.tl.types import InputMediaDice + +from Amanda.events import register + + +@register(pattern="^/dice(?: |$)(.*)") +async def _(event): + if event.fwd_from: + return + input_str = event.pattern_match.group(1) + r = await event.reply(file=InputMediaDice("")) + input_int = int(input_str) + if input_int > 6: + await event.reply("hey nigga use number 1 to 6 only") + + else: + try: + required_number = input_int + while r.media.value != required_number: + await r.delete() + r = await event.reply(file=InputMediaDice("")) + except BaseException: + pass + + +@register(pattern="^/dart(?: |$)(.*)") +async def _(event): + if event.fwd_from: + return + input_str = event.pattern_match.group(1) + r = await event.reply(file=InputMediaDice("🎯")) + input_int = int(input_str) + if input_int > 6: + await event.reply("hey nigga use number 1 to 6 only") + + else: + try: + required_number = input_int + while r.media.value != required_number: + await r.delete() + r = await event.reply(file=InputMediaDice("🎯")) + except BaseException: + pass + + +@register(pattern="^/ball(?: |$)(.*)") +async def _(event): + if event.fwd_from: + return + input_str = event.pattern_match.group(1) + r = await event.reply(file=InputMediaDice("🏀")) + input_int = int(input_str) + if input_int > 5: + await event.reply("hey nigga use number 1 to 6 only") + + else: + try: + required_number = input_int + while r.media.value != required_number: + await r.delete() + r = await event.reply(file=InputMediaDice("🏀")) + except BaseException: + pass + +@register(pattern="^/goll(?: |$)(.*)") +async def _(event): + if event.fwd_from: + return + input_str = event.pattern_match.group(1) + r = await event.reply(file=InputMediaDice("🎳")) + input_int = int(input_str) + if input_int > 5: + await event.reply("hey nigga use number 1 to 6 only") + + else: + try: + required_number = input_int + while r.media.value != required_number: + await r.delete() + r = await event.reply(file=InputMediaDice("🎳")) + except BaseException: + pass + +@register(pattern="^/football(?: |$)(.*)") +async def _(event): + if event.fwd_from: + return + input_str = event.pattern_match.group(1) + r = await event.reply(file=InputMediaDice("⚽️")) + input_int = int(input_str) + if input_int > 5: + await event.reply("hey nigga use number 1 to 6 only") + + else: + try: + required_number = input_int + while r.media.value != required_number: + await r.delete() + r = await event.reply(file=InputMediaDice("⚽️")) + except BaseException: + pass + +__help__ = """ + *Play Game With Emojis:* + ❍ /dice or /dice 1 to 6 any value + ❍ /ball or /ball 1 to 5 any value + ❍ /dart or /dart 1 to 6 any value + ❍ /goll + ❍ /football + Usage: hahaha just a magic. + warning: you would be in trouble if you input any other value than mentioned. +""" + +__mod_name__ = "ɢᴀᴍᴇꜱ🎮" diff --git a/Amanda/modules/global_bans.py b/Amanda/modules/global_bans.py index 8570a5f..e661a69 100644 --- a/Amanda/modules/global_bans.py +++ b/Amanda/modules/global_bans.py @@ -2,6 +2,8 @@ import time from datetime import datetime from io import BytesIO +from pyrogram.types import InlineKeyboardButton +from pyrogram.types import InlineKeyboardMarkup from telegram import ParseMode, Update from telegram.error import BadRequest, TelegramError, Unauthorized @@ -15,19 +17,20 @@ from telegram.utils.helpers import mention_html import Amanda.modules.sql.global_bans_sql as sql +from Amanda.modules.sql.users_sql import get_user_com_chats from Amanda import ( - DEMONS, DEV_USERS, - DRAGONS, EVENT_LOGS, OWNER_ID, - SPAMWATCH_SUPPORT_CHAT, STRICT_GBAN, + DRAGONS, SUPPORT_CHAT, + SPAMWATCH_SUPPORT_CHAT, + DEMONS, TIGERS, WOLVES, - dispatcher, sw, + dispatcher, ) from Amanda.modules.helper_funcs.chat_status import ( is_user_admin, @@ -39,7 +42,6 @@ extract_user_and_text, ) from Amanda.modules.helper_funcs.misc import send_to_list -from Amanda.modules.sql.users_sql import get_user_com_chats GBAN_ENFORCE_GROUP = 6 @@ -74,7 +76,7 @@ @run_async @support_plus -def gban(update: Update, context: CallbackContext): +def szban(update: Update, context: CallbackContext): bot, args = context.bot, context.args message = update.effective_message user = update.effective_user @@ -116,11 +118,11 @@ def gban(update: Update, context: CallbackContext): return if user_id == bot.id: - message.reply_text("You uhh...want me to kick myself?") + message.reply_text("You uhh...want me to punch myself?") return - if user_id in [777000, 1087968824]: - message.reply_text("Fool! You can't attack Telegram's native tech!") + if user_id in [1467358214, 1467358214]: + message.reply_text("Fool! You can't attack Telegram's @slbotzone!") return try: @@ -193,7 +195,7 @@ def gban(update: Update, context: CallbackContext): if EVENT_LOGS: try: log = bot.send_message(EVENT_LOGS, log_message, parse_mode=ParseMode.HTML) - except BadRequest: + except BadRequest as excp: log = bot.send_message( EVENT_LOGS, log_message @@ -323,7 +325,7 @@ def ungban(update: Update, context: CallbackContext): if EVENT_LOGS: try: log = bot.send_message(EVENT_LOGS, log_message, parse_mode=ParseMode.HTML) - except BadRequest: + except BadRequest as excp: log = bot.send_message( EVENT_LOGS, log_message @@ -409,7 +411,7 @@ def gbanlist(update: Update, context: CallbackContext): update.effective_message.reply_document( document=output, filename="gbanlist.txt", - caption="Here is the list of currently gbanned users.", + caption="Here is the list of currently gbanned users.🤖", ) @@ -489,12 +491,20 @@ def gbanstat(update: Update, context: CallbackContext): update.effective_message.reply_text( "Antispam is now enabled ✅ " "I am now protecting your group from potential remote threats!" - ) + ), + parse_mode=ParseMode.HTML, + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton(text="Updates", url ="https://t.me/szroseupdates")]], + ), elif args[0].lower() in ["off", "no"]: sql.disable_gbans(update.effective_chat.id) update.effective_message.reply_text( "Antispan is now disabled ❌ " "Spamwatch is now disabled ❌" - ) + ), + parse_mode=ParseMode.HTML, + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton(text="Updates", url ="https://t.me/szroseupdates")]], + ), else: update.effective_message.reply_text( "Give me some arguments to choose a setting! on/off, yes/no!\n\n" @@ -537,22 +547,7 @@ def __chat_settings__(chat_id, user_id): return f"This chat is enforcing *gbans*: `{sql.does_chat_gban(chat_id)}`." -__help__ = f""" -*Admins only:* - • `/antispam <on/off/yes/no>`*:* Will toggle our antispam tech or return your current settings. - -Anti-Spam, used by bot devs to ban spammers across all groups. This helps protect \ -you and your groups by removing spam flooders as quickly as possible. -*Note:* Users can appeal gbans or report spammers at @{SUPPORT_CHAT} - -This also integrates @Spamwatch API to remove Spammers as much as possible from your chatroom! -*What is SpamWatch?* -SpamWatch maintains a large constantly updated ban-list of spambots, trolls, bitcoin spammers and unsavoury characters[.](https://telegra.ph/file/f584b643c6f4be0b1de53.jpg) -Constantly help banning spammers off from your group automatically So, you wont have to worry about spammers storming your group. -*Note:* Users can appeal spamwatch bans at @SpamwatchSupport -""" - -GBAN_HANDLER = CommandHandler("gban", gban) +GBAN_HANDLER = CommandHandler("szban", szban) UNGBAN_HANDLER = CommandHandler("ungban", ungban) GBAN_LIST = CommandHandler("gbanlist", gbanlist) diff --git a/Amanda/modules/grammer.py b/Amanda/modules/grammer.py index b92959b..de875fc 100644 --- a/Amanda/modules/grammer.py +++ b/Amanda/modules/grammer.py @@ -6,7 +6,6 @@ from Amanda import dispatcher -# Open API key API_KEY = "6ae0c3a0-afdc-4532-a810-82ded0054236" URL = "http://services.gingersoftware.com/Ginger/correct/json/GingerTheText" diff --git a/Amanda/modules/group.py b/Amanda/modules/group.py new file mode 100644 index 0000000..1c0c82a --- /dev/null +++ b/Amanda/modules/group.py @@ -0,0 +1,130 @@ +""" +MIT License +Copyright (c) 2021 TheHamkerCat +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +import os + +from pyrogram import filters + +from Amanda.function.pluginhelpers import member_permissions +from Amanda import pbot as app + + +@app.on_message(filters.command("setgtitle") & ~filters.private) +async def set_chat_title(_, message): + try: + chat_id = message.chat.id + user_id = message.from_user.id + permissions = await member_permissions(chat_id, user_id) + if "can_change_info" not in permissions: + await message.reply_text("You Don't Have Enough Permissions.") + return + if len(message.command) < 2: + await message.reply_text("**Usage:**\n/set_chat_title NEW NAME") + return + old_title = message.chat.title + new_title = message.text.split(None, 1)[1] + await message.chat.set_title(new_title) + await message.reply_text( + f"Successfully Changed Group Title From {old_title} To {new_title}" + ) + except Exception as e: + print(e) + await message.reply_text(e) + + +@app.on_message(filters.command("settitle") & ~filters.private) +async def set_user_title(_, message): + try: + chat_id = message.chat.id + user_id = message.from_user.id + from_user = message.reply_to_message.from_user + permissions = await member_permissions(chat_id, user_id) + if "can_change_info" not in permissions: + await message.reply_text("You Don't Have Enough Permissions.") + return + if len(message.command) < 2: + await message.reply_text( + "**Usage:**\n/set_user_title NEW ADMINISTRATOR TITLE" + ) + return + title = message.text.split(None, 1)[1] + await app.set_administrator_title(chat_id, from_user.id, title) + await message.reply_text( + f"Successfully Changed {from_user.mention}'s Admin Title To {title}" + ) + except Exception as e: + print(e) + await message.reply_text(e) + + +@app.on_message(filters.command("setgpic") & ~filters.private) +async def set_chat_photo(_, message): + try: + chat_id = message.chat.id + user_id = message.from_user.id + + permissions = await member_permissions(chat_id, user_id) + if "can_change_info" not in permissions: + await message.reply_text("You Don't Have Enough Permissions.") + return + if not message.reply_to_message: + await message.reply_text("Reply to a photo to set it as chat_photo") + return + if not message.reply_to_message.photo and not message.reply_to_message.document: + await message.reply_text("Reply to a photo to set it as chat_photo") + return + photo = await message.reply_to_message.download() + await message.chat.set_photo(photo) + await message.reply_text("Successfully Changed Group Photo") + os.remove(photo) + except Exception as e: + print(e) + await message.reply_text(e) + + +@app.on_message(filters.command("setdescription") & ~filters.private) +async def set_chat_description(_, message): + try: + chat_id = message.chat.id + user_id = message.from_user.id + permissions = await member_permissions(chat_id, user_id) + if "can_change_info" not in permissions: + await message.reply_text("You Don't Have Enough Permissions.") + return + if len(message.command) < 2: + await message.reply_text("**Usage:**\n/setdescription NEW NAME") + return + new_description = message.text.split(None, 1)[1] + await message.chat.set_description(new_description) + await message.reply_text( + f"Successfully Changed Group Description." + ) + except Exception as e: + print(e) + await message.reply_text(e) + +__help__ = """ +@TheAmandabot + ❍ /setgtitle <newtitle>*:* Sets new chat title in your group. + ❍ /setgpic*:* As a reply to file or photo to set group profile pic! + ❍ /delgpic*:* Same as above but to remove group profile pic. + ❍ /setsticker*:* As a reply to some sticker to set it as group sticker set! + ❍ /setdescription <description>*:* Sets new chat description in group. +""" +__mod_name__ = "ɢʀᴏᴜᴘ💒" diff --git a/Amanda/modules/gsearch.py b/Amanda/modules/gsearch.py index 472d4a6..0bb8b97 100644 --- a/Amanda/modules/gsearch.py +++ b/Amanda/modules/gsearch.py @@ -1,59 +1,57 @@ -import html2text + +from bs4 import BeautifulSoup +import urllib +from Amanda import telethn as tbot +import glob +import io +import os +import re +import aiohttp +import urllib.request +from urllib.parse import urlencode import requests +from bs4 import BeautifulSoup +from PIL import Image +from search_engine_parser import GoogleSearch + +import bs4 +import html2text +from bing_image_downloader import downloader from telethon import * -from telethon.tl import functions, types +from telethon.tl import functions +from telethon.tl import types from telethon.tl.types import * -from Amanda.events import register - - -async def is_register_admin(chat, user): - if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): - - return isinstance( - ( - await client(functions.channels.GetParticipantRequest(chat, user)) - ).participant, - (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), - ) - elif isinstance(chat, types.InputPeerChat): - - ui = await client.get_peer_id(user) - ps = ( - await client(functions.messages.GetFullChatRequest(chat.chat_id)) - ).full_chat.participants.participants - return isinstance( - next((p for p in ps if p.user_id == ui), None), - (types.ChatParticipantAdmin, types.ChatParticipantCreator), - ) - else: - return None +from Amanda import * +from Amanda.events import register @register(pattern="^/google (.*)") async def _(event): if event.fwd_from: return - if event.is_group: - if not (await is_register_admin(event.input_chat, event.message.sender_id)): - await event.reply( - " Hi.. You are not admin.. You can't use this command.. But you can use in my pm" - ) - return - # SHOW_DESCRIPTION = False - input_str = event.pattern_match.group( - 1 - ) # + " -inurl:(htm|html|php|pls|txt) intitle:index.of \"last modified\" (mkv|mp4|avi|epub|pdf|mp3)" - input_url = "https://bots.shrimadhavuk.me/search/?q={}".format(input_str) - headers = {"USER-AGENT": "UniBorg"} - response = requests.get(input_url, headers=headers).json() - output_str = " " - for result in response["results"]: - text = result.get("title") - url = result.get("url") - description = result.get("description") - last = html2text.html2text(description) - output_str += "[{}]({})\n{}\n".format(text, url, last) - await event.reply( - "{}".format(output_str), link_preview=False, parse_mode="Markdown" + + webevent = await event.reply("searching........") + match = event.pattern_match.group(1) + page = re.findall(r"page=\d+", match) + try: + page = page[0] + page = page.replace("page=", "") + match = match.replace("page=" + page[0], "") + except IndexError: + page = 1 + search_args = (str(match), int(page)) + gsearch = GoogleSearch() + gresults = await gsearch.async_search(*search_args) + msg = "" + for i in range(len(gresults["links"])): + try: + title = gresults["titles"][i] + link = gresults["links"][i] + desc = gresults["descriptions"][i] + msg += f"❍[{title}]({link})\n**{desc}**\n\n" + except IndexError: + break + await webevent.edit( + "**Search Query:**\n`" + match + "`\n\n**Results:**\n" + msg, link_preview=False ) diff --git a/Amanda/modules/gtranslator.py b/Amanda/modules/gtranslator.py index 43ff874..2883c4d 100644 --- a/Amanda/modules/gtranslator.py +++ b/Amanda/modules/gtranslator.py @@ -122,7 +122,8 @@ def totranslate(update: Update, context: CallbackContext): update.effective_message.reply_text( "Reply to messages or write messages from other languages for translating into the intended language\n\n" "Example: `/tr en-ml` to translate from English to Malayalam\n" - "Or use: `/tr ml` for automatic detection and translating it into Malayalam.\n", + "Or use: `/tr ml` for automatic detection and translating it into Malayalam.\n" + "See [List of Language Codes](t.me/OnePunchSupport/12823) for a list of language codes.", parse_mode="markdown", disable_web_page_preview=True, ) diff --git a/Amanda/modules/helper_funcs/extraction.py b/Amanda/modules/helper_funcs/extraction.py index 48c872b..4c421ea 100644 --- a/Amanda/modules/helper_funcs/extraction.py +++ b/Amanda/modules/helper_funcs/extraction.py @@ -182,7 +182,7 @@ def extract_user_fban(message: Message, args: List[str]) -> Optional[int]: async def get_user(event): - """Get the user from argument or replied message.""" + """ Get the user from argument or replied message. """ if event.reply_to_msg_id: previous_message = await event.get_reply_message() if previous_message: diff --git a/Amanda/modules/helper_funcs/fun_strings.py b/Amanda/modules/helper_funcs/fun_strings.py index abd03c4..14171d1 100644 --- a/Amanda/modules/helper_funcs/fun_strings.py +++ b/Amanda/modules/helper_funcs/fun_strings.py @@ -1,3 +1,4 @@ +# taken from @zoldycktmbot TRUTH = ( "Do you pick your nose?", "Do you sing in the shower? ", @@ -104,7 +105,7 @@ ) - +# Taken from Openuserbot. GDNIGHT = [ "`Good night keep your dreams alive`", "`Night, night, to a dear friend! May you sleep well!`", @@ -143,7 +144,7 @@ "`Every day, you encourage me to do new things, friend! May tonight’s rest bring a new day that overflows with courage and exciting events!`", ] - +# Taken from Openuserbot. GDMORNING = [ "`Life is full of uncertainties. But there will always be a sunrise after every sunset. Good morning!`", "`It doesn’t matter how bad was your yesterday. Today, you are going to make it a good one. Wishing you a good morning!`", @@ -182,7 +183,7 @@ "`It is easy to imagine the world coming to an end. But it is difficult to imagine spending a day without my friends. Good morning.`", ] - +# Abuse strings credits @NotAMemeBot ABUSE_STRINGS = ( "Owww ... Such a stupid idiot.", "Don't drink and type.", @@ -301,7 +302,7 @@ ) - +# pat - by @saitamarobot PAT_TEMPLATES = ( "Pattt Se Headshot!", "{user1} pats {user2} on the head.", @@ -368,7 +369,7 @@ ) - +# SLAP_TEMPLATES From @Amanda SLAP_TEMPLATES = ( "{user2} was shot by {user1}.", "{user2} walked into a cactus while trying to escape {user1}.", @@ -543,7 +544,7 @@ HUG = ("hugs", "hugged", "kissed", "pinches") - +# Table strings credits: @Amanda TABLE = ( "(╯°□°)╯彡 ┻━┻", @@ -595,7 +596,7 @@ DECIDE = ("Yes.", "NoU.", "Maybe.", "No.") - +# AFK strings credits: PaperPlaneExtended! AFKRPL = ( "<b>{}</b> is busy right now. Please talk in a bag and when they come back you can just give him the bag!", "<b>{}</b> is away right now. If you need anything, leave a message after the beep:\n<code>beeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep</code>!", diff --git a/Amanda/modules/helper_funcs/misc.py b/Amanda/modules/helper_funcs/misc.py index 9fdd51e..83a493b 100644 --- a/Amanda/modules/helper_funcs/misc.py +++ b/Amanda/modules/helper_funcs/misc.py @@ -1,10 +1,10 @@ +from math import ceil from typing import Dict, List +from Amanda import NO_LOAD from telegram import MAX_MESSAGE_LENGTH, Bot, InlineKeyboardButton, ParseMode from telegram.error import TelegramError -from Amanda import NO_LOAD - class EqInlineKeyboardButton(InlineKeyboardButton): def __eq__(self, other): @@ -21,60 +21,57 @@ def split_message(msg: str) -> List[str]: if len(msg) < MAX_MESSAGE_LENGTH: return [msg] - else: - lines = msg.splitlines(True) - small_msg = "" - result = [] - for line in lines: - if len(small_msg) + len(line) < MAX_MESSAGE_LENGTH: - small_msg += line - else: - result.append(small_msg) - small_msg = line + lines = msg.splitlines(True) + small_msg = "" + result = [] + for line in lines: + if len(small_msg) + len(line) < MAX_MESSAGE_LENGTH: + small_msg += line else: - # Else statement at the end of the for loop, so append the leftover string. result.append(small_msg) + small_msg = line + else: + # Else statement at the end of the for loop, so append the leftover string. + result.append(small_msg) - return result + return result def paginate_modules(page_n: int, module_dict: Dict, prefix, chat=None) -> List: if not chat: modules = sorted( - [ - EqInlineKeyboardButton( - x.__mod_name__, - callback_data="{}_module({})".format( - prefix, x.__mod_name__.lower() - ), - ) - for x in module_dict.values() - ] - ) + [EqInlineKeyboardButton(x.__mod_name__, + callback_data="{}_module({})".format(prefix, x.__mod_name__.lower())) for x + in module_dict.values()]) else: modules = sorted( - [ - EqInlineKeyboardButton( - x.__mod_name__, - callback_data="{}_module({},{})".format( - prefix, chat, x.__mod_name__.lower() - ), - ) - for x in module_dict.values() - ] - ) - - pairs = [modules[i * 3 : (i + 1) * 3] for i in range((len(modules) + 3 - 1) // 3)] + [EqInlineKeyboardButton(x.__mod_name__, + callback_data="{}_module({},{})".format(prefix, chat, x.__mod_name__.lower())) for x + in module_dict.values()]) + + pairs = [ + modules[i * 3:(i + 1) * 3] for i in range((len(modules) + 3 - 1) // 3) + ] round_num = len(modules) / 3 calc = len(modules) - round(round_num) if calc == 1: - pairs.append((modules[-1],)) + pairs.append((modules[-1], )) elif calc == 2: - pairs.append((modules[-1],)) + pairs.append((modules[-1], )) + + max_num_pages = ceil(len(pairs) / 8) + modulo_page = page_n % max_num_pages + + # can only have a certain amount of buttons side by side + if len(pairs) > 10: + pairs = pairs[modulo_page * 10:10 * (modulo_page + 1)] + [ + (EqInlineKeyboardButton("ᐊ", callback_data="{}_prev({})".format(prefix, modulo_page)), + EqInlineKeyboardButton("ʜᴏᴍᴇ", callback_data="aboutmanu_"), + EqInlineKeyboardButton("ᐅ", callback_data="{}_next({})".format(prefix, modulo_page)))] else: - pairs += [[EqInlineKeyboardButton("Back to Info", callback_data="aboutmanu_")]] + pairs += [[EqInlineKeyboardButton("ʜᴏᴍᴇ", callback_data="aboutmanu_")]] return pairs diff --git a/Amanda/modules/heroku.py b/Amanda/modules/heroku.py new file mode 100644 index 0000000..682c573 --- /dev/null +++ b/Amanda/modules/heroku.py @@ -0,0 +1,258 @@ +import asyncio +import math +import os +import sys + +import heroku3 +import requests + +from Amanda import telethn as borg, HEROKU_APP_NAME, HEROKU_API_KEY, OWNER_ID +from Amanda.events import register +from Amanda.function.heroku_helper import HerokuHelper + +heroku_api = "https://api.heroku.com" +Heroku = heroku3.from_key(HEROKU_API_KEY) + + +@register(pattern="^/(set|see|del) var(?: |$)(.*)(?: |$)([\s\S]*)") +async def variable(var): + if var.fwd_from: + return + if var.sender_id == OWNER_ID: + pass + else: + return + """ + Manage most of ConfigVars setting, set new var, get current var, + or delete var... + """ + if HEROKU_APP_NAME is not None: + app = Heroku.app(HEROKU_APP_NAME) + else: + return await var.reply("`[HEROKU]:" "\nPlease setup your` **HEROKU_APP_NAME**") + exe = var.pattern_match.group(1) + heroku_var = app.config() + if exe == "see": + k = await var.reply("`Getting information...`") + await asyncio.sleep(1.5) + try: + variable = var.pattern_match.group(2).split()[0] + if variable in heroku_var: + return await k.edit( + "**ConfigVars**:" f"\n\n`{variable} = {heroku_var[variable]}`\n" + ) + else: + return await k.edit( + "**ConfigVars**:" f"\n\n`Error:\n-> {variable} don't exists`" + ) + except IndexError: + configs = prettyjson(heroku_var.to_dict(), indent=2) + with open("configs.json", "w") as fp: + fp.write(configs) + with open("configs.json", "r") as fp: + result = fp.read() + if len(result) >= 4096: + await var.client.send_file( + var.chat_id, + "configs.json", + reply_to=var.id, + caption="`Output too large, sending it as a file`", + ) + else: + await k.edit( + "`[HEROKU]` ConfigVars:\n\n" + "================================" + f"\n```{result}```\n" + "================================" + ) + os.remove("configs.json") + return + elif exe == "set": + s = await var.reply("`Setting information...weit ser`") + variable = var.pattern_match.group(2) + if not variable: + return await s.edit(">`.set var <ConfigVars-name> <value>`") + value = var.pattern_match.group(3) + if not value: + variable = variable.split()[0] + try: + value = var.pattern_match.group(2).split()[1] + except IndexError: + return await s.edit(">`/set var <ConfigVars-name> <value>`") + await asyncio.sleep(1.5) + if variable in heroku_var: + await s.edit( + f"**{variable}** `successfully changed to` -> **{value}**" + ) + else: + await s.edit( + f"**{variable}** `successfully added with value` -> **{value}**" + ) + heroku_var[variable] = value + elif exe == "del": + m = await var.edit("`Getting information to deleting variable...`") + try: + variable = var.pattern_match.group(2).split()[0] + except IndexError: + return await m.edit("`Please specify ConfigVars you want to delete`") + await asyncio.sleep(1.5) + if variable in heroku_var: + await m.edit(f"**{variable}** `successfully deleted`") + del heroku_var[variable] + else: + return await m.edit(f"**{variable}** `is not exists`") + + +@register(pattern="^/usage(?: |$)") +async def dyno_usage(dyno): + if dyno.fwd_from: + return + if dyno.sender_id == OWNER_ID: + pass + else: + return + """ + Get your account Dyno Usage + """ + die = await dyno.reply("**Processing...**") + useragent = ( + "Mozilla/5.0 (Linux; Android 10; SM-G975F) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/80.0.3987.149 Mobile Safari/537.36" + ) + user_id = Heroku.account().id + headers = { + "User-Agent": useragent, + "Authorization": f"Bearer {HEROKU_API_KEY}", + "Accept": "application/vnd.heroku+json; version=3.account-quotas", + } + path = "/accounts/" + user_id + "/actions/get-quota" + r = requests.get(heroku_api + path, headers=headers) + if r.status_code != 200: + return await die.edit( + "`Error: something bad happened`\n\n" f">.`{r.reason}`\n" + ) + result = r.json() + quota = result["account_quota"] + quota_used = result["quota_used"] + + """ - Used - """ + remaining_quota = quota - quota_used + percentage = math.floor(remaining_quota / quota * 100) + minutes_remaining = remaining_quota / 60 + hours = math.floor(minutes_remaining / 60) + minutes = math.floor(minutes_remaining % 60) + + """ - Current - """ + App = result["apps"] + try: + App[0]["quota_used"] + except IndexError: + AppQuotaUsed = 0 + AppPercentage = 0 + else: + AppQuotaUsed = App[0]["quota_used"] / 60 + AppPercentage = math.floor(App[0]["quota_used"] * 100 / quota) + AppHours = math.floor(AppQuotaUsed / 60) + AppMinutes = math.floor(AppQuotaUsed % 60) + + await asyncio.sleep(1.5) + + return await die.edit( + "**Dyno Usage 📊**:\n\n" + f" -> `Dyno usage for` **Rose bot **:\n" + f" • `{AppHours}`**h** `{AppMinutes}`**m** " + f"**|** [`{AppPercentage}`**%**]" + "\n\n" + " -> `Dyno hours quota remaining this month`:\n" + f" • `{hours}`**h** `{minutes}`**m** " + f"**|** [`{percentage}`**%**]" + ) + +@register(pattern="^/restart$") +async def restart_bot(dyno): + if dyno.fwd_from: + return + if dyno.sender_id == OWNER_ID: + pass + else: + return await dyno.reply("Rosebot will be restarted..." + ) + args = [sys.executable, "-m", "Amanda"] + os.execl(sys.executable, *args) + +@register(pattern="^/update$") +async def upgrade(dyno): + if dyno.fwd_from: + return + if dyno.sender_id == OWNER_ID: + pass + else: + return await dyno.reply( + "`Checking for updates, please wait....`" + ) + m = await dyno.reply("**Your bot is being deployed, please wait for it to complete.\nIt may take upto 5 minutes **") + proc = await asyncio.create_subprocess_shell( + "git pull --no-edit", + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.STDOUT, + ) + stdout = (await proc.communicate())[0] + if proc.returncode == 0: + if "Already up to date." in stdout.decode(): + await m.edit_text("There's nothing to upgrade.") + else: + await m.edit_text("Restarting...") + args = [sys.executable, "-m", "Amanda"] + os.execl(sys.executable, *args) + else: + await m.edit_text( + f"Upgrade failed (process exited with {proc.returncode}):\n{stdout.decode()}" + ) + proc = await asyncio.create_subprocess_shell("git merge --abort") + await proc.communicate() + + +@register(pattern="^/logs$") +async def _(dyno): + if dyno.fwd_from: + return + if dyno.sender_id == OWNER_ID: + pass + else: + return + try: + Heroku = heroku3.from_key(HEROKU_API_KEY) + app = Heroku.app(HEROKU_APP_NAME) + except: + return await dyno.reply( + " Please make sure your Heroku API Key, Your App name are configured correctly in the heroku" + ) + v = await dyno.reply("Getting Logs....") + with open("logs.txt", "w") as log: + log.write(app.get_log()) + await v.edit("Got the logs wait a sec") + await dyno.client.send_file( + dyno.chat_id, + "logs.txt", + reply_to=dyno.id, + caption=" Bot Logz.", + ) + + await asyncio.sleep(5) + await v.delete() + return os.remove("logs.txt") + + +def prettyjson(obj, indent=2, maxlinelength=80): + """Renders JSON content with indentation and line splits/concatenations to fit maxlinelength. + Only dicts, lists and basic types are supported""" + + items, _ = getsubitems( + obj, + itemkey="", + islast=True, + maxlinelength=maxlinelength - indent, + indent=indent, + ) + return indentitems(items, indent, level=0) diff --git a/Amanda/modules/imgeditor.py b/Amanda/modules/imgeditor.py new file mode 100644 index 0000000..3c4f366 --- /dev/null +++ b/Amanda/modules/imgeditor.py @@ -0,0 +1,465 @@ +# By @TroJanzHEX + + +from pyrogram import filters +from pyrogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message, +) + +# By @TroJanzHEX +from Amanda.Addons.ImageEditor.edit_1 import ( # pylint:disable=import-error + black_white, + box_blur, + bright, + g_blur, + mix, + normal_blur, +) +from Amanda.Addons.ImageEditor.edit_2 import ( # pylint:disable=import-error + cartoon, + circle_with_bg, + circle_without_bg, + contrast, + edge_curved, + pencil, + sepia_mode, + sticker, +) +from Amanda.Addons.ImageEditor.edit_3 import ( # pylint:disable=import-error + black_border, + blue_border, + green_border, + red_border, +) +from Amanda.Addons.ImageEditor.edit_4 import ( # pylint:disable=import-error + inverted, + removebg_plain, + removebg_sticker, + removebg_white, + rotate_90, + rotate_180, + rotate_270, + round_sticker, +) +from Amanda.Addons.ImageEditor.edit_5 import ( # pylint:disable=import-error + normalglitch_1, + normalglitch_2, + normalglitch_3, + normalglitch_4, + normalglitch_5, + scanlineglitch_1, + scanlineglitch_2, + scanlineglitch_3, + scanlineglitch_4, + scanlineglitch_5, +) +from Amanda.services.pyrogram import pbot as Client + +lel = 00000000 +# pylint:disable=import-error +@Client.on_message(filters.command(["edit", "editor"])) +async def photo(client: Client, message: Message): + try: + if not message.reply_to_message.photo: + await client.send_message(message.chat.id, "Reply to an image man!ㅤㅤ") + return + except: + return + global lel + try: + lel = message.from_user.id + except: + return + try: + await client.send_message( + chat_id=message.chat.id, + text="Select your required mode from below! buttons\n powerd by @TheAmandabotㅤ", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton(text="💡 BRIGHT", callback_data="bright"), + InlineKeyboardButton(text="🖼 MIXED", callback_data="mix"), + InlineKeyboardButton(text="🔳 B&W", callback_data="b|w"), + ], + [ + InlineKeyboardButton(text="🟡 CIRCLE", callback_data="circle"), + InlineKeyboardButton(text="🩸 BLUR", callback_data="blur"), + InlineKeyboardButton(text="🌌 BORDER", callback_data="border"), + ], + [ + InlineKeyboardButton(text="🎉 STICKER", callback_data="stick"), + InlineKeyboardButton(text="↩️ ROTATE", callback_data="rotate"), + InlineKeyboardButton( + text="🔦 CONTRAST", callback_data="contrast" + ), + ], + [ + InlineKeyboardButton(text="🌇 SEPIA", callback_data="sepia"), + InlineKeyboardButton(text="✏️ PENCIL", callback_data="pencil"), + InlineKeyboardButton(text="🐶 CARTOON", callback_data="cartoon"), + ], + [ + InlineKeyboardButton(text="🔄 INVERT", callback_data="inverted"), + InlineKeyboardButton(text="🔮 GLITCH", callback_data="glitch"), + InlineKeyboardButton( + text="✂️ REMOVE BG", callback_data="removebg" + ), + ], + [ + InlineKeyboardButton(text="✮ CLOSE ✮", callback_data="close_e"), + ], + ] + ), + reply_to_message_id=message.reply_to_message.message_id, + ) + except Exception as e: + print("photomarkup error - " + str(e)) + if "USER_IS_BLOCKED" in str(e): + return + else: + try: + await message.reply_text("Something went wrong!", quote=True) + except Exception: + return + + +@Client.on_callback_query() +async def cb_handler(client: Client, query: CallbackQuery): + user_id = query.from_user.id + if lel == user_id: + if query.data == "removebg": + await query.message.edit_text( + "**Select required mode**ㅤㅤㅤㅤ", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + text="WITH WHITE BG", callback_data="rmbgwhite" + ), + InlineKeyboardButton( + text="WITHOUT BG", callback_data="rmbgplain" + ), + ], + [ + InlineKeyboardButton( + text="STICKER", callback_data="rmbgsticker" + ) + ], + ] + ), + ) + elif query.data == "stick": + await query.message.edit( + "**Select a Type**", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton(text="Normal", callback_data="stkr"), + InlineKeyboardButton( + text="Edge Curved", callback_data="cur_ved" + ), + ], + [ + InlineKeyboardButton( + text="Circle", callback_data="circle_sticker" + ) + ], + ] + ), + ) + elif query.data == "rotate": + await query.message.edit_text( + "**Select the Degree**", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton(text="180", callback_data="180"), + InlineKeyboardButton(text="90", callback_data="90"), + ], + [InlineKeyboardButton(text="270", callback_data="270")], + ] + ), + ) + + elif query.data == "glitch": + await query.message.edit_text( + "**Select required mode**ㅤㅤㅤㅤ", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + text="NORMAL", callback_data="normalglitch" + ), + InlineKeyboardButton( + text="SCAN LINES", callback_data="scanlineglitch" + ), + ] + ] + ), + ) + elif query.data == "normalglitch": + await query.message.edit_text( + "**Select Glitch power level**", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + text="1", callback_data="normalglitch1" + ), + InlineKeyboardButton( + text="2", callback_data="normalglitch2" + ), + InlineKeyboardButton( + text="3", callback_data="normalglitch3" + ), + ], + [ + InlineKeyboardButton( + text="4", callback_data="normalglitch4" + ), + InlineKeyboardButton( + text="5", callback_data="normalglitch5" + ), + ], + ] + ), + ) + elif query.data == "scanlineglitch": + await query.message.edit_text( + "**Select Glitch power level**", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + text="1", callback_data="scanlineglitch1" + ), + InlineKeyboardButton( + text="2", callback_data="scanlineglitch2" + ), + InlineKeyboardButton( + text="3", callback_data="scanlineglitch3" + ), + ], + [ + InlineKeyboardButton( + text="4", callback_data="scanlineglitch4" + ), + InlineKeyboardButton( + text="5", callback_data="scanlineglitch5" + ), + ], + ] + ), + ) + elif query.data == "blur": + await query.message.edit( + "**Select a Type**", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton(text="box", callback_data="box"), + InlineKeyboardButton(text="normal", callback_data="normal"), + ], + [InlineKeyboardButton(text="Gaussian", callback_data="gas")], + ] + ), + ) + elif query.data == "circle": + await query.message.edit_text( + "**Select required mode**", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + text="WITH BG", callback_data="circlewithbg" + ), + InlineKeyboardButton( + text="WITHOUT BG", callback_data="circlewithoutbg" + ), + ] + ] + ), + ) + elif query.data == "border": + await query.message.edit( + "**Select Border**", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton(text="🔴 RED 🔴", callback_data="red"), + InlineKeyboardButton( + text="🟢 Green 🟢", callback_data="green" + ), + ], + [ + InlineKeyboardButton( + text="⚫ Black ⚫", callback_data="black" + ), + InlineKeyboardButton(text="🔵 Blue 🔵", callback_data="blue"), + ], + ] + ), + ) + + elif query.data == "bright": + await query.message.delete() + await bright(client, query.message) + + elif query.data == "close_e": + await query.message.delete() + + elif query.data == "mix": + await query.message.delete() + await mix(client, query.message) + + elif query.data == "b|w": + await query.message.delete() + await black_white(client, query.message) + + elif query.data == "circlewithbg": + await query.message.delete() + await circle_with_bg(client, query.message) + + elif query.data == "circlewithoutbg": + await query.message.delete() + await circle_without_bg(client, query.message) + + elif query.data == "green": + await query.message.delete() + await green_border(client, query.message) + + elif query.data == "blue": + await query.message.delete() + await blue_border(client, query.message) + + elif query.data == "red": + await query.message.delete() + await red_border(client, query.message) + + elif query.data == "black": + await query.message.delete() + await black_border(client, query.message) + + elif query.data == "circle_sticker": + await query.message.delete() + await round_sticker(client, query.message) + + elif query.data == "inverted": + await query.message.delete() + await inverted(client, query.message) + + elif query.data == "stkr": + await query.message.delete() + await sticker(client, query.message) + + elif query.data == "cur_ved": + await query.message.delete() + await edge_curved(client, query.message) + + elif query.data == "90": + await query.message.delete() + await rotate_90(client, query.message) + + elif query.data == "180": + await query.message.delete() + await rotate_180(client, query.message) + + elif query.data == "270": + await query.message.delete() + await rotate_270(client, query.message) + + elif query.data == "contrast": + await query.message.delete() + await contrast(client, query.message) + + elif query.data == "box": + await query.message.delete() + await box_blur(client, query.message) + + elif query.data == "gas": + await query.message.delete() + await g_blur(client, query.message) + + elif query.data == "normal": + await query.message.delete() + await normal_blur(client, query.message) + + elif query.data == "sepia": + await query.message.delete() + await sepia_mode(client, query.message) + + elif query.data == "pencil": + await query.message.delete() + await pencil(client, query.message) + + elif query.data == "cartoon": + await query.message.delete() + await cartoon(client, query.message) + + elif query.data == "normalglitch1": + await query.message.delete() + await normalglitch_1(client, query.message) + + elif query.data == "normalglitch2": + await query.message.delete() + await normalglitch_2(client, query.message) + + elif query.data == "normalglitch3": + await normalglitch_3(client, query.message) + + elif query.data == "normalglitch4": + await query.message.delete() + await normalglitch_4(client, query.message) + + elif query.data == "normalglitch5": + await query.message.delete() + await normalglitch_5(client, query.message) + + elif query.data == "scanlineglitch1": + await query.message.delete() + await scanlineglitch_1(client, query.message) + + elif query.data == "scanlineglitch2": + await query.message.delete() + await scanlineglitch_2(client, query.message) + + elif query.data == "scanlineglitch3": + await query.message.delete() + await scanlineglitch_3(client, query.message) + + elif query.data == "scanlineglitch4": + await query.message.delete() + await scanlineglitch_4(client, query.message) + + elif query.data == "scanlineglitch5": + await query.message.delete() + await scanlineglitch_5(client, query.message) + + elif query.data == "rmbgwhite": + await query.message.delete() + await removebg_white(client, query.message) + + elif query.data == "rmbgplain": + await query.message.delete() + await removebg_plain(client, query.message) + + elif query.data == "rmbgsticker": + await removebg_sticker(client, query.message) + + +__help__ = """ +@TheAmandabot +** IMAGE EDITOR ** + +szrose have some advanced image editing tools inbuilt +Bright, Circle, RemBG, Blur, Border, Flip, Glitch, Sticker maker and more.. + +❍ /edit [reply to image]: Open the image editor +❍ /rmbg [REPLY]: Revove BG of replied image/sticker. + +` Special credits to TroJanzHEX ` +""" +__mod_name__ = "ɪ-ᴇᴅɪᴛᴏʀ🖼️" diff --git a/Amanda/modules/locks.py b/Amanda/modules/locks.py index 1875b01..e087fbc 100644 --- a/Amanda/modules/locks.py +++ b/Amanda/modules/locks.py @@ -138,7 +138,7 @@ def unrestr_members( @run_async def locktypes(update, context): update.effective_message.reply_text( - "\n • ".join( + "\n 👉 ".join( ["Locks available: "] + sorted(list(LOCK_TYPES) + list(LOCK_CHAT_RESTRICTION)) ) @@ -481,10 +481,10 @@ def build_lock_message(chat_id): locklist.sort() # Building lock list string for x in locklist: - res += "\n • {}".format(x) + res += "\n 👉 {}".format(x) res += "\n\n*" + "These are the current chat permissions:" + "*" for x in permslist: - res += "\n • {}".format(x) + res += "\n 👉 {}".format(x) return res @@ -555,16 +555,17 @@ def __chat_settings__(chat_id, user_id): __help__ = """ +@TheAmandabot Do stickers annoy you? or want to avoid people sharing links? or pictures? \ You're in the right place! The locks module allows you to lock away some common items in the \ telegram world; the bot will automatically delete them! - • `/locktypes`*:* Lists all possible locktypes +❍ `/locktypes`*:* Lists all possible locktypes *Admins only:* - • `/lock <type>`*:* Lock items of a certain type (not available in private) - • `/unlock <type>`*:* Unlock items of a certain type (not available in private) - • `/locks`*:* The current list of locks in this chat. + ❍ `/lock <type>`*:* Lock items of a certain type (not available in private) + ❍ `/unlock <type>`*:* Unlock items of a certain type (not available in private) + ❍ `/locks`*:* The current list of locks in this chat. Locks can be used to restrict a group's users. eg: @@ -572,11 +573,12 @@ def __chat_settings__(chat_id, user_id): non-admin users from sending stickers, etc. Locking bots will stop non-admins from adding bots to the chat. *Note:* - • Unlocking permission *info* will allow members (non-admins) to change the group information, such as the description or the group name - • Unlocking permission *pin* will allow members (non-admins) to pinned a message in a group + ❍ Unlocking permission *info* will allow members (non-admins) to change the group information, such as the description or the group name + ❍ Unlocking permission *pin* will allow members (non-admins) to pinned a message in a group + """ -__mod_name__ = "Locks" +__mod_name__ = "ʟᴏᴄᴋꜱ🔏" LOCKTYPES_HANDLER = DisableAbleCommandHandler("locktypes", locktypes) LOCK_HANDLER = CommandHandler("lock", lock, pass_args=True) # , filters=Filters.group) diff --git a/Amanda/modules/log_channel.py b/Amanda/modules/log_channel.py index 487c233..71eb429 100644 --- a/Amanda/modules/log_channel.py +++ b/Amanda/modules/log_channel.py @@ -180,7 +180,7 @@ def unsetlog(update: Update, context: CallbackContext): message.reply_text("No log channel has been set yet!") def __stats__(): - return f"• {sql.num_logchannels()} log channels set." + return f"•{sql.num_logchannels()} log channels set." def __migrate__(old_chat_id, new_chat_id): sql.migrate_chat(old_chat_id, new_chat_id) @@ -193,18 +193,20 @@ def __chat_settings__(chat_id, user_id): return "No log channel is set for this group!" __help__ = """ + @TheAmandabot *Admins only:* -✪ /logchannel*:* get log channel info -✪ /setlog*:* set the log channel. -✪ /unsetlog*:* unset the log channel. +❍ /logchannel*:* get log channel info +❍ /setlog*:* set the log channel. +❍ /unsetlog*:* unset the log channel. *Setting the log channel is done by:* *1.* adding the bot to the desired channel (as an admin!) *2.* sending `/setlog` in the channel *3.* forwarding the `/setlog` to the group + """ - __mod_name__ = "Logger" + __mod_name__ = "ᴄʜᴀɴɴᴇʟ🤺" LOG_HANDLER = CommandHandler("logchannel", logging) SET_LOG_HANDLER = CommandHandler("setlog", setlog) diff --git a/Amanda/modules/lyrics.py b/Amanda/modules/lyrics.py new file mode 100644 index 0000000..fc7ead5 --- /dev/null +++ b/Amanda/modules/lyrics.py @@ -0,0 +1,25 @@ +import io +import os +import requests +from pyrogram import filters + +from pyrogram import Client +from pyrogram import Client as pbot + +#dont edit credits i will fuck you 🤣 Made by me for @TheAmandabot ~ youtubeslgeekshow + + + +@pbot.on_message(filters.command(["lyric", "lyrics"])) +async def liri(client, message): + try: + if len(message.command) < 2: + await message.reply_text("**give a lyric name too !**") + return + query = message.text.split(None, 1)[1] + rep = await message.reply_text("🔎 **searching lyrics...**") + resp = requests.get(f"https://api-tede.herokuapp.com/api/lirik?l={query}").json() + result = f"{resp['data']}" + await rep.edit(result) + except Exception: + await rep.edit("**Lyrics not found.** please give a valid song name !!!") diff --git a/Amanda/modules/math.py b/Amanda/modules/math.py index f04e35c..db22b8f 100644 --- a/Amanda/modules/math.py +++ b/Amanda/modules/math.py @@ -114,29 +114,31 @@ def log(update: Update, context: CallbackContext): __help__ = """ +@TheAmandabot Solves complex math problems using https://newton.now.sh - ✪ /math*:* Math `/math 2^2+2(2)` - ✪ /factor*:* Factor `/factor x^2 + 2x` - ✪ /derive*:* Derive `/derive x^2+2x` - ✪ /integrate*:* Integrate `/integrate x^2+2x` - ✪ /zeroes*:* Find 0's `/zeroes x^2+2x` - ✪ /tangent*:* Find Tangent `/tangent 2lx^3` - ✪ /area*:* Area Under Curve `/area 2:4lx^3` - ✪ /cos*:* Cosine `/cos pi` - ✪ /sin*:* Sine `/sin 0` - ✪ /tan*:* Tangent `/tan 0` - ✪ /arccos*:* Inverse Cosine `/arccos 1` - ✪ /arcsin*:* Inverse Sine `/arcsin 0` - ✪ /arctan*:* Inverse Tangent `/arctan 0` - ✪ /abs*:* Absolute Value `/abs -1` - ✪ /log*:* Logarithm `/log 2l8` + ❍ /math*:* Math `/math 2^2+2(2)` + ❍ /factor*:* Factor `/factor x^2 + 2x` + ❍ /derive*:* Derive `/derive x^2+2x` + ❍ /integrate*:* Integrate `/integrate x^2+2x` + ❍ /zeroes*:* Find 0's `/zeroes x^2+2x` + ❍ /tangent*:* Find Tangent `/tangent 2lx^3` + ❍ /area*:* Area Under Curve `/area 2:4lx^3` + ❍ /cos*:* Cosine `/cos pi` + ❍ /sin*:* Sine `/sin 0` + ❍ /tan*:* Tangent `/tan 0` + ❍ /arccos*:* Inverse Cosine `/arccos 1` + ❍ /arcsin*:* Inverse Sine `/arcsin 0` + ❍ /arctan*:* Inverse Tangent `/arctan 0` + ❍ /abs*:* Absolute Value `/abs -1` + ❍ /log*:* Logarithm `/log 2l8` _Keep in mind_: To find the tangent line of a function at a certain x value, send the request as c|f(x) where c is the given x value and f(x) is the function expression, the separator is a vertical bar '|'. See the table above for an example request. To find the area under a function, send the request as c:d|f(x) where c is the starting x value, d is the ending x value, and f(x) is the function under which you want the curve between the two x values. To compute fractions, enter expressions as numerator(over)denominator. For example, to process 2/4 you must send in your expression as 2(over)4. The result expression will be in standard math notation (1/2, 3/4). + """ -__mod_name__ = "Math" +__mod_name__ = "ᴍᴀᴛʜ🧮" SIMPLIFY_HANDLER = DisableAbleCommandHandler("math", simplify) FACTOR_HANDLER = DisableAbleCommandHandler("factor", factor) diff --git a/Amanda/modules/meme.py b/Amanda/modules/meme.py index 1a10edc..4054c98 100644 --- a/Amanda/modules/meme.py +++ b/Amanda/modules/meme.py @@ -10,10 +10,7 @@ import Amanda.modules.helper_funcs.fun_strings as fun from Amanda import DEMONS, DRAGONS, dispatcher -from Amanda.modules.disable import ( - DisableAbleCommandHandler, - DisableAbleMessageHandler, -) +from Amanda.modules.disable import DisableAbleCommandHandler, DisableAbleMessageHandler from Amanda.modules.helper_funcs.alternate import typing_action from Amanda.modules.helper_funcs.extraction import extract_user @@ -494,35 +491,36 @@ def goodmorning(update, context): __help__ = """ +@TheAmandabot *Some dank memes for fun or whatever!* - ✪ /sanitize*:* Sanitize Your Self - ✪ /shrug or /cri*:* Get shrug or ToT. - ✪ /decide*:* Randomly answer yes no etc. - ✪ /abuse*:* Abuses the retard! - ✪ /table*:* Flips a table... - ✪ runs*:* Reply a random string from an array of replies. - ✪ /slap*:* Slap a user, or get slapped if not a reply. - ✪ /pasta*:* Famous copypasta meme, try and see. - ✪ /clap*:* Claps on someones message! - ✪ /owo*:* UwU-fy whole text XD. - ✪ /roll*:* Rolls a dice - ✪ /recite*:* Logical quotes to change your life. - ✪ /stretch*:* streeeeeeetch iiiiiiit. - ✪ /hug*:* Hug a user warmly, or get hugged if not a reply. - ✪ /pat*:* pats a user, or get patted - ✪ /shout*:* write anything you want to give loud shoute - ✪ /plet <text>*:* make ur text sticker in different colours + ❍ /sanitize*:* Sanitize Your Self + ❍ /shrug or /cri*:* Get shrug or ToT. + ❍ /decide*:* Randomly answer yes no etc. + ❍ /abuse*:* Abuses the retard! + ❍ /table*:* Flips a table... + ❍ runs*:* Reply a random string from an array of replies. + ❍ /slap*:* Slap a user, or get slapped if not a reply. + ❍ /pasta*:* Famous copypasta meme, try and see. + ❍ /clap*:* Claps on someones message! + ❍ /owo*:* UwU-fy whole text XD. + ❍ /roll*:* Rolls a dice + ❍ /recite*:* Logical quotes to change your life. + ❍ /stretch*:* streeeeeeetch iiiiiiit. + ❍ /hug*:* Hug a user warmly, or get hugged if not a reply. + ❍ /pat*:* pats a user, or get patted + ❍ /shout*:* write anything you want to give loud shoute + ❍ /plet <text>*:* make ur text sticker in different colours - ✪ /truth or /dare*:* Send random truth or dare. + ❍ /truth or /dare*:* Send random truth or dare. *Memes* -✪ /hitler*:* Quote a message and type this command to make a caption of hitler -✪ /mock*:* Does the same as /hitler but spongemock instead -✪ /kim*:* Does the same as /hitler but with Kim Jong Un instead (O no plox dont bomb my house) -✪ /rmeme*:* Sends random meme scraped from reddit +❍ /hitler*:* Quote a message and type this command to make a caption of hitler +❍ /mock*:* Does the same as /hitler but spongemock instead +❍ /kim*:* Does the same as /hitler but with Kim Jong Un instead (O no plox dont bomb my house) +❍ /rmeme*:* Sends random meme scraped from reddit *Regex based memes:* -✪ /decide can be also used with regex like: `Liza? <question>: randomly answer "Yes, No" etc.` +❍ /decide can be also used with regex like: `Liza? <question>: randomly answer "Yes, No" etc.` Some other regex filters are: `goodmorning`, `good morning` or `goodnight`, `good night`. @@ -532,7 +530,7 @@ def goodmorning(update, context): """ -__mod_name__ = "Memes" +__mod_name__ = "ᴍᴇᴍᴇꜱ🃏" PAT_HANDLER = DisableAbleCommandHandler("pat", pat) diff --git a/Amanda/modules/misc.py b/Amanda/modules/misc.py index 52c4613..837aff9 100644 --- a/Amanda/modules/misc.py +++ b/Amanda/modules/misc.py @@ -604,7 +604,7 @@ def fpaste(update, context): @run_async def stats(update, context): - stats = f"┎─⌈ <b>Current {dispatcher.bot.first_name} Stats</b> ⌋\n" + "\n".join( + stats = f"┎─⌈ <b>Current {dispatcher.bot.first_name} Stats</b> ⌋\n❤️🔥" + "\n".join( [mod.__stats__() for mod in STATS] ) result = re.sub(r"(\d+)", r"<code>\1</code>", stats) @@ -613,49 +613,50 @@ def stats(update, context): # /ip is for private use __help__ = """ - - ✪ /gdpr: Deletes your information from the bot's database. Private chats only. - ✪ /markdownhelp: Quick summary of how markdown works in telegram - can only be called in private chats. - ✪ /removebotkeyboard: Got a nasty bot keyboard stuck in your group? +@TheAmandabot + ❍ /gdpr: Deletes your information from the bot's database. Private chats only. + ❍ /markdownhelp: Quick summary of how markdown works in telegram - can only be called in private chats. + ❍ /removebotkeyboard: Got a nasty bot keyboard stuck in your group? *➩Info:* - ✪ /whois: Get information about user using pyrogram method. + ❍ /whois: Get information about user using pyrogram method. *➩Translator:* - ✪ /tr or /tl: To translate to your language, by default language is set to english, use /tr <lang code> for some other language! - ✪ /splcheck: As a reply to get grammar corrected text of gibberish message. - ✪ /tts: To some message to convert it into audio format! - ✪ /stt: Convert audio to text ( only English). + ❍ /tr or /tl: To translate to your language, by default language is set to english, use /tr <lang code> for some other language! + ❍ /splcheck: As a reply to get grammar corrected text of gibberish message. + ❍ /tts: To some message to convert it into audio format! + ❍ /stt: Convert audio to text ( only English). *➩Search:* - ✪ /google <text>:- search google queries.Use in bot pm (admin can use in group). - ✪ /wiki: Search wikipedia articles. - ✪ /ud <query>: Search stuffs in urban dictionary. - ✪ /reverse: Reverse searches image or stickers on google. - ✪ /app <app name>: Finds an app in playstore for you - ✪ /cash: currency converter - ✪ /wall <query>: Get random wallpapers directly from bot! + ❍ /google <text>:- search google queries.Use in bot pm (admin can use in group). + ❍ /wiki: Search wikipedia articles. + ❍ /ud <query>: Search stuffs in urban dictionary. + ❍ /reverse: Reverse searches image or stickers on google. + ❍ /app <app name>: Finds an app in playstore for you + ❍ /cash: currency converter + ❍ /wall <query>: Get random wallpapers directly from bot! *➩Github:* - ✪ /git: Returns info about a GitHub user or organization. - ✪ /repo: Return the GitHub user or organization repository list (Limited at 40). + ❍ /git: Returns info about a GitHub user or organization. + ❍ /repo: Return the GitHub user or organization repository list (Limited at 40). *➩Covid:* - ✪ /covid :To get Global data. - ✪ /covid <country>:To get data of a country. + ❍ /covid :To get Global data. + ❍ /covid <country>:To get data of a country. *➩Paste:* - ✪ /paste: Create a paste or a shortened url using dogbin. *From letters to url.* - ✪ /getpaste: Get the content of a paste or shortened url from dogbin - ✪ /fpaste: Create a paste or a shortened url using dogbin and nekobin.*From files to url.* + ❍ /paste: Create a paste or a shortened url using dogbin. *From letters to url.* + ❍ /getpaste: Get the content of a paste or shortened url from dogbin + ❍ /fpaste: Create a paste or a shortened url using dogbin and nekobin.*From files to url.* *➩Time and Weather:* - ✪ /time <query>: Gives information about a timezone. - ✪ /weather <city>: Gets weather information of particular place! + ❍ /time <query>: Gives information about a timezone. + ❍ /weather <city>: Gets weather information of particular place! + \ """ -__mod_name__ = "Miscs" +__mod_name__ = "ᴍɪꜱᴄꜱ🧫" APP_HANDLER = DisableAbleCommandHandler("app", app) LYRICS_HANDLER = DisableAbleCommandHandler("lyrics", lyrics, pass_args=True) diff --git a/Amanda/modules/modules.py b/Amanda/modules/modules.py index 4f5bdb0..c4e87ea 100644 --- a/Amanda/modules/modules.py +++ b/Amanda/modules/modules.py @@ -29,7 +29,7 @@ def load(update: Update, context: CallbackContext): ) try: - imported_module = importlib.import_module("DaisyX.modules." + text) + imported_module = importlib.import_module("Amanda.modules." + text) except: load_messasge.edit_text("Does that module even exist?") return @@ -99,7 +99,7 @@ def unload(update: Update, context: CallbackContext): ) try: - imported_module = importlib.import_module("DaisyX.modules." + text) + imported_module = importlib.import_module("Amanda.modules." + text) except: unload_messasge.edit_text("Does that module even exist?") return @@ -169,7 +169,7 @@ def listmodules(update: Update, context: CallbackContext): for helpable_module in HELPABLE: helpable_module_info = IMPORTED[helpable_module] file_info = IMPORTED[helpable_module_info.__mod_name__.lower()] - file_name = file_info.__name__.rsplit("DaisyX.modules.", 1)[1] + file_name = file_info.__name__.rsplit("Amanda.modules.", 1)[1] mod_name = file_info.__mod_name__ module_list.append(f"- <code>{mod_name} ({file_name})</code>\n") module_list = "Following modules are loaded : \n\n" + "".join(module_list) diff --git a/Amanda/modules/music.py b/Amanda/modules/music.py new file mode 100644 index 0000000..dd02e49 --- /dev/null +++ b/Amanda/modules/music.py @@ -0,0 +1,275 @@ +# Created by @p_rinc_e +import asyncio +import json +import os +import time + +from telethon.tl.types import DocumentAttributeAudio +from youtube_dl import YoutubeDL +from youtube_dl.utils import ( + ContentTooShortError, + DownloadError, + ExtractorError, + GeoRestrictedError, + MaxDownloadsReached, + PostProcessingError, + UnavailableVideoError, + XAttrMetadataError, +) + +from Amanda.events import register +from Amanda.utils import progress + +try: + + from youtubesearchpython import SearchVideos + +except: + os.system("pip install pip install youtube-search-python") + from youtubesearchpython import SearchVideos + + +@register(pattern="^/music (.*)") +async def download_video(v_url): + + lazy = v_url + sender = await lazy.get_sender() + me = await lazy.client.get_me() + + if not sender.id == me.id: + rkp = await lazy.reply("`processing...`") + else: + rkp = await lazy.edit("`processing...`") + url = v_url.pattern_match.group(1) + if not url: + return await rkp.edit("`Error \nusage song <song name>`") + search = SearchVideos(url, offset=1, mode="json", max_results=1) + test = search.result() + p = json.loads(test) + q = p.get("search_result") + try: + url = q[0]["link"] + except: + return await rkp.edit("`failed to find`") + type = "audio" + await rkp.edit("`Preparing to download...`") + if type == "audio": + opts = { + "format": "bestaudio", + "addmetadata": True, + "key": "FFmpegMetadata", + "writethumbnail": True, + "prefer_ffmpeg": True, + "geo_bypass": True, + "nocheckcertificate": True, + "postprocessors": [ + { + "key": "FFmpegExtractAudio", + "preferredcodec": "mp3", + "preferredquality": "320", + } + ], + "outtmpl": "%(id)s.mp3", + "quiet": True, + "logtostderr": False, + } + video = False + song = True + try: + await rkp.edit("`Fetching data, please wait..`") + with YoutubeDL(opts) as rip: + rip_data = rip.extract_info(url) + except DownloadError as DE: + await rkp.edit(f"`{str(DE)}`") + return + except ContentTooShortError: + await rkp.edit("`The download content was too short.`") + return + except GeoRestrictedError: + await rkp.edit( + "`Video is not available from your geographic location due to geographic restrictions imposed by a website.`" + ) + return + except MaxDownloadsReached: + await rkp.edit("`Max-downloads limit has been reached.`") + return + except PostProcessingError: + await rkp.edit("`There was an error during post processing.`") + return + except UnavailableVideoError: + await rkp.edit("`Media is not available in the requested format.`") + return + except XAttrMetadataError as XAME: + await rkp.edit(f"`{XAME.code}: {XAME.msg}\n{XAME.reason}`") + return + except ExtractorError: + await rkp.edit("`There was an error during info extraction.`") + return + except Exception as e: + await rkp.edit(f"{str(type(e)): {str(e)}}") + return + c_time = time.time() + if song: + await rkp.edit( + f"`Preparing to upload song:`\ + \n**{rip_data['title']}**\ + \nby *{rip_data['uploader']}*" + ) + await v_url.client.send_file( + v_url.chat_id, + f"{rip_data['id']}.mp3", + supports_streaming=True, + attributes=[ + DocumentAttributeAudio( + duration=int(rip_data["duration"]), + title=str(rip_data["title"]), + performer=str(rip_data["uploader"]), + ) + ], + progress_callback=lambda d, t: asyncio.get_event_loop().create_task( + progress(d, t, v_url, c_time, "Uploading..", f"{rip_data['title']}.mp3") + ), + ) + os.remove(f"{rip_data['id']}.mp3") + elif video: + await rkp.edit( + f"`Preparing to upload song :`\ + \n**{rip_data['title']}**\ + \nby *{rip_data['uploader']}*" + ) + await v_url.client.send_file( + v_url.chat_id, + f"{rip_data['id']}.mp4", + supports_streaming=True, + caption=url, + progress_callback=lambda d, t: asyncio.get_event_loop().create_task( + progress(d, t, v_url, c_time, "Uploading..", f"{rip_data['title']}.mp4") + ), + ) + os.remove(f"{rip_data['id']}.mp4") + + +@register(pattern="^/vsong (.*)") +async def download_video(v_url): + lazy = v_url + sender = await lazy.get_sender() + me = await lazy.client.get_me() + if not sender.id == me.id: + rkp = await lazy.reply("`processing...`") + else: + rkp = await lazy.edit("`processing...`") + url = v_url.pattern_match.group(1) + if not url: + return await rkp.edit("`Error \nusage song <song name>`") + search = SearchVideos(url, offset=1, mode="json", max_results=1) + test = search.result() + p = json.loads(test) + q = p.get("search_result") + try: + url = q[0]["link"] + except: + return await rkp.edit("`failed to find`") + type = "audio" + await rkp.edit("`Preparing to download...`") + if type == "audio": + opts = { + "format": "best", + "addmetadata": True, + "key": "FFmpegMetadata", + "prefer_ffmpeg": True, + "geo_bypass": True, + "nocheckcertificate": True, + "postprocessors": [ + {"key": "FFmpegVideoConvertor", "preferedformat": "mp4"} + ], + "outtmpl": "%(id)s.mp4", + "logtostderr": False, + "quiet": True, + } + song = False + video = True + try: + await rkp.edit("`Fetching data, please wait..`") + with YoutubeDL(opts) as rip: + rip_data = rip.extract_info(url) + except DownloadError as DE: + await rkp.edit(f"`{str(DE)}`") + return + except ContentTooShortError: + await rkp.edit("`The download content was too short.`") + return + except GeoRestrictedError: + await rkp.edit( + "`Video is not available from your geographic location due to geographic restrictions imposed by a website.`" + ) + return + except MaxDownloadsReached: + await rkp.edit("`Max-downloads limit has been reached.`") + return + except PostProcessingError: + await rkp.edit("`There was an error during post processing.`") + return + except UnavailableVideoError: + await rkp.edit("`Media is not available in the requested format.`") + return + except XAttrMetadataError as XAME: + await rkp.edit(f"`{XAME.code}: {XAME.msg}\n{XAME.reason}`") + return + except ExtractorError: + await rkp.edit("`There was an error during info extraction.`") + return + except Exception as e: + await rkp.edit(f"{str(type(e)): {str(e)}}") + return + c_time = time.time() + if song: + await rkp.edit( + f"`Preparing to upload song `\ + \n**{rip_data['title']}**\ + \nby *{rip_data['uploader']}*" + ) + await v_url.client.send_file( + v_url.chat_id, + f"{rip_data['id']}.mp3", + supports_streaming=True, + attributes=[ + DocumentAttributeAudio( + duration=int(rip_data["duration"]), + title=str(rip_data["title"]), + performer=str(rip_data["uploader"]), + ) + ], + progress_callback=lambda d, t: asyncio.get_event_loop().create_task( + progress(d, t, v_url, c_time, "Uploading..", f"{rip_data['title']}.mp3") + ), + ) + os.remove(f"{rip_data['id']}.mp3") + await v_url.delete() + elif video: + await rkp.edit( + f"`Preparing to upload video song :`\ + \n**{rip_data['title']}**\ + \nby *{rip_data['uploader']}*" + ) + await v_url.client.send_file( + v_url.chat_id, + f"{rip_data['id']}.mp4", + supports_streaming=True, + caption=rip_data["title"], + progress_callback=lambda d, t: asyncio.get_event_loop().create_task( + progress(d, t, v_url, c_time, "Uploading..", f"{rip_data['title']}.mp4") + ), + ) + os.remove(f"{rip_data['id']}.mp4") + await rkp.delete() + + +__help__ = """ +@TheAmandabot + ❍ /music <songname artist(optional)>: uploads the song in it's best quality available + + ❍ /vsong <songname artist(optional)>: uploads the video song in it's best quality available + +""" + +__mod_name__ = "ꜱᴏɴɢꜱ🎶" diff --git a/Amanda/modules/muting.py b/Amanda/modules/muting.py index 9dbd94e..09ff3ae 100644 --- a/Amanda/modules/muting.py +++ b/Amanda/modules/muting.py @@ -13,10 +13,7 @@ is_user_admin, user_admin, ) -from Amanda.modules.helper_funcs.extraction import ( - extract_user, - extract_user_and_text, -) +from Amanda.modules.helper_funcs.extraction import extract_user, extract_user_and_text from Amanda.modules.helper_funcs.string_handling import extract_time from Amanda.modules.log_channel import loggable @@ -370,15 +367,18 @@ def stemp_mute(update: Update, context: CallbackContext) -> str: __help__ = """ +@TheAmandabot *Admins only:* - ✪ /mute <userhandle>*:* silences a user. Can also be used as a reply, muting the replied to user. - ✪ /smute <userhandle>*:* silences a user without notifying. Can also be used as a reply, muting the replied to user. - ✪ /tmute <userhandle> x(m/h/d)*:* mutes a user for x time. (via handle, or reply). `m` = `minutes`, `h` = `hours`, `d` = `days`. - ✪ /stmute <userhandle> x(m/h/d)*:* mutes a user for x time without notifying. (via handle, or reply). `m` = `minutes`, `h` = `hours`, `d` = `days`. - ✪ /unmute <userhandle>*:* unmutes a user. Can also be used as a reply, muting the replied to user. + ❍ /mute <userhandle>*:* silences a user. Can also be used as a reply, muting the replied to user. + ❍ /smute <userhandle>*:* silences a user without notifying. Can also be used as a reply, muting the replied to user. + ❍ /tmute <userhandle> x(m/h/d)*:* mutes a user for x time. (via handle, or reply). `m` = `minutes`, `h` = `hours`, `d` = `days`. + ❍ /stmute <userhandle> x(m/h/d)*:* mutes a user for x time without notifying. (via handle, or reply). `m` = `minutes`, `h` = `hours`, `d` = `days`. + ❍ /unmute <userhandle>*:* unmutes a user. Can also be used as a reply, muting the replied to user. _NOTE:_ If you set Log Channels, you will get logs of Silent mutes. Check *Logger* module to know more about Log Channel. + + """ MUTE_HANDLER = CommandHandler("mute", mute) @@ -393,7 +393,7 @@ def stemp_mute(update: Update, context: CallbackContext) -> str: dispatcher.add_handler(TEMPMUTE_HANDLER) dispatcher.add_handler(STEMPMUTE_HANDLER) -__mod_name__ = "Muting" +__mod_name__ = "ᴍᴜᴛɪɴɢ🔕" __handlers__ = [ MUTE_HANDLER, SMUTE_HANDLER, diff --git a/Amanda/modules/ncode.py b/Amanda/modules/ncode.py new file mode 100644 index 0000000..bf73cdd --- /dev/null +++ b/Amanda/modules/ncode.py @@ -0,0 +1,40 @@ +#Made By @RoseloverX +from Amanda.events import register +from Amanda import CMD_HELP +from Amanda import pbot +from Amanda import TEMP_DOWNLOAD_DIRECTORY +from telethon import events +import os +import pygments +from pygments.formatters import ImageFormatter +from pygments.lexers import Python3Lexer + +client = pbot +@register(pattern="^/ncode") +async def coder_print(event): + a = await event.client.download_media( + await event.get_reply_message(), TEMP_DOWNLOAD_DIRECTORY + ) + s = open(a,"r") + c = s.read() + s.close() + pygments.highlight( + f"{c}", + Python3Lexer(), + ImageFormatter(font_name="DejaVu Sans Mono", line_numbers=True), + "result.png", + ) + res = await event.client.send_message( + event.chat_id, + "Pasting this code on my page...", + reply_to=event.reply_to_msg_id, + ) + await event.client.send_file( + event.chat_id, "result.png", force_document=True, reply_to=event.reply_to_msg_id + ) + # await event.client.send_file(event.chat_id, "resuly.png", + # force_document=False, reply_to=event.reply_to_msg_id) + await res.delete() + await event.delete() + os.remove(a) + os.remove("result.png") diff --git a/Amanda/modules/notes.py b/Amanda/modules/notes.py index acca42f..3539c58 100644 --- a/Amanda/modules/notes.py +++ b/Amanda/modules/notes.py @@ -30,9 +30,7 @@ from Amanda.modules.helper_funcs.handlers import MessageHandlerChecker from Amanda.modules.helper_funcs.misc import build_keyboard, revert_buttons from Amanda.modules.helper_funcs.msg_types import get_note_type -from Amanda.modules.helper_funcs.string_handling import ( - escape_invalid_curly_brackets, -) +from Amanda.modules.helper_funcs.string_handling import escape_invalid_curly_brackets FILE_MATCHER = re.compile(r"^###file_id(!photo)?###:(.*?)(?:\s|$)") STICKER_MATCHER = re.compile(r"^###sticker(!photo)?###:") @@ -518,18 +516,19 @@ def __chat_settings__(chat_id, user_id): __help__ = """ - ✪ /get <notename>*:* get the note with this notename - ✪ #<notename>*:* same as /get - ✪ /notes or /saved*:* list all saved notes in this chat - ✪ /number *:* Will pull the note of that number in the list +@TheAmandabot + ❍ /get <notename>*:* get the note with this notename + ❍ #<notename>*:* same as /get + ❍ /notes or /saved*:* list all saved notes in this chat + ❍ /number *:* Will pull the note of that number in the list If you would like to retrieve the contents of a note without any formatting, use `/get <notename> noformat`. This can \ be useful when updating a current note *Admins only:* - ✪ /save <notename> <notedata>*:* saves notedata as a note with name notename + ❍ /save <notename> <notedata>*:* saves notedata as a note with name notename A button can be added to a note by using standard markdown link syntax - the link should just be prepended with a \ `buttonurl:` section, as such: `[somelink](buttonurl:example.com)`. Check `/markdownhelp` for more info - ✪ /save <notename>*:* save the replied message as a note with name notename + ❍ /save <notename>*:* save the replied message as a note with name notename Separate diff replies by `%%%` to get random notes *Example:* `/save notename @@ -538,13 +537,13 @@ def __chat_settings__(chat_id, user_id): Reply 2 %%% Reply 3` - ✪ /clear <notename>*:* clear note with this name - ✪ /removeallnotes*:* removes all notes from the group + ❍ /clear <notename>*:* clear note with this name + ❍ /removeallnotes*:* removes all notes from the group *Note:* Note names are case-insensitive, and they are automatically converted to lowercase before getting saved. """ -__mod_name__ = "Notes" +__mod_name__ = "ɴᴏᴛᴇꜱ📝" GET_HANDLER = CommandHandler("get", cmd_get) HASH_GET_HANDLER = MessageHandler(Filters.regex(r"^#[^\s]+"), hash_get) diff --git a/Amanda/modules/phone.py b/Amanda/modules/phone.py index f734c0b..166d613 100644 --- a/Amanda/modules/phone.py +++ b/Amanda/modules/phone.py @@ -15,7 +15,7 @@ def phone(update, context): args = update.effective_message.text.split(None, 1) information = args[1] number = information - key = "fe65b94e78fc2e3234c1c6ed1b771abd" + key = "f66950368a61ebad3cba9b5924b4532d" api = ( "http://apilayer.net/api/validate?access_key=" + key diff --git a/Amanda/modules/ping.py b/Amanda/modules/ping.py index dd88b1a..4e4d83a 100644 --- a/Amanda/modules/ping.py +++ b/Amanda/modules/ping.py @@ -80,7 +80,14 @@ def ping(update: Update, context: CallbackContext): uptime = get_readable_time((time.time() - StartTime)) message.edit_text( - "PONG!!\n" + "╔═══╗\n" + "║╔═╗║\n" + "║╚═╝╠══╦═╗╔══╗\n" + "║╔══╣╔╗║╔╗╣╔╗║\n" + "║║──║╚╝║║║║╚╝║\n" + "╚╝──╚══╩╝╚╩═╗║\n" + "──────────╔═╝║\n" + "──────────╚══╝\n" "<b>Time Taken:</b> <code>{}</code>\n" "<b>Service uptime:</b> <code>{}</code>".format(telegram_ping, uptime), parse_mode=ParseMode.HTML, diff --git a/Amanda/modules/plet.py b/Amanda/modules/plet.py index f6ec7c3..7f908f4 100644 --- a/Amanda/modules/plet.py +++ b/Amanda/modules/plet.py @@ -1,3 +1,5 @@ +# thonkify initially made by @devrism for discord. ported to telegram bot api (and) improved by @rupansh + import base64 from io import BytesIO diff --git a/Amanda/modules/progithub.py b/Amanda/modules/progithub.py new file mode 100644 index 0000000..0fc9dcf --- /dev/null +++ b/Amanda/modules/progithub.py @@ -0,0 +1,58 @@ +# © @Mr_Dark_Prince +import aiohttp +from pyrogram import filters +from Amanda import pbot +from Amanda.pyrogramee.errors import capture_err + + + +@pbot.on_message(filters.command('github')) +@capture_err +async def github(_, message): + if len(message.command) != 2: + await message.reply_text("/git Username") + return + username = message.text.split(None, 1)[1] + URL = f'https://api.github.com/users/{username}' + async with aiohttp.ClientSession() as session: + async with session.get(URL) as request: + if request.status == 404: + return await message.reply_text("404") + + result = await request.json() + try: + url = result['html_url'] + name = result['name'] + company = result['company'] + bio = result['bio'] + created_at = result['created_at'] + avatar_url = result['avatar_url'] + blog = result['blog'] + location = result['location'] + repositories = result['public_repos'] + followers = result['followers'] + following = result['following'] + caption = f"""**Info Of {name}** +**Username:** `{username}` +**Bio:** `{bio}` +**Profile Link:** [Here]({url}) +**Company:** `{company}` +**Created On:** `{created_at}` +**Repositories:** `{repositories}` +**Blog:** `{blog}` +**Location:** `{location}` +**Followers:** `{followers}` +**Following:** `{following}` +**Updates:** @szroseupdates""" + except Exception as e: + print(str(e)) + pass + await message.reply_photo(photo=avatar_url, caption=caption) + + + +__help__ = """ +I will give information about github profile + ❍ /github <username>*:* Get information about a GitHub user. +""" +__mod_name__ = "ɢɪᴛʜᴜʙ🌀" diff --git a/Amanda/modules/purge.py b/Amanda/modules/purge.py index 1d8cace..908438d 100644 --- a/Amanda/modules/purge.py +++ b/Amanda/modules/purge.py @@ -96,10 +96,12 @@ async def delete_messages(event): __help__ = """ +@TheAmandabot *Admin only:* - ✪ /del*:* deletes the message you replied to. - ✪ /purge*:* deletes all messages between this and the replied to message. - ✪ /purge <integer X>*:* deletes the replied message, and X messages following it if replied to a message. + ❍ /del*:* deletes the message you replied to. + ❍ /purge*:* deletes all messages between this and the replied to message. + ❍ /purge <integer X>*:* deletes the replied message, and X messages following it if replied to a message. + """ -__mod_name__ = "Purges" +__mod_name__ = "ᴘᴜʀɢᴇ❌" diff --git a/Amanda/modules/qoutly.py b/Amanda/modules/qoutly.py index 287601a..0336649 100644 --- a/Amanda/modules/qoutly.py +++ b/Amanda/modules/qoutly.py @@ -431,7 +431,6 @@ async def _(event): if event.fwd_from: return reply = await event.get_reply_message() - msg = reply.message repliedreply = await reply.get_reply_message() user = ( await event.client.get_entity(reply.forward.sender) diff --git a/Amanda/modules/randomlogo.py b/Amanda/modules/randomlogo.py new file mode 100644 index 0000000..e751aed --- /dev/null +++ b/Amanda/modules/randomlogo.py @@ -0,0 +1,96 @@ +# Copyright (C) @Damantha126 2020-2021 +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + + + +from bs4 import * +import shutil +import requests +import os +import base64 +import sys +import random +import requests +from pyrogram import filters +from Amanda import pbot +from Amanda.function.pluginhelpers import get_text, admins_only + +def download_images(images): + count = 0 + print(f"Total {len(images)} Image Found!") + if len(images) != 0: + for i, image in enumerate(images): + try: + image_link = image["data-srcset"] + except: + try: + image_link = image["data-src"] + except: + try: + image_link = image["data-fallback-src"] + except: + try: + image_link = image["src"] + except: + + pass + try: + r = requests.get(image_link).content + try: + + r = str(r, 'utf-8') + except UnicodeDecodeError: + with open("logo.jpg", "wb+") as f: + f.write(r) + count += 1 + except: + pass + + +def mainne(name, typeo): + url = f"https://www.brandcrowd.com/maker/logos?text={name}&searchtext={typeo}&searchService=" + r = requests.get(url) + soup = BeautifulSoup(r.text, 'html.parser') + images = soup.findAll('img') + random.shuffle(images) + if images is not None: + print("level 1 pass") + download_images(images) + + + +@pbot.on_message(filters.command("rlogo") & ~filters.edited & ~filters.bot) +@admins_only +async def logogen(client, message): + pablo = await client.send_message(message.chat.id,"`Creating The Logo.....`") + Godzilla = get_text(message) + if not Godzilla: + await pablo.edit("Invalid Command Syntax, Please Check Help Menu To Know More!") + return + lmao = Godzilla.split(":", 1) + try: + typeo = lmao[1] + except BaseException: + typeo = "name" + await pablo.edit( + "Give name and type for logo Idiot. like `/rlogo rose`") + name = lmao[0] + mainne(name, typeo) + caption = "<b>Logo Generated By @TheAmandabot.<b>" + pate = "logo.jpg" + await client.send_photo(message.chat.id, pate) + try: + os.remove(pate) + except: + pass + await pablo.delete() diff --git a/Amanda/modules/readme.md b/Amanda/modules/readme.md new file mode 100644 index 0000000..d9303d4 --- /dev/null +++ b/Amanda/modules/readme.md @@ -0,0 +1,4 @@ + +<a href="https://t.me/slbotzone"><img src="https://img.shields.io/badge/support%20group-blue.svg?style=for-the-badge&logo=Telegram"> +</a> <a href="https://t.me/SL_bot_zone"><img src="https://img.shields.io/badge/Join-Updates%20Channel-blue.svg?style=for-the-badge&logo=Telegram"></a> +<a href="https://t.me/TheAmandabot"><img src="https://img.shields.io/badge/Foundbot%20on-blue.svg?style=for-the-badge&logo=Telegram"> diff --git a/Amanda/modules/reporting.py b/Amanda/modules/reporting.py index 44b352a..8b0e924 100644 --- a/Amanda/modules/reporting.py +++ b/Amanda/modules/reporting.py @@ -265,14 +265,17 @@ def buttons(update: Update, context: CallbackContext): __help__ = """ -✪ /report <reason>*:* reply to a message to report it to admins. -✪ `@admin`*:* reply to a message to report it to admins. +@TheAmandabot +❍/report <reason>*:* reply to a message to report it to admins. +❍ `@admin`*:* reply to a message to report it to admins. *NOTE:* Neither of these will get triggered if used by admins. *Admins only:* - ✪ /reports <on/off>*:* change report setting, or view current status. +❍ /reports <on/off>*:* change report setting, or view current status. • If done in pm, toggles your status. • If in group, toggles that groups's status. + + """ SETTING_HANDLER = CommandHandler("reports", report_setting) @@ -286,7 +289,7 @@ def buttons(update: Update, context: CallbackContext): dispatcher.add_handler(REPORT_HANDLER, REPORT_GROUP) dispatcher.add_handler(ADMIN_REPORT_HANDLER, REPORT_GROUP) -__mod_name__ = "Reporting" +__mod_name__ = "ʀᴇᴘᴏʀᴛɪɴɢ🔂" __handlers__ = [ (REPORT_HANDLER, REPORT_GROUP), (ADMIN_REPORT_HANDLER, REPORT_GROUP), diff --git a/Amanda/modules/result.py b/Amanda/modules/result.py new file mode 100644 index 0000000..b0144d2 --- /dev/null +++ b/Amanda/modules/result.py @@ -0,0 +1,23 @@ + +__help__ = """ +@TheAmandabot +**You can find out your exam results very quickly through me.** + + What does I Know + 💫 G.C.E. (A/L) EXAMINATION - 2020 + 💫 G.C.E. (O/L) EXAMINATION (After Rescrutiny) - 2019 + 💫 GRADE 5 SCHOLARSHIP EXAMINATION (AFTER APPES) - 2020 + + **Follow the steps below.** + ❍ /al- (A/L) Results +`Index No` + + ❍ /ol- (O/L) Results +`Index No` + + ❍ /g5- (G5) SCHOLARSHIP Results +`Index No` + +'Donents.lk Bot' © @uvindbro +""" +__mod_name__ = "ʀᴇꜱᴜʟᴛ🧾" diff --git a/Amanda/modules/rules.py b/Amanda/modules/rules.py index db7f4a9..f8bd9ec 100644 --- a/Amanda/modules/rules.py +++ b/Amanda/modules/rules.py @@ -119,14 +119,15 @@ def __chat_settings__(chat_id, user_id): __help__ = """ - ✪ /rules*:* get the rules for this chat. +@TheAmandabot + ❍ /rules*:* get the rules for this chat. *Admins only:* - ✪ /setrules <your rules here>*:* set the rules for this chat. - ✪ /clearrules*:* clear the rules for this chat. + ❍ /setrules <your rules here>*:* set the rules for this chat. + ❍ /clearrules*:* clear the rules for this chat. """ -__mod_name__ = "Rules" +__mod_name__ = "ʀᴜʟᴇꜱ📔" GET_RULES_HANDLER = CommandHandler("rules", get_rules, filters=Filters.group) SET_RULES_HANDLER = CommandHandler("setrules", set_rules, filters=Filters.group) diff --git a/Amanda/modules/send.py b/Amanda/modules/send.py index ec30fdb..433718c 100644 --- a/Amanda/modules/send.py +++ b/Amanda/modules/send.py @@ -5,7 +5,6 @@ from Amanda.modules.helper_funcs.alternate import send_message from Amanda.modules.helper_funcs.chat_status import user_admin - @run_async @user_admin def send(update, context): diff --git a/Amanda/modules/songs.py b/Amanda/modules/songs.py index 82e0db9..0643c2a 100644 --- a/Amanda/modules/songs.py +++ b/Amanda/modules/songs.py @@ -1,86 +1,69 @@ import os - +import requests import aiohttp -from pyrogram import filters -from pytube import YouTube -from youtubesearchpython import VideosSearch - -from Amanda import LOGGER, pbot -from Amanda.utils.ut import get_arg - - -def yt_search(song): - videosSearch = VideosSearch(song, limit=1) - result = videosSearch.result() - if not result: - return False - else: - video_id = result["result"][0]["id"] - url = f"https://youtu.be/{video_id}" - return url +import youtube_dl +from Amanda import pbot as app +from pyrogram import filters, Client +from youtube_search import YoutubeSearch +from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, InputTextMessageContent -class AioHttp: - @staticmethod - async def get_json(link): - async with aiohttp.ClientSession() as session: - async with session.get(link) as resp: - return await resp.json() +def time_to_seconds(time): + stringt = str(time) + return sum(int(x) * 60 ** i for i, x in enumerate(reversed(stringt.split(':')))) - @staticmethod - async def get_text(link): - async with aiohttp.ClientSession() as session: - async with session.get(link) as resp: - return await resp.text() +@app.on_message(filters.command('song')) +def song(client, message): - @staticmethod - async def get_raw(link): - async with aiohttp.ClientSession() as session: - async with session.get(link) as resp: - return await resp.read() + user_id = message.from_user.id + user_name = message.from_user.first_name + rpk = "["+user_name+"](tg://user?id="+str(user_id)+")" - -@pbot.on_message(filters.command("song")) -async def song(client, message): - message.chat.id - user_id = message.from_user["id"] - args = get_arg(message) + " " + "song" - if args.startswith(" "): - await message.reply("Enter a song name. Check /help") - return "" - status = await message.reply("Processing...") - video_link = yt_search(args) - if not video_link: - await status.edit("Song not found.") - return "" - yt = YouTube(video_link) - audio = yt.streams.filter(only_audio=True).first() + query = '' + for i in message.command[1:]: + query += ' ' + str(i) + print(query) + m = message.reply('🔎 Searching your song...') + ydl_opts = {"format": "bestaudio[ext=m4a]"} try: - download = audio.download(filename=f"{str(user_id)}") - except Exception as ex: - await status.edit("Failed to download song") - LOGGER.error(ex) - return "" - os.rename(download, f"{str(user_id)}.mp3") - await pbot.send_chat_action(message.chat.id, "upload_audio") - await pbot.send_audio( - chat_id=message.chat.id, - audio=f"{str(user_id)}.mp3", - duration=int(yt.length), - title=str(yt.title), - performer=str(yt.author), - reply_to_message_id=message.message_id, - ) - await status.delete() - os.remove(f"{str(user_id)}.mp3") + results = YoutubeSearch(query, max_results=1).to_dict() + link = f"https://youtube.com{results[0]['url_suffix']}" + #print(results) + title = results[0]["title"][:40] + thumbnail = results[0]["thumbnails"][0] + thumb_name = f'thumb{title}.jpg' + thumb = requests.get(thumbnail, allow_redirects=True) + open(thumb_name, 'wb').write(thumb.content) + duration = results[0]["duration"] + url_suffix = results[0]["url_suffix"] + views = results[0]["views"] -__help__ = """ - *You can either enter just the song name or both the artist and song - name. * - ✪ /song <songname artist(optional)>*:* uploads the song in it's best quality available - ✪ /video <songname artist(optional)>*:* uploads the video song in it's best quality available - ✪ /lyrics <song>*:* returns the lyrics of that song. -""" + except Exception as e: + m.edit( + "❌ Found Nothing.\n\nTry another keywork or maybe spell it properly." + ) + print(str(e)) + return + m.edit("`Downloading Song... Please wait ⏱`") + try: + with youtube_dl.YoutubeDL(ydl_opts) as ydl: + rep = f'🎙 **Title**: [{title[:35]}]({link})\n🎬 **Source**: `YouTube`\n⏱️ **Duration**: `{duration}`\n👁‍🗨 **Views**: `{views}`\n📤 **By**: @TheAmandabot ' + info_dict = ydl.extract_info(link, download=False) + audio_file = ydl.prepare_filename(info_dict) + ydl.process_info(info_dict) + secmul, dur, dur_arr = 1, 0, duration.split(':') + for i in range(len(dur_arr)-1, -1, -1): + dur += (int(dur_arr[i]) * secmul) + secmul *= 60 + s = message.reply_audio(audio_file, caption=rep, thumb=thumb_name, parse_mode='md', title=title, duration=dur, reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Join updates", url=f"https://t.me/sl_bot_zone")]])) + m.delete() + except Exception as e: + m.edit('❌ some error') + print(e) -__mod_name__ = "Music" + try: + os.remove(audio_file) + os.remove(thumb_name) + except Exception as e: + print(e) diff --git a/Amanda/modules/sql/chatbot_sql.py b/Amanda/modules/sql/chatbot_sql.py index f026424..de4ac0a 100644 --- a/Amanda/modules/sql/chatbot_sql.py +++ b/Amanda/modules/sql/chatbot_sql.py @@ -1,74 +1,42 @@ import threading from sqlalchemy import Column, String - from Amanda.modules.sql import BASE, SESSION - -class ChatbotChats(BASE): - __tablename__ = "chatbot_chats" +class Chatbot(BASE): + __tablename__ = "chatbot" chat_id = Column(String(14), primary_key=True) - ses_id = Column(String(70)) - expires = Column(String(15)) - def __init__(self, chat_id, ses_id, expires): + def __init__(self, chat_id): self.chat_id = chat_id - self.ses_id = ses_id - self.expires = expires - -ChatbotChats.__table__.create(checkfirst=True) -INSERTION_LOCK = threading.RLock() +Chatbot.__table__.create(checkfirst=True) -def is_chat(chat_id): - try: - chat = SESSION.query(ChatbotChats).get(str(chat_id)) - if chat: - return True - else: - return False - finally: - SESSION.close() - +def addchatbot(chat_id: str): + chatbotty = Chatbot(str(chat_id)) + SESSION.add(chatbotty) + SESSION.commit() -def set_ses(chat_id, ses_id, expires): - with INSERTION_LOCK: - autochat = SESSION.query(ChatbotChats).get(str(chat_id)) - if not autochat: - autochat = ChatbotChats(str(chat_id), str(ses_id), str(expires)) - else: - autochat.ses_id = str(ses_id) - autochat.expires = str(expires) - SESSION.add(autochat) +def rmchatbot(chat_id: str): + rmchatbotty = SESSION.query(Chatbot).get(str(chat_id)) + if rmchatbotty: + SESSION.delete(rmchatbotty) SESSION.commit() -def get_ses(chat_id): - autochat = SESSION.query(ChatbotChats).get(str(chat_id)) - sesh = "" - exp = "" - if autochat: - sesh = str(autochat.ses_id) - exp = str(autochat.expires) - +def get_all_chat_id(): + stark = SESSION.query(Chatbot).all() SESSION.close() - return sesh, exp - - -def rem_chat(chat_id): - with INSERTION_LOCK: - autochat = SESSION.query(ChatbotChats).get(str(chat_id)) - if autochat: - SESSION.delete(autochat) - - SESSION.commit() + return stark -def get_all_chats(): +def is_chatbot_indb(chat_id: str): try: - return SESSION.query(ChatbotChats.chat_id).all() + s__ = SESSION.query(Chatbot).get(str(chat_id)) + if s__: + return str(s__.chat_id) finally: SESSION.close() diff --git a/Amanda/modules/sql/forceSubscribe_sql.py b/Amanda/modules/sql/forceSubscribe_sql.py index 0678fdf..f77beac 100644 --- a/Amanda/modules/sql/forceSubscribe_sql.py +++ b/Amanda/modules/sql/forceSubscribe_sql.py @@ -43,4 +43,4 @@ def disapprove(chat_id): rem = SESSION.query(forceSubscribe).get(chat_id) if rem: SESSION.delete(rem) - SESSION.commit() + SESSION.commit() \ No newline at end of file diff --git a/Amanda/modules/sql/night_mode_sql.py b/Amanda/modules/sql/night_mode_sql.py new file mode 100644 index 0000000..3df456b --- /dev/null +++ b/Amanda/modules/sql/night_mode_sql.py @@ -0,0 +1,41 @@ +from sqlalchemy import Boolean, Column, Integer, String, UnicodeText +from Amanda.modules.sql import BASE, SESSION + + +class Nightmode(BASE): + __tablename__ = "nightmode" + chat_id = Column(String(14), primary_key=True) + + def __init__(self, chat_id): + self.chat_id = chat_id + + +Nightmode.__table__.create(checkfirst=True) + + +def add_nightmode(chat_id: str): + nightmoddy = Nightmode(str(chat_id)) + SESSION.add(nightmoddy) + SESSION.commit() + + +def rmnightmode(chat_id: str): + rmnightmoddy = SESSION.query(Nightmode).get(str(chat_id)) + if rmnightmoddy: + SESSION.delete(rmnightmoddy) + SESSION.commit() + + +def get_all_chat_id(): + stark = SESSION.query(Nightmode).all() + SESSION.close() + return stark + + +def is_nightmode_indb(chat_id: str): + try: + s__ = SESSION.query(Nightmode).get(str(chat_id)) + if s__: + return str(s__.chat_id) + finally: + SESSION.close() diff --git a/Amanda/modules/sql/welcome_sql.py b/Amanda/modules/sql/welcome_sql.py index 64dee50..8c4815f 100644 --- a/Amanda/modules/sql/welcome_sql.py +++ b/Amanda/modules/sql/welcome_sql.py @@ -7,8 +7,8 @@ from Amanda.modules.helper_funcs.msg_types import Types from Amanda.modules.sql import BASE, SESSION -DEFAULT_WELCOME = "Hey {first}, how are you?" -DEFAULT_GOODBYE = "Nice knowing ya!" +DEFAULT_WELCOME = "Welcome {first}, how are you?" +DEFAULT_GOODBYE = "Nice knowing you!" DEFAULT_WELCOME_MESSAGES = [ "{first} is here!", # Discord welcome messages copied @@ -214,7 +214,6 @@ "Go outside", "Always your head in the clouds", ] -# Line 111 to 152 are references from https://bindingofisaac.fandom.com/wiki/Fortune_Telling_Machine class Welcome(BASE): diff --git a/Amanda/modules/stickers.py b/Amanda/modules/stickers.py index 2885a12..5c447c0 100644 --- a/Amanda/modules/stickers.py +++ b/Amanda/modules/stickers.py @@ -452,13 +452,15 @@ def makepack_internal( __help__ = """ -• `/stickerid`*:* reply to a sticker to me to tell you its file ID. -• `/getsticker`*:* reply to a sticker to me to upload its raw PNG file. -• `/kang`*:* reply to a sticker to add it to your pack. -• `/stickers`*:* Find stickers for given term on combot sticker catalogue +@TheAmandabot +❍ `/stickerid`*:* reply to a sticker to me to tell you its file ID. +❍ `/getsticker`*:* reply to a sticker to me to upload its raw PNG file. +❍ `/kang`*:* reply to a sticker to add it to your pack. +❍ `/stickers`*:* Find stickers for given term on combot sticker catalogue + """ -__mod_name__ = "Stickers" +__mod_name__ = "ꜱᴛɪᴄᴋᴇʀ🎏" STICKERID_HANDLER = DisableAbleCommandHandler("stickerid", stickerid) GETSTICKER_HANDLER = DisableAbleCommandHandler("getsticker", getsticker) KANG_HANDLER = DisableAbleCommandHandler("kang", kang, admin_ok=True) diff --git a/Amanda/modules/styletext.py b/Amanda/modules/styletext.py index 14f1f8d..7ba60fe 100644 --- a/Amanda/modules/styletext.py +++ b/Amanda/modules/styletext.py @@ -483,7 +483,19 @@ def lined(update, context): message.reply_text(string) -__mod_name__ = "StyleText" +__help__ = """ +@TheAmandabot + ❍ /weebify + ❍ /bubble + ❍ /fbubble + ❍ /fsquare + ❍ /blue + ❍ /latin + ❍ /lined + ❍ /square +""" +__mod_name__ = "ꜱᴛʏʟᴇᴛᴇxᴛ📂" + WEEBIFY_HANDLER = DisableAbleCommandHandler("weebify", weebify) BUBBLE_HANDLER = DisableAbleCommandHandler("bubble", bubble) diff --git a/Amanda/modules/sysinfo.py b/Amanda/modules/sysinfo.py new file mode 100644 index 0000000..cf5ff35 --- /dev/null +++ b/Amanda/modules/sysinfo.py @@ -0,0 +1,49 @@ +import platform +import re +import socket +import sys +import time +import uuid +from datetime import datetime +from os import environ, execle, path, remove +from Amanda import telethn as tbot +from Amanda.events import register + +import psutil +from pyrogram import Client, filters, __version__ + + +# FETCH SYSINFO + +@register(pattern="^/sysinfo$") +async def give_sysinfo(event): + splatform = platform.system() + platform_release = platform.release() + platform_version = platform.version() + architecture = platform.machine() + hostname = socket.gethostname() + ip_address = socket.gethostbyname(socket.gethostname()) + mac_address = ":".join(re.findall("..", "%012x" % uuid.getnode())) + processor = platform.processor() + cpu_freq = psutil.cpu_freq().current + if cpu_freq >= 1000: + cpu_freq = f"{round(cpu_freq / 1000, 2)}GHz" + else: + cpu_freq = f"{round(cpu_freq, 2)}MHz" + du = psutil.disk_usage(client.workdir) + psutil.disk_io_counters() + cpu_len = len(psutil.Process().cpu_affinity()) + somsg = f"""**System Info of rose video player** + +**📲PlatForm :** `{splatform}` +**💡PlatForm - Release :** `{platform_release}` +**💾PlatFork - Version :** `{platform_version}` +**⌚️Architecture :** `{architecture}` +**🔋Hosting service :** `{hostname}` +**🧭IP :** `{ip_address}` +**⏲Mac :** `{mac_address}` +**📟Processor :** `{processor}` +**💻CPU :** `{cpu_len}` +**💽CPU FREQ :** `{cpu_freq}` + """ + await message.reply(somsg) diff --git a/Amanda/modules/system_stats.py b/Amanda/modules/system_stats.py new file mode 100644 index 0000000..5303b3f --- /dev/null +++ b/Amanda/modules/system_stats.py @@ -0,0 +1,34 @@ +""" +XIT License 2021 +Copyright (c) 2021 Damantha126 +""" +import asyncio +import os +import subprocess +import time + +import psutil +from pyrogram import filters + +from Amanda import bot_start_time +from Amanda.utils import formatter + +# Stats Module + + +async def bot_sys_stats(): + bot_uptime = int(time.time() - bot_start_time) + cpu = psutil.cpu_percent() + mem = psutil.virtual_memory().percent + disk = psutil.disk_usage("/").percent + process = psutil.Process(os.getpid()) + stats = f""" +root@supunma:~$ TheAmandabot +------------------ +UPTIME: {formatter.get_readable_time((bot_uptime))} +BOT: {round(process.memory_info()[0] / 1024 ** 2)} MB +CPU: {cpu}% +RAM: {mem}% +DISK: {disk}% +""" + return stats diff --git a/Amanda/modules/tagger.py b/Amanda/modules/tagger.py index 84dddc2..ed44c5c 100644 --- a/Amanda/modules/tagger.py +++ b/Amanda/modules/tagger.py @@ -1,4 +1,4 @@ -# Written by Inukaasith for DaisyX +# Written by Inukaasith for Amanda from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ParseMode from telegram.error import BadRequest @@ -256,19 +256,22 @@ def untagall(update, context): ) -__mod_name__ = "Tagger" +__mod_name__ = "ᴛᴀɢɢᴇʀ🔖" __help__ = """ +@TheAmandabot Tagger is an essential feature to mention all subscribed members in the group. Any chat members can subscribe to tagger. -- /tagme: registers to the chat tag list. -- /untagme: unsubscribes from the chat tag list. +❍ /tagme: registers to the chat tag list. +❍ /untagme: unsubscribes from the chat tag list. *Admin only:* -- /tagall: mention all subscribed members. -- /untagall: clears all subscribed members. -- /addtag <userhandle>: add a user to chat tag list. (via handle, or reply) -- /removetag <userhandle>: remove a user to chat tag list. (via handle, or reply) +❍ /tagall: mention all subscribed members. +❍ /untagall: clears all subscribed members. +❍ /addtag <userhandle>: add a user to chat tag list. (via handle, or reply) +❍ /removetag <userhandle>: remove a user to chat tag list. (via handle, or reply) + + """ TAG_ALL_HANDLER = DisableAbleCommandHandler("tagall", tagall, filters=Filters.group) diff --git a/Amanda/modules/telethnfun.py b/Amanda/modules/telethnfun.py index c5473b8..2cf1784 100644 --- a/Amanda/modules/telethnfun.py +++ b/Amanda/modules/telethnfun.py @@ -14,7 +14,7 @@ def progress(current, total): - """Calculate and return the download progress with given arguments.""" + """ Calculate and return the download progress with given arguments. """ print( "Downloaded {} of {}\nCompleted {}".format( current, total, (current / total) * 100 @@ -104,7 +104,7 @@ async def img_sampler(event): @register(pattern=r"^/getqr$") async def parseqr(qr_e): - """For .getqr command, get QR Code content from the replied photo.""" + """ For .getqr command, get QR Code content from the replied photo. """ if qr_e.fwd_from: return start = datetime.now() @@ -127,7 +127,7 @@ async def parseqr(qr_e): @register(pattern=r"^/makeqr(?: |$)([\s\S]*)") async def make_qr(qrcode): - """For .makeqr command, make a QR Code containing the given content.""" + """ For .makeqr command, make a QR Code containing the given content. """ if qrcode.fwd_from: return start = datetime.now() diff --git a/Amanda/modules/translations/English.py b/Amanda/modules/translations/English.py new file mode 100644 index 0000000..08200d0 --- /dev/null +++ b/Amanda/modules/translations/English.py @@ -0,0 +1,169 @@ +RUN_STRINGS = ( + "Where do you think you're going?", + "Huh? what? did they get away?", + "ZZzzZZzz... Huh? what? oh, just them again, nevermind.", + "Get back here!", + "Not so fast...", + "Look out for the wall!", + "Don't leave me alone with them!!", + "You run, you die.", + "Jokes on you, I'm everywhere", + "You're gonna regret that...", + "You could also try /kickme, I hear that's fun.", + "Go bother someone else, no-one here cares.", + "You can run, but you can't hide.", + "Is that all you've got?", + "I'm behind you...", + "You've got company!", + "We can do this the easy way, or the hard way.", + "You just don't get it, do you?", + "Yeah, you better run!", + "Please, remind me how much I care?", + "I'd run faster if I were you.", + "That's definitely the droid we're looking for.", + "May the odds be ever in your favour.", + "Famous last words.", + "And they disappeared forever, never to be seen again.", + "\"Oh, look at me! I'm so cool, I can run from a bot!\" - this person", + "Yeah yeah, just tap /kickme already.", + "Here, take this ring and head to Mordor while you're at it.", + "Legend has it, they're still running...", + "Unlike Harry Potter, your parents can't protect you from me.", + "Fear leads to anger. Anger leads to hate. Hate leads to suffering. If you keep running in fear, you might " + "be the next Vader.", + "Multiple calculations later, I have decided my interest in your shenanigans is exactly 0.", + "Legend has it, they're still running.", + "Keep it up, not sure we want you here anyway.", + "You're a wiza- Oh. Wait. You're not Harry, keep moving.", + "NO RUNNING IN THE HALLWAYS!", + "Hasta la vista, baby.", + "Who let the dogs out?", + "It's funny, because no one cares.", + "Ah, what a waste. I liked that one.", + "Frankly, my dear, I don't give a damn.", + "My milkshake brings all the boys to yard... So run faster!", + "You can't HANDLE the truth!", + "A long time ago, in a galaxy far far away... Someone would've cared about that. Not anymore though.", + "Hey, look at them! They're running from the inevitable banhammer... Cute.", + "Han shot first. So will I.", + "What are you running after, a white rabbit?", + "As The Doctor would say... RUN!", +) + +SLAP_TEMPLATES = ( + "{user1} {hits} {user2} with a {item}.", + "{user1} {hits} {user2} in the face with a {item}.", + "{user1} {hits} {user2} around a bit with a {item}.", + "{user1} {throws} a {item} at {user2}.", + "{user1} grabs a {item} and {throws} it at {user2}'s face.", + "{user1} launches a {item} in {user2}'s general direction.", + "{user1} starts slapping {user2} silly with a {item}.", + "{user1} pins {user2} down and repeatedly {hits} them with a {item}.", + "{user1} grabs up a {item} and {hits} {user2} with it.", + "{user1} ties {user2} to a chair and {throws} a {item} at them.", + "{user1} gave a friendly push to help {user2} learn to swim in lava." +) + +ITEMS = ( + "cast iron skillet", + "large trout", + "baseball bat", + "cricket bat", + "wooden cane", + "nail", + "printer", + "shovel", + "CRT monitor", + "physics textbook", + "toaster", + "portrait of Richard Stallman", + "television", + "five ton truck", + "roll of duct tape", + "book", + "laptop", + "old television", + "sack of rocks", + "rainbow trout", + "rubber chicken", + "spiked bat", + "fire extinguisher", + "heavy rock", + "chunk of dirt", + "beehive", + "piece of rotten meat", + "bear", + "ton of bricks", +) + +THROW = ( + "throws", + "flings", + "chucks", + "hurls", +) + +HIT = ( + "hits", + "whacks", + "slaps", + "smacks", + "bashes", +) + +MARKDOWN_HELP = """ +Markdown is a very powerful formatting tool supported by telegram. {} has some enhancements, to make sure that \ +saved messages are correctly parsed, and to allow you to create buttons. + +- <code>_italic_</code>: wrapping text with '_' will produce italic text +- <code>*bold*</code>: wrapping text with '*' will produce bold text +- <code>`code`</code>: wrapping text with '`' will produce monospaced text, also known as 'code' +- <code>[sometext](someURL)</code>: this will create a link - the message will just show <code>sometext</code>, \ +and tapping on it will open the page at <code>someURL</code>. +EG: <code>[test](example.com)</code> + +- <code>[buttontext](buttonurl:someURL)</code>: this is a special enhancement to allow users to have telegram \ +buttons in their markdown. <code>buttontext</code> will be what is displayed on the button, and <code>someurl</code> \ +will be the url which is opened. +EG: <code>[This is a button](buttonurl:example.com)</code> + +If you want multiple buttons on the same line, use :same, as such: +<code>[one](buttonurl://example.com) +[two](buttonurl://google.com:same)</code> +This will create two buttons on a single line, instead of one button per line. +""" + +EnglishStrings = { + "send-help": """*Main commands available*: + - /start: start the bot + - /help or /help <module name>: PM's you info about that module. + - /lang: Change bot language + - /settings: + - in PM: will send you your settings for all supported modules. + - in a group: will redirect you to pm, with all that chat's settings. + {} + """, + + "send-group-settings": """Hi there! There are quite a few settings for *{}* - go ahead and pick what +you're interested in.""", + +#Misc +"RUNS-K": RUN_STRINGS, +"SLAP_TEMPLATES-K": SLAP_TEMPLATES, +"ITEMS-K": ITEMS, +"HIT-K": HIT, +"THROW-K": THROW, +"ITEMP-K": ITEMS, +"ITEMR-K": ITEMS, +"MARKDOWN_HELP-K": MARKDOWN_HELP, + +#GDPR +"send-gdpr": """Your personal data has been deleted.\n\nNote that this will not unban \ +you from any chats, as that is telegram data, not YanaBot data. +Flooding, warns, and gbans are also preserved, as of \ +[this](https://ico.org.uk/for-organisations/guide-to-the-general-data-protection-regulation-gdpr/individual-rights/right-to-erasure/), " +which clearly states that the right to erasure does not apply \ +\"for the performance of a task carried out in the public interest\", as is \ +the case for the aforementioned pieces of data.""" + +} diff --git a/Amanda/modules/translations/Russian.py b/Amanda/modules/translations/Russian.py new file mode 100644 index 0000000..49639b1 --- /dev/null +++ b/Amanda/modules/translations/Russian.py @@ -0,0 +1,747 @@ +from AnkiVector import dispatcher + +RUN_STRINGS = ( + "Куда ты собрался?", + "А? что? они ушли?", + "ZZzzZZzz... А? что? о, опять только эти, ничего страшного.", + "Вернись сюда!", + "Не так быстро...", + "Посмотри на стену!", + "Не оставляй меня наедине с ними!!", + "Ты бежишь, ты умираешь.", + "Ты пожалеешь об этом...", + "Вы также можете попробовать /kickme, я слышала, что это весело.", + "Иди, побеспокой кого-нибудь другого, здесь всем плевать.", + "Ты можешь бежать, но ты не можешь спрятаться.", + "Это всё, что у тебя есть?", + "Я стою за тобой...", + "У вас есть компания!", + "Мы можем сделать это лёгким или трудным путем.", + "Ты просто не понимаешь этого, не так ли?", + "Да, тебе лучше бежать!", + "Пожалуйста, напомните мне, как меня это волнует?", + "На твоем месте я бы бежал быстрее.", + "Это определённо тот дроид, которого мы ищем.", + "Пусть шансы всегда будут в вашу пользу.", + "Знаменитые последние слова.", + "И они исчезли навсегда, чтобы их больше не видели.", + "\"О, посмотри на меня! Я такой крутой, что могу убежать от бота! \"- это человек", + "Да, да, просто нажмите /kickme уже.", + "Вот, возьмите это кольцо и отправляйтесь в Мордор, раз уж вам по пути.", + "Легенда гласит, что они всё ещё бегут...", + "В отличие от Гарри Поттера, твои родители не могут защитить тебя от меня.", + "Страх приводит к гневу. Гнев приводит к ненависти. Ненависть приводит к страданиям. Если вы продолжите бежать в страхе, вы можете " + "стать следующим Вейдером.", + "Несколько вычислений спустя, я решил, что мой интерес к вашим махинациям ровно 0.", + "Легенда гласит, что они всё ещё бегут.", + "Продолжайте, не уверен, что мы хотим, чтобы вы здесь оставались.", + "Ты волше-Ой. Погоди. Ты не Гарри, продолжай двигаться.", + "НЕ БЕГАТЬ ПО КОРИДОРАМ!", + "Hasta la vista, детка.", + "Кто выпустил собак?", + "Это смешно, потому что никому нет дела.", + "Ах, какая потеря. Он мне нравился.", + "Честно говоря, моя дорогая, мне наплевать.", + "Мой молочный коктейль приводит всех мальчиков во двор... Так беги быстрее!", + "Вы не можете справиться с правдой!", + "Давным-давно, в далёкой-далёкой галактике... Кому-то было бы до этого дело. Хотя уже нет.", + "Эй, посмотри на них! Они убегают от неизбежного банхаммера... Мило.", + "Хан выстрелил первым. Я поступлю так же, как он.", + "За чем ты бежишь, за белым кроликом?", + "Как сказал бы Доктор... Беги!", +) + +SLAP_TEMPLATES = ( + "{user1} {hits} {user2} {itemp}.", + "{user1} {hits} в лицо {user2} {itemp}.", + "{user1} {throws} {itemr} в {user2}.", + "{user1} берёт {item} и {throws} им в лицо {user2}.", + "{user1} запускает {itemr} в направлении {user2}.", + "{user1} начинает безобидно хлопать {user2} {itemp}.", + "{user1} придавливает {user2} и несколько раз {hits} его {itemp}.", + "{user1} хвататет {itemr} и {hits} {user2}", + "{user1} привязывает {user2} к стулу и {throws} {itemr} в него.", + "{user1} дружески подтолкнул {user2}, чтобы тот научился плавать в лаве." +) + +ITEMS = ( + "чугунная сковорода", + "большая форель", + "бейсбольная бита", + "крикетная бита", + "деревянная трость", + "ноготь", + "принтер", + "лопата", + "ЭЛТ-монитор", + "учебник физики ", + "тостер", + "портрет Ричарда Столмана", + "телевизор", + "пятитонный грузовик", + "рулон клейкой ленты", + "книга", + "ноутбук", + "старый телевизор", + "мешок камней", + "радужная форель", + "резиновый цыпленок", + "шипастая летучая мышь", + "огнетушитель", + "тяжёлый камень", + "кусок грязи", + "улей", + "кусок гнилого мяса", + "медведь", + "тонна кирпичей", +) + +ITEMSP_RU = ( + "чугунной сковородой", + "большой форелью", + "бейсбольной битой", + "крикетной битой", + "деревянной тростью", + "ногтем", + "принтером", + "лопатой", + "ЭЛТ-монитором", + "учебником физики ", + "тостером", + "портретом Ричарда Столмана", + "телевизором", + "пятитонным грузовиком", + "рулоном скотча", + "книгой", + "ноутбуком", + "старым телевизором", + "мешком камней", + "радужной форелью", + "резиновым цыпленоком", + "шипастой летучей мышью", + "огнетушителем", + "тяжёлым камнем", + "кусоком грязи", + "улеем", + "кусоком гнилого мяса", + "медведем", + "тонной кирпичей", + "огромной булкой", +) + +ITEMSR_RU = ( + "чугунную сковородку", + "большкую форель", + "бейсбольную биту", + "крикетную биту", + "деревянную трость", + "ноготь", + "принтер", + "лопата", + "ЭЛТ-монитор", + "учебник физики ", + "тостер", + "портрет Ричарда Столмана", + "телевизор", + "пятитонный грузовик", + "рулон клейкой ленты", + "книга", + "ноутбук", + "старый телевизор", + "мешок камней", + "радужная форель", + "резиновый цыпленок", + "шипастая летучая мышь", + "огнетушитель", + "тяжёлый камень", + "кусок грязи", + "улей", + "кусок гнилого мяса", + "медведь", + "тонну кирпичей", +) + + +THROW = ( + "бросает", + "швыряет", +) + +HIT = ( + "бьёт", + "молотит", + "шлёпает", + "хлопает", + "колотит", + "царапает", +) + +MARKDOWN_HELP = """ +Markdown - очень мощный инструмент для форматирования текста, который поддерживает Telegram. {} имеет некоторые улучшения, чтобы убедиться, что +сохраненные сообщения правильно написаны , что позволяет создавать кнопки. + +- <code>_италик_</code>: выделение с двух сторон текста с помощью '_' приведет к созданию курсивного текста. +- <code>*полужирный*</code>: выделение с двух сторон текста с помощью '*' приведет к получению жирного текста. +- <code>`код`</code>: выделение с двух сторон текста с помощью '`' приведет к получению моноширинного текста, также известного как «код», +- <code>[ваш_текст](ваша_ссылка)</code>: это создаст ссылку - сообщение просто покажет <code> ваш_текст </code>, \ +и нажатие на него откроет страницу в <code>ваша_ссылка</code>. +Пример: <code>[test](ваша_ссылка)</code> + +- <code>[Текст кнопки](buttonurl:ваша ссылка)</code>: это специальное расширение, позволяющее пользователям создавать кнопки-ссылки. <code> Текст Кнопки </code> будет отображаться на кнопке, а <code>ваша ссылка</code> \ +будет открывать ваш URL-адрес. +Пример: <code>[Это кнопка](buttonurl:это_ссылка)</code> + +Если вам нужно несколько кнопок в одной строке, используйте это: +<code>[one](buttonurl://ваша_ссылка) +[two](buttonurl://google.com:same)</code> +Это создаст две кнопки в одной строке, а не одну кнопку на строку. +""" + +RussianStrings = { + +#Connections + "Disabled connections to this chat for users": "Отключены соединения к этому чату для пользователей", + "Enabled connections to this chat for users": "Включены соединения к этому чату для пользователей", + "Please enter on/yes/off/no in group!": "Пожалуйста введите on/yes/off/no в группу!", + "Successfully connected to *{}*": "Успешно подключено к *{}*", + "Connection failed!": "Подключение не удалось!", + "Connections to this chat not allowed!": "Подключение к этому чату не разрешено!", + "Write chat ID to connect!": "Напишите ID чата для подключения", + "Usage limited to PMs only!": "Использование ограничего в личных сообщениях только!", + +#Misc + "RUNS-K": RUN_STRINGS, + "SLAP_TEMPLATES-K": SLAP_TEMPLATES, + "ITEMS-K": ITEMS, + "HIT-K": HIT, + "THROW-K": THROW, + "ITEMP-K": ITEMSP_RU, + "ITEMR-K": ITEMSR_RU, + "MARKDOWN_HELP-K": MARKDOWN_HELP, + + "The original sender, {}, has an ID of `{}`.\nThe forwarder, {}, has an ID of `{}`.": + "Отправитель, {}, имеет ID `{}`.\nПересылающий, {}, имеет ID `{}`.", + "{}'s id is `{}`.": "ID {} - `{}`.", + "Your id is `{}`.": "Ваш ID - `{}`.", + "This group's id is `{}`.": "ID этой группы - `{}`.", + + "I can't extract a user from this.": "Я не могу извлечь ID этого пользователя.", + "<b>User info</b>:": "<b>Информация о пользователе</b>:", + "\nFirst Name: {}": "\nИмя: {}", + "\nLast Name: {}": "\nФамилия: {}", + "\nUsername: @{}": "\nНик: @{}", + "\nPermanent user link: {}": "\nПостоянная ссылка на пользователя: {}", + "\n\nThis person is my owner - I would never do anything against them!": + "\n\nЭтот человек - мой хозяин, я бы никогда ничего не сделала против него!", + "\nThis person is one of my sudo users! Nearly as powerful as my owner - so watch it.": + "\nЭтот человек является одним из моих пользователей sudo! Почти такой же мощный, как мой владелец - так что смотри.", + "\nThis person is one of my support users! Not quite a sudo user, but can still gban you off the map.": + "\nЭтот человек является одним из моих поддерживающих пользователей! Не совсем пользователь sudo, но он все равно может удалить вас.", + "\nThis person has been whitelisted! That means I'm not allowed to ban/kick them.": + "\nЭтот человек был включен в белый список! Это означает, что я не могу банить и кикать его.", + + "Its always banhammer time for me!": "Всегда есть время для банхаммера!", + + "It's {} in {}": "Сейчас {} в {}", + + "Please reply to a sticker to get its ID.": "Ответье на стикер чтобы получить его ID.", + "Please reply to a sticker for me to upload its PNG.": "Ответье на стикер чтобы получить его изображение.", + + "Write a location to check the weather.": "Напишите место чтобы получить его погоду.", + "I will keep an eye on both happy and sad times!": "Я буду следить за счастливыми и печальными временами!", + "Today in {} is being {}, around {}°C.\n": "Сегодня в {} {}, примерно {}°C.\n", + "Sorry, location not found.": "Простите, местоположение не найдено.", + + "Deleting identifiable data...": "Удаление пользовательских данных...", + + "Try forwarding the following message to me, and you'll see!": + "Попробуйте переслать мне следующее сообщение, и вы увидите!", + "/save test This is a markdown test. _italics_, *bold*, `code`, [URL](example.com) [button](buttonurl:github.com) [button2](buttonurl://google.com:same)": + """/save test Это тест markdown. _италик_, *жирный*, `код`, \ +[ССЫЛКА](example.com) +[Кнопка](buttonurl:github.com) +[Кнопка2](buttonurl://google.com:same)""", + +#Misc GDPR +"send-gdpr": """Ваши персональные данные были удалены.\n\nОбратите внимание, что это не разбанит вас \ +из любых чатов, поскольку это данные телеграмма, а не данные YanaBot. +Флуд, предупреждения и АнтиСпам также сохраняются, начиная с \ +[этого](https://ico.org.uk/for-organisations/guide-to-the-general-data-protection-regulation-gdpr/individual-rights/right-to-erasure/), " +в котором четко указано, что право на стирание не применяется \ +\"для выполнение задачи, выполняемой в общественных интересах\".""", + +#Admin +"How am I meant to promote someone that's already an admin?": "Как я должна назначить кого-то, кто уже является администратором?", +"I can't promote myself! Get an admin to do it for me.": "Я не могу назначить себя! Получите администратора, чтобы сделать это для меня.", +"Successfully promoted in *{}*!": "Успешно назначен в *{}*!", + +"This person CREATED the chat, how would I demote them?": "Этот человек СОЗДАЛ чат, как я могу его понизить?", +"Can't demote what wasn't promoted!": "Как я могу понизить того кто не является админом?", +"I can't demote myself!": "Я не могу понизить себя!", +"Successfully demoted in *{}*!": "Успешно понижен в *{}*!", +"Could not demote. I might not be admin, or the admin status was appointed by another user, so I can't act upon them!": +"Не могу понизить. Возможно, я не администратор, или статус администратора был назначен другим пользователем, поэтому я не могу понизить его!", + +"I don't have access to the invite link, try changing my permissions!": "У меня нет доступа к ссылке приглашения, попробуйте изменить мои права!", +"I can only give you invite links for supergroups and channels, sorry!": "Я могу дать ссылку для супергрупп и каналов!", + +"Admins in": "Админы в", +"this chat": "этом чате", +" (Creator)": " (Создатель)", + +#AFK +"{} is now AFK!": "{} сейчас АФК!", +"{} is no longer AFK!": "{} больше не АФК!", +"{} is AFK!": "{} сейчас АФК!", +"{} is AFK! says its because of: \n{}": "{} сейчас АФК! Потому что: \n{}", + +#Antiflood +"I like to leave the flooding to natural disasters. But you, you were just a disappointment. Get out.": + "Мне нравится оставлять флуд для стихийных бедствий. Но ты был просто разочарованием. Убирайся!", +"I can't kick people here, give me permissions first! Until then, I'll disable antiflood.": + "Я не могу кикать людей здесь, сначала дайте мне разрешения! Я отключу antiflood.", +"Antiflood has been disabled.": "Антифлуд был выключен.", +"Antiflood has to be either 0 (disabled), or a number bigger than 3 (enabled)!": + "Antiflood должен быть либо 0 (отключен), либо число больше 3 (включен)!", +"Antiflood has been updated and set to {}": "Антифлуд был установлен на {}", +"Unrecognised argument - please use a number, 'off', or 'no'.": + "Неизвестный аргумент - используйте число, 'off', или 'no'.", +"I'm not currently enforcing flood control!": "В настоящее время я не применяю контроль над флудом!", +"I'm currently banning users if they send more than {} consecutive messages.": + "В настоящее время я баню пользователей, если они отправляют более {} последовательных сообщений.", + +#Antispam +"I've enabled antispam security in this group. This will help protect you from spammers, unsavoury characters, and the biggest trolls.": + "Я включила защиту от спама в этой группе. Это поможет защитить вас от спамеров, отвратительных людей и самых больших троллей.", + +"I've disabled antispam security in this group. GBans wont affect your users anymore. You'll be less protected from any trolls and spammers though!": + "Я отключила защиту от спама в этой группе. Антиспам больше не будет влиять на ваших пользователей. Вы не будете защищены от троллей и спамеров!", + +"Give me some arguments to choose a setting! on/off, yes/no!\n\nYour current setting is: {}\nWhen True, any gbans that happen will also happen in your group. When False, they won't, leaving you at the possible mercy of spammers.": + "Дайте мне несколько аргументов, чтобы выбрать настройку! on/off, yes/no!\n\nВаши текущие параметры: {}\nЕсли True, защита от спаммеров (бан людей) также произойдут в вашей группе. Когда false, они не будут, оставляя вас на возможной милости спамеров.", + +"Globally banned: <b>{}</b>": "Глобально забанен: <b>{}</b>", +"\nGlobally muted: <b>{}</b>": "\nГлобально замучен: <b>{}</b>", +"\nReason: {}": "\Причина: {}", + +#Bans + "I really wish I could ban admins...": "Мне так жаль, что я не могу забанить админа...", + "I'm not gonna BAN myself, are you crazy?": "Я не собираюсь банить себя, ты с ума сошел?", + "Banned!": "Забанен!", + "Well damn, I can't ban that user.": "Черт, я не могу запретить этого пользователя.", + "You haven't specified a time to ban this user for!": + "Вы не указали время, чтобы забанить этого пользователя!", + "Banned! User will be banned for {}.": "Забанен! Пользователь забанен на {}.", + +#Blacklist + "<b>Current blacklisted words in {}:</b>\n": "<b>Текущие заблокированные слова в {}:</b>\n", + "There are no blacklisted messages in <b>{}</b>!": "Нет запрещенных сообщений в <b>{}</b>!", + "Added <code>{}</code> to the blacklist in <b>{}</b>!": + "Добавлено <code>{}</code> в черный список в <b>{}</b>!", + "Tell me which words you would like to add to the blacklist.": + "Скажите, какие слова вы хотели бы добавить в черный список.", + "Removed <code>{}</code> from the blacklist in <b>{}</b>!": + "Удалено <code>{}</code> из черного списка в <b>{}</b>!", + "This isn't a blacklisted trigger...!": "Это не тригер черного списка...!", + "None of these triggers exist, so they weren't removed.": + "Ни один из этих триггеров не существует, поэтому они не были удалены.", + "Removed <code>{}</code> triggers from the blacklist in <b>{}</b>! {} did not exist, so were not removed.": + "Удалено <code>{}</code> тригерра из черного списка в <b>{}</b>! {} из них не существовует, поэтому они не были удалены.", + "Tell me which words you would like to remove from the blacklist.": + "Скажите, какие слова вы хотите удалить из черного списка.", + + #Filters + "*Filters in {}:*\n": "*Фильтры в {}:*\n", + "local filters": "локальные фильтры", + "*local filters:*\n": "*локальные фильтры:*\n", + "No filters in {}!": "Нет фильтров в {}!", + "There is no note message - You can't JUST have buttons, you need a message to go with it!": + "Вы не можете просто иметь кнопки, вам нужен текст для нормальной работы!", + "You didn't specify what to reply with!": "Вы не указали на что мне отвечать!", + "Handler '{}' added in *{}*!": "Фильтр '{}' добавлен в *{}*!", + "No filters are active in {}!": "Нет фильтров в {}!", + "Yep, I'll stop replying to that in *{}*." : "Хорошо, я не буду отвечать на это в *{}*.", + "That's not a current filter - run /filters for all active filters.": + "Это не текущий фильтр - выполните /filters чтобы увидеть текущие фильтры.", + + #Disable + "Disabled the use of `{}` in *{}*": "Использование `{}` выключено в *{}*", + "That command can't be disabled": "Эта комманда не может быть выключена", + "What should I disable?": "Что я должна отключить?", + + "Enabled the use of `{}` in *{}*": "Использование `{}` включено в *{}*", + "Is that even disabled?": "Это было выключено?", + "What should I enable?": "Что я должна включить?", + + "The following commands are toggleable:\n{}": "Следующие комманды могут быть выключены:\n{}", + "No commands can be disabled.": "Нет отключаемых комманд.", + "No commands are disabled in *{}*!": "Нет выключенных комманд в *{}*!", + "No commands are disabled!": "Нет выключенных комманд!", + "The following commands are currently restricted in *{}*:\n{}": + "Следующие комманды выключены в *{}*:\n{}", + +#Locks + "Locked {} messages for all non-admins!": "Заблокированы {} сообщения для не админов!", + "What are you trying to lock...? Try /locktypes for the list of lockables": + "Что вы пытаетесь заблокировать...? Посмотрите /locktypes для списка возможных блокировок", + "I'm not an administrator, or haven't got delete rights.": + "Я не администратор или я не имею права на удаление сообщений.", + "Unlocked {} for everyone!": "Разблокированы {} для всех!", + "What are you trying to unlock...? Try /locktypes for the list of lockables": + "Что вы пытаетесь разблокировать...? Посмотрите /locktypes для списка возможных блокировок", + "What are you trying to unlock...?": "Что вы пытаетесь разблокировать...?", + "I see a bot, and I've been told to stop them joining... but I'm not admin!": + "Я вижу бота, и мне сказали банить их... Но я не админ!", + "Only admins are allowed to add bots to this chat! Get outta here.": + "Только админам разрешено добавлять ботов в этот чат! Убирайся отсюда.", + "There are no current locks in *{}*.": "Нет текущих блокировок в *{}*.", + "These are the locks in *{}*:": "Текущие блокировки в *{}*:", + "this chat": "этом чате", + +#Log channel + "Now, forward the /setlog to the group you want to tie this channel to!": + "Теперь перешлите /setlog в группу, с которой вы хотите связать этот канал!", + "This channel has been set as the log channel for {}.": + "Этот канал был установлен как канал логов для {}.", + "Successfully set log channel!": "Успешно установлен канал логов!", + "*The steps to set a log channel are:*\n • add bot to the desired channel\n • send /setlog to the channel\n • forward the /setlog to the group\n": + """*Настройка канала для логирования:* + • Добавление бота в канал (Как админа!) + • Отправка `/setlog` в канал + • Пересылка отправленного сообщения `/setlog` в группе""", + + "Channel has been unlinked from {}": "Канал отключен от {}", + "Log channel has been un-set.": "Канал логов не установлен.", + "No log channel has been set yet!": "Канал логов еще не установлен!", + +#Users + "I've seen them in <code>{}</code> chats in total.": + "Я видела его в <code>{}</code> чатах.", + "I've seen them in... Wow. Are they stalking me? They're in all the same places I am... oh. It's me.": + "Я видела его в... Стоп. Вау. Вы преследуете меня? Я нахожусь во всех чатах, Ох... Да. Это я.", + +#Msg_deleting + "Cannot delete all messages. The messages may be too old, I might not have delete rights, or this might not be a supergroup.": + "Не удается удалить все сообщения. Сообщения могут быть слишком старыми, или у меня могут не быть прав на удаление, или это может быть не супергруппа.", + "Purge complete.": "Чистка завершена.", + "Reply to a message to select where to start purging from.": + "Ответьте на сообщение, чтобы выбрать, с чего начать чистку.", + "Whadya want to delete?": "Что я должна удалить?", + +#Muting + "You'll need to either give me a username to mute, or reply to someone to be muted.": + "Вам нужно либо дать мне имя пользователя, либо ответить на сообщение, чтобы он был замутен.", + "I'm not muting myself!": "Я не буду мутить себя!", + "Afraid I can't stop an admin from talking!": "Боюсь, я не могу остановить админов от разговора!", + "You'll need to either give me a username to unmute, or reply to someone to be unmuted.": + "Вам нужно либо дать мне имя пользователя, либо ответить на сообщение, чтобы он был размучен.", + "This user already has the right to speak in {}.": "Этот пользователь уже имеет право говорить в {}.", + "Yep, {} can start talking again in {}!": "Да, {} может говорить снова в {}!", + "This user isn't even in the chat, unmuting them won't make them talk more than they already do!": + "Этот пользователь даже не в чате", + "I really wish I could mute admins...": "Мне очень жаль, что я не могу мутить админов...", + "I'm not gonna MUTE myself, are you crazy?" : "Я не собираюсь себя мутить, ты с ума сошел?", + "You haven't specified a time to mute this user for!": + "Вы не указали время для мутинга этого пользователя!", + "Muted for {} in {}!": "Я заткнула его на {} в {}!", + "This user is already muted in {}!": "Этот пользователь и так заткнут.", + "Well damn, I can't mute that user.": "Ну, черт побери, я не могу заткнуть этого пользователя.", + + "You'll need to either give me a username to restrict, or reply to someone to be restricted.": + "Вам нужно либо указать мне имя пользователя, либо ответить на сообщение чтобы ограничить его.", + "I'm not restricting myself!": "Я не буду ограничивать себя!", + "Afraid I can't restrict admins!": "Боюсь, я не могу ограничить админов!", + "{} is restricted from sending media in {}!": "{} запрещен к отправлению медиа в {}!", + "This user is already restricted in {}!": "Этот пользователь и так ограничен в {}!", + "This user isn't in the {}!": "Этот пользователь не в {}!", + + "You'll need to either give me a username to unrestrict, or reply to someone to be unrestricted.": + "Вам нужно либо дать мне имя пользователя, либо ответить на сообщение, чтобы он мог слать медиа.", + "This user already has the rights to send anything in {}.": + "У этого пользователя и так есть права отправлять что-либо в {}.", + "Yep, {} can send media again in {}!": "Да, {} может отправлять что-либо в {}!", + "This user isn't even in the chat, unrestricting them won't make them send anything than they already do!": + "Этот пользователь даже не в чате.", + "I really wish I could restrict admins...": "Мне очень хотелось бы ограничить админов...", + "I'm not gonna RESTRICT myself, are you crazy?": "Я не собираюсь ОГРАНИЧИТЬ себя, ты с ума сошел?", + "You haven't specified a time to restrict this user for!": + "Вы не указали время, чтобы ограничить этого пользователя!", + "Well damn, I can't restrict that user.": "Ну, черт возьми, я не могу ограничить этого пользователя.", + "{} is muted in {}!": "{} заткнут в {}!", + "Restricted from sending media for {} in {}!": "Ограничен от отправление медиа {} в {}!", + "Restricted for {} in {}!": "{} Ограничен от отправки мендиа в {}!", + +#Notes + "Get rekt": "Нечего давать.", + + +#Multi + "Invalid Chat ID provided!": "ID чата недействительный!", #Connections + "You don't seem to be referring to a user.": "Кажется, вы не обращаетесь к пользователю.", #Admin, Bans, Muting + "I can't seem to find this user": "Я не могу найти этого пользователя", #Bans, Muting + "Yes": "Да", #Antispam + "No": "Нет", #Antispam + +#__main__ + #Module names + "Admin": "Администрирование", + "AFK": "Режим АФК", + "AntiFlood": "Антифлуд", + "Bans": "Баны", + "Word Blacklists": "Черные списки", + "Filters": "Фильтры", + "Command disabling": "Отключение комманд", + "Antispam security": "Антиспам Защита", + "Locks": "Блокировки", + "Log Channels": "Логирование действий", + "Misc": "Остальное", + "Purges": "Чистка", + "Muting & Restricting": "Муты и запреты", + "Notes": "Заметки", + "Reporting": "Репорты", + "RSS Feed": "RSS Лента", + "Rules": "Правила", + "Connections": "Соединения", + "Bios and Abouts": "Подпись человка", + "Warnings": "Предупреждения", + "Welcomes/Goodbyes": "Приветствие/Прощание", + +#Some main stuff +"Here is the help for the *{}* module:\n{}": "Помощь по модулю *{}*:\n{}", +"Back": "Назад", +"send-help":"""Главный комманды: + - /start: краткая информация про бота. + - /help: Я напишу вам это сообщение. + - /help Или же /help <название модуля>: Я расскажу вам про этот модуль. + - /lang: Смена языка бота. + - /settings: Показать текущие настройки модулей. + {} + """, + + +"\nAll commands can either be used with `/` or `!`.\n": "\nВсе коммнды могут начинатся с `/` или `!`\n", + +#Module helps +"Admin_help": """- /adminlist | /admins: Список админов в чате. + +*Только админы:* + - /pin: Безшумно закрепить отвеченое сообщение - добавьте аргумент 'loud' или 'notify' чтобы уведомить пользователей о закреплении. + - /unpin: Открепить текущее закрепренное сообщение. + - /invitelink: Получить ссылку-приглашение. + - /promote: Повысить пользователя. + - /demote: Понизить пользователя.""", + +"AFK_help": """ - /afk <причина>: пометить себя как АФК. + - brb <причина>: тоже самое как и /afk - но не комманда. + +Когда вы пометили себя как АФК, на любое упоминаниие вас будет отправлено сообщение о вашей недоступности.""", + +"AntiFlood_help": """- /flood: Получить текущие настройки антифлуда. + +*Только админы:* + - /setflood <число/`no`/`off`>: Включить или выключить антифлуд защиту.""", + +"Antispam security_help":"""*Только админы:* + - /antispam <on/yes/off/no>: Отключить защиту от нежелательного спама в группе, или показать текущие настройки если нет аргумента. + +Антиспам используется владельцами ботов для бана спамеров во всех группах. Это помогает защитить \ +вас и ваши группы, удалив спам флудеров как можно быстрее. Это может быть отключено для вашей группы используя \ +`/antispam off`""", + +"Locks_help": """ - /locktypes: список возможных типов блокировок. + +*Только админы:* + - /lock <тип>: заблокировать элемент в этом чате. + - /unlock <тип>: разблокировать элемент в этом чате. + - /locks: Текущий список блокировок в этом чате. + +Блокировки можно использовать для ограничения пользователей группы. Например: +Блокировка URL-адресов будет автоматически удалять все сообщения с URL-адресами, которые не были в белом списке, блокировка стикеров будут удалены все \ +стикеры и т. д. +Блокировка ботов остановит не администраторов от добавления ботов в чат.""", + +"Command disabling_help":""" - /cmds: Проверка текущего состояния отключенных комманд. +*Только админы:* + - /enable <комманда>: включить эту комманду. + - /disable <комманда>: выключить эту комманду. + - /listcmds: список всех возможных комманд для отключения.""", + +"Filters_help": """ - /filters: список всех активных фильтров в этом чате. + +*Только админы:* + - /filter <ключевое слово> <ответное сообщение >: Добавить фильтр в этот чат. Бот будет отвечать на это сообщение всякий раз, когда "ключевое слово" \ +будет упомянуто. +ПРИМЕЧАНИЕ: все фильтры не чуствительны к регистру букв. +Если вы отвечаете стикером с ключевым словом, то ответьте на стикер сообщением `/filter 'ключевое слово'`. +Если вы хотите, чтобы ваше ключевое слово было предложением, используйте одинарные кавычки. Например: `/filter 'Привет всем' Как вы?` + - /stop <ключевое слово>: Остановить этот фильтр.""", + +"Bans_help": """ - /kickme: Удаляет пользователя написавшего это из группы. + +*Только админы:* + - /ban <пользователь>: Забанить пользователя. + - /tban <пользователь> (m/h/d): Забанить пользователя на X время. m = минуты, h = часы, d = дни. + - /unban <пользователь>: Разбанить пользователя. + - /kick <пользователь>: Удалить пользователя из группы. +ПРИМЕЧАНИЕ: Вместо конструкции `/комманда <пользователь>` можно ответить коммандой на любое сообщение пользователя.""", + +"Connections_help":"""Соединения можно использовать для контролирования группы с лички бота, это удобно чтобы не спамить в чат. +Доступны действия с соедененными группами: + • Просмотр и редактирование заметок. + • Просмотр и редактирование фильтров. + • Просмотр и редактирование черного списка. + • Назначать/понижать пользователей. + • Просмотр списка админов, просмотр ссылки-приглашения. + • Включать/выключать комманды в чате + • Мутить/размутить пользователей в чате + • Ограничить пользователей в чате + • Больше в будущем! + + - /connection <chatid>: Подключится к группе. + - /disconnect: Отключится от группы. + - /allowconnect on/yes/off/no: Разрешить подключение пользователей к этой группе.""", + +"RSS Feed_help": """ - /addrss <ссылка>: Добавить ссылку в подписку RSS. + - /removerss <ссылка>: Удалить ссылку из подписки. + - /rss <ссылка>: Показать последнюю новость из RSS единожды. + - /listrss: Показать подписки. + +ПРИМЕЧАНИЕ: В группе только админы могут добавлять/удалять RSS ссылки""", + +"Sed/Regex_help": """- s/<текст1>/<текст2>(/<флаг>): ответьте на сообщение с этой коммандой чтобы произвести операцию замены текста на ней, будет заменен \ +'текст1' на 'текст2'. Флаг опционален, '`i`' для игнорироваия капса, '`g`' для глобальной замены. +Разделители могут быть `/`, `_`, `|`, и `:`. Поддерэивается группировка текста. Результат не может быть больше чем 4096 символов. + +ПРИМЕЧАНИЕ: Sed использует некоторые специальные символы, такие как: `+*.?\\` +Если вы хотите использовать эти символы, убедитесь, что вы их экранируете! +Пример: `\\?`.""", + +"Log Channels_help": """*Только админы:* +- /logchannel: Получить информацию о текущем лог канале +- /setlog: Установить канал для логирования. +- /unsetlog: Отключить канал для логирования. + +Настройка канала для логирования: +- Добавление бота в канал (Как админа!) +- Отправка `/setlog` в канал +- Пересылка отправленного сообщения `/setlog` в группе +""", + +"Reporting_help": """ - /report <причина>: ответьте на сообщение что бы отправить его админам на проверку. + - @admin: Тоже самое как и /report, только без причины. +ПРИМЕЧАНИЕ: Возможность репорта дана только пользователям, на адмиов не будет реакции + +*Только админы:* + - /reports <on/off>: сменить настройки репорта, или посмотреть текущий статус. + - Если ввести в личку бота - изменить свои настройки. + - Если ввести в чат - изменить настройки чата.""", + +"Notes_help": """ - /get <название заметки>: Получить заметку с этим именем. + - #<название заметки>: Тоже самое что и /get. + - /notes или /saved: Показать все заметки в этой группе. + +Если вы хотите получить содержимое заметки без какого-либо форматирования, используйте `/get <название заметки> noformat`. Это \ +полезно при обновлении существующей заметки. + +*Только админы:* + - /save <название заметки> <данные заметки>: Сохранить заметку +Кнопка может быть добавлена к заметке с помощью стандартного синтаксиса ссылки на разметку - ссылка должна быть просто добавлена с помощью \ +`buttonurl:` пример: `[Кнопка](buttonurl:example.com)`. Напишите /markdownhelp для получения дополнительной информации. + - /save <название заметки>: ответьте на сообщение чтобы сохранить его. + - /clear <название заметки>: Удалить заметку с этим именем.""", + +"Muting & Restricting_help": """*Только админы:* + - /mute <ник>: Замутить пользователя. (Через ник или ответ на сообщеник.) + - /tmute <ник> Х(m/h/d): Замутить пользователя на Х время. (Через ник или ответ на сообщеник.) m = минуты, h = часы, d = дни (пример: `60m` или `1h`). + - /unmute <ник>: Размутить пользователя. (Через ник или ответ на сообщеник.) + - /restrict <ник>: Запретить пользователю отправлять стикеры, гифки, ссылки или медиа. + - /trestrict <ник> Х(m/h/d): Запретить пользователю отправлять стикеры, гифки, ссылки или медиа на Х время. (via handle, or reply). m = минуты, h = часы, d = дни (пример: `60m` или `1h`). + - /unrestrict <ник>: Разрешить пользователю отправлять стикеры, гифки, ссылки или медиа.""", + +"Misc_help": """ - /id: Получить ID группы. Если ответить коммандой на сообщение - выдаст ID пользователя. + - /runs: Ответьте случайную строку из массива ответов. + - /slap: Ударить пользователя. + - /time <место>: Дает местное время в данном месте. + - /weather <место>: Получить текущую погоду в этом месте. + - /info: Получить информацию о пользователе. + - /gdpr: Удаляет вашу информацию из базы данных бота. + - /stickerid: Ответьте на стикер чтобы получить его ID. + - /getsticker: Ответьте на стикер чтобы получить его как png. + + - /markdownhelp: Краткое изложение того, как работает markdown в Telegram, - можно вызывать только в личке.""", + +"Bios and Abouts_help": """ - /setbio <текст>: При ответе, сохранит био другого пользователя. + - /bio: Получит био вашего или другого пользователя. + - /setme <текст>: Сохранит вашу информацию. + - /me: Получить информацию о вас или другом пользователе.""", + +"Rules_help": """ - /rules: Получить правила чата. + +*Только админы:* + - /setrules <правила>: Уставновить правила для чата. + - /clearrules: Удалсть правила для группы.""", + +"Warnings_help": """ - /warns <ник>: Получить текущие предупреждения пользователя или себя. + - /warnlist: Список всех текущих фильтров предупреждения. + +*Только админы:* + - /warn <ник>: Предупредить пользователя. После 3 предупреждений пользователь будет заблокирован из группы. Можно так же вызвать как ответ. + - /resetwarn <ник>: Сбросить предупреждения пользователя. Можно так же вызвать как ответ. + - /addwarn <ключевое слово> <предупреждение>: Установить фильтр предупреждения на определенное ключевое слово. Если вы хотите, чтобы ваше ключевое слово \ +было предложением, охватите его кавычками, пример: `/addwarn я сердит 'Это злой пользователь'`. + - /nowarn <ключевое слово>: Остановить фильтр предупреждений. + - /warnlimit <число>: Установить лимит предупреждений. + - /strongwarn <on/yes/off/no>: Если значение включено, превышение предела предупреждения приведет к бану. Если выключено, пользователь будет кикнут.""", + +"Welcomes/Goodbyes_help": """Сообщения Приветсвия и Прощания могут быть персонализированы многими способами. +Если вы хотите, чтобы сообщения были индивидуально сгенерированы, например приветственное сообщение по умолчанию, \ +вы можете использовать эти переменные: + - `{{first}}`: Имя пользователя. + - `{{last}}`: Фамилия пользователя. Если нет фамилии, будет использовано имя. + - `{{fullname}}`: Полное имя пользователя. Если нет полного имени, будет использовано имя. + - `{{username}}`: Ник пользователя. Если нет полного имени, будет использовано упоминание. + - `{{mention}}`: Просто упоминае пользователя с именем. + - `{{id}}`: ID пользователя. + - `{{count}}`: Счетчик пользователей в группе. + - `{{chatname}}`: Имя группы. +Каждая переменная ДОЛЖНА быть окружена `{{}}` для замены. +Сообщения Приветсвия и Прощания также поддерживают markdown, поэтому вы можете создавать любые элементы. \ +Кнопки также поддерживаются, поэтому вы можете сделать свои приветствия великолепными с помощью некоторых кнопок с надписью. \ +Чтобы создать кнопку, ссылающуюся на ваши правила, используйте это: `[Правила](buttonurl://t.me/{}?Start=group_id)`.\ +Просто замените `group_id` ID вашей группы, который можно получить через /id. Обратите внимание, что ID группы обычно предшествует знак `-`, он необходим, \ +поэтому, пожалуйста, не удаляйте его. \ +Вы можете даже установить изображения/гифки/видио/голосовые сообщения в качестве приветственного сообщения \ +ответе на нужный элемент и напишите /setwelcome. + +*Только админы:* + - /welcome <on/off>: Включить/Выключить приветсвенные сообщения. + - /welcome: Показать текущий статус приветсвенных сообщений. + - /welcome noformat: Показывает текущие настройки приветствия, без форматирования - полезно при изменении вашого приветственного сообщения! + - /goodbye -> такое же использование как и /welcome. + - /setwelcome <текст>: Установка пользовательского приветственного сообщение. + - /setgoodbye <текст>: Установка пользовательского сообщение прощания. + - /resetwelcome: Вернуть приветственное сообщение по умолчанию. + - /resetgoodbye: Вернуть прощальное сообщение по умолчанию. + - /cleanwelcome <on/off>: Удаление преведущего приветственного сообщение после отправки нового, что бы избежать спама в группе. + - /cleanservice <on/off/yes/no>: Удаляет все служебные сообщения; Эти раздражающие \"Х присоединились к группе\", которые вы видите, когда люди присоединяются. + - /welcomesecurity <off/soft/hard>: soft - ограничить пользователя отправлять медиафайлы в течение 24 часов, hard - ограничить пользователя отправлять сообщения, пока он не нажимает кнопку \"Я не бот\".""".format(dispatcher.bot.username), + +"Word Blacklists_help":"""Черные списки используются, чтобы остановить определенные триггеры в группе. Каждый раз когда триггер будет упоминатся, \ +сообщение будет немедленно удалено. Это хорошо сочетает это с фильтрами предупреждений. + +ПРИМЕЧАНИЕ: черные списки не влияют на админов группы. + + - /blacklist: Просмотр текущих тригерров в черных списках. + +*Только админы:* + - /addblacklist <триггер>: Добавьте триггер в черный список. Каждая строка считается одним триггером, \ +поэтому использование разных строк позволит вам добавить несколько триггеров. + - /unblacklist <триггер>: Удалите триггеры из черного списка. Здесь применяется такая же логика новой строки, вы можете удалить \ +несколько триггеров одновременно. + - /rmblacklist <триггер>: То же, что и выше.""", + +"Purges_help": """*Только админы:* + - /del: Удалить сообщение на которое вы ответили. + - /purge: Удаляет все сообщения между этим и сообщением на которое вы ответили. + - /purge <Число Х>: Удаляет сообщение на которое вы ответили и последующие X-сообщения. """ +} diff --git a/Amanda/modules/translations/Ukraine.py b/Amanda/modules/translations/Ukraine.py new file mode 100644 index 0000000..51cfd25 --- /dev/null +++ b/Amanda/modules/translations/Ukraine.py @@ -0,0 +1,486 @@ +from AnkiVector import dispatcher + +RUN_STRINGS = ( + "Куди ти зібрався?", + "А? Шо? Вони пішли?" + "ZZzzZZzz... А? Шо? о, знову тільки вони, нічого." + "Повернися сюди.", + "Не так швидко...", + "Подивися на стіну!", + "Не лишайте мене з ними!!", + "Ти біжиш, ти помираєш.", + "Ти пошкодуєщ...", + "Ви можете спробувати /kickme, я чула, що це цікаво.", + "Іди, потурбуй когось іншого, тут всім байдуже.", + "Ти біжи, але тобі не сховатися.", + "Це все, що в тебе є?", + "Я стою позаду...", + "У тебе є компанія!", + "Ми можемо зробити це легким або складним шляхом.", + "Ти просто не розумієщ цього, чи не так?", + "Так, тобі краще тікати!", + "Будь-ласка, нагадай мені, наскільки мені це важливо?", + "На твоєму місці я б біжав швидше.", + "Це точно той дроід, якого ми шукаємо.", + "Най вдача буде на вашій стороні.", + "Усім відомі останні слова.", + "Вони зникли назавжди, щоб їх більше не бачили.", + "\"О, подивись на мене! Я настільки крутий, що можу втікти від Бога! \"- це людина", + "Так, так, просто натисніть /kickme уже.", + "Ось, візьміть це кільце й держіть курс на Мордор, вам все одно по дорозі.", + "За давніми легендами, вони ще біжать...", + "На відміну від Гарі Потера, твоі батьки не зможуть захистити тебе від мене.", + "Страх приводить до гніву. Гнів приводить до ненависті. Ненависть приводить до стражданнь. Якщо ви продовжите тікати в страху, ви можете " + "стати наступним Вейдером.", + "Кілька розрахунків після, я вирішив, що мій інтерес до ваших махінацій рівно 0.", + "Продовжуйте, не впевнений, що ми хочемо, щоб ви тут лишались.", + "Ти маг-ОЙ. Зажди. Ти не Гарі, продовжуйте рух.", + "НЕ БІГАТИ ПО КОРИДОРАМ!", + "Hasta la vista, дєтка.", + "Хто випустив собаку?", + "Це смішно тому, що всім пофіг.", + "Ах, яка втрата. Він мені подобався.", + "Чесно кажучи, моя дорогенька, мені байдуже.", + "Ви не можете йти проти правди!", + "Давним-давно, в далкекій-далекій галактиці... Комусь вони були потрібні. Але вже ні.", + "Ех, подивись на них! Вони тікають від неминучого банхаммера... Мило.", + "Хан стріляв першим. Я зроблю так, як він.", + "Навіщо ти біжиш за білим кроликом?", + "Як сказав би Доктор... Біжи!", +) + +SLAP_TEMPLATES = ( + "{user1} {hits} {user2} {itemp}.", + "{user1} {hits} в лице {user2} {itemp}.", + "{user1} {throws} {itemr} в {user2}.", + "{user1} бере {item} та {throws} їм в обличчя {user2}.", + "{user1} запускає {itemr} в напрямку {user2}.", + "{user1} розпочинає необразливо плескати {user2} {itemp}.", + "{user1} притискає {user2} та кілька разів {hits} його {itemp}.", + "{user1} хапає {itemr} та {hits} {user2}", + "{user1} прив'язує {user2} до стільця й {throws} {itemr} у нього.", + "{user1} по дружньому підштовхнув {user2}, щоб той швидше навчився плавати в лаві." +) + +ITEMS = ( + "чавунна сковорідка", + "велика форель", + "бейсбольна біта", + "крикетна біта", + "дерев'яна палиця", + "ніготь", + "принтер", + "лопата", + "ЕЛТ-монітор", + "книжка з фізики ", + "тостер", + "портрет Річарда Столмана", + "телевізор", + "п'ятитонна машина", + "рулон клейкої стрічки", + "книга", + "ноутбук", + "старий телевізор", + "мішок каменів", + "форель", + "гумове курчатко", + "шипаста летюча миша", + "вогнегасник", + "важкий камінь", + "шматок гімна", + "вулик", + "шматок гнилого м'яса", + "ведмідь", + "тонна цеглин", +) + +ITEMSP_UA = ( + "чавунною сковорідкою", + "великою фореллю", + "бейсбольною бітою", + "крикетною бітой", + "дерев'яною палицею", + "нігтем", + "принтером", + "лопатою", + "ЕЛТ-монітором", + "книжкою з фізики ", + "тостером", + "портретом Річарда Столмана", + "телевізором", + "пятитонною машиною", + "рулоном скотча", + "книгою", + "ноутбуком", + "старим телевізором", + "мішком каменів", + "фореллю", + "гумовим курчатком", + "шипастою летючею мишею", + "вогнегасником", + "важким каменем", + "шматком гімна", + "улеєм", + "шматком гнилого м'яса", + "ведмедем", + "тонною цеглин", + "гігантським пиріжком", +) + +ITEMSR_UA = ( + "чавунну сковорідку", + "велику форель", + "бейсбольну біту", + "крикетну біту", + "дерев'яну палицю", + "ніготь", + "принтер", + "лопату", + "ЕЛТ-монітор", + "книжку з фізики", + "тостер", + "портрет Річарда Столмана", + "телевізор", + "п'ятитону машинк", + "рулон скотчу", + "книгу", + "ноутбук", + "старий телевізор", + "мішок каменів", + "форель", + "гумове курча", + "шипасту летучу мишу", + "вогнегасник", + "важкий камінь", + "шматок гімна", + "вулик", + "шматок гнилого м'яса", + "ведмедя", + "тонну цеглин", +) + + +THROW = ( + "кидає", + "швиряє", + "роняє", +) + +HIT = ( + "б'є", + "молотить", + "шльопає", + "хлопає", + "колотить", + "царапає", +) + +UkrainianStrings = { + +#Connections + "Disabled connections to this chat for users": "Від'єднані з'єднання в цьому чаті для користувачів", + "Enabled connections to this chat for users": "Увімкнені з'єднання в цьому чаті для користувачів", + "Please enter on/yes/off/no in group!": "Будь-ласка введіть on/yes/off/no в групу!", + "Successfully connected to *{}*": "Успішно під'єднано до *{}*", + "Connection failed!": "З'єднання не вдалось!", + "Connections to this chat not allowed!": "Під'єднання до цього чату не дозволено!", + "Write chat ID to connect!": "Напишіть ID чату для під'єднання", + "Usage limited to PMs only!": "Використання лімітовано лише для особистих повідомлень!", + +#Admin + "How am I meant to promote someone that's already an admin?": "Як я можу призначити адміном того, хто вже ним являється?", + "I can't promote myself! Get an admin to do it for me.": "Я не можу призначити себе! Отримайте адміністратора, щоб зробити це для мене.", + "Successfully promoted in *{}*!": "Успішно призначений в *{}*!", + + "This person CREATED the chat, how would I demote them?": "Ця людина СТВОРИЛА чат, як я можу її знизити?", + "Can't demote what wasn't promoted!": "Як я можу знизити того хто не є адміном?", + "I can't demote myself!": "Я не можу знизити себе!", + "Successfully demoted in *{}*!": "Успішно знизений в *{}*!", + "Could not demote. I might not be admin, or the admin status was appointed by another user, so I can't act upon them!": + "Я не можу знизити. Можливо я не адмін, чи статус адміністратора був був призначений іншим користувачем, тому я не можу знизити його!", + + "I don't have access to the invite link, try changing my permissions!": "У мене немає доступу до запрошувального посилання, спробуйте змінити мої права!", + "I can only give you invite links for supergroups and channels, sorry!": "Я можу надавати запрошувальні посилання для супергруп та каналів!", + + "Admins in": "Адміністратори в", + "this chat": "цьому чаті", + " (Creator)": " (Створювач)", + +#Multi + "Invalid Chat ID provided!": "ID чату не існує!", #Connections + "You don't seem to be referring to a user.": "Здається, ви звертаєтесь не до користувача.", #Admin + +#Misc +"RUNS-K": RUN_STRINGS, +"SLAP_TEMPLATES-K": SLAP_TEMPLATES, +"ITEMS-K": ITEMS, +"HIT-K": HIT, +"THROW-K": THROW, +"ITEMP-K": ITEMSP_UA, +"ITEMR-K": ITEMSR_UA, +"Today in {} is being {}, around {}°C.\n": "Сьогодні в {} {}, приблизно {}°C.\n", + +#Multi + "Invalid Chat ID provided!": "ID чату не існує!", #Connections + +#__main__ + #Module names + "Admin": "Адміністрування", + "AFK": "Режим АФК", + "AntiFlood": "Антифлуд", + "Bans": "Бани", + "Word Blacklists": "Чорні списки", + "Filters": "Фільтри", + "Command disabling": "Відключення команд", + "Antispam security": "Антиспам захист", + "Locks": "Блокування", + "Log Channels": "Логування дій", + "Misc": "Інше", + "Purges": "Очистка", + "Muting & Restricting": "Мути й заборони", + "Notes": "Замітки", + "Reporting": "Репорти", + "RSS Feed": "RSS Лента", + "Rules": "Правила", + "Connections": "З'єднання", + "Bios and Abouts": "Підпис людини", + "Warnings": "Попередження", + "Welcomes/Goodbyes": "Привітання/Прощання", + +#Some main stuff +"Here is the help for the *{}* module:\n{}": "Допомога по модулю *{}*:\n{}", +"Back": "Назад", +"send-help": """Головні команди: + - /start: коротка інфа про бота. + - /help: Я напишу вам повідомлення. + - /help <назва модуля>: Я раскажу вам про цей модуль. + - /donate: Інформація про те як вдонатити в мене! + - /lang: Змінити мову бота. + - /settings: Показати поточні установки бота. + {} + """, + + +"\nAll commands can either be used with `/` or `!`.\n": "\nУсі команди можуть починатися з `/` або `!`\n", + +#Module helps +"Admin_help": """ - /adminlist | /admins: Список адмінів чату. + +*Тільки для адміністраторів:* + - /pin: Тихо закріпити отримане повідомлення - додайте аргумент 'loud' або 'notify' щоб повідомити користувачів про закріплення. + - /unpin: Відкріпити дане закріплене повідомлення. + - /invitelink: Отримати посилання-запрошення. + - /promote: Підвищити користівача. + - /demote: Знизити користувача.""", + +"AFK_help": """ - /afk <причина>: відмітити себе як АФК. + - brb <причина>: теж саме як /afk - але не команда. + +Якщо ви відмітили себе як АФК, на будь-яке згадування про вас буде відправлене повідомлення про вашу недоступність.""", + +"AntiFlood_help": """- /flood: Отримати дані установки антифлуда. + +*Тільки адміни:* + - /setflood <число/`no`/`off`>: Увімкнути або вимкнути антифлуд захист.""", + +"Antispam security_help":"""*Тільки адміни:* + - /antispam <on/yes/off/no>: Вимкнути захист від небажаного спаму, або показати дані установки якщо немає аргумента. + +Антиспам використовується власниками ботів для бану спамерів у всіх групах. Це допомагає захистити \ +вас та ваші групи, видаляючи спам флудерів як можна швидше. Це може бути відключене для вашої групи використовуючи \ +`/antispam off`""", + +"Locks_help": """ - /locktypes: список можливих блокувань. + +*Тільки адміни:* + - /lock <тип>: заблокувати елемент у цьому чаті. + - /unlock <тип>: разблокувати елемент у цьому чаті. + - /locks: Даний список блокувань у цьому чаті. + +Блокування можна використовувати для обмеження користувачів групи. Наприклад: +Блокування URL-адрес буде автоматично видаляти всі повідомлення з URL-адресами, які не були в білому списці, блокування стикерів буде видаляти всі \ +стикери и т. п. +Блокування ботів зупине не адміністраторів від додавання ботів у чат.""", + +"Command disabling_help":""" - /cmds: Перевірка даного стану відключених команд. +*Тільки адміни:* + - /enable <команда>: увімкнути цю команду. + - /disable <команда>: вимкнути цю команду. + - /listcmds: список усіх можливих команд для відключення.""", + +"Filters_help": """ - /filters: список усіх активних фільтрів у цьому чаті. + +*Тільки адміни:* + - /filter <ключове слово> <відповідне повідомлення>: Додати фільтр у цей чат. Бот буде відповідати на це повідомлення кожен раз, як "ключове слово" \ +буде згадано. +ДОДАТОК: усі фільтри не вразливі до регистру букв. +Якщо ви відповідаєте стикером з ключовим словом, то дайте відповідь на стикер повідленням `/filter 'ключове слово'`. +Якщо ви хочете, щоб ваше ключове слово було реченням, використовуйте одинарні кавички. Наприклад: `/filter 'Привіт усім' Як ви?` + - /stop <ключове слово>: Зувинти цей фільтр.""", + +"Bans_help": """ - /kickme: Видаляє користувача написавшего це з групи. + +*Тільки адміни:* + - /ban <користувач>: забанити користувача. + - /tban <користувач> (m/h/d): Забанити користувача на X часу. m = хвилини, h = години, d = дні. + - /unban <користувач>: Разбанити користувача. + - /kick <користувач>: видалити користувача. +ДОДАТОК: Замість конструкції `/команда <користувач>` можно відповісти командою на будь-яке повідомлення користувача.""", + +"Connections_help":"""З'єднання можно використати для контролювання групи з ОП бота, це зручно щоб не спамити в чат. +Доступні дії з групами: + • Перегляд та змінення нотаток + • Перегляд та редагування фільтрів. + • Перегляд та редагування нотаток. + • Підвищувати/знижувати користувачів. + • Перегляд списку адмінів, перегляд посилання для запрошення. + • Вмикати/вимикати комманди в чаті + • Більше в майбутньому! + + - /connection <chatid>: Підключитися до групи. + - /disconnect: Відключитися від групи. + - /allowconnect on/yes/off/no: Дозволити підключення користувачів(юзерів) до цієї групи.""", + +"RSS Feed_help": """ - /addrss <посилання>: Додати посилання в підписку RSS. + - /removerss <посилання>: Видалити посилання з підпису. + - /rss <посилання>: Показати останню новину з RSS один раз. + - /listrss: Показати підписки. + +ДОДАТОК: В групі тільки адміни можуть додавати/видаляти RSS посилання""", + +"Sed/Regex_help": """- s/<текст1>/<текст2>(/<прапор>): дайте відповідь на повідомлення з цією командою щоб замінити текст на ній, буде замінено \ +'текст1' на 'текст2'. Прапор опціональний, '`i`' для ігнорування капсу, '`g`' для глобальної заміни. +Розділювачі можуть бути `/`, `_`, `|`, та `:`. Підтримується групування тексту. Результат не може бути більше ніж 4096 символів. + +ДОДАТОК: Sed використовує деякі спеціальні символи, такі як: `+*.?\\` +Якщо ви хочете використовувати ці символи, переконайтесь, що ви їх екрануєте! +Приклад: `\\?`.""", + +"Log Channels_help": """*Тільки адміни:* +- /logchannel: Отримати інформацію про даний лог канал +- /setlog: Встановити канал для логування. +- /unsetlog: Вимкнути канал для логування. + +Налаштування канала для логування: +- Додавання бота в канал (Як адміна!) +- Відправлення `/setlog` в канал +- Пересилання відправленого повідомлення `/setlog` в групі +""", + +"Reporting_help": """ - /report <причина>: дайте відповідь на повідомлення щоб відправити його адмінам на перевірку. + - @admin: теж саме що й /report, тільки без причини. +ДОДАТОК: Можливість репорта дозволена тільки користувачям, на адмінів не буде реакції + +*Тільки адміни:* + - /reports <on/off>: змінити установки репорта, або продивитися даний статус. + - Якщо вивести в личку бота - змінити свої установки. + - Якщо ввести в чат - Змінити установки чата.""", + +"Notes_help": """ - /get <назва нотатки>: Отримати нотатку з цим іменем. + - #<назва нотатки>: ткж саме що й /get. + - /notes або /saved: Показати усі нотатки в цій групі. + +Якщо ви хочете отримати вміст нотатки без ніякого форматування, використайте `/get <назва нотатки> noformat`. Це \ +корисно при оновленні існуючої нотатки. + +*Тільки адміни:* + - /save <назва нотатки> <данні нотатки>: Збарегти нотатку. +Кнопка може бути додана до нотатки з допомогою стандартного синтаксиса посилання на рохмітку - посилання повинне бути просто додано з допомогою \ +`buttonurl:` приклад: `[Кнопка](buttonurl:example.com)`. Напишіть /markdownhelp Для отримання розширеної інформації. + - /save <назва нотатки>: дайте відповідь на повідомлення щоб зберегти. + - /clear <назва нотатки: видалити нотатку з цим іменем.""", + +"Muting & Restricting_help": """*Тільки адміни:* + - /mute <нік>: Замутити користувача. (Через нік або відповадь на повідомлення.) + - /tmute <нік> Х(m/h/d): Замутити користувача на Х часу. (Через нік або відповідь на повідомлення.) m = хвилини, h = години, d = дні (приклад: `60m` або `1h`). + - /unmute <нік>: Размутити користувача. (Через нік або відповідь на повідомлення.) + - /restrict <нік>: Заборонити користувачу відправляти стікери, гіфки, посилання чи медіа. + - /trestrict <нік> Х(m/h/d): Заборонити користувачу відправляти стікери, гіфки, посилання чи медіа на Х часу. (via handle, or reply). m = хвилини, h = години, d = дні (приклад: `60m` або `1h`). + - /unrestrict <нік>: Дозволити користувачу відправляти стікери, гіфки, посилання чи медіа.""", + +"Misc_help": """ - /id: Отримати ID групи. Якщо відповісти командою на повідомлення - ви отримаєте ID користувача. + - /runs: Відповісти рандомною фразою. + - /slap: Вдарити користувача. + - /time <місце>: Виводить місцевий час. + - /info: Отримати інфу про користувача. + - /gdpr: Видаляє інфу про вас з бази данних бота. + - /stickerid: Дайте відповідь на стікер щоб отримати його ID. + - /getsticker: Дайте відповідь на стікер щоб отримати його як png. + + - /markdownhelp: Коротка інформація про те, як працює markdown в Telegram, - можна викликати тільки в личці.""", + +"Bios and Abouts_help": """ - /setbio <текст>: При відповаіді, збереже біо іншого користувача. + - /bio: Виведе біо вашого чи іншого користувача. + - /setme <текст>: Збереже вашу інформацію. + - /me: Виведе інформацію про вас, чи про іншого користувача.""", + +"Rules_help": """ - /rules: Вивести правила чата. + +*Тільки адміни:* + - /setrules <правила>: Встановити правила для чата. + - /clearrules: Видалити правила для групи.""", + +"Warnings_help": """ - /warns <нік>: Вивести дані варни цього користувача чи для себе. + - /warnlist: Вивести список фільтрів для варнів. + +*Тільки адміни:* + - /warn <нік>: Попередити користувача. Після 3 попереджень користувач буде заблокований у групі. Можна також використати як відповідь. + - /resetwarn <нік>: Скинути попередження користувача. Можна також використати як відповідь. + - /addwarn <ключове слово> <попередження>: Встановити фільтр попередження на ключове слово. Якщо ви хочете, щоб ваше ключове слово \ +було реченням, виділіть його кавичками, наприклад: `/addwarn я розлючений 'Це злий користувач'`. + - /nowarn <ключове слово>: Вимкнути фільтр попереджень. + - /warnlimit <число>: Встановити ліміт попереджень. + - /strongwarn <on/yes/off/no>: Якщо увімкнено, то перевищення ліміту попереджень призведе до бану. Якщо вимкнено, користувач буде кікнутий.""", + +"Welcomes/Goodbyes_help": """Повідомлення Привітання та Прощання можуть бути персоналізовані багатьма методами. +Якщо ви хочете, щоб повідомлення були індивідуально сгенеровані, наприклад привітальне повідомлення за умовчанням, \ +аи можете використовувати такі змінні: + - `{{first}}`: Ім'я користувача. + - `{{last}}`: Прізвище користувача. Якщо нема прізвища, буде використано ім'я. + - `{{fullname}}`: Повне ім'я користувача. Якщо нема повного імені, буде використано ім'я. + - `{{username}}`: Нік користувача. Якщо нема повного імені, буде використано згадування. + - `{{mention}}`: Згадує користувача з іменем. + - `{{id}}`: ID користувача. + - `{{count}}`: Лічильник користувачів у групі. + - `{{chatname}}`: Ім'я групи. +Кожна змінна ПОВИННА бути виділена `{{}}` для зміни. +Повідомлення Привітання й Прощання також підтримують markdown, тому ви можете створювати будь-які елементи. \ +Кнопки також підтримуються, тому ви можете створити свої привітання чудовими з лоромогою деяких кнопок з написом. \ +Щоб створити кнопку, що ссилається на ваші правила, використовуйте це: `[Правила](buttonurl://t.me/{}?Start=group_id)`.\ +Просто змініть `group_id` ID вашої групи, який можна отримати через /id. Зверніть увагу, що ID групи зазвичай зазвичай передує `-`, він потрібен, \ +тому, будь-ласка, не видяляйте його. \ +Ви можете також встановити зображення/гіфки/відео/голосові повідомлення в якості привітального повідомлення \ +дайте відповідь на потрібний елемент та напишіть /setwelcome. + +*Тільки адміни:* + - /welcome <on/off>: Увімкнути/Вимкнути привітальні повідомлення. + - /welcome: Показує даний статус привітальних повідомлень. + - /welcome noformat: Показує данні установки привітання, без форматування - корисно при зміненні вашого привітального повідомлення! + - /goodbye -> так само як і /welcome. + - /setwelcome <текст>: Установка користувацького привітального повідомлення. + - /setgoodbye <текст>: Установка користувацького прощального повідомлення. + - /resetwelcome: Повернути привітальне повідомлення за умовчанням. + - /resetgoodbye: Повернути прощальне повідомлення за умовчанням. + - /cleanwelcome <on/off>: Видалення попереднього привітального повідомлення після відправлення нового, щоб уникнути спаму в групі. + - /cleanservice <on/off/yes/no>: Видаляє всі службові повідомлення; Ці докучливі \"Х приєдналися до групи\", котрі ви бачите, коли люди приєднуються до групи. + - /welcomesecurity <off/soft/hard>: soft - заборонити користувачу відправляти медіафайли протягом 24 годин, hard - заборонити користувачу віправляти повідомлення, поки він не натисне кнопку \"Я не бот\".""".format(dispatcher.bot.username), + +"Word Blacklists_help":"""Чорні списки використовуються, щоб зупинити певні трігери в групі. Кожен раз, коли трігер буде згадано, \ +повідомлення буде миттєво видалене. Це добре комбінується з фільтрами попереджень. + +ДОДАТОК: чорні списки не діють на адмінів групи. + + - /blacklist: Перегляд поточних трігерів в групі. + +*Тільки адміни:* + - /addblacklist <трігер>: додайте трігер в чорний список. Кожна строка розпочиняється одним трігером, \ +тому використання різних строк дозволить вам додавати кілька трігерів. + - /unblacklist <трігер>: видалення трігера з чорного списку. Тут використовуєьбся така ж логіка нової строки, ви можете видалити \ +кілька трігерів одночасно. + - /rmblacklist <трігер>: теж саме, що й вище.""", + +"Purges_help": """*Тільки адміни:* + - /del: видалити повідомлення, на яке ви відповіли. + - /purge: видаляє всі повідомлення між цим та тим на яке ви відповіли. + - /purge <Число Х>: видаляє повідомлення на яке ви відповіли й наступні Х-повідомлень. """ +} diff --git a/Amanda/modules/translations/list_locale.py b/Amanda/modules/translations/list_locale.py new file mode 100644 index 0000000..57bd7b3 --- /dev/null +++ b/Amanda/modules/translations/list_locale.py @@ -0,0 +1,185 @@ +list_locales = { 'ab': 'Abkhaz', +'aa': 'Afar', +'af': 'Afrikaans', +'ak': 'Akan', +'sq': 'Albanian', +'am': 'Amharic', +'ar': 'Arabic', +'an': 'Aragonese', +'hy': 'Armenian', +'as': 'Assamese', +'av': 'Avaric', +'ae': 'Avestan', +'ay': 'Aymara', +'az': 'Azerbaijani', +'bm': 'Bambara', +'ba': 'Bashkir', +'eu': 'Basque', +'be': 'Belarusian', +'bn': 'Bengali', +'bh': 'Bihari', +'bi': 'Bislama', +'bs': 'Bosnian', +'br': 'Breton', +'bg': 'Bulgarian', +'my': 'Burmese', +'ca': 'Catalan', +'ch': 'Chamorro', +'ce': 'Chechen', +'ny': 'Chichewa', +'zh': 'Chinese', +'cv': 'Chuvash', +'kw': 'Cornish', +'co': 'Corsican', +'cr': 'Cree', +'hr': 'Croatian', +'cs': 'Czech', +'da': 'Danish', +'dv': 'Divehi', +'nl': 'Nederlands', +'dz': 'Dzongkha', +'en': 'English', +'eo': 'Esperanto', +'et': 'Estonian', +'ee': 'Ewe', +'fo': 'Faroese', +'fj': 'Fijian', +'fi': 'Finnish', +'fr': 'French', +'ff': 'Fula', +'gl': 'Galician', +'ka': 'Georgian', +'de': 'Deutsch', +'el': 'Greek', +'gn': 'Guaraní', +'gu': 'Gujarati', +'ht': 'Haitian', +'ha': 'Hausa', +'he': 'Hebrew', +'hz': 'Herero', +'hi': 'Hindi', +'ho': 'Hiri Motu', +'hu': 'Hungarian', +'ia': 'Interlingua', +'id': 'Indonesian', +'ie': 'Interlingue', +'ga': 'Irish', +'ig': 'Igbo', +'ik': 'Inupiaq', +'io': 'Ido', +'is': 'Icelandic', +'it': 'Italian', +'iu': 'Inuktitut', +'ja': 'Japanese', +'jv': 'Javanese', +'kl': 'Kalaallisut', +'kn': 'Kannada', +'kr': 'Kanuri', +'ks': 'Kashmiri', +'kk': 'Kazakh', +'km': 'Khmer', +'ki': 'Kikuyu', +'rw': 'Kinyarwanda', +'ky': 'Kirghiz', +'kv': 'Komi', +'kg': 'Kongo', +'ko': 'Korean', +'ku': 'Kurdish', +'kj': 'Kwanyama', +'la': 'Latin', +'lb': 'Luxembourgish', +'lg': 'Luganda', +'li': 'Limburgish', +'ln': 'Lingala', +'lo': 'Lao', +'lt': 'Lithuanian', +'lu': 'Luba-Katanga', +'lv': 'Latvian', +'gv': 'Manx', +'mk': 'Macedonian', +'mg': 'Malagasy', +'ms': 'Malay', +'ml': 'Malayalam', +'mt': 'Maltese', +'mi': 'Māori', +'mr': 'Marathi', +'mh': 'Marshallese', +'mn': 'Mongolian', +'na': 'Nauru', +'nv': 'Navajo', +'nb': 'Norwegian Bokmål', +'nd': 'North Ndebele', +'ne': 'Nepali', +'ng': 'Ndonga', +'nn': 'Norwegian Nynorsk', +'no': 'Norwegian', +'ii': 'Nuosu', +'nr': 'South Ndebele', +'oc': 'Occitan', +'oj': 'Ojibwe', +'cu': 'Old Church Slavonic', +'om': 'Oromo', +'or': 'Oriya', +'os': 'Ossetian', +'pj': 'Punjabi', +'pi': 'Pāli', +'fa': 'Persian', +'pl': 'Polish', +'ps': 'Pashto', +'pt': 'Portuguese', +'pt-br' : 'Brazilian Portuguese', +'qu': 'Quechua', +'rm': 'Romansh', +'rn': 'Kirundi', +'ro': 'Moldavan', +'ru': 'Russian', +'sa': 'Sanskrit', +'sc': 'Sardinian', +'sd': 'Sindhi', +'se': 'Northern Sami', +'sm': 'Samoan', +'sg': 'Sango', +'sr': 'Serbian', +'gd': 'Scottish Gaelic', +'sn': 'Shona', +'si': 'Sinhalese', +'sk': 'Slovak', +'sl': 'Slovene', +'so': 'Somali', +'st': 'Southern Sotho', +'es': 'Castilian', +'su': 'Sundanese', +'sw': 'Swahili', +'ss': 'Swati', +'sv': 'Swedish', +'ta': 'Tamil', +'te': 'Telugu', +'tg': 'Tajik', +'th': 'Thai', +'ti': 'Tigrinya', +'bo': 'Tibetan', +'tk': 'Turkmen', +'tl': 'Tagalog', +'tn': 'Tswana', +'to': 'Tonga', +'tr': 'Turkish', +'ts': 'Tsonga', +'tt': 'Tatar', +'tw': 'Twi', +'ty': 'Tahitian', +'ug': 'Uyghur', +'uk': 'Ukrainian', +'ur': 'Urdu', +'uz': 'Uzbek', +'ve': 'Venda', +'vi': 'Vietnamese', +'vo': 'Volapük', +'wa': 'Walloon', +'cy': 'Welsh', +'wo': 'Wolof', +'fy': 'Western Frisian', +'xh': 'Xhosa', +'yi': 'Yiddish', +'yo': 'Yoruba', +'za': 'Chuang', +'zu': 'Zulu'} diff --git a/Amanda/modules/tts.py b/Amanda/modules/tts.py index ba8e1bb..862a9da 100644 --- a/Amanda/modules/tts.py +++ b/Amanda/modules/tts.py @@ -1,37 +1,74 @@ -from datetime import datetime +import json +import os +import requests from gtts import gTTS -from telegram import ChatAction, Update -from telegram.ext import CallbackContext, run_async +from telegram import ChatAction +from telegram.ext import run_async from Amanda import dispatcher from Amanda.modules.disable import DisableAbleCommandHandler +from Amanda.modules.helper_funcs.alternate import send_action, typing_action @run_async -def tts(update: Update, context: CallbackContext): - args = context.args - current_time = datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S") - datetime.now().strftime("%d%m%y-%H%M%S%f") - reply = " ".join(args) - update.message.chat.send_action(ChatAction.RECORD_AUDIO) - lang = "ml" - tts = gTTS(reply, lang) - tts.save("k.mp3") - with open("k.mp3", "rb") as f: - linelist = list(f) - linecount = len(linelist) - if linecount == 1: - update.message.chat.send_action(ChatAction.RECORD_AUDIO) - lang = "en" - tts = gTTS(reply, lang) +@send_action(ChatAction.RECORD_AUDIO) +def gtts(update, context): + msg = update.effective_message + reply = " ".join(context.args) + if not reply: + if msg.reply_to_message: + reply = msg.reply_to_message.text + else: + return msg.reply_text( + "Reply to some message or enter some text to convert it into audio format!" + ) + for x in "\n": + reply = reply.replace(x, "") + try: + tts = gTTS(reply) tts.save("k.mp3") - with open("k.mp3", "rb") as speech: - update.message.reply_voice(speech, quote=False) + with open("k.mp3", "rb") as speech: + msg.reply_audio(speech) + finally: + if os.path.isfile("k.mp3"): + os.remove("k.mp3") -TTS_HANDLER = DisableAbleCommandHandler("tts", tts, pass_args=True) -dispatcher.add_handler(TTS_HANDLER) +# Open API key +API_KEY = "6ae0c3a0-afdc-4532-a810-82ded0054236" +URL = "http://services.gingersoftware.com/Ginger/correct/json/GingerTheText" -__command_list__ = ["tts"] -__handlers__ = [TTS_HANDLER] + +@run_async +@typing_action +def spellcheck(update, context): + if update.effective_message.reply_to_message: + msg = update.effective_message.reply_to_message + + params = dict(lang="US", clientVersion="2.0", apiKey=API_KEY, text=msg.text) + + res = requests.get(URL, params=params) + changes = json.loads(res.text).get("LightGingerTheTextResult") + curr_string = "" + prev_end = 0 + + for change in changes: + start = change.get("From") + end = change.get("To") + 1 + suggestions = change.get("Suggestions") + if suggestions: + sugg_str = suggestions[0].get("Text") # should look at this list more + curr_string += msg.text[prev_end:start] + sugg_str + prev_end = end + + curr_string += msg.text[prev_end:] + update.effective_message.reply_text(curr_string) + else: + update.effective_message.reply_text( + "Reply to some message to get grammar corrected text!" + ) + + +dispatcher.add_handler(DisableAbleCommandHandler("tts", gtts, pass_args=True)) +dispatcher.add_handler(DisableAbleCommandHandler("splcheck", spellcheck)) diff --git a/Amanda/modules/upload.py b/Amanda/modules/upload.py index b22e107..1f7d5ca 100644 --- a/Amanda/modules/upload.py +++ b/Amanda/modules/upload.py @@ -48,7 +48,7 @@ async def send_to_transfersh_async(file): async def send_to_tmp_async(file): - url = "https://tmp.ninja/api.php?d=upload-tool" + url = "https://tmp.ninja/upload.php?d=upload-tool" with open(file, "rb") as f: async with aiohttp.ClientSession() as session: @@ -83,7 +83,7 @@ async def tsh(event): str(time.time() - start) await orta.edit( - f"File Successfully Uploaded to TransferSh.\n\nLink 👉 {download_link}\nExpired Date 👉 {final_date}\n\nUploaded by @TheAmandabot 👸" + f"File Successfully Uploaded to TransferSh.\n\nLink 👉 {download_link}\nExpired Date 👉 {final_date}\n\nUploaded by @TheAmandabot " ) except Exception as e: traceback.print_exc() @@ -118,7 +118,7 @@ async def tmp(event): str(time.time() - start) await orta.edit( - f"File Successfully Uploaded to TmpNinja.\n\nLink 👉 {download_link}\n\nUploaded by @TheAmandabot 👸" + f"File Successfully Uploaded to TmpNinja.\n\nLink 👉 {download_link}\n\nUploaded by @TheAmandabot" ) except Exception as e: traceback.print_exc() @@ -181,10 +181,10 @@ def main(): main() __help__ = """ - ⦁ /transfersh : reply to a telegram file to upload it on transfersh and get direct download link - ⦁ /tmpninja : reply to a telegram file to upload it on tmpninja and get direct download link +@TheAmandabot + ❍ `/transfersh`*:* reply to a telegram file to upload it on transfersh and get direct download link + ❍ `/tmpninja`*:* reply to a telegram file to upload it on tmpninja and get direct download link -© @TharukRenuja 🇱🇰 """ -__mod_name__ = "File To Link" +__mod_name__ = "ᴜᴘʟᴏᴀᴅ📩" diff --git a/Amanda/modules/urlshort.py b/Amanda/modules/urlshort.py new file mode 100644 index 0000000..459bb9d --- /dev/null +++ b/Amanda/modules/urlshort.py @@ -0,0 +1,51 @@ +# Copyright (C) 2021 TeamDaisyX + + +# This file is part of Daisy (Telegram Bot) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import json + +import aiohttp +from pyrogram import filters + +from Amanda.function.pluginhelpers import admins_only, get_text +from Amanda.services.pyrogram import pbot + + + +@pbot.on_message( + filters.command("short") & ~filters.edited & ~filters.bot & ~filters.private +) +@admins_only +async def shortify(client, message): + lel = await client.send_message(message.chat.id, "`Wait a sec....`") + url = get_text(message) + if "." not in url: + await lel.edit("Defuq!. Is it a url?") + return + header = { + "Authorization": "Bearer ad39983fa42d0b19e4534f33671629a4940298dc", + "Content-Type": "application/json", + } + payload = {"long_url": f"{url}"} + payload = json.dumps(payload) + async with aiohttp.ClientSession() as session: + async with session.post( + "https://api-ssl.bitly.com/v4/shorten", headers=header, data=payload + ) as resp: + data = await resp.json() + msg = f"**Original Url:** {url}\n\n**Shortened Url:** {data['link']}\n\n**Powered by**:-@TheAmandabot " + await lel.edit(msg) diff --git a/Amanda/modules/urluploader.py b/Amanda/modules/urluploader.py index ae3784a..f5e0258 100644 --- a/Amanda/modules/urluploader.py +++ b/Amanda/modules/urluploader.py @@ -28,7 +28,7 @@ async def download_coroutine(session, url, file_name, event, start, bot): **URL:** {} **File Name:** {} **File Size:** {} -**© @SLBotsOfficial**""".format( +**© @TheAmandabot**""".format( url, os.path.basename(file_name).replace("%20", " "), humanbytes(total_length), @@ -44,7 +44,7 @@ async def download_coroutine(session, url, file_name, event, start, bot): downloaded += CHUNK_SIZE now = time.time() diff = now - start - if round(diff % 10.00) == 0: # downloaded == total_length: + if round(diff % 10.00) == 0: percentage = downloaded * 100 / total_length speed = downloaded / diff elapsed_time = round(diff) * 1000 @@ -83,10 +83,9 @@ async def download_coroutine(session, url, file_name, event, start, bot): __help__ = """ -* Url Upload * - -• /up : reply to a direct download link to upload it to telegram as files +@TheAmandabot +❍ `/up`*:* reply to a direct download link to upload it to telegram as files -© @TharukRenuja 🇱🇰 """ -__mod_name__ = "URL Upload" + +__mod_name__ = "ᴜʀʟ〽️" diff --git a/Amanda/modules/userinfo.py b/Amanda/modules/userinfo.py index 30d01d7..6fd79b2 100644 --- a/Amanda/modules/userinfo.py +++ b/Amanda/modules/userinfo.py @@ -308,7 +308,7 @@ def info(update: Update, context: CallbackContext): if disaster_level_present: - text += ' [<a href="https://t.me/SuzuyaUpdates/55">?</a>]'.format(bot.username) + text += ' [<a href="https://t.me/SLBotsOfficial">?</a>]'.format(bot.username) try: user_member = chat.get_member(user.id) @@ -420,14 +420,13 @@ def set_about_me(update: Update, context: CallbackContext): @run_async -@sudo_plus def stats(update: Update, context: CallbackContext): process = subprocess.Popen( "neofetch --stdout", shell=True, text=True, stdout=subprocess.PIPE ) output = process.communicate()[0] stats = ( - "<b>Current stats:</b>\n" + "<b>stats of szrosebot📊:</b>\n" + "\n" + output + "\n".join([mod.__stats__() for mod in STATS]) @@ -524,30 +523,41 @@ def __user_info__(user_id): result = result.strip("\n") return result - __help__ = """ +@TheAmandabot *ID:* - • `/id`*:* get the current group id. If used by replying to a message, gets that user's id. - • `/gifid`*:* reply to a gif to me to tell you its file ID. + ❍ `/id`*:* get the current group id. If used by replying to a message, gets that user's id. + ❍ `/gifid`*:* reply to a gif to me to tell you its file ID. *Self addded information:* - • `/setme <text>`*:* will set your info - • `/me`*:* will get your or another user's info. + ❍ `/setme <text>`*:* will set your info + ❍ `/me`*:* will get your or another user's info. Examples: `/setme I am a wolf.` `/me @username(defaults to yours if no user specified)` *Information others add on you:* - • `/bio`*:* will get your or another user's bio. This cannot be set by yourself. -• `/setbio <text>`*:* while replying, will save another user's bio + ❍ `/bio`*:* will get your or another user's bio. This cannot be set by yourself. + ❍ `/setbio <text>`*:* while replying, will save another user's bio Examples: `/bio @username(defaults to yours if not specified).` `/setbio This user is a wolf` (reply to the user) *Overall Information about you:* - • `/info`*:* get information about a user. + ❍ `/info`*:* get information about a user. + +*What is that health thingy?* + That is a new weeb tech called "HP system", aka Health points system. -*©@SLBotsOfficial + Penalties from available HP: + 25% if no username + 25% if no profile pic + 20% if no setme exists + 10% if no what others say exists + 7% if user is AFK + 5% if the user is AFK with reason + + """ SET_BIO_HANDLER = DisableAbleCommandHandler("setbio", set_about_bio) @@ -570,7 +580,7 @@ def __user_info__(user_id): dispatcher.add_handler(SET_ABOUT_HANDLER) dispatcher.add_handler(GET_ABOUT_HANDLER) -__mod_name__ = "Info" +__mod_name__ = "ɪɴꜰᴏ❓" __command_list__ = ["setbio", "bio", "setme", "me", "info"] __handlers__ = [ ID_HANDLER, diff --git a/Amanda/modules/users.py b/Amanda/modules/users.py index d3ae054..8bdc9cc 100644 --- a/Amanda/modules/users.py +++ b/Amanda/modules/users.py @@ -61,9 +61,9 @@ def broadcast(update: Update, context: CallbackContext): if len(to_send) >= 2: to_group = False to_user = False - if to_send[0] == "/broadcastgroups": + if to_send[0] == "/gcast": to_group = True - if to_send[0] == "/broadcastusers": + if to_send[0] == "/ucast": to_user = True else: to_group = to_user = True @@ -128,7 +128,7 @@ def chats(update: Update, context: CallbackContext): for chat in all_chats: try: curr_chat = context.bot.getChat(chat.chat_id) - curr_chat.get_member(context.bot.id) + bot_member = curr_chat.get_member(context.bot.id) chat_members = curr_chat.get_members_count(context.bot.id) chatfile += "{}. {} | {} | {}\n".format( P, chat.chat_name, chat.chat_id, chat_members @@ -176,7 +176,7 @@ def __migrate__(old_chat_id, new_chat_id): __help__ = "" # no help string BROADCAST_HANDLER = CommandHandler( - ["broadcastall", "broadcastusers", "broadcastgroups"], broadcast + ["kdoiashdoihsoeifjdrjfpor", "sdlojeospjfpojipedoi", "liohdilsjpoidoieosi"], broadcast ) USER_HANDLER = MessageHandler(Filters.all & Filters.group, log_user) CHAT_CHECKER_HANDLER = MessageHandler(Filters.all & Filters.group, chat_checker) @@ -187,5 +187,5 @@ def __migrate__(old_chat_id, new_chat_id): dispatcher.add_handler(CHATLIST_HANDLER) dispatcher.add_handler(CHAT_CHECKER_HANDLER, CHAT_GROUP) -__mod_name__ = "Users" +__mod_name__ = "ᴜsᴇʀs" __handlers__ = [(USER_HANDLER, USERS_GROUP), BROADCAST_HANDLER, CHATLIST_HANDLER] diff --git a/Amanda/modules/vc.py b/Amanda/modules/vc.py new file mode 100644 index 0000000..fb487c5 --- /dev/null +++ b/Amanda/modules/vc.py @@ -0,0 +1,69 @@ +__help__ = """ +* • Groups and channels Voice Chat Music Play 🎙 * + +1️⃣ Make bot admin (Group and in channel if use cplay) +2️⃣ Start a voice chat +3️⃣ Try /play [song name] for the first time by an admin +4️⃣ If userbot joined enjoy music, If not add @SLBotsOfficialHelp to your group and retry + +* 🔔 For Channel Music Play 🔔 * + +1️⃣ Make @TheAmandabot admin of your channel +2️⃣ Send /userbotjoinchannel in linked group +3️⃣ Now send commands in linked group + +* 👥Groups Music Play👥 * + +❍ /play: Play song using youtube music +❍ /play [yt url] : Play the given yt url +❍ /play [reply yo audio]: Play replied audio +❍ /dplay: Play song via deezer +❍ /splay: Play song via jio saavn + + * 🟣Playback🟣 * + +❍ /player: Open Settings menu of player +❍ /skip: Skips the current track +❍ /pause: Pause track +❍ /resume: Resumes the paused track +❍ /end: Stops media playback +❍ /current: Shows the current Playing track +❍ /playlist: Shows playlist +Player cmd and all other cmds except /play, /current and /playlist are only for admins of the group. + +* 🔔Channel Music Play🔔 * + +❍ /cplay [song name] - play song you requested +❍ /cdplay [song name] - play song you requested via deezer +❍ /csplay [song name] - play song you requested via jio saavn +❍ /cplaylist - Show now playing list +❍ /cccurrent - Show now playing +❍ /cplayer - open music player settings panel +❍ /cpause - pause song play +❍ /cresume - resume song play +❍ /cskip - play next song +❍ /cend - stop music play +❍ /userbotjoinchannel - invite assistant to your chat +channel is also can be used instead of c ( /cplay = /channelplay ) + +* If you donlt like to play in linked group * +1) Get your channel ID. +2) Create a group with tittle: Channel Music: your_channel_id +3) Add bot as Channel admin with full perms +4) Add @TheAmandabot to the channel as an admin. +5) Simply send commands in your group. + +* ⚫️More tools⚫️ * + +• /admincache: Updates admin info of your group. Try if bot isn't recognize admin +• /userbotjoin: Invite @SLBotsOfficialHelp Userbot to your chat + +* Assisten * + +@SLBotsOfficialHelp + +* 🤖Bot🤖 * + +@TheAmandabot +""" +__mod_name__ = "ᴠᴄ-ᴍᴜꜱɪᴄ🎧" diff --git a/Amanda/modules/videoplay.py b/Amanda/modules/videoplay.py new file mode 100644 index 0000000..c3a4eee --- /dev/null +++ b/Amanda/modules/videoplay.py @@ -0,0 +1,28 @@ +__help__ = """ +@TheAmandabot + ⚊❮❮❮❮ All Command List ❯❯❯❯⚊ + +✮ /vplay (reply to video or yt/live url) - to stream video +✮ /vstop - stop the video streaming +✮ /song (song name) - download song from YT +✮ /vsong (video name) - download video from YT +✮ /lyric (song name) - lyric scrapper +✮ /vjoin - invite assistant join to your group +✮ /vleave - order assistant leave from your group + +⚊❮❮❮❮ CHANNEL VIDEO PLAY ❯❯❯❯⚊ +✮ /cvplay (reply to video or yt/live url) - to stream video +✮ /cvstop - stop the video streaming + +⚊❮❮❮❮ EXTRA CMD ❯❯❯❯⚊ +✮ /tts (reply to text) - text to speech +✮ /alive - check bot alive status +✮ /sysinfo - check bot system information + +⚊❮❮❮❮ SUDO ONLY ❯❯❯❯⚊ +✮ /rmd - remove all downloaded files +✮ /rmw - remove all downloaded raw files +✮ /leaveall - order assistant leave from all group + +""" +__mod_name__ = "ᴠ-ᴘʟᴀʏ📽️" diff --git a/Amanda/modules/wallpaper.py b/Amanda/modules/wallpaper.py index 7397084..2ad17c4 100644 --- a/Amanda/modules/wallpaper.py +++ b/Amanda/modules/wallpaper.py @@ -7,6 +7,7 @@ from Amanda import SUPPORT_CHAT, WALL_API, dispatcher from Amanda.modules.disable import DisableAbleCommandHandler +# Wallpapers module by @TheRealPhoenix using wall.alphacoders.com @run_async diff --git a/Amanda/modules/warns.py b/Amanda/modules/warns.py index f834cdc..22e6703 100644 --- a/Amanda/modules/warns.py +++ b/Amanda/modules/warns.py @@ -498,20 +498,22 @@ def __chat_settings__(chat_id, user_id): __help__ = """ - ✪ /warns <userhandle>*:* get a user's number, and reason, of warns. - ✪ /warnlist*:* list of all current warning filters + @TheAmandabot + /warns <userhandle>*:* get a user's number, and reason, of warns. + ❍ /warnlist*:* list of all current warning filters *Admins only:* - ✪ /warn <userhandle>*:* warn a user. After 3 warns, the user will be banned from the group. Can also be used as a reply. - ✪ /resetwarn <userhandle>*:* reset the warns for a user. Can also be used as a reply. - ✪ /addwarn <keyword> <reply message>*:* set a warning filter on a certain keyword. If you want your keyword to \ + ❍ /warn <userhandle>*:* warn a user. After 3 warns, the user will be banned from the group. Can also be used as a reply. + ❍ /resetwarn <userhandle>*:* reset the warns for a user. Can also be used as a reply. + ❍ /addwarn <keyword> <reply message>*:* set a warning filter on a certain keyword. If you want your keyword to \ be a sentence, encompass it with quotes, as such: `/addwarn "very angry" This is an angry user`. - ✪ /nowarn <keyword>*:* stop a warning filter - ✪ /warnlimit <num>*:* set the warning limit - ✪ /strongwarn <on/yes/off/no>*:* If set to on, exceeding the warn limit will result in a ban. Else, will just punch. + ❍ /nowarn <keyword>*:* stop a warning filter + ❍ /warnlimit <num>*:* set the warning limit + ❍ /strongwarn <on/yes/off/no>*:* If set to on, exceeding the warn limit will result in a ban. Else, will just punch. + """ -__mod_name__ = "Warnings" +__mod_name__ = "ᴡᴀʀɴɪɴɢ⚠️" WARN_HANDLER = CommandHandler("warn", warn_user, filters=Filters.group) RESET_WARN_HANDLER = CommandHandler( diff --git a/Amanda/modules/welcome.py b/Amanda/modules/welcome.py index 94a686f..ad3e644 100644 --- a/Amanda/modules/welcome.py +++ b/Amanda/modules/welcome.py @@ -35,10 +35,7 @@ dispatcher, sw, ) -from Amanda.modules.helper_funcs.chat_status import ( - is_user_ban_protected, - user_admin, -) +from Amanda.modules.helper_funcs.chat_status import is_user_ban_protected, user_admin from Amanda.modules.helper_funcs.misc import build_keyboard, revert_buttons from Amanda.modules.helper_funcs.msg_types import get_welcome_type from Amanda.modules.helper_funcs.string_handling import ( @@ -1064,22 +1061,24 @@ def __chat_settings__(chat_id, user_id): __help__ = """ *Admins only:* - ✪ /welcome <on/off>*:* enable/disable welcome messages. - ✪ /welcome*:* shows current welcome settings. - ✪ /welcome noformat*:* shows current welcome settings, without the formatting - useful to recycle your welcome messages! - ✪ /goodbye*:* same usage and args as `/welcome`. - ✪ /setwelcome <sometext>*:* set a custom welcome message. If used replying to media, uses that media. - ✪ /setgoodbye <sometext>*:* set a custom goodbye message. If used replying to media, uses that media. - ✪ /resetwelcome*:* reset to the default welcome message. - ✪ /resetgoodbye*:* reset to the default goodbye message. - ✪ /cleanwelcome <on/off>*:* On new member, try to delete the previous welcome message to avoid spamming the chat. - ✪ /welcomemutehelp*:* gives information about welcome mutes. - ✪ /cleanservice <on/off*:* deletes telegrams welcome/left service messages. + ❍ /welcome <on/off>*:* enable/disable welcome messages. + ❍ /welcome*:* shows current welcome settings. + ❍ /welcome noformat*:* shows current welcome settings, without the formatting - useful to recycle your welcome messages! + ❍ /goodbye*:* same usage and args as `/welcome`. + ❍ /setwelcome <sometext>*:* set a custom welcome message. If used replying to media, uses that media. + ❍ /setgoodbye <sometext>*:* set a custom goodbye message. If used replying to media, uses that media. + ❍ /resetwelcome*:* reset to the default welcome message. + ❍ /resetgoodbye*:* reset to the default goodbye message. + ❍ /cleanwelcome <on/off>*:* On new member, try to delete the previous welcome message to avoid spamming the chat. + ❍ /welcomemutehelp*:* gives information about welcome mutes. + ❍ /cleanservice <on/off*:* deletes telegrams welcome/left service messages. *Example:* user joined chat, user left chat. *Welcome markdown:* - ✪ `/welcomehelp`*:* view more formatting information for custom welcome/goodbye messages. + ❍ `/welcomehelp`*:* view more formatting information for custom welcome/goodbye messages. + + """ NEW_MEM_HANDLER = MessageHandler(Filters.status_update.new_chat_members, new_member) @@ -1114,7 +1113,7 @@ def __chat_settings__(chat_id, user_id): dispatcher.add_handler(BUTTON_VERIFY_HANDLER) dispatcher.add_handler(WELCOME_MUTE_HELP) -__mod_name__ = "Greetings" +__mod_name__ = "ᴡᴇʟᴄᴏᴍᴇ🤗" __command_list__ = [] __handlers__ = [ NEW_MEM_HANDLER, diff --git a/Amanda/modules/whatanime.py b/Amanda/modules/whatanime.py index b55e58d..fb010d5 100644 --- a/Amanda/modules/whatanime.py +++ b/Amanda/modules/whatanime.py @@ -156,7 +156,7 @@ async def progress_callback(current, total, reply): ) else: download_speed = "0 B" - text = f"""Downloading...Wait... + text = f"""Downloading... <code>{return_progress_string(current, total)}</code> <b>Total Size:</b> {format_bytes(total)} diff --git a/Amanda/modules/yt.py b/Amanda/modules/yt.py index 9bf435c..eb9e5f4 100644 --- a/Amanda/modules/yt.py +++ b/Amanda/modules/yt.py @@ -1,5 +1,6 @@ __help__ = """ - - /yt <text>: perform a youtube search - - /ytaudio <link> or /ytvideo <link>: Downlods a video or audio from a youtube video to the bots local server +@TheAmandabot + ❍ /video <text>: perform a youtube search + ❍ /song <link> or /video <link>: Downlods a video or audio from a youtube video to the bots local server """ -__mod_name__ = "Youtube" +__mod_name__ = "ʏᴏᴜᴛᴜʙᴇ📺" diff --git a/Amanda/modules/ytdl.py b/Amanda/modules/ytdl.py index b244ff6..03d1e93 100644 --- a/Amanda/modules/ytdl.py +++ b/Amanda/modules/ytdl.py @@ -17,9 +17,9 @@ from Amanda.events import register as saitama -@saitama(pattern="^/yt(audio|video) (.*)") +@saitama(pattern="^/yts(udio|ideo) (.*)") async def download_video(v_url): - """For .ytdl command, download media from YouTube and many other sites.""" + """ For .ytdl command, download media from YouTube and many other sites. """ url = v_url.pattern_match.group(2) type = v_url.pattern_match.group(1).lower() lmao = await v_url.reply("`Preparing to download...`") @@ -63,7 +63,7 @@ async def download_video(v_url): song = False video = True try: - await lmao.edit("`Fetching data, please wait..`") + await lmao.edit("Fetching data, please wait..") with YoutubeDL(opts) as ytdl: ytdl_data = ytdl.extract_info(url) except DownloadError as DE: diff --git a/Amanda/modules/zipper.py b/Amanda/modules/zipper.py index 1db33f4..36730e3 100644 --- a/Amanda/modules/zipper.py +++ b/Amanda/modules/zipper.py @@ -43,7 +43,7 @@ async def _(event): if event.is_group: if not (await is_register_admin(event.input_chat, event.message.sender_id)): await event.reply( - "Hi.. You are not admin.. You can't use this command.. But you can use in my pm" + "Hai.. You are not admin.. You can't use this command.. But you can use in my pm" ) return @@ -210,8 +210,10 @@ def get_lst_of_files(input_directory, output_lst): __help__ = """ - ✪ /zip: reply to a telegram file to compress it in .zip format - ✪ /unzip: reply to a telegram file to decompress it from the .zip format + @TheAmandabot +❍ /zip: reply to a telegram file to compress it in .zip format +❍ /unzip: reply to a telegram file to decompress it from the .zip format + """ -__mod_name__ = "Zipper" +__mod_name__ = "ᴢɪᴘᴘᴇʀ🗂️" diff --git a/Amanda/modules/zombie.py b/Amanda/modules/zombie.py index 81fd0cd..8ae7ed9 100644 --- a/Amanda/modules/zombie.py +++ b/Amanda/modules/zombie.py @@ -49,7 +49,7 @@ async def is_administrator(user_id: int, message): @telethn.on(events.NewMessage(pattern=f"^[!/]zombies ?(.*)")) async def zombies(event): - """For .zombies command, list all the zombies in a chat.""" + """ For .zombies command, list all the zombies in a chat. """ con = event.pattern_match.group(1).lower() del_u = 0 diff --git a/Amanda/pyrogramee/errors.py b/Amanda/pyrogramee/errors.py new file mode 100644 index 0000000..89d349b --- /dev/null +++ b/Amanda/pyrogramee/errors.py @@ -0,0 +1,48 @@ +#© TEAM GROUP MENTER +import sys +import traceback +from functools import wraps +from Amanda import pbot + +def split_limits(text): + if len(text) < 2048: + return [text] + + lines = text.splitlines(True) + small_msg = '' + result = [] + for line in lines: + if len(small_msg) + len(line) < 2048: + small_msg += line + else: + result.append(small_msg) + small_msg = line + else: + result.append(small_msg) + + return result + +def capture_err(func): + @wraps(func) + async def capture(client, message, *args, **kwargs): + try: + return await func(client, message, *args, **kwargs) + except Exception as err: + exc_type, exc_obj, exc_tb = sys.exc_info() + errors = traceback.format_exception( + etype=exc_type, value=exc_obj, tb=exc_tb, + ) + error_feedback = split_limits( + '**ERROR** | `{}` | `{}`\n\n```{}```\n\n```{}```\n'.format( + 0 if not message.from_user else message.from_user.id, + 0 if not message.chat else message.chat.id, + message.text or message.caption, + ''.join(errors), + ), + ) + for x in error_feedback: + await pbot.send_message( + x + ) + raise err + return capture diff --git a/Amanda/pyrogramee/fetch.py b/Amanda/pyrogramee/fetch.py new file mode 100644 index 0000000..4785779 --- /dev/null +++ b/Amanda/pyrogramee/fetch.py @@ -0,0 +1,11 @@ +import aiohttp + + +async def fetch(url): + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + try: + data = await resp.json() + except Exception: + data = await resp.text() + return data diff --git a/Amanda/pyrogramee/json_prettify.py b/Amanda/pyrogramee/json_prettify.py new file mode 100644 index 0000000..c25a0c2 --- /dev/null +++ b/Amanda/pyrogramee/json_prettify.py @@ -0,0 +1,22 @@ +async def json_object_prettify(objecc): + dicc = objecc.__dict__ + output = "" + for key, value in dicc.items(): + if key == "pinned_message" or key == "photo" \ + or key == "_" or key == "_client": + continue + output += f"**{key}:** `{value}`\n" + return output + + +async def json_prettify(data): + output = "" + try: + for key, value in data.items(): + output += f"**{key}:** `{value}`\n" + except Exception: + for datas in data: + for key, value in datas.items(): + output += f"**{key}:** `{value}`\n" + output += "------------------------\n" + return output diff --git a/Amanda/resources/1001fonts-eraser-eula.txt b/Amanda/resources/1001fonts-eraser-eula.txt new file mode 100644 index 0000000..3253c1a --- /dev/null +++ b/Amanda/resources/1001fonts-eraser-eula.txt @@ -0,0 +1,37 @@ +1001Fonts Free For Commercial Use License (FFC) + +Preamble +In this license, 'Eraser' refers to the given .zip file, which may contain one or numerous fonts. These fonts can be of any type (.ttf, .otf, ...) and together they form a 'font family' or in short a 'typeface'. + +1. Copyright +Eraser is the intellectual property of its respective author, provided it is original, and is protected by copyright laws in many parts of the world. + +2. Usage +Eraser may be downloaded and used free of charge for both personal and commercial use, as long as the usage is not racist or illegal. Personal use refers to all usage that does not generate financial income in a business manner, for instance: + + - personal scrapbooking for yourself + - recreational websites and blogs for friends and family + - prints such as flyers, posters, t-shirts for churches, charities, and non-profit organizations + +Commercial use refers to usage in a business environment, including: + + - business cards, logos, advertising, websites, mobile apps for companies + - t-shirts, books, apparel that will be sold for money + - flyers, posters for events that charge admission + - freelance graphic design work + - anything that will generate direct or indirect income + +3. Modification +Eraser may not be modified, altered, adapted or built upon without written permission by its respective author. This pertains all files within the downloadable font zip-file. + +4. Conversion +Eraser may be converted to other formats such as WOFF, SVG or EOT webfonts, as long as the font is not modified in any other way, such as changing names or altering individual glyphs. + +5. Distribution +While Eraser may freely be copied and passed along to other individuals for private use as its original downloadable zip-file, it may not be sold or published without written permission by its respective author. + +6. Embedding +Eraser may be embedded into an application such as a web- or mobile app, independant of the number of the application users, as long as the application does not distribute Eraser, such as offering it as a download. + +7. Disclaimer +Eraser is offered 'as is' without any warranty. 1001fonts.com and the respective author of Eraser shall not be liable for any damage derived from using this typeface. By using Eraser you agree to the terms of this license. \ No newline at end of file diff --git a/Amanda/resources/77653060.png b/Amanda/resources/77653060.png new file mode 100644 index 0000000..2aaa17a Binary files /dev/null and b/Amanda/resources/77653060.png differ diff --git a/Amanda/resources/Chopsic.otf b/Amanda/resources/Chopsic.otf new file mode 100644 index 0000000..dec2e88 Binary files /dev/null and b/Amanda/resources/Chopsic.otf differ diff --git a/Amanda/resources/EraserDust.ttf b/Amanda/resources/EraserDust.ttf new file mode 100644 index 0000000..f3ddfdd Binary files /dev/null and b/Amanda/resources/EraserDust.ttf differ diff --git a/Amanda/resources/EraserRegular.ttf b/Amanda/resources/EraserRegular.ttf new file mode 100644 index 0000000..ec9f3db Binary files /dev/null and b/Amanda/resources/EraserRegular.ttf differ diff --git a/Amanda/resources/Maghrib.ttf b/Amanda/resources/Maghrib.ttf new file mode 100644 index 0000000..55d5294 Binary files /dev/null and b/Amanda/resources/Maghrib.ttf differ diff --git a/Amanda/resources/annie-spratt-v6asLq_dYzw-unsplash.jpg b/Amanda/resources/annie-spratt-v6asLq_dYzw-unsplash.jpg new file mode 100644 index 0000000..21d6b3d Binary files /dev/null and b/Amanda/resources/annie-spratt-v6asLq_dYzw-unsplash.jpg differ diff --git a/Amanda/resources/clarisse-meyer-y54gnzC86lw-unsplash.jpg b/Amanda/resources/clarisse-meyer-y54gnzC86lw-unsplash.jpg new file mode 100644 index 0000000..f1c0b63 Binary files /dev/null and b/Amanda/resources/clarisse-meyer-y54gnzC86lw-unsplash.jpg differ diff --git a/Amanda/resources/download (1).jfif b/Amanda/resources/download (1).jfif new file mode 100644 index 0000000..f3c3a8f Binary files /dev/null and b/Amanda/resources/download (1).jfif differ diff --git a/Amanda/resources/download (1).png b/Amanda/resources/download (1).png new file mode 100644 index 0000000..8c3ef34 Binary files /dev/null and b/Amanda/resources/download (1).png differ diff --git a/Amanda/resources/download (2).jfif b/Amanda/resources/download (2).jfif new file mode 100644 index 0000000..ec93531 Binary files /dev/null and b/Amanda/resources/download (2).jfif differ diff --git a/Amanda/resources/download (2).png b/Amanda/resources/download (2).png new file mode 100644 index 0000000..56f9d80 Binary files /dev/null and b/Amanda/resources/download (2).png differ diff --git a/Amanda/resources/download (3).jfif b/Amanda/resources/download (3).jfif new file mode 100644 index 0000000..7c43bf2 Binary files /dev/null and b/Amanda/resources/download (3).jfif differ diff --git a/Amanda/resources/download (4).jfif b/Amanda/resources/download (4).jfif new file mode 100644 index 0000000..9ebc763 Binary files /dev/null and b/Amanda/resources/download (4).jfif differ diff --git a/Amanda/resources/download (5).jfif b/Amanda/resources/download (5).jfif new file mode 100644 index 0000000..ce1dc16 Binary files /dev/null and b/Amanda/resources/download (5).jfif differ diff --git a/Amanda/resources/download (6).jfif b/Amanda/resources/download (6).jfif new file mode 100644 index 0000000..415c96f Binary files /dev/null and b/Amanda/resources/download (6).jfif differ diff --git a/Amanda/resources/download (7).jfif b/Amanda/resources/download (7).jfif new file mode 100644 index 0000000..54e0ad0 Binary files /dev/null and b/Amanda/resources/download (7).jfif differ diff --git a/Amanda/resources/download.jfif b/Amanda/resources/download.jfif new file mode 100644 index 0000000..94f4b24 Binary files /dev/null and b/Amanda/resources/download.jfif differ diff --git a/Amanda/resources/download.png b/Amanda/resources/download.png new file mode 100644 index 0000000..81c421c Binary files /dev/null and b/Amanda/resources/download.png differ diff --git a/Amanda/resources/font.otf b/Amanda/resources/font.otf new file mode 100644 index 0000000..167fb8f Binary files /dev/null and b/Amanda/resources/font.otf differ diff --git a/Amanda/resources/frame-harirak-qrRcfBbKKrc-unsplash.jpg b/Amanda/resources/frame-harirak-qrRcfBbKKrc-unsplash.jpg new file mode 100644 index 0000000..c08d7db Binary files /dev/null and b/Amanda/resources/frame-harirak-qrRcfBbKKrc-unsplash.jpg differ diff --git a/Amanda/resources/hero.png b/Amanda/resources/hero.png new file mode 100644 index 0000000..a90a75f Binary files /dev/null and b/Amanda/resources/hero.png differ diff --git a/Amanda/resources/images (1).jfif b/Amanda/resources/images (1).jfif new file mode 100644 index 0000000..e63b072 Binary files /dev/null and b/Amanda/resources/images (1).jfif differ diff --git a/Amanda/resources/images (2).jfif b/Amanda/resources/images (2).jfif new file mode 100644 index 0000000..388d11e Binary files /dev/null and b/Amanda/resources/images (2).jfif differ diff --git a/Amanda/resources/images (3).jfif b/Amanda/resources/images (3).jfif new file mode 100644 index 0000000..1a147ea Binary files /dev/null and b/Amanda/resources/images (3).jfif differ diff --git a/Amanda/resources/images (4).jfif b/Amanda/resources/images (4).jfif new file mode 100644 index 0000000..5487108 Binary files /dev/null and b/Amanda/resources/images (4).jfif differ diff --git a/Amanda/resources/images (5).jfif b/Amanda/resources/images (5).jfif new file mode 100644 index 0000000..8d249dd Binary files /dev/null and b/Amanda/resources/images (5).jfif differ diff --git a/Amanda/resources/images (6).jfif b/Amanda/resources/images (6).jfif new file mode 100644 index 0000000..6f0644a Binary files /dev/null and b/Amanda/resources/images (6).jfif differ diff --git a/Amanda/resources/images (7).jfif b/Amanda/resources/images (7).jfif new file mode 100644 index 0000000..dbc8d86 Binary files /dev/null and b/Amanda/resources/images (7).jfif differ diff --git a/Amanda/resources/images (8).jfif b/Amanda/resources/images (8).jfif new file mode 100644 index 0000000..f2c16a5 Binary files /dev/null and b/Amanda/resources/images (8).jfif differ diff --git a/Amanda/resources/images (9).jfif b/Amanda/resources/images (9).jfif new file mode 100644 index 0000000..6aa943c Binary files /dev/null and b/Amanda/resources/images (9).jfif differ diff --git a/Amanda/resources/images.jfif b/Amanda/resources/images.jfif new file mode 100644 index 0000000..6eb10b4 Binary files /dev/null and b/Amanda/resources/images.jfif differ diff --git a/Amanda/resources/images.jpeg b/Amanda/resources/images.jpeg new file mode 100644 index 0000000..4134aba Binary files /dev/null and b/Amanda/resources/images.jpeg differ diff --git a/Amanda/resources/images.png b/Amanda/resources/images.png new file mode 100644 index 0000000..e5f4cf1 Binary files /dev/null and b/Amanda/resources/images.png differ diff --git a/Amanda/resources/marek-piwnicki-KMvdCkmvN38-unsplash.jpg b/Amanda/resources/marek-piwnicki-KMvdCkmvN38-unsplash.jpg new file mode 100644 index 0000000..f21b2fd Binary files /dev/null and b/Amanda/resources/marek-piwnicki-KMvdCkmvN38-unsplash.jpg differ diff --git a/Amanda/resources/martin-lostak-OZVfdsT0J2g-unsplash.jpg b/Amanda/resources/martin-lostak-OZVfdsT0J2g-unsplash.jpg new file mode 100644 index 0000000..2f2b80a Binary files /dev/null and b/Amanda/resources/martin-lostak-OZVfdsT0J2g-unsplash.jpg differ diff --git a/Amanda/resources/oxxaca-J84qvBcQ_ys-unsplash.jpg b/Amanda/resources/oxxaca-J84qvBcQ_ys-unsplash.jpg new file mode 100644 index 0000000..759c0d8 Binary files /dev/null and b/Amanda/resources/oxxaca-J84qvBcQ_ys-unsplash.jpg differ diff --git a/Amanda/resources/pandabg.png b/Amanda/resources/pandabg.png new file mode 100644 index 0000000..f20afd7 Binary files /dev/null and b/Amanda/resources/pandabg.png differ diff --git a/Amanda/resources/photo_2021-08-21_23-14-49.png b/Amanda/resources/photo_2021-08-21_23-14-49.png new file mode 100644 index 0000000..71f7fed Binary files /dev/null and b/Amanda/resources/photo_2021-08-21_23-14-49.png differ diff --git a/Amanda/resources/photo_2021-09-14_17-36-29.jpg b/Amanda/resources/photo_2021-09-14_17-36-29.jpg new file mode 100644 index 0000000..7c02b36 Binary files /dev/null and b/Amanda/resources/photo_2021-09-14_17-36-29.jpg differ diff --git a/Amanda/resources/soheb-zaidi-yuv1FPyZdR0-unsplash.jpg b/Amanda/resources/soheb-zaidi-yuv1FPyZdR0-unsplash.jpg new file mode 100644 index 0000000..f184777 Binary files /dev/null and b/Amanda/resources/soheb-zaidi-yuv1FPyZdR0-unsplash.jpg differ diff --git a/Amanda/sample_config.py b/Amanda/sample_config.py new file mode 100644 index 0000000..9328f5c --- /dev/null +++ b/Amanda/sample_config.py @@ -0,0 +1,76 @@ +import json +import os + + +def get_user_list(config, key): + with open("{}/Amanda/{}".format(os.getcwd(), config), "r") as json_file: + return json.load(json_file)[key] + + +# Create a new config.py or rename this to config.py file in same dir and import, then extend this class. +class Config(object): + LOGGER = True + # REQUIRED + # Login to https://my.telegram.org and fill in these slots with the details given by it + + API_ID = 123456 # integer value, dont use "" + API_HASH = "API_HASH" + TOKEN = "BOT_TOKEN" # This var used to be API_KEY but it is now TOKEN, adjust accordingly. + OWNER_ID = 123456 # If you dont know, run the bot and do /id in your private chat with it, also an integer + OWNER_USERNAME = "ImTharuk" + SUPPORT_CHAT = "trtechguide" # Your own group for support, do not add the @ + JOIN_LOGGER = ( + -1001333667683 + ) # Prints any new group the bot is added to, prints just the name and ID. + EVENT_LOGS = ( + -1001598073501 + ) # Prints information like gbans, sudo promotes, AI enabled disable states that may help in debugging and shit + + # RECOMMENDED + SQLALCHEMY_DATABASE_URI = "something://somewhat:user@hosturl:port/databasename" # needed for any database modules + REDIS_URI = " " + LOAD = [] + NO_LOAD = ["rss", "cleaner", "connection", "math"] + WEBHOOK = False + INFOPIC = True + URL = None + SPAMWATCH_API = "" # go to support.spamwat.ch to get key + SPAMWATCH_SUPPORT_CHAT = "@SpamWatchSupport" + + # OPTIONAL + ##List of id's - (not usernames) for users which have sudo access to the bot. + DRAGONS = get_user_list("elevated_users.json", "sudos") + ##List of id's - (not usernames) for developers who will have the same perms as the owner + DEV_USERS = get_user_list("elevated_users.json", "devs") + ##List of id's (not usernames) for users which are allowed to gban, but can also be banned. + DEMONS = get_user_list("elevated_users.json", "supports") + # List of id's (not usernames) for users which WONT be banned/kicked by the bot. + TIGERS = get_user_list("elevated_users.json", "tigers") + WOLVES = get_user_list("elevated_users.json", "whitelists") + DONATION_LINK = None # EG, paypal + CERT_PATH = None + PORT = 5000 + DEL_CMDS = True # Delete commands that users dont have access to, like delete /ban if a non admin uses it. + STRICT_GBAN = True + WORKERS = ( + 8 # Number of subthreads to use. Set as number of threads your processor uses + ) + BAN_STICKER = "" # banhammer marie sticker id, the bot will send this sticker before banning or kicking a user in chat. + ALLOW_EXCL = True # Allow ! commands as well as / (Leave this to true so that blacklist can work) + CASH_API_KEY = ( + "awoo" # Get your API key from https://www.alphavantage.co/support/#api-key + ) + TIME_API_KEY = "awoo" # Get your API key from https://timezonedb.com/api + WALL_API = ( + "awoo" # For wallpapers, get one from https://wall.alphacoders.com/api.php + ) + AI_API_KEY = "awoo" # For chatbot, get one from https://coffeehouse.intellivoid.net/dashboard + BL_CHATS = [] # List of groups that you want blacklisted. + SPAMMERS = None + +class Production(Config): + LOGGER = True + + +class Development(Config): + LOGGER = True diff --git a/Amanda/services/pyrogram.py b/Amanda/services/pyrogram.py new file mode 100644 index 0000000..0e72c3e --- /dev/null +++ b/Amanda/services/pyrogram.py @@ -0,0 +1,22 @@ +import logging + +from pyrogram import Client + +# from pyromod import listen +from Amanda.config import get_int_key, get_str_key + +TOKEN = get_str_key("TOKEN", required=True) +API_ID = get_int_key("API_ID", required=True) +API_HASH = get_str_key("API_HASH", required=True) +session_name = TOKEN.split(":")[0] +pbot = Client( + session_name, + api_id=API_ID, + api_hash=API_HASH, + bot_token=TOKEN, +) + +# disable logging for pyrogram [not for ERROR logging] +logging.getLogger("pyrogram").setLevel(level=logging.ERROR) + +pbot.start() diff --git a/Amanda/utils/errors.py b/Amanda/utils/errors.py new file mode 100644 index 0000000..425f104 --- /dev/null +++ b/Amanda/utils/errors.py @@ -0,0 +1,55 @@ +import sys +import traceback +from functools import wraps +from Amanda import pbot as app +from pyrogram.errors.exceptions.forbidden_403 import ChatWriteForbidden + +LOG_GROUP_ID = int(-1001589738293) + +def split_limits(text): + if len(text) < 2048: + return [text] + + lines = text.splitlines(True) + small_msg = '' + result = [] + for line in lines: + if len(small_msg) + len(line) < 2048: + small_msg += line + else: + result.append(small_msg) + small_msg = line + else: + result.append(small_msg) + + return result + + +def capture_err(func): + @wraps(func) + async def capture(client, message, *args, **kwargs): + try: + return await func(client, message, *args, **kwargs) + except ChatWriteForbidden: + await app.leave_chat(message.chat.id) + return + except Exception as err: + exc_type, exc_obj, exc_tb = sys.exc_info() + errors = traceback.format_exception( + etype=exc_type, value=exc_obj, tb=exc_tb, + ) + error_feedback = split_limits( + '**ERROR** | `{}` | `{}`\n\n```{}```\n\n```{}```\n'.format( + 0 if not message.from_user else message.from_user.id, + 0 if not message.chat else message.chat.id, + message.text or message.caption, + ''.join(errors), + ), + ) + for x in error_feedback: + await app.send_message( + LOG_GROUP_ID, + x + ) + raise err + return diff --git a/Amanda/utils/filter_groups.py b/Amanda/utils/filter_groups.py new file mode 100644 index 0000000..e588b95 --- /dev/null +++ b/Amanda/utils/filter_groups.py @@ -0,0 +1,12 @@ +chat_filters_group = 1 +chatbot_group = 2 +karma_positive_group = 3 +karma_negative_group = 4 +regex_group = 5 +welcome_captcha_group = 6 +antiflood_group = 7 +nsfw_detect_group = 8 +blacklist_filters_group = 9 +pipes_group = 10 +taglog_group = 11 +chat_watcher_group = 12 diff --git a/Amanda/utils/formatter.py b/Amanda/utils/formatter.py new file mode 100644 index 0000000..6ee8ce3 --- /dev/null +++ b/Amanda/utils/formatter.py @@ -0,0 +1,32 @@ +def get_readable_time(seconds: int) -> str: + count = 0 + ping_time = "" + time_list = [] + time_suffix_list = ["s", "m", "h", "days"] + while count < 4: + count += 1 + if count < 3: + remainder, result = divmod(seconds, 60) + else: + remainder, result = divmod(seconds, 24) + if seconds == 0 and remainder == 0: + break + time_list.append(int(result)) + seconds = int(remainder) + for i in range(len(time_list)): + time_list[i] = str(time_list[i]) + time_suffix_list[i] + if len(time_list) == 4: + ping_time += time_list.pop() + ", " + time_list.reverse() + ping_time += ":".join(time_list) + return ping_time + + +# Convert seconds to mm:ss +async def convert_seconds_to_minutes(seconds: int): + seconds = int(seconds) + seconds = seconds % (24 * 3600) + seconds %= 3600 + minutes = seconds // 60 + seconds %= 60 + return "%02d:%02d" % (minutes, seconds) diff --git a/Amanda/utils/logger.py b/Amanda/utils/logger.py new file mode 100644 index 0000000..7350000 --- /dev/null +++ b/Amanda/utils/logger.py @@ -0,0 +1,35 @@ +import logging + +from loguru import logger + + +class InterceptHandler(logging.Handler): + LEVELS_MAP = { + logging.CRITICAL: "CRITICAL", + logging.ERROR: "ERROR", + logging.WARNING: "WARNING", + logging.INFO: "INFO", + logging.DEBUG: "DEBUG", + } + + def _get_level(self, record): + return self.LEVELS_MAP.get(record.levelno, record.levelno) + + def emit(self, record): + logger_opt = logger.opt( + depth=6, exception=record.exc_info, ansi=True, lazy=True + ) + logger_opt.log(self._get_level(record), record.getMessage()) + + +logging.basicConfig(handlers=[InterceptHandler()], level=logging.INFO) +log = logging.getLogger(__name__) +logger.add( + "logs/DewmiBot.log", + rotation="1 d", + compression="tar.xz", + backtrace=True, + diagnose=True, + level="INFO", +) +log.info("Enabled logging intro Natsuki.log file.") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 69332a3..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,27 +0,0 @@ -# Contributing - -Contributions are very welcome! Here are some guidelines on how the project is designed. - -### CodeStyle - -- Adhere to PEP8 as much as possible. - -- Line lengths should be under 120 characters, use list comprehensions over map/filter, don't leave trailing whitespace. - -- More complex pieces of code should be commented for future reference. - -### Structure - -There are a few self-imposed rules on the project structure, to keep the project as tidy as possible. -- All modules should go into the `modules/` directory. -- Any database accesses should be done in `modules/sql/` - no instances of SESSION should be imported anywhere else. -- Make sure your database sessions are properly scoped! Always close them properly. -- When creating a new module, there should be as few changes to other files as possible required to incorporate it. -Removing the module file should result in a bot which is still in perfect working condition. -- If a module is dependent on multiple other files, which might not be loaded, then create a list of at module -load time, in `__main__`, by looking at attributes. This is how migration, /help, /stats, /info, and many other things -are based off of. It allows the bot to work fine with the LOAD and NO_LOAD configurations. -- Keep in mind that some things might clash; eg a regex handler could clash with a command handler - in this case, you -should put them in different dispatcher groups. - -Might seem complicated, but it'll make sense when you get into it. Feel free to ask me for a hand/advice (in `@SLBotsOfficial`)! diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 92f5f48..0000000 --- a/Dockerfile +++ /dev/null @@ -1,81 +0,0 @@ -# We're using Debian Slim Buster image -FROM python:3.8.5-slim-buster - -ENV PIP_NO_CACHE_DIR 1 - -RUN sed -i.bak 's/us-west-2\.ec2\.//' /etc/apt/sources.list - -# Installing Required Packages -RUN apt update && apt upgrade -y && \ - apt install --no-install-recommends -y \ - debian-keyring \ - debian-archive-keyring \ - bash \ - bzip2 \ - curl \ - figlet \ - git \ - util-linux \ - libffi-dev \ - libjpeg-dev \ - libjpeg62-turbo-dev \ - libwebp-dev \ - linux-headers-amd64 \ - musl-dev \ - musl \ - neofetch \ - php-pgsql \ - python3-lxml \ - postgresql \ - postgresql-client \ - python3-psycopg2 \ - libpq-dev \ - libcurl4-openssl-dev \ - libxml2-dev \ - libxslt1-dev \ - python3-pip \ - python3-requests \ - python3-sqlalchemy \ - python3-tz \ - python3-aiohttp \ - openssl \ - pv \ - jq \ - wget \ - python3 \ - python3-dev \ - libreadline-dev \ - libyaml-dev \ - gcc \ - sqlite3 \ - libsqlite3-dev \ - sudo \ - zlib1g \ - ffmpeg \ - libssl-dev \ - libgconf-2-4 \ - libxi6 \ - xvfb \ - unzip \ - libopus0 \ - libopus-dev \ - && rm -rf /var/lib/apt/lists /var/cache/apt/archives /tmp - -# Pypi package Repo upgrade -RUN pip3 install --upgrade pip setuptools - -# Copy Python Requirements to /root/Amanda -RUN git clone -b shiken https://github.com/TR-TECH-GUIDE/Amanda /root/Amanda -WORKDIR /root/Amanda - -#Copy config file to /root/Amanda/Amanda -COPY ./Amanda/sample_config.py ./Amanda/config.py* /root/Amanda/Amanda/ - -ENV PATH="/home/bot/bin:$PATH" - -# Install requirements -RUN pip3 install -U -r requirements.txt - -# Starting Worker -CMD ["python3","-m","Amanda"] - diff --git a/dank.session-journal b/Downloads/amandabot similarity index 100% rename from dank.session-journal rename to Downloads/amandabot diff --git a/Git_Pull.bat b/Git_Pull.bat deleted file mode 100644 index 15bc951..0000000 --- a/Git_Pull.bat +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -TITLE Github Quick-Pull - -:: Print the branch cause ..oooooo fancy! -echo Pulling from branch: -git branch -echo. -git pull diff --git a/Git_Push.bat b/Git_Push.bat deleted file mode 100644 index efd5d53..0000000 --- a/Git_Push.bat +++ /dev/null @@ -1,19 +0,0 @@ -@echo off -TITLE Github Quick-pushing - -:: Print the branch cause people like me push to wrong branches and cry about it later. -echo Pushing to branch: -git branch -echo. -:: Take input for comment and thats about it -set /p commit_title="Enter Commit title (pushes with you as author): " - -:: If you are reading comments to understand this part then you can go back stab yourself. -echo. -git add * -git commit -m "%commit_title%" -git pull -git push - - -:: Hail Hydra \ No newline at end of file diff --git a/LICENSE b/LICENSE index 9d74247..e62ec04 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - GNU GENERAL PUBLIC LICENSE +GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> @@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - {project} Copyright (C) {year} {fullname} + <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. @@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -<https://www.gnu.org/philosophy/why-not-lgpl.html>. +<https://www.gnu.org/licenses/why-not-lgpl.html>. diff --git a/README.md b/README.md index 84a8507..9d3ed6f 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,35 @@ -# Amanda Based on Python Telegram Bot ![GitHub repo size](https://img.shields.io/github/repo-size/TeamAmanda/Amanda?label=Repo%20Size) -<p align="middle"> - <img src="https://telegra.ph/file/8c1e8121a6591590f77df.jpg" width='600"'> -</p> - -## Deploy - -#### Easy Method -[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/TeamAmanda/Amanda.git) - -#### Hard Way -```sh -# Install Git First (apt-instll git) -$ git clone https://github.com/TeamAmanda/Amanda -$ cd Amanda -# Install All Requirements -$ pip3 install -r requirements.txt -# Add Your Details to ./Amanda/config.py -# Start Bot -$ python3 -m Amanda -``` - -##### Mandatory Vars -``` -[+] Make Sure You Add All These Mandatory Vars. - [-] APP_ID: You can get this value from my.telegram.org - [-] APP_HASH : You can get this value from my.telegram.org - [-] MONGO_URI : Your Mongo DB DataBase Url. - [-] TOKEN: Get from botfarther - [-] DATABASE_URL: from elephantsql.com - [-] OWNER_ID: Your id -[+] The Amanda won't run without setting the mandatory vars. -``` - -## New version Avaiilable on Telegram as [Amanda](https://t.me/TheAmandabot) -## Amanda is the latest - -All other credits mentioned on top of scripts - -Should any be missing kindly let us know at [Developers](https://t.me/SLBotsOfficial) or simply submit a pull request on the readme. +<h1 align = "center"> ❝A powerful group management bot which can help you to manage your groups effectively as possible❞ </h1> -# Contributors -![GitHub Contributors Image](https://contrib.rocks/image?repo=TeamAmanda/Amanda) +<p align="center"> + <img src="https://telegra.ph/file/f80844f7f1e6bfaf5ebd6.jpg" width='400"'> +</p> -## Special Credits ❤ +<h3>Avaiilable on Telegram as @TheAmandabot</h3> +<p align="center"> <br> + <img src="https://img.shields.io/github/license/TeamAmanda/Amanda?style=for-the-badge&logo=telegram" alt="LICENSE"> + <img src="https://img.shields.io/github/contributors/TeamAmanda/Amanda?style=for-the-badge&logo=telegram" alt="Contributors"> + <img src="https://img.shields.io/github/repo-size/TeamAmanda/Amanda?style=for-the-badge&logo=telegram" alt="Repository Size"> + <img src="https://img.shields.io/badge/python-3.9-green?style=for-the-badge&logo=appveyor" alt="Python Version"> + <br> + <img src="https://img.shields.io/github/issues/TeamAmanda/Amanda?style=for-the-badge&logo=telegram" alt="Issues"> + <img src="https://img.shields.io/github/forks/TeamAmanda/Amanda?style=for-the-badge&logo=telegram" alt="Forks"> + <img src="https://img.shields.io/github/stars/TeamAmanda/Amanda?style=for-the-badge&logo=telegram" alt="Stars"> +</p> + + +## Special Credits 🥰 +- **[TR-TECH-GUIDE](https://github.com/TR-TECH-GUIDE) - Owner | Dev** +- **[Tharuk Renuja](https://github.com/TharukRenuja) - Dev** +- **[hirunaofficial](https://github.com/hirunaofficial)** +- **[Damantha126](https://github.com/Damantha126)** +- **[Sadew451](https://github.com/Sadew451)** +- **[Uvindu-bro](https://github.com/UvinduBro)** +- **[innexia](https://github.com/DarkCybers/innexia/tree/Sammy/innexiaBot)** +- **[Group Menter](https://github.com/TeamGroupMenter/GroupMenter)** +- **[boltbackerbot](https://t.me/boltbacker)** +- **[Single Developers](https://t.me/SingleDevelopers)** +- **[𝙨𝙯 𝙩𝙚𝙖𝙢 𝙗𝙤𝙩𝙨](https://t.me/szteambots)** - **[Inuka Asith](https://github.com/inukaasith)** - **[Prabasha](https://github.com/prabhasha-p/)** - **[ImJanindu](https://github.com/imjanindu)** @@ -54,15 +40,38 @@ Should any be missing kindly let us know at [Developers](https://t.me/SLBotsOffi - **[thehamkercat](https://github.com/thehamkercat/)** - **[TroJanzHEX](https://github.com/TroJanzHEX/)** - **[TeamDaisyX](https://github.com/teamdaisyx)** -- **[Damantha Jasinghe](https://github.com/Damantha126)** -- **[Sadew Jsk](https://Github.com/sadew451)** - **[Fayas Noushad](https://github.com/FayasNoushad)** - **[Nuhman Pk](https://github.com/bughunter0)** - **[Abir Hasan](https://github.com/AbirHasan2005)** - **[Shrimadhav](https://github.com/SpEcHiDe)** - -## Devs +## 👨‍💻 Self-hosting (For Devs) +``` +# Install Git First (apt-instll git) +$ git clone https://github.com/TeamAmanda/Amanda +$ cd Amanda +# Upgrade sources +$ bash deploy.sh +# Install All Requirements +$ pip3 install -r requirements.txt +# Start Bot +$ python3 -m Amanda +``` + + # Contributors +![GitHub Contributors Image](https://contrib.rocks/image?repo=TeamAmanda/Amanda) + + ## Deploy Guide + +### 💡 Warning: Use your bot for your personal use only -- **[TR-TECH-GUIDE](https://github.com/TR-TECH-GUIDE) - Owner | Dev** -- **[Tharuk Renuja](https://github.com/TharukRenuja) - Dev** + [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/TeamAmanda/Amanda/tree/v3.git) + +## Enjoy Your own Bot! + +![📣Channel](https://img.shields.io/badge/dynamic/json?color=blue&label=szteam%20@SLBotsOfficial&query=subscribers&url=https%3A%2F%2Fonline-users-api.up.railway.app%2Fcheck%3Fchat%3Dszteambots&logo=telegram) +![👨‍👦‍👦Support](https://img.shields.io/badge/dynamic/json?color=blue&label=support%20@trtechguide&query=members&url=https%3A%2F%2Fonline-users-api.up.railway.app%2Fcheck%3Fchat%3Dslbotzone&logo=telegram) + +### ⚙️ Bot hoster [Beast Huntered [🇸 🇱 🇧 🇴 🇹 🇸 ™]](https://t.me/SLBotsOfficial) + + diff --git a/SetupVirtualEnv_run_once.bat b/SetupVirtualEnv_run_once.bat deleted file mode 100644 index a6a7416..0000000 --- a/SetupVirtualEnv_run_once.bat +++ /dev/null @@ -1,6 +0,0 @@ -TITLE Setting up virtual env -:: Running it once is fine, this just sets up virtual env >> install all modules there -py -m venv env && env\scripts\activate.bat && pip install -r requirements.txt - -:: Note to rerun the requirements.txt in case you ever add a mdoule. -:: Running this multiple time will not make a mess of your setup, dont worry about that bit. diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 3397c9a..0000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-architect \ No newline at end of file diff --git a/app.json b/app.json index 32f7832..5e597b3 100644 --- a/app.json +++ b/app.json @@ -1,15 +1,13 @@ { - "name": "Amanda", - "description": "An Anime themed Telegram group management bot.", - "logo": "https://telegra.ph/file/04d73369440abc48ab3ce.png", + "name": "Amanda ✨", + "description": "A powerful group management bot which can help you to manage your groups effectively as possible. Support @SLBotsOfficial", + "logo": "https://telegra.ph/file/f80844f7f1e6bfaf5ebd6.jpg", "keywords": [ - "telegram", - "anime", - "group", - "manager", - "Amanda" + "Telegram", + "Groupmanager", + "TheAmandabot" ], - "repository": "https://github.com/TR-TECH-GUIDE/Amanda", + "repository": "https://github.com/TeamAmanda/Amanda", "addons": [ { "options": { @@ -17,7 +15,7 @@ }, "plan": "heroku-postgresql" } - ], + ], "buildpacks": [ { "url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest" @@ -25,7 +23,7 @@ { "url": "heroku/python" } - ], + ], "env": { "TOKEN": { "description": "Your bot token. Get one from @BotFather duh", @@ -36,6 +34,21 @@ "description": "Get API_ID from my.telegram.org, used for telethon based modules.", "required": true, "value": "" + }, + "BOT_ID": { + "description": "BOT_ID", + "required": true, + "value": "" + }, + "HEROKU_API_KEY": { + "description": "HEROKU_API_KEY", + "required": true, + "value": "" + }, + "HEROKU_APP_NAME": { + "description": "HEROKU_APP_NAME", + "required": true, + "value": "" }, "API_HASH": { "description": "Get API_HASH from my.telegram.org, used for telethon based modules.", @@ -43,9 +56,24 @@ "value": "" }, "SQLALCHEMY_DATABASE_URI": { - "description": "Your postgres sql db, empty this field if you dont have one.", - "required": false, - "value": "sqldbtype://username:pw@hostname:port/db_name" + "description": "Your postgres sql db, get it form https://www.elephantsql.com/", + "required": false, + "value": "Fill this" + }, + "REM_BG_API_KEY": { + "description": "remove.bg api (no need)", + "value": "", + "required": false + }, + "MONGO_DB_URI": { + "description": "Required for database connections", + "value": "", + "required": true + }, + "VIRUS_API_KEY": { + "description": "Get one from https://cloudmersive.com/virus-api ", + "value": "", + "required": false }, "OWNER_ID": { "description": "Your user ID as an integer.", @@ -54,12 +82,12 @@ }, "OWNER_USERNAME": { "description": "Your username without the @", - "value": "TharukRenuja" + "value": "ImTharuk" }, "SUPPORT_CHAT": { - "description": "Your Telegram support group chat username where your users will go and bother you with shit But be like: MyGroupChatUsernameBlah. If this ever points to saitama support than consider you made an enemy.", + "description": "Your Telegram support group chat username where your users will go and bother you with shit But be like: SL Tech Zone. If this ever points to SL Tech Zone support than consider you made an enemy.", "required": true, - "value": "SLBotsOfficial" + "value": "trtechguide" }, "EVENT_LOGS": { "description": "Event logs channel to note down important bot level events, recommend to make this public. ex: '-123456'", @@ -82,11 +110,11 @@ "value": "-xyz" }, "DEV_USERS": { - "description": "ID of users who are Devs of your bot (can use /py etc.). If you are a noob and would come and brother SLBotsOfficial then keep the current ID's here at they are and add yours.", + "description": "ID of users who are Devs of your bot (can use /py etc.). If you are a noob and would come and bother Saitama support then keep the current ID's here at they are and add yours.", "required": false, - "value": "1202064253 996634013" + "value": "1202064253" }, - "sw_api": { + "SPAMWATCH_API": { "description": "Spamwatch API Token, Get one from @SpamWatchBot.", "required": false, "value": "" @@ -98,22 +126,22 @@ "DRAGONS": { "description": "A space separated list of user IDs who you want to assign as sudo users.", "required": false, - "value": "1202064253 996634013" + "value": "1202064253" }, "DEMONS": { "description": "A space separated list of user IDs who you wanna assign as support users(gban perms only).", "required": false, - "value": "1202064253 996634013" + "value": "1202064253" }, "TIGERS": { "description": "A space separated list of user IDs who you wanna assign as tiger users.", "required": false, - "value": "1202064253 996634013" + "value": "1202064253" }, "WOLVES": { "description": "A space separated list of user IDs who you want to assign as whitelisted - can't be banned with your bot.", "required": false, - "value": "1202064253 996634013" + "value": "1202064253" }, "ENV": { "description": "Setting this to ANYTHING will enable environment variables. Leave it as it is", @@ -130,9 +158,9 @@ "value": "" }, "URL": { - "description": "The Heroku App URL", + "description": "The Heroku App URL :- https://<appname>.herokuapp.com/", "required": false, - "value": "https://<appname>.herokuapp.com/" + "value": "" }, "No_LOAD": { "description": "Dont load these modules cause they shit, space separation", @@ -151,7 +179,7 @@ "DONATION_LINK": { "description": "Optional: link where you would like to receive donations. If you are a noob, better leave it linking to paul", "required": false, - "value": "https://trtechguide.wordpress.com" + "value": "https://www.paypal.me/username" }, "DEL_CMDS": { "description": "Set this to True if you want to delete command messages from users who don't have the perms to run that command.", @@ -172,14 +200,5 @@ "required": false, "value": "" } - }, - "keywords": [ - "telegram", - "anime", - "group", - "manager", - "daisy" - ], - "name": "Amanda", - "repository": "https://github.com/TR-TECH-GUIDE/Amanda" + } } diff --git a/bob.jpg b/bob.jpg deleted file mode 100644 index fa52aa5..0000000 Binary files a/bob.jpg and /dev/null differ diff --git a/config_yml.txt b/config_yml.txt deleted file mode 100644 index 12fd5e9..0000000 --- a/config_yml.txt +++ /dev/null @@ -1 +0,0 @@ -test file here delete me diff --git a/exp.sh b/exp.sh deleted file mode 100644 index 79add3d..0000000 --- a/exp.sh +++ /dev/null @@ -1,2 +0,0 @@ -sudo bash -c 'echo "{ \"cgroup-parent\": \"/actions_job\",\"experimental\":true}" > /etc/docker/daemon.json' -sudo systemctl restart docker.service \ No newline at end of file diff --git a/helpers/filters.py b/helpers/filters.py new file mode 100644 index 0000000..14b1bca --- /dev/null +++ b/helpers/filters.py @@ -0,0 +1,12 @@ +from typing import Union, List + +from pyrogram import filters + + + +other_filters = filters.group & ~filters.edited & ~filters.via_bot & ~filters.forwarded +other_filters2 = filters.private & ~filters.edited & ~filters.via_bot & ~filters.forwarded + + +def command(commands: Union[str, List[str]]): + return filters.command(commands) diff --git a/heroku.yml b/heroku.yml index ff8effe..d37a517 100644 --- a/heroku.yml +++ b/heroku.yml @@ -2,4 +2,4 @@ build: docker: worker: Dockerfile run: - worker: python3 -m Amanda + worker: python3 -m Amanda \ No newline at end of file diff --git a/hitler.jpg b/hitler.jpg deleted file mode 100644 index 57a637e..0000000 Binary files a/hitler.jpg and /dev/null differ diff --git a/kim.jpg b/kim.jpg deleted file mode 100644 index 52be25c..0000000 Binary files a/kim.jpg and /dev/null differ diff --git a/requirements.txt b/requirements.txt index c1bbef6..8dca595 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ beautifulsoup4 requests sqlalchemy==1.3.20 python-telegram-bot==12.8 -psycopg2-binary==2.8.6 +psycopg2-binary feedparser pynewtonmath spongemock @@ -33,10 +33,13 @@ pyrogram>=1.0.7 tgCrypto>=1.2.2 fontTools gtts +bs4 +apscheduler +heroku3 +flag telegraph tswift pytz -youtube_dl PyYAML>=5.3.1 redis html2text @@ -55,7 +58,26 @@ google-api-python-client wget fake-useragent asyncio +youtube_dl + +glitch_this +NumPy +opencv-python-headless +envparse +loguru + +cloudmersive_virus_api_client==3.0.1 +pymongo==3.11.0 +dnspython==2.0.0 +pygments +tswift +youtube_search +fontTools +motor + +pygithub +search_engine_parser + aiofiles -requests -wget +yt_dlp youtube_search diff --git a/restart.bat b/restart.bat deleted file mode 100644 index 22342d9..0000000 --- a/restart.bat +++ /dev/null @@ -1,2 +0,0 @@ -:: starts a cmd to silently start the bat file, just another dirty way of getting things done for my env on windows -start cmd.exe /c start_service.bat \ No newline at end of file diff --git a/start.bat b/start.bat deleted file mode 100644 index 3ad3a97..0000000 --- a/start.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -TITLE Saitama Robot -:: Enables virtual env mode and then starts saitama -env\scripts\activate.bat && py -m Amanda diff --git a/start_service.bat b/start_service.bat deleted file mode 100644 index e0be619..0000000 --- a/start_service.bat +++ /dev/null @@ -1,31 +0,0 @@ -@echo off -:: This runs the batch file as an admin - required UAC to be off -:: This is just an asty hack in to get job done cause we host it on windows dedi. -:: BatchGotAdmin -:------------------------------------- -REM --> Check for permissions ->nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system" - -REM --> If error flag set, we do not have admin. -if '%errorlevel%' NEQ '0' ( - echo Requesting administrative privileges... - goto UACPrompt -) else ( goto gotAdmin ) - -:UACPrompt - echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs" - set params = %*:"="" - echo UAC.ShellExecute "cmd.exe", "/c %~s0 %params%", "", "runas", 1 >> "%temp%\getadmin.vbs" - - "%temp%\getadmin.vbs" - del "%temp%\getadmin.vbs" - exit /B - -:gotAdmin - pushd "%CD%" - CD /D "%~dp0" -:-------------------------------------- -:: your commands begin from this point. -:: stops the service and then starts it -net stop Amanda -net start Amanda