diff --git a/.gitignore b/.gitignore index 2bb90af..00d9e75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,19 @@ config.env -__pycache__/* -.DS_Store/* brains.check learning-data-root.check blacklist.check sedenbot.db sedenbot.session* -sedenbot/__pycache__/* -sedenbot/.DS_Store/* -sedenbot/modules/.DS_Store/* -sedenbot/modules/__pycache__/* -sedenbot/modules/admin/.DS_Store/* -sedenbot/modules/admin/__pycache__/* -sedenecem/core/__pycache__/* -sedenecem/core/.DS_Store/* -sedenecem/sql/.DS_Store/* -sedenecem/sql/__pycache__/* -sedenecem/.DS_Store/* -sedenecem/__pycache__/* -sedenecem/translator/__pycache__/* +.DS_Store/ +__pycache__/ +downloads/ +.vscode/ *.check *.session *.session-journal .progress -.vscode/* -.idea/* +.idea/ *secret* *.log -bin/* +bin/ dump.rdb diff --git a/Dockerfile b/Dockerfile index 097f8cb..58c182a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,3 +6,6 @@ WORKDIR /DerUntergang/ # Clone Repo RUN git clone -b seden https://github.com/TeamDerUntergang/Telegram-SedenUserBot.git /DerUntergang/ + +# Run bot +CMD ["python3", "seden.py"] \ No newline at end of file diff --git a/README.md b/README.md index f2788f5..f7e2cb5 100755 --- a/README.md +++ b/README.md @@ -1,56 +1,75 @@ -Seden UserBot -== - -![GitHub repo size](https://img.shields.io/github/repo-size/TeamDerUntergang/Telegram-SedenUserBot?color=red&style=plastic) -![GitHub](https://img.shields.io/github/license/TeamDerUntergang/Telegram-SedenUserBot?color=red&style=plastic) - -Telegram Python Bot running on Python3 with a Postgresql Sqlalchemy database. It is an modular and simple to use bot. - -```c -#include -/** - Your Telegram account may be banned. - I'm not responsible for misuse of bot, responsibility belongs entirely to user. - This bot is maintained for fun as well as managing groups efficiently. - If you think you will have fun by spamming groups, you are wrong. - In case of any spam ban, if you come and write that my account has been banned, - I'll just laugh at you. -/** +# Seden UserBot _Feel my hands in your hair_ +### Telegram Python Bot running on Python3 with a Postgresql Sqlalchemy database. It is a modular and easy-to-use bot. + +![GitHub](https://img.shields.io/github/license/TeamDerUntergang/Telegram-SedenUserBot?color=red) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) + +## Disclaimer +```python +# -*- coding: utf-8 -*- +""" +DISCLAIMER: +The use of this Telegram bot is subject to the following terms: + +- Your Telegram account may be banned due to misuse of the bot. +- The responsibility for any misuse of the bot lies entirely with the user. +- This bot is primarily maintained for facilitating efficient group management and for entertainment purposes. +- Engaging in spam activities within groups is strongly discouraged. +- If your account is banned due to spamming, any requests for assistance will not be entertained. +""" ``` + ## Run Bot +
+ Click to expand! + ```bash # Clone repo git clone https://github.com/TeamDerUntergang/Telegram-SedenUserBot.git cd Telegram-SedenUserBot -# Install pip dependincies -pip3 install -r requirements.txt +# Create and activate a virtual environment (you can change 'sedenify-venv' to your preferred name) +python3 -m venv sedenify-venv +source sedenify-venv/bin/activate -# Generate session from session.py (skip if there is already) +# Install Python dependencies +pip install -r requirements.txt + +# Generate a session file if it doesn't exist python3 session.py -# Create config.env and fill variables -mv sample_config.env config.env +# Create a configuration file and fill in the required variables +cp sample_config.env config.env +# Then fill in the necessary variables in config.env # Run bot python3 seden.py + ``` +### Nix/NixOS +To set up the bot in a Nix/NixOS environment, navigate to the bot folder and execute the `nix-shell` command. +
-## Heroku Deploy -[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/TeamDerUntergang/Telegram-SedenUserBot/tree/seden) +## Q&A +If you have any requests, complaints, or suggestions, please feel free to reach out to us. -If you have any requests & complaints & suggestions, you can join our [support group](https://t.me/SedenUserBotSupport) or please contact us through a [GitHub issue](https://github.com/TeamDerUntergang/Telegram-SedenUserBot/issues). +- Join our [support group](https://t.me/SedenUserBotSupport) for assistance. +- Submit a [GitHub issue](https://github.com/TeamDerUntergang/Telegram-SedenUserBot/issues) to report any problems or provide feedback. -Please go to our [GitHub.io](https://teamderuntergang.github.io/pyrogram.html) page for installation instructions! Questions asked without reading the instruction will not be answered. +For installation instructions, please visit our [GitHub.io](https://teamderuntergang.github.io/installation.html) page. We kindly request that you read the instructions carefully before asking questions, as questions that can be answered by following the instructions may not receive a response. ## Credits -* [@NaytSeyd](https://github.com/NaytSeyd) - Founder +
+ Click to expand! + +* [@naytseyd](https://github.com/naytseyd) - Founder * [@frknkrc44](https://github.com/frknkrc44) - Operator * [@Sedenogen](https://github.com/ciyanogen) - Co-Founder * [@Delivrance](https://github.com/pyrogram/pyrogram) - Pyrogram Library * [@Skittles9823](https://github.com/skittles9823) - Memes * [@RaphielGang](https://github.com/raphielgang) - Other Modules +* [All Contributors](https://github.com/TeamDerUntergang/Telegram-SedenUserBot/graphs/contributors) +
## License - -This project is licensed under the [AGPL-3](https://www.gnu.org/licenses/agpl-3.0.html). +This project is licensed under the [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html). \ No newline at end of file diff --git a/app.json b/app.json index 693d5ab..7cf4608 100644 --- a/app.json +++ b/app.json @@ -12,7 +12,7 @@ "url": "https://github.com/heroku/heroku-buildpack-chromedriver" } ], - "description": "A modular Telegram userbot running on Python 3.8 with an sqlalchemy database,", + "description": "A modular Telegram userbot running on Python 3.9 with an sqlalchemy database,", "env": { "ALIVE_MSG": { "description": "Custom message for .alive command. We have our own alive message by default.", @@ -28,7 +28,7 @@ "required": true }, "BOT_PREFIX": { - "description": "It changes your bot pattern (default pattern is '.'(dot).", + "description": "It changes your bot pattern (default pattern is '.' (dot)).", "required": false }, "CHROME_DRIVER": { @@ -36,39 +36,14 @@ "required": false, "value": "/usr/bin/chromedriver" }, - "DOWNLOAD_DIRECTORY": { - "description": "Download location for many modules (.download etc..)", - "required": false, - "value": "./downloads/" - }, - "GENIUS_TOKEN": { - "description": "Get this value from https://genius.com/developers.", - "required": false - }, "HEROKU_APPNAME": { "description": "Add the Heroku app name here. Required for updates.", "required": true }, "HEROKU_KEY": { - "description": "Your Heroku API key, get it from 'https://dashboard.heroku.com/account. Required for updates.", + "description": "Heroku API key, get it from 'https://dashboard.heroku.com/account. Required for updates.", "required": true }, - "LASTFM_API": { - "description": "Get this value from https://www.last.fm/api/account/create.", - "required": false - }, - "LASTFM_PASSWORD": { - "description": "Last.FM Password.", - "required": false - }, - "LASTFM_SECRET": { - "description": "Secret Key. Get this value from https://www.last.fm/api/account/create.", - "required": false - }, - "LASTFM_USERNAME": { - "description": "Last.FM Username.", - "required": false - }, "LOG_ID": { "description": "Chat ID of the Log group. If value is 0, log feature not work. If value is left blank, logs send in Saved Messages", "required": false @@ -78,14 +53,6 @@ "required": false, "value": "False" }, - "LYDIA_APIKEY": { - "description": "Get this value from https://coffeehouse.intellivoid.info/dashboard.", - "required": false - }, - "OCR_APIKEY": { - "description": "OCR API Key for .ocr command. Get from https://ocr.space/ocrapi", - "required": false - }, "PM_AUTO_BAN": { "description": "PM Auto Ban Feature. Also known as 'bleep blop, this is a bot...'.", "required": false, @@ -100,10 +67,6 @@ "description": "Custom message for PM Auto Ban feature. Also known as Pmpermit Module.", "required": false }, - "RBG_APIKEY": { - "description": "RBG API Key for .rbg command. Get from https://www.remove.bg/api", - "required": false - }, "REPO_URL": { "description": "It helps your bot updates, if you maintain a fork repo you can add your repo URL here.", "required": true, @@ -115,12 +78,8 @@ "value": "en" }, "SESSION": { - "description": "Get this value by running python3 session.py in terminal.", + "description": "Get this value by running python3 session.py in terminal or bash script.", "required": true - }, - "WEATHER": { - "description": "Set the default city for Seden UserBot's Weather Module.", - "required": false } }, "formation": { @@ -138,7 +97,7 @@ "pyrogram", "postgresql" ], - "logo": "https://i.resimyukle.xyz/7eaU8d.png", + "logo": "https://i.imgur.com/GL1yJpN.png", "name": "Telegram SedenBot", "repository": "https://github.com/TeamDerUntergang/Telegram-SedenUserBot", "stack": "container", diff --git a/cookies.txt b/cookies.txt new file mode 100644 index 0000000..3a2a81d --- /dev/null +++ b/cookies.txt @@ -0,0 +1,5 @@ +# HTTP Cookie File +# Generated by Wget on 2024-03-14 06:02:22. +# Edit at your own risk. + +.google.com TRUE / TRUE 1773457342 __Secure-3PSID g.a000hQgeotAjF0s4Bo1rG0uA-Rs--F4uvWXHlSTgXrmA1YgNa-70WcsTGGbhSM0FtrsKqzuOkAACgYKAVgSAQASFQHGX2MiDObGGYCE-af_tbsVmv15NhoVAUF8yKpYtxp1Xn_8m3ogYEdAJ1QM0076 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ec73a42 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.9' +services: + postgres: + container_name: postgres + image: postgres:latest + environment: + - POSTGRES_USER=sedenuserbot + - POSTGRES_PASSWORD=TeamDerUntergang + - POSTGRES_DB=userbotdb + ports: + - "5432:5432" + restart: always + + seden: + init: true + restart: unless-stopped + network_mode: host + build: + context: https://github.com/TeamDerUntergang/Telegram-SedenUserBot.git#seden + env_file: + - ./config.env + command: python seden.py \ No newline at end of file diff --git a/gdrive_auth.py b/gdrive_auth.py new file mode 100644 index 0000000..332fba2 --- /dev/null +++ b/gdrive_auth.py @@ -0,0 +1,7 @@ +from pydrive.auth import GoogleAuth + +gauth = GoogleAuth() +gauth.LocalWebserverAuth() +gauth.SaveCredentialsFile('creds.txt') + +print(f'creds.txt Dosyası oluşturuldu.Dosyayı kontrol edin.') \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 58c69e3..323a2a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,28 +1,33 @@ bs4 -coffeehouse cowpy -deethon emoji +exifread gitpython -googletrans==3.1.0a0 +google-api-python-client +google-auth-httplib2 +google-auth-oauthlib +googletrans==4.0.0rc1 gtts heroku3 humanize +image_to_Ascii lyricsgenius pillow psycopg2-binary pybase64 +pydrive pylast -pyrogram==1.1.13 +pyrofork +pysmartDL python-barcode python-dotenv qrcode removebg requests selenium +spamwatch speedtest-cli +spotipy sqlalchemy tgcrypto -urbandict -wikipedia -youtube-dl +yt-dlp diff --git a/sample_config.env b/sample_config.env index 3e0adb2..7d3fd89 100644 --- a/sample_config.env +++ b/sample_config.env @@ -1,9 +1,6 @@ -# Delete this line before you do anything -___________DELETE_______THIS_____LINE__________=True - # Get these from https://my.telegram.org/ # REQUIRED -API_ID='' +API_ID= # int API_HASH='' # Get this value by running python3 session.py locally @@ -11,14 +8,17 @@ API_HASH='' SESSION='' # Your Database URL -# Example: 'postgres://sedenbot:sedenbot@localhost:5432/sedenbot' -DATABASE_URL='' +# Leave default if you deploy on local machine otherwise enter the url by following format. +# Example: 'postgresql://username:passwd@localhost:5432/db_name' +DATABASE_URL='postgresql://sedenuserbot:TeamDerUntergang@localhost:5432/userbotdb' +DATABASE_URL_ALTERNATIVE='sqlite:///userbotdb' # Chat ID for Log Group LOG_ID='' # Location of ChromeDriver for .carbon module -# Example for Linux Machines : '/usr/bin/chromedriver' +# Example for Linux : '/usr/bin/chromedriver' +# Windows: 'C:\Users\\scoop\apps\chromedriver\current\chromedriver' CHROME_DRIVER='' # To change Alive message @@ -44,11 +44,26 @@ OCR_APIKEY='' # Genius lyrics get this value from https://genius.com/developers GENIUS_TOKEN='' +# Spotify Client ID +SPOTIPY_CLIENT_ID='' + +# Spotify Client Secret +SPOTIPY_CLIENT_SECRET='' + +# Gdrive Client ID +DRIVE_CLIENT='' + +# Gdrive Secret +DRIVE_SECRET='' + +# Gdrive Folder ID +GDRIVE_FOLDER_ID='' + # If you need Verbosity on the Logging LOG_VERBOSE=False # PM Auto Ban and other PmPermit Stuffs -PM_AUTO_BAN='' +PM_AUTO_BAN=False PM_MSG_COUNT='' PM_UNAPPROVED='' @@ -59,6 +74,6 @@ LASTFM_SECRET='' LASTFM_USERNAME='' # Last.fm Username LASTFM_PASSWORD='' # Last.fm Password -# Lydia module -# Get from https://coffeehouse.intellivoid.info/dashboard -LYDIA_APIKEY='' +# SpamWatch module +# Get from https://t.me/SpamWatchBot?start=token +SPAMWATCH_KEY='' \ No newline at end of file diff --git a/seden.py b/seden.py index efa99a2..3267c43 100644 --- a/seden.py +++ b/seden.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -9,4 +9,5 @@ if __name__ == '__main__': from sedenbot import app - app.run() + + app.run() \ No newline at end of file diff --git a/seden_translate_sorter.py b/seden_translate_sorter.py index 4564c01..1591816 100644 --- a/seden_translate_sorter.py +++ b/seden_translate_sorter.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. diff --git a/sedenbot/__init__.py b/sedenbot/__init__.py index b681c51..333d3d9 100644 --- a/sedenbot/__init__.py +++ b/sedenbot/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,24 +7,26 @@ # All rights reserved. See COPYING, AUTHORS. # -from sqlite3 import connect -from sys import version_info -from os.path import isfile + +import socket +from contextlib import closing +from importlib import import_module +from logging import CRITICAL, DEBUG, INFO, basicConfig, getLogger from os import environ, listdir, path, remove +from os.path import isfile, join, isdir +from pathlib import PurePath from re import search as resr -from distutils.util import strtobool as sb -from importlib import import_module -from logging import basicConfig, getLogger, INFO, DEBUG, CRITICAL -from requests import get -from pyrogram import Client +from sqlite3 import connect +from sys import version_info +from traceback import format_exc +from typing import Any, Dict +from dotenv import load_dotenv, set_key, unset_key +from pyrogram import Client, filters from pyrogram.handlers import MessageHandler +from requests import get -from pyrogram import filters - -from dotenv import load_dotenv, set_key, unset_key import sedenecem.translator as _tr -from traceback import format_exc def reload_env(): @@ -35,6 +37,12 @@ def reload_env(): LOGS = getLogger(__name__) +# +# Bot lang +# +# If missted, the default lang is English. +SEDEN_LANG = environ.get('SEDEN_LANG', 'en') + def get_translation(transKey, params: list = None): ret = _tr.get_translation(SEDEN_LANG, transKey) @@ -48,68 +56,55 @@ def get_translation(transKey, params: list = None): return ret -if version_info[0] < 3 or version_info[1] < 8: +if version_info[0] < 3 or version_info[1] < 10: LOGS.warn(get_translation('pythonVersionError')) quit(1) -HELP = {} +HELP: Dict[str, str] = {} BRAIN = [] BLACKLIST = [] -VALID_PROXY_URL = [] -CONVERSATION = {} -PM_COUNT = {} -PM_LAST_MSG = {} -TEMP_SETTINGS = {} +CONVERSATION: Dict[Any, Any] = {} +TEMP_SETTINGS: Dict[Any, Any] = {} +TEMP_SETTINGS['PM_COUNT'] = {} +TEMP_SETTINGS['PM_LAST_MSG'] = {} # Console verbose logging -LOG_VERBOSE = sb(environ.get('LOG_VERBOSE', 'False')) +LOG_VERBOSE = bool(environ.get('LOG_VERBOSE', 'False')) basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', level=DEBUG if LOG_VERBOSE else INFO, ) -# -# Bot lang -# -# If missted, the default lang is English. -SEDEN_LANG = environ.get('SEDEN_LANG', 'en') - def set_local_env(key: str, value: str): - return set_key('config.env', key, value) + return set_key(PurePath('config.env'), key, value) def unset_local_env(key: str): if key in environ: del environ[key] - return unset_key('config.env', key) + return unset_key(PurePath('config.env'), key) def set_logger(): - # Turns off out printing Session value - pyrogram_syncer = getLogger('pyrogram.syncer') - pyrogram_syncer.setLevel(CRITICAL) - - # Closes some junk outputs - pyrogram_session = getLogger('pyrogram.session.session') - pyrogram_session.setLevel(CRITICAL) + loggers = [ + 'pyrogram.syncer', + 'pyrogram.session.session', + 'pyrogram.session.auth', + 'asyncio', + 'aiosqlite', + ] + level = CRITICAL - pyrogram_auth = getLogger('pyrogram.session.auth') - pyrogram_auth.setLevel(CRITICAL) + for logger_name in loggers: + logger = getLogger(logger_name) + logger.setLevel(level) set_logger() -# Check that the config is edited using the previously used variable. -# Basically, check for config file. -CONFIG_CHECK = environ.get( - '___________DELETE_______THIS_____LINE__________', None) - -if CONFIG_CHECK: - LOGS.warn(get_translation('removeFirstLine')) - quit(1) - # Telegram APP ID and HASH API_ID = environ.get('API_ID', None) if not API_ID: @@ -121,7 +116,7 @@ def set_logger(): LOGS.warn(get_translation('apiHashError')) quit(1) -BOT_VERSION = '1.4.2 Beta' +BOT_VERSION = '1.8.1' SUPPORT_GROUP = 'SedenUserBotSupport' CHANNEL = 'SedenUserBot' @@ -131,16 +126,24 @@ def set_logger(): # Genius module GENIUS_TOKEN = environ.get('GENIUS_TOKEN', None) or environ.get('GENIUS', None) -# Lydia API -LYDIA_APIKEY = environ.get('LYDIA_APIKEY', None) +# Spoify Client ID +SPOTIPY_CLIENT_ID = environ.get('SPOTIPY_CLIENT_ID') + +# Spotify Client SECRET +SPOTIPY_CLIENT_SECRET = environ.get('SPOTIPY_CLIENT_SECRET') + +# Gdrive Client +DRIVE_CLIENT = environ.get('DRIVE_CLIENT') + +# Gdrive Secret +DRIVE_SECRET = environ.get('DRIVE_SECRET') + +# Gdrive Folder ID +GDRIVE_FOLDER_ID = environ.get('GDRIVE_FOLDER_ID', None) # Change Alive Message ALIVE_MSG = environ.get('ALIVE_MSG', None) -# For neofetch -HOSTNAME = environ.get('HOSTNAME', 'DerUntergang') -USER = environ.get('USER', 'sedenecem') - # Chrome Driver and Headless Google Chrome Binaries CHROME_DRIVER = environ.get('CHROME_DRIVER', 'chromedriver') @@ -157,99 +160,73 @@ def set_logger(): PACKNAME = environ.get('PACKNAME', None) PACKNICK = environ.get('PACKNICK', None) -# Deezer ARL Token -DEEZER_TOKEN = environ.get('DEEZER_TOKEN', None) - # SQL Database URL DATABASE_URL = environ.get('DATABASE_URL', None) +if DATABASE_URL and DATABASE_URL.startswith('postgres://'): + DATABASE_URL = DATABASE_URL.replace('postgres://', 'postgresql://', 1) -# Download directory -DOWNLOAD_DIRECTORY = environ.get('DOWNLOAD_DIRECTORY', './downloads') +# If PostgreSQL socket is not available (which is cause issues) use SQLite dialect +host, port = DATABASE_URL.split('@')[-1].split('/')[0].split(':') +with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock: + if sock.connect_ex((host, int(port))) != 0: + DATABASE_URL = environ.get('DATABASE_URL_ALTERNATIVE', None) # SedenBot Session -SESSION = environ.get('SESSION', 'sedenuserbot') +SESSION = environ.get('SESSION', 'sedenify') # SedenBot repo url for updater REPO_URL = environ.get( - 'REPO_URL', 'https://github.com/TeamDerUntergang/SedenUserBot') + 'REPO_URL', 'https://github.com/TeamDerUntergang/Telegram-SedenUserBot' +) # Heroku Credentials for updater HEROKU_KEY = environ.get('HEROKU_KEY', None) HEROKU_APPNAME = environ.get('HEROKU_APPNAME', None) -# Chat ID for Bot Logs -LOG_ID = environ.get('LOG_ID', None) -LOG_ID = int(LOG_ID) if LOG_ID and resr(r'^-?\d+$', LOG_ID) else None +# SpamWatch API key +SPAMWATCH_KEY = environ.get('SPAMWATCH_KEY', None) -# Connect to the test server -# -# You'll have a separate account, -# but you won't be able to access contacts -# or messages on the regular server -# -# Also known as Deep Telegram -# -# For more information: https://docs.pyrogram.org/topics/test-servers -DEEPGRAM = sb(environ.get('DEEPGRAM', 'False')) +# Chat ID for Bot Logs +_LOG_ID = environ.get('LOG_ID', None) +LOG_ID = int(_LOG_ID) if _LOG_ID and resr(r'^-?\d+$', _LOG_ID) else None +del _LOG_ID # PmPermit PM Auto Ban Stuffs -PM_AUTO_BAN = sb(environ.get('PM_AUTO_BAN', 'False')) -PM_MSG_COUNT = environ.get('PM_MSG_COUNT', 'default') -PM_MSG_COUNT = int(PM_MSG_COUNT) if PM_MSG_COUNT.isdigit() else 5 +PM_AUTO_BAN = bool(environ.get('PM_AUTO_BAN', 'False')) +_PM_MSG_COUNT = environ.get('PM_MSG_COUNT', 'default') +PM_MSG_COUNT = int(_PM_MSG_COUNT) if _PM_MSG_COUNT.isdigit() else 5 +del _PM_MSG_COUNT PM_UNAPPROVED = environ.get('PM_UNAPPROVED', None) # Bot Prefix (Defaults to dot) BOT_PREFIX = environ.get('BOT_PREFIX', None) -ENV_RESTRICTED_KEYS = [ - 'HEROKU_KEY', - 'HEROKU_APPNAME', - 'SESSION', - 'API_ID', - 'API_HASH', - 'DATABASE_URL'] - - -def load_brain(): - if path.exists('learning-data-root.check'): - remove('learning-data-root.check') - URL = 'https://raw.githubusercontent.com/NaytSeyd/'\ - 'databasescape/master/learning-data-root.check' - with open('learning-data-root.check', 'wb') as load: - load.write(get(URL).content) - DB = connect('learning-data-root.check') - CURSOR = DB.cursor() - CURSOR.execute('SELECT * FROM BRAIN1') - ALL_ROWS = CURSOR.fetchall() - for i in ALL_ROWS: - BRAIN.append(i[0]) - DB.close() - - -def load_bl(): - if path.exists('blacklist.check'): - remove('blacklist.check') - URL = 'https://raw.githubusercontent.com/NaytSeyd/'\ - 'databaseblacklist/master/blacklist.check' - with open('blacklist.check', 'wb') as load: - load.write(get(URL).content) - DB = connect('blacklist.check') - CURSOR = DB.cursor() - CURSOR.execute('SELECT * FROM RETARDS') - ALL_ROWS = CURSOR.fetchall() - for i in ALL_ROWS: - BLACKLIST.append(i[0]) - DB.close() - - -load_brain() -load_bl() - -me = [] +ENV_RESTRICTED_KEYS = ['HEROKU_KEY', 'HEROKU_APPNAME', 'SESSION', 'API_ID', 'API_HASH'] -class PyroClient(Client): +def load_data(file_name, table_name, data_list): + try: + if path.exists(file_name): + remove(file_name) + URL = f'https://raw.githubusercontent.com/NaytSeyd/databasescape/master/{file_name}' + with open(file_name, 'wb') as load: + load.write(get(URL).content) + DB = connect(file_name) + CURSOR = DB.cursor() + CURSOR.execute(f'SELECT * FROM {table_name}') + ALL_ROWS = CURSOR.fetchall() + for i in ALL_ROWS: + data_list.append(i[0]) + DB.close() + except BaseException: + pass + +load_data('learning-data-root.check', 'BRAIN1', BRAIN) +load_data('blacklist.check', 'RETARDS', BLACKLIST) + + +class PyroClient(Client): @staticmethod def store_msg(_, message): try: @@ -264,45 +241,64 @@ def store_msg(_, message): def __init__(self, session, **args): super().__init__(session, **args) - self.add_handler(MessageHandler( - PyroClient.store_msg, filters.incoming)) - - -app = PyroClient( - SESSION, - api_id=API_ID, - api_hash=API_HASH, - app_version=f'Seden UserBot', - device_model='DerUntergang', - system_version=f'v{BOT_VERSION}', - lang_code='tr', - test_mode=DEEPGRAM -) + self.add_handler(MessageHandler(PyroClient.store_msg, filters.incoming)) + + def start(self): + super().start() + LOGS.info(get_translation('runningBot', [SUPPORT_GROUP])) + LOGS.info(get_translation('sedenVersion', [BOT_VERSION])) + + def stop(self): + super().stop() + LOGS.info(get_translation('goodbyeMsg')) + + def export_session_string(self): + raise NotImplementedError + + +app = PyroClient('sedenify', api_id=API_ID, api_hash=API_HASH, session_string=SESSION) + + +# delete these variables to add some security +del SESSION +del API_ID +del API_HASH def __get_modules(): folder = 'sedenbot/modules' - modules = [ - f[:-3] for f in listdir(folder) - if isfile(f'{folder}/{f}') and f[-3:] == '.py' and f != '__init__.py' + subdirectories = [ + sub + for sub in listdir(folder) + if isdir(join(folder, sub)) ] + modules = [] + for subdirectory in subdirectories: + subfolder_path = join(folder, subdirectory) + py_files = [ + f[:-3] + for f in listdir(subfolder_path) + if isfile(join(subfolder_path, f)) and f.endswith('.py') + ] + for module in py_files: + module_name = f"{subdirectory.replace('/', '.')}.{module}" + modules.append(module_name) return modules def __import_modules(): - modules = sorted(__get_modules()) - LOGS.info(get_translation('loadedModules', [modules])) - for module in modules: + get_modules = sorted(__get_modules()) + for module in get_modules: try: - LOGS.info(get_translation('loadedModules2', [module])) import_module(f'sedenbot.modules.{module}') except Exception: if LOG_VERBOSE: LOGS.warn(format_exc()) LOGS.warn(get_translation('loadedModulesError', [module])) + imported_modules = [module.split('.')[-1] for module in get_modules if module.count('.') > 0] + modules = ', '.join(imported_modules) + LOGS.info(get_translation('loadedModules', [modules])) -__import_modules() -LOGS.info(get_translation('runningBot', [SUPPORT_GROUP])) -LOGS.info(get_translation('sedenVersion', [BOT_VERSION])) +__import_modules() diff --git a/sedenbot/modules/admin/__init__.py b/sedenbot/modules/admin/__init__.py deleted file mode 100644 index 1a21422..0000000 --- a/sedenbot/modules/admin/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from .helpers import * diff --git a/sedenbot/modules/admin/ecem.py b/sedenbot/modules/admin/ecem.py new file mode 100644 index 0000000..10cd58e --- /dev/null +++ b/sedenbot/modules/admin/ecem.py @@ -0,0 +1,392 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from random import choice + +from sedenbot import HELP +from sedenecem.core import edit, get_translation, sedenify + +# ================= CONSTANT ================= +ECEM_STRINGS = [ + "Çocukluk aşkımsın", + "Erkek arkadaşlarımın arkadaşlarının adlarını bilmem normal bir şey", + "Şu an acele işim var, daha sonra çekilsek olmaz mı? Zaten yine geleceğim.", + "Yok kızmadım", + "Bunun farkına vardıysan benim için artık problem yok", + "Bundan sonra artık hayatında Ecem diye biri yok!", + "Yağmurlu havada sweatini veren arkadaşlarınız olsun 👅", + "Yalnız çiçek açar mısın? Yalnız, çiçek.", + "Affetmiyorum, annemi çağıracağım. O gereğini yapacak.", + "Saçımla oynama", + "Saçımla oynamamanı söylediğim halde oynuyorsun. Arkamdan git lütfen.", + "Ben gelmiyorum bedene falan, kenarda ölmeyi bekleyeceğim", + "Düşman değiliz sadece onun olduğu yerde rahat edemem", + "Git arkamdan, istemiyorum seni", + "Murat Boz şarkıları beni hiç eğlendirmiyor", + "Tövbe tövbe kapatın şunu çarpılacağız", + "Üç kızım olsun istiyorum", + "Eğer onunla evlenirsem nikah şahidim sen ol", + "Ehehehe çok komik, hadi defolun gidin", + "Gidip onunla konuşsana?", + "Ne oldu sana? Anlatmak ister misin?", + "Lütfen anlat işte. Çok mu özel ki?", + "Hadi gel erkeklerin yanına geçelim", + "İyi misin?", + "Teşekkür ederim 😊", + "Daha sonra yine geleceğim, o zaman çekiliriz, olmaz mı?", + "Grubu dağıtıyoruz gençler", + "Grubu dağıtıyoruz gençler??? Ne ara dedim onu ya ben?", + "Allah belanı vermesin", + "Hadi yönetici sensin, dağıt grubu", + "İyi akşamlar", + "Her şeyi ben söylüyorum, biraz da siz söyleyin", + "Ben onu esprisine demiştim diyette değilim", + "Sizde var mı bilmiyorum sorim dedim birkaç kişiye daha sordum, çıktı alabilme şansın var mı?", + "Üçgende çıkmış sorular, 3. site", + "Eğer olmadıysa gerek kalmadı", + "Yok yani, ben buldum birinden", + "Evet evet hallettim ben sağol", + "Atim sen de kontrol et bir, o mu diye", + "Neyse sen çıkarttığını da getirirsin olmadı", + "Kim ne dedi sana?", + "Onlar benim kardeşim gibi olduğu için sahipleniyorlar", + "Kötü bir durum yok", + "Bir anda yapınca rahatsız oldum ama sonra sana zaten sorun yok dedim", + "Onlar da biliyor, ondan tepki vermiştir", + "Sorun yok yani", + "Rica ederim", + "Teşekkür ederim kusura bakma telefonum bozuktu yeni görebildim 😊", + "Evet. Yalan borcum mu var size? Evet, yalan borcum mu var? Yalan borcum mu var?", + "Ne malsın ya", + "Testleri bitirmeyin Allah aşkına", + "Sen mesajı silsen de bildirimde gözüküyor", + "O ne demişti ki?", + "Ben birine bir şey yazmştım, sonra sildim. Ne yazdın dedi, söylemedim. Sonra yazdığım şeyi bildirimden açıp bana yazdı.", + "Mzkddkslslldldldldld", + "Toplu olarak yapıyoruz değil mi, hiç hoş olmayan şeyler çıkmasın sonra.", + "Lan sen konuşma", + "Abi yalan değil ki, yetişmiyor", + "Ya hep mi korktuğum başıma gelir ya?", + "Bende şans olsa zaten", + "Ben oyuna falan katılamam", + "Tövbe, hep olmaması gereken sınıfları söylüyorsun", + "Neyse ben konuşmuyorum sizinle zaten", + "Ne uzattınız ya, değişmiş işte", + "Sen sus, seninle konuşan yok", + "Çok özledim 😔 ♥️", + "Saygı, minnet ve özlemle", + "Canım, dayım, her şeyim yolun açık olsun askerim ♥️", + "#NoFilter", + "Yorgunum ve ağrılar", + "Sıkıldım ya, konuşun. Söz valla terslemeyeceğim.", + "Çok kaptırmış kendini o", + "Okul mal o zaman, o kadar yanlışla 11. olduysam", + "Kime diyorsunuz, ben bir şey anlamadım", + "Sen hani hastaydın Lan", + "Sen ne diyon ya sksödödödöföfçgçgçhçhş", + "Hiç bu kadar güldüğümü hatırlamıyorum", + "Kaç dakika uğraştın, gerizekalı ya", + "Lan sus sende sabahtan beri Ecem Ecem dmdkdmdmdmföfögö", + "Evde gerizekalı gibi gülüyorum susun artık", + "Tövbe yarabbim", + "Üstüme gitmeyin şu saatlerde benim", + "Sakn ol raki", + "5 saattir anlamaya çalışıyor", + "Yeter, gidin yatın", + "Sus lan mal", + "Gahahaha diye gülenin dediğine bak", + "Yarın hiç hoş olmayacaksın", + "Sinirliyim zaten, uğraşmayın benimle sjdkdkkdmfgm", + "Bu arada yüzümü telefon gözükmesin diye öyle tutmuyordum sebebini bilmiyorum djjd", + "Doğum günü kızıyım ben şşş. Nasıl bir değişiklik bekliyordunuz dkdkdk", + "İnternetten baksana", + "Müzik dersi hangi gün? Hocaya söz vermiştim.", + "Hem arkamdan kaşar diyorsun, hem de kardeşim diyorsun", + "Böyle böyle yürüyor ya ahahahahahahaha", + "Her an'ımda", + "BFF♥️", + "Yine, yeni, yeniden ♥️", + "Buraya sığdıramayacağım milyon tane iyi anımız var, hep de olsun.", + "İyi ki doğdun kız kardeşim, seni çok seviyorum ♥️", + "Best gün ilan ediyorum ♥️", + "Beraber uyuyoruz ama yine de sen bilirsin sjskskksksks️", + "Özledik be️", + "Ula 😔", + "Konum yeterli", + "Yazdan kalmalarla avunmaya devam", + "Kanka ben zaten tetikteyim ağlamak için yapma bak yanarız️", + "Ben de çok özledim nolacak şimdi", + "Kajsjskskskskdksksdsjjsksks istemiyom bırak beni️", + "O olsaydı iki güldürür kendime gelirdim kafayı yicem dayanamıyorum️", + "Hemde nasılll", + "😔 her seferinde düşürür", + "Yaz gelsin artıkkkkk", + "YERİM SENİ", + "Sen niye beni sinirlendiriyorsun 😠😠", + "Sensin 1.55", + "Bak aferin nasıl biliyor", + "Gevezelik yapma sjsjkskskskskkslslsksksksklsks", + "Güldür güldür", + "Allah allah çok özledim bir şaşırdım", + "Ne demek 2 yıl geçmiş", + "Her şeyi anladım da Çin ne sjdjdkmdmxmxmmxmxkc", + "Her yere atacam", + "Işık bulursam 37478383 tane fotoğraf çekilirim mutlaka", + "Aşkım olduğunu söylemiş miydim ??", + "Ya salak mısın sjdjdkdkkdkdkdkdkd", + "Bana bu kadar kilo aldıran hayat size neler yapmaz - '2018", + "Hatırla çabuk", + "Ayyyyyyy 😳 ♥️", + "Biz bunu hangi kafayla çekilmişiz asksksksllslsldkd", + "Asıl kesmeseydim o zaman küserdik sjskkdkdksksmdk", + "Olay benim kilomdan buraya nasıl geldi", + "Kanka beni bir yıldır görmüyorsun ya hani", + "Salın artık bizi", + "Çatalla", + "Diyete başlıyorum konu kapanmıştır", + "Bakılır neden olmasınn", + "Main storyden vazgeçilmiştir", + "Bilen bilir mutlu olduğum dönemler - işte anca 2018 -", + "Yorum yapmıyorum artıkkk", + "Az dalgası dönmedi şu aynanın 👅", + "Hadi bay", + "Aşırı aşırı aşırı özledim", + "Hikayede kalmasın", + "Bulduğu her yerde fotoğraf çekmeyen de ne bilim", + "Nasıl orada havalar", + "Sana sahip olduğum için çok şanslıyım", + "En acilinden bu günlere geri dönebilir miyiz", + "Buralar artık benim", + "Seni ve uzun saçlarımı özledim 😔", + "Sonunda kauvştuk ♥️", + "Sizi bile özledim 😔", + "Allahım yine çok mutluyum", + "1 yıl önce mutluymuşum", + "Yaaaaaa 😔 ♥️", + "HER ŞEYİİİM ❤️💜🧡🖤💗💙💖💕💝💛💘♥💓", + "Hikayede kalmasın serisinden devam", + "Abi ben niye her fotoğrafta farklı çıkıyorum", + "Benim küçük sevgilim ♥️", + "Göz altı morluklarım ve ben bunları hak etmedik", + "Sjskdksksjdksl gergin olmadığım tek bir saniye yok", + "Gözlerimden yorgunluk akmadığı bir gün bile yok", + "Yaz geri gelsin ve biz her gün içelim", + "Köpeğin yılışıklığı yüzünden kavga ettiğimiz günlerden biri djjdjdjd", + "Bu da senin için skskkdkdkdlskskd", + "Ne demiş kumarda kazanan", + "SJSKKDKSKSKSKS DÜNYANIN EN DELİ ARKADAŞLARI BENDE iyi ki varsınız ♥️♥️♥️♥️", + "Canlarım asla normal duramazlar ♥️♥️♥️♥️", + "Anasının kuzusuuuu", + "Barbie değil harbi", + "Özledim be bu zamanları", + "9. sınıf, canım İzmir 😔 ♥️", + "377383843. Deneme sanırım neysss bugünlük yeterr", + "Çokçokçok özledimm", + "Post olmaya hak kazandı", + "Birthday baby ♥️♥️", + "Daha haklı bir tweet görmedim", + "Bu dediğime kendim bile inanamıyorum ama çok özlediiim ♥️♥️♥️", + "Sanırım eski saçımı özledim", + "10 yıl da geçse aynı espriye devamke", + "Başladık yine", + "Sosyal medyaya sjskskskdkkdkdkdk", + "Bir ara ben de", + "Yeter laaan", + "Sjskjdkdkskskskskkskks", + "Ben de nerde kaldın diye merak ettim", + "Ararsın bu günleri", + "GN", + "gn", + "Dünyanın en tatlı varlığı", + "Ya hahahahahahahahaha o benim tek aşkım", + "Hadi İzmire", + "Buyrun Seden hanım", + "Arkada yeni farketmem djskskskskskkssksk", + "Okulun güzel yanı", + "Kes önce benim mesajlarıma bak sen", + "Sonunda kavuştuk", + "Kuzeniz çünkü 👅😘♥", + "Şşş", + "Abartmaya bayılıyorsun", + "İg", + "BARIŞALIM ARTIK", + "Aşkım ♥♥♥", + "Geceden kalan", + "Oooo kanka büyük laf soktun o nası laf sokmak", + "Canımmsınn♥", + "Djdkdkkd çektirenler utansın", + "Bebeğiiiiim♥♥♥♥♥", + "Goodnighttttt♥♥♥♥♥", + "Aşk hayatımın özeti", + "Bağışıklık yapınca böyle oluyor", + "Aşkımmm♥♥♥♥♥", + "İyi gecelerrr", + "Tecrübe konuşuyor", + "Ben de 😔😔♥♥", + "Sevdaaaa çiçeğiim♥♥♥", + "Bu da senin için skskkdkdkdlskskd", + "Ksnkskzksls ♥♥♥♥", + "Hasta ve yorgun", + "Ksjsksksksksks", + "Canlarımmm♥♥♥♥", + "Msmsksks", + "Bir konserimiz daha yok mu ama", + "Aşkımaşkımm♥♥", + "Aynen", + "Ya defol git djdkdmfmgöögmdmdmdmd", + "Lan tamam bin pişman ettiniz ya sjskdkkskskslsmskldmdlddk", + "Seri üzgün sjkdkdkdksls", + "Bir oyun kazandık onu da çok gördünüz", + "Benim sayemde", + "Bütün maçları bana sorup oynuyon sonra bir de benim privimde artisli yapıyorsun ", + "Ksksksksksksksksks", + "SHJSJSKDKDKSKDK", + "Yeter uyuyun hadi dersiniz var sabah", + "kdkdkdkdkdkdmdmdmdm lan sınıfta öksürmekten canım çıktı bu kadarı da fazla sjskkfksks", + "Tamam", + "Sjkdkdkdkdlskdkxkdkdkdld şela en son bir kus istersen demişti ama yine de sen bilirsin", + "SJDKFKLGLDKDKDLSLDKDKDKDLDLDLLD", + "Toplu igg", + "Hatırım kalır", + "Bir dahakine kafamı kırmayın ama", + "NSSNMDMDMDMSMSMDM", + "Nsnsmsmdmdmdmskkdms", + "İyi kiii♥♥♥♥", + "Senin sesin yüzünden sesi kapalı attım haykırmışsın arkadaş gibi arkadaş kskskskskkdmsksmsmsmmdmddk", + "Dağa taşa yazacan yakında bir olsaydık şu sınavı skskdkdkksksks", + "Düşünsene yarın hoca sayıları değiştiriyor", + "Kalırım sjjdjfjdkskskdkdkdkkdksks", + "Olay beraber okumak değilmiş abi beraber geçirdiğimiz ortammış", + "Skskskksks sadece bunlar var elimdee ♥♥", + "Kordonun dili olsa da konuşsa", + "Bende serisi var sjsjskdmmsksks", + "Abartttttt", + "Babuş açı iyi bir de biz de zayıftık bir zamanlar", + "Tabi koçç", + "Yuh 2 sene önceydi o", + "Aslan 2 ay sonra barıştık biz onun üzerinden", + "Djdjdkkdkdkdkd çünkü o Seden", + "Uğraşmaaa", + "Yayaya♥♥♥", + "Birthday baby♥♥ -acil fotoğraf @sedenogen", + "Sjsjdjjsksksks özlediğime değil ÇOK özlediğime", + "Yaaaaa", + "Deme bak gider boyatırım", + "Sen kesinlikle benim ilerideki çocuğumsun", + "Ayyyyyyy♥♥", + "Bu fotoğrafta çöktüğümü fark etmişimdir", + "Current mood: DAY OFF!", + "Sıkıntıdan patlamama ramak kaldı", + "Bir bihter ziyagil değildik ama biz de çok acı çektik sjjsjsjskd", + "Efektten bıkana kadar devam", + "Şov başlasın dedi", + "Bir pijamamı bir de seni♥", + "Yeni yıla evde tek başına girmek mi 0/10", + "Seri yorgun", + "10/10", + "Black is my favorite color", + "Çok şükür bugün de yorgunluktan öldük", + "Best couple", + "İggg♥", + "Definitely Tired", + "Good night", + "Saç kestirmek net pişmanlıktır", + "Güne puanım 0", + "Teyzoşuuuum♥", + "Aşşkııım♥♥♥", + "Sensin en güzelllll♥♥♥", + "Bebeğiiim♥", + "Bir kere ya bir kere güzel bir şey at", + "Canımm♥", + "Aşkımmm♥", + "Ben de seni çokk seviyorum birtanem♥", + "Emoji yanlış oldu sanırım 👅 ♥", + "Teşekkürler ♥", + "Birtanemm♥", + "Hem de çokk♥", + "En özell♥", + "Balll♥", + "Seviyorum seniii♥", + "Psikolojik tedavi için dm", + "Çözüyormuş gibi çek knk", + "Yalan söyleme dakika sayıyordun bitsin diye", + "Seninle arkada marş söyleyip çerez yemeği özledim", + "Gözümü galatasaray marşıyla açıyordum sjsjdkdkdkkdkd", + "Aferin böyle yola gel sjskkfskdkfdkkdkddk", + "Gn priv ailemm♥️♥️♥️", + "Uyumuyorum çaktırma sjskkskdkdkd", + "Kara gözlüm ölesim var", + "Bu bağlantıyı kimse anlamayacak sjsksksksksk", + "Yapma yanarız", + "skamaksksksksksks", + "inşallahh bence de görelim artık", + "hem de nasılllll", + "Konumumuz belli", + "Herkes böyle bacı bulamaz şanslıyım tabi 👅 ♥️♥️♥️♥️", + "En değerlilerim misiniz nesinizz????", + "😳♥️♥️", + "Pardon da neyin var acabaaa", + "Bebekkkk gibisin aşkımm", + "Şşş sus yoksa inanırım", + "İyi geceler canlarım️♥♥♥", + "Sizi seveni üzün, düzene uyun", + "Tanıyamadım", + "Hangi sınıf", + "Okuldan kimse takip etmiyor?", + "12 dahil takip ettiğim kişiler var", + "Ama seninle hiçbiri takipleşmiyor", + "Kavuştukkkk♥", + "Kuzucuğumla💘", + "Bu özlemin tarifi yok♥", + "Mood.", + "Sondaki gülüşe düşmeyen de ne bilim", + "Herkes bu kadar bencil olmak zorunda mı?", + "❔", + "Bekle dedi gitti 👅♥", + "Bu da burda kalsın", + "SENİİİN👅", + "Aşşşşşkkıım", + "💗💗", + "Ah Seden'im... küçük civcivim", + "💓💓💓💓", + "Özlemini tişörtünle gideriyorum.", + "Sus ya sen çok farklısın sanki", + "Seden'e derdimi anlatıyorumdur. Seden:", + "Canımın içiiiii 💙", + "😘😘", + "Anlık sinir krizleri geçirildi", + "Çevrimiçi olup olmadığımızı test ediyoruzdur #whatsapp yaktın bizi", + "Djjdkdkmdöödööd", + "Ben almadım Seden almış", + "Hocam lütfen bir sonraki dersimize olsun", + "Biraz da şerefsiz arkadaşlarımızı paylaşalım ♥️♥️♥️♥️", + "sadece Doğum günümde yazıyorsunuz", + "Yalansa yalan de djjdjdkdkksks", + "👅♥️♥️♥️", + "SJSJJDJDKSKSKSKKSKSKDK yok artık hala mı", + "Bütün okul anladı @CiyanogenOneTeams ona yâr olmayacağımı anlamadı Jsjsjdkskskskskskl", + "KSKDKDKDKDKSKKSKDKDKKD", + "LAN HATIRLATMA sjjdjdjdkdkdkdkdk bir fotoğraf çekilelim mi NE SJSKSKSKSK", + "Yemin ederim daha fazla sevenini görmedim jdjdkdkdkd", +] +# ================= CONSTANT ================= +'''Copyright (c) @Sedenogen | 2020''' + + +@sedenify(pattern='^.ecem$') +def ecemify(message): + ecem(message) + + +def ecem(message): + # Ecem'in sözlüğü + edit(message, choice(ECEM_STRINGS)) + + +HELP.update({'ecem': get_translation('ecemInfo')}) diff --git a/sedenbot/modules/env.py b/sedenbot/modules/admin/env.py similarity index 67% rename from sedenbot/modules/env.py rename to sedenbot/modules/admin/env.py index 2832d53..bcd6f43 100644 --- a/sedenbot/modules/env.py +++ b/sedenbot/modules/admin/env.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,24 +8,30 @@ # from time import sleep -from heroku3 import from_key -from dotenv import dotenv_values - -from sedenbot.modules.horeke import restart -from sedenbot import (HELP, HEROKU_KEY, HEROKU_APPNAME, environ, - set_local_env, unset_local_env, reload_env, - ENV_RESTRICTED_KEYS) -from sedenecem.core import edit, sedenify, extract_args, get_translation - -@sedenify(pattern='^.env', compat=False) -def manage_env(client, message): +from dotenv import dotenv_values +from heroku3 import from_key +from sedenbot import ( + ENV_RESTRICTED_KEYS, + HELP, + HEROKU_APPNAME, + HEROKU_KEY, + environ, + reload_env, + set_local_env, + unset_local_env, +) +from sedenbot.modules.miscs.horeke import restart +from sedenecem.core import edit, extract_args, get_translation, sedenify + + +@sedenify(pattern='^.env') +def manage_env(message): action = extract_args(message).split(' ', 1) - if action[0] == 'list': + if action and action[0] == 'list': pass - elif len(action) < 2 or action[0] not in [ - 'get', 'set', 'rem', 'copy', 'move']: + elif len(action) < 2 or action[0] not in ['get', 'set', 'rem', 'copy', 'move']: edit(message, f"`{get_translation('wrongCommand')}`") return @@ -39,7 +45,8 @@ def manage_env(client, message): if not HEROKU_APPNAME: edit( message, - f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`') + f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`', + ) for app in heroku_applications: if app.name == HEROKU_APPNAME: heroku_app = app @@ -48,7 +55,8 @@ def manage_env(client, message): if heroku_app is None: edit( message, - f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`') + f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`', + ) return reload_env() @@ -56,8 +64,11 @@ def manage_env(client, message): if action[0] == 'set': items = action[1].split(' ', 1) - if len(items) < 2 or len( - items[1]) < 1 or items[0].upper() in ENV_RESTRICTED_KEYS: + if ( + len(items) < 2 + or len(items[1]) < 1 + or items[0].upper() in ENV_RESTRICTED_KEYS + ): edit(message, f"`{get_translation('wrongCommand')}`") return @@ -72,7 +83,7 @@ def manage_env(client, message): edit(message, get_translation('envSetSuccess', ['`', '**', key])) sleep(2) - restart(client, message) + restart(message) elif action[0] == 'get': items = action[1].split(' ', 1) @@ -87,16 +98,10 @@ def manage_env(client, message): elif not heroku_mode and (value := environ.get(items[0], None)): pass else: - edit( - message, get_translation( - 'envNotFound', [ - '`', '**', items[0]])) + edit(message, get_translation('envNotFound', ['`', '**', items[0]])) return - edit( - message, get_translation( - 'envGetValue', [ - '`', '**', items[0], value])) + edit(message, get_translation('envGetValue', ['`', '**', items[0], value])) elif action[0] == 'rem': items = action[1].split(' ', 1) @@ -111,20 +116,22 @@ def manage_env(client, message): elif not heroku_mode and (value := environ.get(items[0], None)): unset_local_env(items[0]) else: - edit( - message, get_translation( - 'envNotFound', [ - '`', '**', items[0]])) + edit(message, get_translation('envNotFound', ['`', '**', items[0]])) return edit(message, get_translation('envRemSuccess', ['`', '**', items[0]])) sleep(2) - restart(client, message) + restart(message) elif action[0] in ['copy', 'move']: items = action[1].split(' ', 1) - if len(items) < 2 or len(items[0]) < 1 or items[0].upper() in ENV_RESTRICTED_KEYS or items[1].upper( - ) in ENV_RESTRICTED_KEYS or items[0].upper() == items[1].upper(): + if ( + len(items) < 2 + or len(items[0]) < 1 + or items[0].upper() in ENV_RESTRICTED_KEYS + or items[1].upper() in ENV_RESTRICTED_KEYS + or items[0].upper() == items[1].upper() + ): edit(message, f"`{get_translation('wrongCommand')}`") return @@ -136,10 +143,7 @@ def manage_env(client, message): elif not heroku_mode and (value := environ.get(items[0], None)): pass else: - edit( - message, get_translation( - 'envNotFound', [ - '`', '**', items[0]])) + edit(message, get_translation('envNotFound', ['`', '**', items[0]])) return if heroku_mode: @@ -153,19 +157,18 @@ def manage_env(client, message): elif not heroku_mode and (value := environ.get(items[0], None)): unset_local_env(items[0]) edit( - message, get_translation( - 'envMoveSuccess', [ - '`', '**', items[0], items[1]])) + message, + get_translation('envMoveSuccess', ['`', '**', items[0], items[1]]), + ) sleep(2) - restart(client, message) + restart(message) return edit( - message, get_translation( - 'envCopySuccess', [ - '`', '**', items[0], items[1]])) + message, get_translation('envCopySuccess', ['`', '**', items[0], items[1]]) + ) sleep(2) - restart(client, message) + restart(message) elif action[0] == 'list': out = '' if heroku_mode: @@ -175,8 +178,7 @@ def manage_env(client, message): out += f'%1•%1 %2{i.replace("%", "½")}%2\n' else: keys = dotenv_values('config.env').keys() - keys = sorted([x for x in keys if x.upper() - not in ENV_RESTRICTED_KEYS]) + keys = sorted([x for x in keys if x.upper() not in ENV_RESTRICTED_KEYS]) for i in keys: out += f'%1•%1 %2{i.replace("%", "½")}%2\n' diff --git a/sedenbot/modules/admin/helpers.py b/sedenbot/modules/admin/helpers.py deleted file mode 100644 index e165a11..0000000 --- a/sedenbot/modules/admin/helpers.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from sedenbot import app - -_admin_status_list = ['creator', 'administrator'] - -def is_admin(message): - if not 'group' in message.chat.type: - return True - - user = app.get_chat_member(chat_id=message.chat.id, - user_id=message.from_user.id) - return user.status in _admin_status_list diff --git a/sedenbot/modules/admin/seden.py b/sedenbot/modules/admin/seden.py new file mode 100644 index 0000000..1c13aa7 --- /dev/null +++ b/sedenbot/modules/admin/seden.py @@ -0,0 +1,32 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from collections import OrderedDict +from re import match + +from sedenbot import HELP +from sedenecem.core import edit, extract_args, get_translation, reply, sedenify + + +@sedenify(pattern='^.(seden|help)') +def seden_cmds(message): + args = extract_args(message).lower() + cmds = OrderedDict(sorted(HELP.items())) + + if not args: + edit(message, get_translation('sedenUsage2', ['**', '`'])) + metin = f'{get_translation("sedenShowLoadedModules", ["**", "`", len(cmds)])}\n' + metin += '\n'.join([f'• `{item}`' for item in cmds]) + return reply(message, metin) + + matching_cmds = [cmd for cmd in cmds if match(args, cmd)] + if matching_cmds: + edit(message, str(cmds[matching_cmds[0]])) + else: + edit(message, f'**{get_translation("sedenUsage")}**') \ No newline at end of file diff --git a/sedenbot/modules/admin/system.py b/sedenbot/modules/admin/system.py new file mode 100644 index 0000000..478fdf2 --- /dev/null +++ b/sedenbot/modules/admin/system.py @@ -0,0 +1,210 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from ast import Add, BinOp, BitXor, Div, Mult, Num, Pow, Sub, UnaryOp, USub, parse +from datetime import datetime +from getpass import getuser +from operator import add, mul, neg, pow, sub, truediv, xor +from shutil import which + +from pyrogram.raw.functions.help import GetNearestDc + +from sedenbot import ALIVE_MSG, BOT_VERSION, CHANNEL, HELP +from sedenbot.modules.admin.ecem import ecem +from sedenecem.core import ( + edit, + extract_args, + get_status_out, + get_translation, + reply, + reply_doc, + sedenify, + send_log, +) + +# ================= CONSTANT ================= +CUSTOM_MSG = ALIVE_MSG or f"`{get_translation('sedenAlive')}`" +# ============================================ + + +@sedenify(pattern='^.neofetch$') +def neofetch(message): + if which('neofetch'): + _, process = get_status_out('neofetch --stdout') + edit(message, f'`{process}`') + else: + edit(message, f'`{get_translation("neofetchNotFound")}`') + + +@sedenify(pattern='^.botver$') +def bot_version(message): + if which('git'): + _, changes = get_status_out('git rev-list --all --count') + edit( + message, + get_translation( + 'sedenShowBotVersion', + ['**', '`', CHANNEL, BOT_VERSION, changes], + ), + preview=False, + ) + else: + edit(message, f'`{get_translation("sedenGitNotFound")}`') + + +@sedenify(pattern='^.ping$') +def ping(message): + start = datetime.now() + edit(message, '**Pong!**') + finish = datetime.now() + time = (finish - start).microseconds / 1000 + edit(message, f'**Pong!**\n`{time}ms`') + + +@sedenify(pattern='^.alive$') +def alive(message): + if CUSTOM_MSG.lower() == 'ecem': + ecem(message) + return + edit(message, f'{CUSTOM_MSG}') + + +@sedenify(pattern='^.echo') +def test_echo(message): + args = extract_args(message) + if len(args) > 0: + message.delete() + reply(message, args) + else: + edit(message, f'`{get_translation("echoHelp")}`') + + +@sedenify(pattern='^.dc$') +def data_center(message): + result = message._client.invoke(GetNearestDc()) + + edit( + message, + get_translation( + 'sedenNearestDC', + ['**', '`', result.country, result.nearest_dc, result.this_dc], + ), + ) + + +@sedenify(pattern='^.term') +def terminal(message): + command = extract_args(message) + + if len(command) < 1: + edit(message, f'`{get_translation("termUsage")}`') + return + + curruser = getuser() + try: + from os import geteuid + + uid = geteuid() + except ImportError: + uid = 0 + + if not command: + edit(message, f'`{get_translation("termHelp")}`') + return + + result = get_translation("termNoResult") + try: + _, result = get_status_out(command) + except BaseException as e: + pass + + if len(result) > 4096: + output = open('output.txt', 'w+') + output.write(result) + output.close() + reply_doc( + message, + 'output.txt', + caption=f'`{get_translation("outputTooLarge")}`', + delete_after_send=True, + ) + return + + edit(message, f'`{curruser}:~{"#" if uid == 0 else "$"} {command}\n{result}`') + + send_log(get_translation('termLog', [command])) + + +@sedenify(pattern='^.eval') +def eval(message): + args = extract_args(message) + if len(args) < 1: + edit(message, f'`{get_translation("evalUsage")}`') + return + + try: + evaluation = safe_eval(args) + if evaluation: + if isinstance(evaluation, str): + if len(evaluation) >= 4096: + file = open('output.txt', 'w+') + file.write(evaluation) + file.close() + reply_doc( + message, + 'output.txt', + caption=f'`{get_translation("outputTooLarge")}`', + delete_after_send=True, + ) + return + edit( + message, + get_translation('sedenQuery', ['**', '`', args, evaluation]), + ) + else: + edit( + message, + get_translation( + 'sedenQuery', ['**', '`', args, get_translation('sedenErrorResult')] + ), + ) + except Exception as err: + edit(message, get_translation('sedenQuery', ['**', '`', args, str(err)])) + + send_log(get_translation('evalLog', [args])) + + +operators = { + Add: add, + Sub: sub, + Mult: mul, + Div: truediv, + Pow: pow, + BitXor: xor, + USub: neg, +} + + +def safe_eval(expr): + expr = expr.lower().replace('x', '*').replace(' ', '') + return str(_eval(parse(expr, mode='eval').body)) + + +def _eval(node): + if isinstance(node, Num): + return node.n + elif isinstance(node, BinOp): + return operators[type(node.op)](_eval(node.left), _eval(node.right)) + elif isinstance(node, UnaryOp): + return operators[type(node.op)](_eval(node.operand)) + else: + raise TypeError(f'`{get_translation("safeEval")}`') + + +HELP.update({'system': get_translation('systemInfo')}) diff --git a/sedenbot/modules/updater.py b/sedenbot/modules/admin/updater.py similarity index 82% rename from sedenbot/modules/updater.py rename to sedenbot/modules/admin/updater.py index ce159ef..63510cc 100644 --- a/sedenbot/modules/updater.py +++ b/sedenbot/modules/admin/updater.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,18 +8,25 @@ # from os import execl, path -from sys import executable, argv -from heroku3 import from_key -from git import Repo -from git.exc import (GitCommandError, InvalidGitRepositoryError, - NoSuchPathError) +from sys import argv, executable -from sedenbot import HELP, HEROKU_KEY, HEROKU_APPNAME, REPO_URL -from sedenecem.core import (extract_args, sedenify, edit, me, - reply, reply_doc, get_translation) +from heroku3 import from_key +from sedenbot import HELP, HEROKU_APPNAME, HEROKU_KEY, REPO_URL +from sedenecem.core import ( + edit, + extract_args, + get_translation, + reply, + reply_doc, + sedenify, +) + +from git import Repo # type: ignore +from git.exc import GitCommandError, InvalidGitRepositoryError, NoSuchPathError requirements_path = path.join( - path.dirname(path.dirname(path.dirname(__file__))), 'requirements.txt') + path.dirname(path.dirname(path.dirname(__file__))), 'requirements.txt' +) def gen_chlog(repo, diff): @@ -45,6 +52,7 @@ def upstream(ups): conf = extract_args(ups) off_repo = REPO_URL force_update = False + repo = None try: txt = f'`{get_translation("updateFailed")}`\n\n' @@ -97,11 +105,11 @@ def upstream(ups): file = open('changelog.txt', 'w+') file.write(changelog) file.close() - reply_doc(ups, ups.chat.id, 'changelog.txt', - delete_after_send=True) + reply_doc(ups, 'changelog.txt', delete_after_send=True) else: - edit(ups, get_translation( - 'updaterHasUpdate', ['**', '`', ac_br, changelog])) + edit( + ups, get_translation('updaterHasUpdate', ['**', '`', ac_br, changelog]) + ) reply(ups, get_translation('updateNow', ['**', '`'])) return @@ -111,12 +119,14 @@ def upstream(ups): edit(ups, f'`{get_translation("updateSedenBot")}`') if HEROKU_KEY: - heroku = from_key(HEROKU_KEY) - heroku_app = None - heroku_applications = heroku.apps() + try: + heroku = from_key(HEROKU_KEY) + heroku_app = None + heroku_applications = heroku.apps() + except BaseException: + return edit(ups, f'`{get_translation("updateHerokuApiKey")}`') if not HEROKU_APPNAME: edit(ups, f'`{get_translation("updateHerokuAppName")}`') - me[1] = False repo.__del__() return for app in heroku_applications: @@ -125,14 +135,14 @@ def upstream(ups): break if heroku_app is None: edit(ups, f'`{get_translation("updateHerokuAppName", [txt])}`') - me[1] = False repo.__del__() return edit(ups, f'`{get_translation("updateBotUpdating")}`') ups_rem.fetch(ac_br) repo.git.reset('--hard', 'FETCH_HEAD') heroku_git_url = heroku_app.git_url.replace( - 'https://', 'https://api:' + HEROKU_KEY + '@') + 'https://', f'https://api:{HEROKU_KEY}@' + ) if 'heroku' in repo.remotes: remote = repo.remote('heroku') remote.set_url(heroku_git_url) @@ -145,10 +155,6 @@ def upstream(ups): repo.__del__() return edit(ups, f'`{get_translation("updateComplete")}`') - try: - heroku_app.scale_formation_process('seden', 1) - except BaseException: - pass else: try: ups_rem.pull(ac_br) @@ -169,8 +175,8 @@ def execute_command(command): sonuc = None try: from subprocess import PIPE, Popen - islem = Popen(command, stdout=PIPE, stderr=PIPE, - universal_newlines=True) + + islem = Popen(command, stdout=PIPE, stderr=PIPE, universal_newlines=True) sonuc, _ = islem.communicate() except BaseException: pass diff --git a/sedenbot/modules/updown.py b/sedenbot/modules/admin/updown.py similarity index 51% rename from sedenbot/modules/updown.py rename to sedenbot/modules/admin/updown.py index 2e82b47..b32665f 100644 --- a/sedenbot/modules/updown.py +++ b/sedenbot/modules/admin/updown.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,9 +8,18 @@ # from os.path import isfile -from sedenbot import HELP -from sedenecem.core import (download_media_wc, sedenify, edit, - extract_args, reply_doc, get_translation) +from time import time + +from sedenbot import HELP, TEMP_SETTINGS +from sedenecem.core import ( + download_media_wc, + edit, + extract_args, + get_translation, + reply_video, + reply_doc, + sedenify, +) @sedenify(pattern='^.download$') @@ -20,9 +29,18 @@ def download(message): edit(message, f'`{get_translation("downloadReply")}`') return + posix = time() + TEMP_SETTINGS[f'upload_{posix}'] = posix + def progress(current, total): - edit(message, get_translation('updownDownload', [ - '`', '(½{:.2f})'.format(current * 100 / total)])) + if (curr_posix := time()) - TEMP_SETTINGS[f'upload_{posix}'] > 5: + TEMP_SETTINGS[f'upload_{posix}'] = curr_posix + edit( + message, + get_translation( + 'updownDownload', ['`', '(½{:.2f})'.format(current * 100 / total)] + ), + ) edit(message, f'`{get_translation("downloadMedia")}`') media = download_media_wc(reply, progress=progress) @@ -32,6 +50,7 @@ def progress(current, total): return edit(message, get_translation('updownDownloadSuccess', ['`', media])) + del TEMP_SETTINGS[f'upload_{posix}'] @sedenify(pattern='^.upload') @@ -42,22 +61,37 @@ def upload(message): edit(message, f'`{get_translation("uploadReply")}`') return + posix = time() + TEMP_SETTINGS[f'upload_{posix}'] = posix + def progress(current, total): - edit(message, get_translation('updownUpload', [ - '`', '(½{:.2f})'.format(current * 100 / total), args])) + if (curr_posix := time()) - TEMP_SETTINGS[f'upload_{posix}'] > 5: + TEMP_SETTINGS[f'upload_{posix}'] = curr_posix + edit( + message, + get_translation( + 'updownUpload', + ['`', '(½{:.2f})'.format(current * 100 / total), args], + ), + ) if isfile(args): try: edit(message, get_translation('updownUpload', ['`', '', args])) - reply_doc(message, args, progress=progress) + if args.endswith('.mp4'): + reply_video(message, args, progress=progress) + else: + reply_doc(message, args, progress=progress) edit(message, f'`{get_translation("uploadFinish")}`') except Exception as e: edit(message, f'`{get_translation("uploadError")}`') raise e + del TEMP_SETTINGS[f'upload_{posix}'] return edit(message, f'`{get_translation("uploadFileError")}`') + del TEMP_SETTINGS[f'upload_{posix}'] HELP.update({'download': get_translation('uploadInfo')}) diff --git a/sedenbot/modules/afk.py b/sedenbot/modules/afk.py deleted file mode 100644 index cea588d..0000000 --- a/sedenbot/modules/afk.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from random import choice, randint -from time import sleep - -from pyrogram import ContinuePropagation, StopPropagation - -from sedenbot import (HELP, TEMP_SETTINGS, PM_AUTO_BAN, app, me as mel) -from sedenecem.core import (extract_args, sedenify, send_log, - edit, reply, get_translation) - -# ========================= CONSTANTS ============================ - -AFKSTR = [get_translation(f'afkstr{i+1}') for i in range(0, 22)] -# ================================================================= - -TEMP_SETTINGS['AFK_USERS'] = {} -TEMP_SETTINGS['IS_AFK'] = False -TEMP_SETTINGS['COUNT_MSG'] = 0 - - -@sedenify(incoming=True, outgoing=False, disable_edited=True, - private=False, bot=False, disable_notify=True) -def mention_afk(mention): - me = mel[0] - if mention.mentioned or mention.reply_to_message and mention.reply_to_message.from_user and mention.reply_to_message.from_user.id == me.id: - if TEMP_SETTINGS['IS_AFK']: - if mention.from_user.id not in TEMP_SETTINGS['AFK_USERS']: - if 'AFK_REASON' in TEMP_SETTINGS: - reply( - mention, get_translation( - "afkMessage2", [ - '**', me.first_name, me.id, - '`', TEMP_SETTINGS['AFK_REASON']])) - else: - reply(mention, f"```{choice(AFKSTR)}```") - TEMP_SETTINGS['AFK_USERS'].update({mention.from_user.id: 1}) - TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 - else: - if TEMP_SETTINGS['AFK_USERS'][ - mention.from_user.id] % randint( - 2, 4) == 0: - if 'AFK_REASON' in TEMP_SETTINGS: - reply( - mention, get_translation( - "afkMessage2", [ - '**', me.first_name, me.id, - '`', TEMP_SETTINGS['AFK_REASON']])) - else: - reply(mention, f"```{choice(AFKSTR)}```") - TEMP_SETTINGS['AFK_USERS'][ - mention.from_user.id] = TEMP_SETTINGS['AFK_USERS'][ - mention.from_user.id] + 1 - TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 - else: - TEMP_SETTINGS['AFK_USERS'][ - mention.from_user.id] = TEMP_SETTINGS['AFK_USERS'][ - mention.from_user.id] + 1 - TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 - raise ContinuePropagation - - -@sedenify(incoming=True, outgoing=False, disable_errors=True, - group=False, bot=False, disable_notify=True) -def afk_on_pm(message): - if PM_AUTO_BAN: - try: - from sedenecem.sql.pm_permit_sql import is_approved - apprv = is_approved(message.from_user.id) - except BaseException: - apprv = True - else: - apprv = True - if apprv and TEMP_SETTINGS['IS_AFK']: - me = mel[0] - if message.from_user.id not in TEMP_SETTINGS['AFK_USERS']: - if 'AFK_REASON' in TEMP_SETTINGS: - reply( - message, get_translation( - "afkMessage2", [ - '**', me.first_name, me.id, - '`', TEMP_SETTINGS['AFK_REASON']])) - else: - reply(message, f"```{choice(AFKSTR)}```") - TEMP_SETTINGS['AFK_USERS'].update({message.from_user.id: 1}) - TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 - else: - if TEMP_SETTINGS['AFK_USERS'][ - message.from_user.id] % randint( - 2, 4) == 0: - if 'AFK_REASON' in TEMP_SETTINGS: - reply( - message, get_translation( - "afkMessage2", [ - '**', me.first_name, me.id, - '`', TEMP_SETTINGS['AFK_REASON']])) - else: - reply(message, f"```{choice(AFKSTR)}```") - TEMP_SETTINGS['AFK_USERS'][ - message.from_user.id] = TEMP_SETTINGS['AFK_USERS'][ - message.from_user.id] + 1 - TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 - else: - TEMP_SETTINGS['AFK_USERS'][ - message.from_user.id] = TEMP_SETTINGS['AFK_USERS'][ - message.from_user.id] + 1 - TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 - raise ContinuePropagation - - -@sedenify(pattern=r'^.afk') -def set_afk(message): - args = extract_args(message) - if len(args) > 0: - TEMP_SETTINGS['AFK_REASON'] = args - edit( - message, get_translation( - 'afkStartReason', [ - '**', '`', TEMP_SETTINGS['AFK_REASON']])) - else: - edit(message, f'**{get_translation("afkStart")}**') - send_log(get_translation('afkLog')) - TEMP_SETTINGS['IS_AFK'] = True - raise StopPropagation - - -@sedenify() -def type_afk_is_not_true(message): - if TEMP_SETTINGS['IS_AFK']: - TEMP_SETTINGS['IS_AFK'] = False - reply(message, f'**{get_translation("afkEnd")}**') - sleep(2) - send_log( - get_translation( - 'afkMessages', - ['`', '**', str(len(TEMP_SETTINGS['AFK_USERS'])), - str(TEMP_SETTINGS['COUNT_MSG'])])) - for i in TEMP_SETTINGS['AFK_USERS']: - name = app.get_chat(i) - name0 = str(name.first_name) - send_log( - get_translation( - 'afkMentionUsers', - ['**', name0, str(i), - '`', str(TEMP_SETTINGS['AFK_USERS'][i])])) - TEMP_SETTINGS['COUNT_MSG'] = 0 - TEMP_SETTINGS['AFK_USERS'] = {} - if 'AFK_REASON' in TEMP_SETTINGS: - del TEMP_SETTINGS['AFK_REASON'] - raise ContinuePropagation - - -HELP.update({'afk': get_translation('afkInfo')}) diff --git a/sedenbot/modules/ban.py b/sedenbot/modules/ban.py deleted file mode 100644 index 50e87b4..0000000 --- a/sedenbot/modules/ban.py +++ /dev/null @@ -1,458 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from time import sleep - -from pyrogram.types import ChatPermissions -from pyrogram.errors import MessageTooLong - -from sedenbot import HELP, BRAIN -from sedenecem.core import (edit, sedenify, send_log, reply_doc, - extract_args, get_translation) -from sedenecem.sql import mute_sql as sql - - -@sedenify(pattern='^.ban', compat=False, private=False, admin=True) -def ban_user(client, message): - args = extract_args(message) - reply = message.reply_to_message - edit(message, f'`{get_translation("banProcess")}`') - if args: - try: - user = client.get_users(args) - except Exception: - edit(message, f'`{get_translation("banFailUser")}`') - return - elif reply: - user_id = reply.from_user.id - user = client.get_users(user_id) - user_id = user.id - else: - edit(message, f'`{get_translation("banFailUser")}`') - return - - try: - replied_user = reply.from_user - if replied_user.is_self: - return edit(message, f'`{get_translation("cannotBanMyself")}`') - except BaseException: - pass - - if user.id in BRAIN: - return edit( - message, get_translation( - 'brainError', [ - '`', '**', user.first_name, user.id])) - - try: - chat_id = message.chat.id - client.kick_chat_member(chat_id, user.id) - edit( - message, get_translation( - 'banResult', [ - '**', user.first_name, user.id, '`'])) - sleep(1) - send_log( - get_translation( - 'banLog', - ['**', user.first_name, user.id, message.chat.title, - '`', chat_id])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(pattern='^.unban', compat=False, private=False, admin=True) -def unban_user(client, message): - args = extract_args(message) - reply = message.reply_to_message - edit(message, f'`{get_translation("unbanProcess")}`') - if args: - try: - user = client.get_users(args) - except Exception: - edit(message, f'`{get_translation("banFailUser")}`') - return - elif reply: - user_id = reply.from_user.id - user = client.get_users(user_id) - user_id = user.id - else: - edit(message, f'`{get_translation("banFailUser")}`') - return - - try: - replied_user = reply.from_user - if replied_user.is_self: - return edit(message, f'`{get_translation("cannotUnbanMyself")}`') - except BaseException: - pass - - try: - chat_id = message.chat.id - client.unban_chat_member(chat_id, user.id) - edit( - message, get_translation( - 'unbanResult', [ - '**', user.first_name, user.id, '`'])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(pattern='^.kick', compat=False, private=False, admin=True) -def kick_user(client, message): - args = extract_args(message) - reply = message.reply_to_message - edit(message, f'`{get_translation("kickProcess")}`') - if args: - try: - user = client.get_users(args) - except Exception: - edit(message, f'`{get_translation("banFailUser")}`') - return - elif reply: - user_id = reply.from_user.id - user = client.get_users(user_id) - user_id = user.id - else: - edit(message, f'`{get_translation("banFailUser")}`') - return - - try: - replied_user = reply.from_user - if replied_user.is_self: - return edit(message, f'`{get_translation("cannotKickMyself")}`') - except BaseException: - pass - - if user.id in BRAIN: - return edit( - message, get_translation( - 'brainError', [ - '`', '**', user.first_name, user.id])) - - try: - chat_id = message.chat.id - client.kick_chat_member(chat_id, user.id) - client.unban_chat_member(chat_id, user.id) - edit( - message, get_translation( - 'kickResult', [ - '**', user.first_name, user.id, '`'])) - sleep(1) - send_log( - get_translation( - 'kickLog', [ - '**', user.first_name, user.id, message.chat.title, '`', message.chat.id])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(pattern='^.mute', compat=False, private=False, admin=True) -def mute_user(client, message): - args = extract_args(message) - reply = message.reply_to_message - edit(message, f'`{get_translation("muteProcess")}`') - if len(args): - try: - user = client.get_users(args) - except Exception: - edit(message, f'`{get_translation("banFailUser")}`') - return - elif reply: - user_id = reply.from_user.id - user = client.get_users(user_id) - user_id = user.id - else: - edit(message, f'`{get_translation("banFailUser")}`') - return - - try: - replied_user = reply.from_user - if replied_user.is_self: - return edit(message, f'`{get_translation("cannotMuteMyself")}`') - except BaseException: - pass - - if user.id in BRAIN: - return edit( - message, get_translation( - 'brainError', [ - '`', '**', user.first_name, user.id])) - - try: - chat_id = message.chat.id - if sql.is_muted(chat_id, user.id): - return - sql.mute(chat_id, user.id) - edit( - message, get_translation( - 'muteResult', [ - '**', user.first_name, user.id, '`'])) - sleep(1) - send_log( - get_translation( - 'muteLog', [ - '**', user.first_name, user.id, message.chat.title, '`', chat_id])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(pattern='^.unmute', compat=False, private=False, admin=True) -def unmute_user(client, message): - args = extract_args(message) - reply = message.reply_to_message - edit(message, f'`{get_translation("unmuteProcess")}`') - if args: - try: - user = client.get_users(args) - except Exception: - edit(message, f'`{get_translation("banFailUser")}`') - return - elif reply: - user_id = reply.from_user.id - user = client.get_users(user_id) - user_id = user.id - else: - edit(message, f'`{get_translation("banFailUser")}`') - return - - try: - replied_user = reply.from_user - if replied_user.is_self: - return edit(message, f'`{get_translation("cannotUnbanMyself")}`') - except BaseException: - pass - - try: - chat_id = message.chat.id - sql.unmute(chat_id, user.id) - client.unban_chat_member(chat_id, user.id) - edit( - message, get_translation( - 'unmuteResult', [ - '**', user.first_name, user.id, '`'])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(pattern='^.promote', admin=True, private=False, compat=False) -def promote_user(client, message): - args = extract_args(message) - reply = message.reply_to_message - rank = None - edit(message, f'`{get_translation("promoteProcess")}`') - if reply: - try: - user_id = reply.from_user.id - user = client.get_users(user_id) - user_id = user.id - rank = args - except Exception: - return edit(message, f'`{get_translation("banFailUser")}`') - elif ' ' not in args: - try: - user = client.get_users(args) - except Exception: - return edit(message, f'`{get_translation("banFailUser")}`') - elif args: - try: - arr = args.split(' ', 1) - user = client.get_users(arr[0]) - rank = arr[1] - except Exception: - return edit(message, f'`{get_translation("banFailUser")}`') - else: - return edit(message, f'`{get_translation("banFailUser")}`') - - try: - chat_id = message.chat.id - client.promote_chat_member(chat_id, user.id, - can_change_info=True, - can_delete_messages=True, - can_restrict_members=True, - can_invite_users=True, - can_pin_messages=True, - can_promote_members=True) - if rank is not None: - if len(rank) > 16: - rank = rank[:16] - client.set_administrator_title(chat_id, user.id, rank) - edit( - message, get_translation( - 'promoteResult', [ - '**', user.first_name, user.id, '`'])) - sleep(1) - send_log( - get_translation( - 'promoteLog', [ - '**', user.first_name, user.id, message.chat.title, '`', chat_id])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(pattern='^.demote', compat=False, private=False, admin=True) -def demote_user(client, message): - args = extract_args(message) - reply = message.reply_to_message - edit(message, f'`{get_translation("demoteProcess")}`') - if args: - try: - user = client.get_users(args) - except Exception: - edit(message, f'`{get_translation("banFailUser")}`') - return - elif reply: - user_id = reply.from_user.id - user = client.get_users(user_id) - user_id = user.id - else: - edit(message, f'`{get_translation("banFailUser")}`') - return - - try: - replied_user = reply.from_user - if replied_user.is_self: - return edit(message, f'`{get_translation("cannotDemoteMyself")}`') - except BaseException: - pass - - try: - chat_id = message.chat.id - client.promote_chat_member(chat_id, user.id, - can_change_info=False, - can_delete_messages=False, - can_restrict_members=False, - can_invite_users=False, - can_pin_messages=False, - can_promote_members=False) - edit( - message, get_translation( - 'demoteResult', [ - '**', user.first_name, user.id, '`'])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(pattern='^.pin$', compat=False, private=False, admin=True) -def pin_message(client, message): - reply = message.reply_to_message - - if not reply: - return edit(message, f'`{get_translation("wrongCommand")}`') - - try: - chat_id = message.chat.id - message_id = reply.message_id - client.pin_chat_message(chat_id, message_id) - edit(message, f'`{get_translation("pinResult")}`') - sleep(1) - send_log( - get_translation( - 'pinLog', [ - '**', message.chat.title, '`', chat_id])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(pattern='^.unpin$', compat=False, private=False, admin=True) -def unpin_message(client, message): - reply = message.reply_to_message - chat_id = message.chat.id - if reply: - try: - client.unpin_chat_message(chat_id, reply.message_id) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - else: - try: - client.unpin_all_chat_messages(chat_id) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - message.delete() - - -@sedenify(pattern='^.(admins|bots|user(s|sdel))$', compat=False, private=False) -def get_users(client, message): - args = message.text.split(' ', 1) - users = args[0][1:5] == 'user' - showdel = users and args[0][-3:] == 'del' - bots = not users and args[0][1:5] == 'bots' - admins = not bots and args[0][1:7] == 'admins' - - out = '' - if users: - out = get_translation( - 'userlist', - ['**', f'{get_translation("deleted") if showdel else ""}', '`', - message.chat.title]) - filtr = 'all' - elif admins: - out = get_translation('adminlist', ['**', '`', message.chat.title]) - filtr = 'administrators' - elif bots: - out = get_translation('botlist', ['**', '`', message.chat.title]) - filtr = 'bots' - - try: - chat_id = message.chat.id - find = client.iter_chat_members(chat_id, filter=filtr) - for i in find: - if not i.user.is_deleted and showdel: - continue - name = f'[{get_translation("deletedAcc") if i.user.is_deleted else i.user.first_name}](tg://user?id={i.user.id}) | `{i.user.id}`' - out += f'\n`•` **{name}**' - except Exception as e: - out += f'\n{get_translation("banError", ["`", "**", e])}' - - try: - edit(message, out) - except MessageTooLong: - edit(message, f'`{get_translation("outputTooLarge")}`') - file = open('userslist.txt', 'w+') - file.write(out) - file.close() - reply_doc( - message, 'userslist.txt', - caption=get_translation( - 'userlist', - ['**', f'{get_translation("deleted") if showdel else ""}', '`', - message.chat.title]), - delete_after_send=True, delete_orig=True) - - -@sedenify(incoming=True, outgoing=False, compat=False) -def mute_check(client, message): - muted = sql.is_muted(message.chat.id, message.from_user.id) - - if muted: - sleep(0.1) - message.delete() - - try: - user_id = message.from_user.id - chat_id = message.chat.id - client.restrict_chat_member(chat_id, user_id, ChatPermissions()) - except BaseException: - pass - - message.continue_propagation() - - -HELP.update({'admin': get_translation('adminInfo')}) diff --git a/sedenbot/modules/birakmamseni.py b/sedenbot/modules/birakmamseni.py deleted file mode 100644 index d3a1a75..0000000 --- a/sedenbot/modules/birakmamseni.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from requests import post -from sedenbot import HELP -from sedenecem.core import edit, sedenify, get_translation - - -@sedenify(pattern='^.b[ıi]rakmamseni$') -def birakmamseni(message): - '''Copyright (c) @Adem68 | 2020''' - url = 'https://birakmamseni.org/' - path = 'api/counter' - - headers = { - 'User-Agent': 'ajax/7.66.0', - 'Accept': '*/*', - 'Accept-Encoding': 'gzip, deflate, br', - 'Access-Control-Allow-Origin': '*', - 'Content-Type': 'application/x-www-form-urlencoded', - 'Origin': '{}'.format(url), - 'X-Requested-With': 'XMLHttpRequest', - } - - try: - response = post(url=url + path, headers=headers) - count = response.json()['counter'].lstrip('0') - except BaseException: - edit(message, f'`{get_translation("covidError")}`') - return - - sonuc = get_translation('birakmamseniResult', ['**', '`', count]) - - edit(message, sonuc, preview=False) - - -HELP.update({'birakmamseni': get_translation('birakmamseniInfo')}) diff --git a/sedenbot/modules/chat/afk.py b/sedenbot/modules/chat/afk.py new file mode 100644 index 0000000..fd30fcc --- /dev/null +++ b/sedenbot/modules/chat/afk.py @@ -0,0 +1,235 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from datetime import datetime +from random import choice, randint +from time import sleep + +from humanize import i18n, naturaltime +from pyrogram import ContinuePropagation, StopPropagation +from sedenbot import HELP, PM_AUTO_BAN, SEDEN_LANG, TEMP_SETTINGS +from sedenecem.core import ( + edit, + extract_args, + get_translation, + reply, + sedenify, + send_log, +) + +# ========================= CONSTANTS ============================ +AFKSTR = [get_translation(f'afkstr{i+1}') for i in range(0, 22)] +TEMP_SETTINGS['AFK_USERS'] = {} +TEMP_SETTINGS['IS_AFK'] = False +TEMP_SETTINGS['COUNT_MSG'] = 0 +TEMP_SETTINGS['AFK_TIME'] = {} + + +def get_time(now, then) -> str: + i18n.activate('tr_TR') if SEDEN_LANG == 'tr' else i18n.deactivate() + time = naturaltime(now - then) + return time + + +# ================================================================= + + +@sedenify( + incoming=True, + outgoing=False, + disable_edited=True, + private=False, + bot=False, + disable_notify=True, +) +def mention_afk(msg): + me = msg._client.me + mentioned = msg.mentioned + rep_m = msg.reply_to_message + if mentioned or rep_m and rep_m.from_user and rep_m.from_user.id == me.id: + if TEMP_SETTINGS['IS_AFK']: + afk_duration = get_time(datetime.now(), TEMP_SETTINGS['AFK_TIME']) + if msg.from_user.id not in TEMP_SETTINGS['AFK_USERS']: + if 'AFK_REASON' in TEMP_SETTINGS: + reply( + msg, + get_translation( + "afkMessage2", + [ + '**', + me.first_name, + me.id, + '`', + TEMP_SETTINGS['AFK_REASON'], + afk_duration, + ], + ), + ) + else: + reply(msg, f"```{choice(AFKSTR)}```") + TEMP_SETTINGS['AFK_USERS'].update({msg.from_user.id: 1}) + TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 + else: + if TEMP_SETTINGS['AFK_USERS'][msg.from_user.id] % randint(1, 2) == 0: + if 'AFK_REASON' in TEMP_SETTINGS: + reply( + msg, + get_translation( + "afkMessage2", + [ + '**', + me.first_name, + me.id, + '`', + TEMP_SETTINGS['AFK_REASON'], + afk_duration, + ], + ), + ) + else: + reply(msg, f"```{choice(AFKSTR)}```") + TEMP_SETTINGS['AFK_USERS'][msg.from_user.id] = ( + TEMP_SETTINGS['AFK_USERS'][msg.from_user.id] + 1 + ) + TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 + else: + TEMP_SETTINGS['AFK_USERS'][msg.from_user.id] = ( + TEMP_SETTINGS['AFK_USERS'][msg.from_user.id] + 1 + ) + TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 + raise ContinuePropagation + + +@sedenify( + incoming=True, + outgoing=False, + disable_edited=True, + group=False, + bot=False, + disable_notify=True, +) +def afk_on_pm(message): + if PM_AUTO_BAN: + try: + from sedenecem.sql.pm_permit_sql import is_approved + + apprv = is_approved(message.from_user.id) + except BaseException: + apprv = True + else: + apprv = True + + if apprv and TEMP_SETTINGS['IS_AFK']: + me = message._client.me + afk_duration = get_time(datetime.now(), TEMP_SETTINGS['AFK_TIME']) + if message.from_user.id not in TEMP_SETTINGS['AFK_USERS']: + if 'AFK_REASON' in TEMP_SETTINGS: + reply( + message, + get_translation( + "afkMessage2", + [ + '**', + me.first_name, + me.id, + '`', + TEMP_SETTINGS['AFK_REASON'], + afk_duration, + ], + ), + ) + else: + reply(message, f"```{choice(AFKSTR)}```") + TEMP_SETTINGS['AFK_USERS'].update({message.from_user.id: 1}) + TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 + else: + if TEMP_SETTINGS['AFK_USERS'][message.from_user.id] % randint(1, 2) == 0: + if 'AFK_REASON' in TEMP_SETTINGS: + reply( + message, + get_translation( + "afkMessage2", + [ + '**', + me.first_name, + me.id, + '`', + TEMP_SETTINGS['AFK_REASON'], + afk_duration, + ], + ), + ) + else: + reply(message, f"```{choice(AFKSTR)}```") + TEMP_SETTINGS['AFK_USERS'][message.from_user.id] = ( + TEMP_SETTINGS['AFK_USERS'][message.from_user.id] + 1 + ) + TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 + else: + TEMP_SETTINGS['AFK_USERS'][message.from_user.id] = ( + TEMP_SETTINGS['AFK_USERS'][message.from_user.id] + 1 + ) + TEMP_SETTINGS['COUNT_MSG'] = TEMP_SETTINGS['COUNT_MSG'] + 1 + raise ContinuePropagation + + +@sedenify(pattern='^.afk') +def set_afk(message): + args = extract_args(message) + if len(args) > 0: + TEMP_SETTINGS['AFK_REASON'] = args + edit( + message, + get_translation('afkStartReason', ['**', '`', TEMP_SETTINGS['AFK_REASON']]), + ) + else: + edit(message, f'**{get_translation("afkStart")}**') + send_log(get_translation('afkLog')) + TEMP_SETTINGS['IS_AFK'] = True + TEMP_SETTINGS['AFK_TIME'] = datetime.now() + raise StopPropagation + + +@sedenify() +def type_afk_is_not_true(message): + if TEMP_SETTINGS['IS_AFK']: + TEMP_SETTINGS['IS_AFK'] = False + reply(message, f'**{get_translation("afkEnd")}**') + sleep(2) + send_log( + get_translation( + 'afkMessages', + [ + '`', + '**', + str(len(TEMP_SETTINGS['AFK_USERS'])), + str(TEMP_SETTINGS['COUNT_MSG']), + ], + ) + ) + for i in TEMP_SETTINGS['AFK_USERS']: + name = message._client.get_chat(int(i)) + name0 = str(name.first_name) + userid = int(name.id) + reply(message, f'[{name0}](tg://user?id={userid})') + send_log( + get_translation( + 'afkMentionUsers', + ['**', name0, int(i), '`', str(TEMP_SETTINGS['AFK_USERS'][i])], + ) + ) + TEMP_SETTINGS['COUNT_MSG'] = 0 + TEMP_SETTINGS['AFK_USERS'] = {} + TEMP_SETTINGS['AFK_TIME'] = {} + if 'AFK_REASON' in TEMP_SETTINGS: + del TEMP_SETTINGS['AFK_REASON'] + raise ContinuePropagation + + +HELP.update({'afk': get_translation('afkInfo')}) diff --git a/sedenbot/modules/chat/ban.py b/sedenbot/modules/chat/ban.py new file mode 100644 index 0000000..1fb73bc --- /dev/null +++ b/sedenbot/modules/chat/ban.py @@ -0,0 +1,622 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from os import remove +from time import sleep + +from PIL import Image +from pyrogram import enums +from pyrogram.errors import ( + ImageProcessFailed, + MessageTooLong, + PhotoCropSizeSmall, + UserAdminInvalid, +) +from pyrogram.types import ChatPermissions, ChatPrivileges +from sedenbot import BRAIN, HELP +from sedenecem.core import ( + download_media_wc, + edit, + extract_args, + extract_args_split, + extract_user, + get_download_dir, + get_translation, + is_admin, + reply_doc, + sedenify, + send_log, +) + + +def get_reason(message): + args = extract_args(message) + reply = message.reply_to_message + if reply: + if args: + reason = get_translation('banReason', ['**', '`', args]) + else: + reason = '' + else: + text = args.split(' ', 1) + if len(text) > 1: + reason = get_translation('banReason', ['**', '`', text[1]]) + else: + reason = '' + pass + return reason + + +@sedenify(pattern='^.ban', private=False, admin=True) +def ban_user(message): + reply = message.reply_to_message + edit(message, f'`{get_translation("banProcess")}`') + + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + reason = get_reason(message) + + try: + replied_user = reply.from_user + if replied_user.is_self: + return edit(message, f'`{get_translation("cannotBanMyself")}`') + except BaseException: + pass + + for user in find_user: + if user.id in BRAIN: + return edit( + message, + get_translation('brainError', ['`', '**', user.first_name, user.id]), + ) + try: + chat = message.chat + chat.ban_member(user.id) + edit( + message, + get_translation( + 'banResult', + ['**', user.first_name, user.id, '`', reason if reason else ''], + ), + ) + sleep(1) + send_log( + get_translation( + 'banLog', + [ + user.first_name, + user.id, + chat.title, + '`', + chat.id, + reason if reason else '', + ], + ) + ) + except UserAdminInvalid: + edit(message, f'`{get_translation("banAdminError")}`') + except BaseException as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + + +@sedenify(pattern='^.unban', private=False, admin=True) +def unban_user(message): + args = extract_args_split(message) + if len(args) > 1: + return edit(message, f'`{get_translation("wrongCommand")}`') + reply = message.reply_to_message + edit(message, f'`{get_translation("unbanProcess")}`') + + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + try: + replied_user = reply.from_user + if replied_user.is_self: + return edit(message, f'`{get_translation("cannotUnbanMyself")}`') + except BaseException: + pass + + for user in find_user: + try: + chat = message.chat + chat.unban_member(user.id) + edit( + message, + get_translation('unbanResult', ['**', user.first_name, user.id, '`']), + ) + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + + +@sedenify(pattern='^.kick', private=False, admin=True) +def kick_user(message): + reply = message.reply_to_message + edit(message, f'`{get_translation("kickProcess")}`') + + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + reason = get_reason(message) + + try: + replied_user = reply.from_user + if replied_user.is_self: + return edit(message, f'`{get_translation("cannotKickMyself")}`') + except BaseException: + pass + + for user in find_user: + if user.id in BRAIN: + return edit( + message, + get_translation('brainError', ['`', '**', user.first_name, user.id]), + ) + try: + chat = message.chat + chat.ban_member(user.id) + chat.unban_member(user.id) + edit( + message, + get_translation( + 'kickResult', + ['**', user.first_name, user.id, '`', reason if reason else ''], + ), + ) + sleep(1) + send_log( + get_translation( + 'kickLog', + [ + user.first_name, + user.id, + chat.title, + '`', + chat.id, + reason if reason else '', + ], + ) + ) + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + + +@sedenify(pattern='^.mute', private=False, admin=True) +def mute_user(message): + try: + from sedenecem.sql import mute_sql as sql + except BaseException: + edit(message, f'`{get_translation("nonSqlMode")}`') + return + + reply = message.reply_to_message + edit(message, f'`{get_translation("muteProcess")}`') + + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + reason = get_reason(message) + + try: + replied_user = reply.from_user + if replied_user.is_self: + return edit(message, f'`{get_translation("cannotMuteMyself")}`') + except BaseException: + pass + + for user in find_user: + if user.id in BRAIN: + return edit( + message, + get_translation('brainError', ['`', '**', user.first_name, user.id]), + ) + try: + chat = message.chat + if sql.is_muted(chat.id, user.id): + return edit(message, 'kullanici zaten susturulmus') + sql.mute(chat.id, user.id) + edit( + message, + get_translation( + 'muteResult', + ['**', user.first_name, user.id, '`', reason if reason else ''], + ), + ) + sleep(1) + send_log( + get_translation( + 'muteLog', + [ + user.first_name, + user.id, + chat.title, + '`', + chat.id, + reason if reason else '', + ], + ) + ) + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + + +@sedenify(pattern='^.unmute', private=False, admin=True) +def unmute_user(message): + try: + from sedenecem.sql import mute_sql as sql + except BaseException: + edit(message, f'`{get_translation("nonSqlMode")}`') + return + + args = extract_args_split(message) + if len(args) > 1: + return edit(message, f'`{get_translation("wrongCommand")}`') + reply = message.reply_to_message + edit(message, f'`{get_translation("unmuteProcess")}`') + + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + try: + replied_user = reply.from_user + if replied_user.is_self: + return edit(message, f'`{get_translation("cannotMuteMyself")}`') + except BaseException: + pass + + for user in find_user: + try: + chat = message.chat + sql.unmute(chat.id, user.id) + chat.unban_member(user.id) + edit( + message, + get_translation('unmuteResult', ['**', user.first_name, user.id, '`']), + ) + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + + +@sedenify(pattern='^.promote', admin=True, private=False) +def promote_user(message): + args = extract_args(message) + reply = message.reply_to_message + rank = None + edit(message, f'`{get_translation("promoteProcess")}`') + + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + if reply: + if args: + rank = args + else: + rank = '' + else: + text = args.split(' ', 1) + if len(text) > 1: + rank = text[1] + else: + rank = '' + + for user in find_user: + try: + chat = message.chat + chat.promote_member( + user.id, + privileges=ChatPrivileges( + can_manage_chat=True, + can_delete_messages=True, + can_manage_video_chats=True, + can_restrict_members=True, + can_change_info=True, + can_invite_users=True, + can_pin_messages=True, + can_promote_members=True, + ), + ) + if rank is not None: + if len(rank) > 16: + rank = rank[:16] + message._client.set_administrator_title(chat.id, user.id, rank) + edit( + message, + get_translation('promoteResult', ['**', user.first_name, user.id, '`']), + ) + sleep(1) + send_log( + get_translation( + 'promoteLog', + [user.first_name, user.id, chat.title, '`', chat.id], + ) + ) + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + + +@sedenify(pattern='^.demote', private=False, admin=True) +def demote_user(message): + args = extract_args_split(message) + if len(args) > 1: + return edit(message, f'`{get_translation("wrongCommand")}`') + reply = message.reply_to_message + edit(message, f'`{get_translation("demoteProcess")}`') + + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + try: + replied_user = reply.from_user + if replied_user.is_self: + return edit(message, f'`{get_translation("cannotDemoteMyself")}`') + except BaseException: + pass + + for user in find_user: + try: + chat = message.chat + chat.promote_member( + user.id, + privileges=ChatPrivileges( + can_manage_chat=False, + can_delete_messages=False, + can_manage_video_chats=False, + can_restrict_members=False, + can_change_info=False, + can_invite_users=False, + can_pin_messages=False, + can_promote_members=False, + ), + ) + edit( + message, + get_translation('demoteResult', ['**', user.first_name, user.id, '`']), + ) + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + + +@sedenify(pattern='^.pin', private=False, admin=True) +def pin_message(message): + args = extract_args(message).lower() + reply = message.reply_to_message + + if reply: + try: + chat = message.chat + if args == 'loud': + reply.pin(disable_notification=False) + else: + reply.pin(disable_notification=True) + edit(message, f'`{get_translation("pinResult")}`') + sleep(1) + send_log(get_translation('pinLog', [message.chat.title, '`', chat.id])) + except Exception as e: + return edit(message, get_translation('banError', ['`', '**', e])) + else: + edit(message, f'`{get_translation("wrongCommand")}`') + + +@sedenify(pattern='^.unpin', private=False, admin=True) +def unpin_message(message): + args = extract_args(message).lower() + reply = message.reply_to_message + chat = message.chat + if reply: + try: + reply.unpin() + message.delete() + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + elif 'all' in args: + try: + chat.unpin_all_messages() + message.delete() + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + else: + edit(message, f'`{get_translation("wrongCommand")}`') + + +@sedenify(pattern='^.(admins|bots|user(s|sdel))$', private=False) +def get_users(message): + args = message.text.split(' ', 1) + users = args[0][1:5] == 'user' + showdel = users and args[0][-3:] == 'del' + bots = not users and args[0][1:5] == 'bots' + admins = not bots and args[0][1:7] == 'admins' + + out = '' + if users: + out = get_translation( + 'userlist', + [ + '**', + f'{get_translation("deleted") if showdel else ""}', + '`', + message.chat.title, + ], + ) + filtr = enums.ChatMembersFilter.SEARCH + elif admins: + out = get_translation('adminlist', ['**', '`', message.chat.title]) + filtr = enums.ChatMembersFilter.ADMINISTRATORS + elif bots: + out = get_translation('botlist', ['**', '`', message.chat.title]) + filtr = enums.ChatMembersFilter.BOTS + + try: + chat_id = message.chat.id + find = message._client.get_chat_members(chat_id, filter=filtr) + for i in find: + if not i.user.is_deleted and showdel: + continue + name = f'[{get_translation("deletedAcc") if i.user.is_deleted else i.user.first_name}](tg://user?id={i.user.id}) | `{i.user.id}`' + out += f'\n`•` **{name}**' + except Exception as e: + out += f'\n{get_translation("banError", ["`", "**", e])}' + + try: + edit(message, out) + except MessageTooLong: + edit(message, f'`{get_translation("outputTooLarge")}`') + file = open('userslist.txt', 'w+') + file.write(out) + file.close() + reply_doc( + message, + 'userslist.txt', + caption=get_translation( + 'userlist', + [ + '**', + f'{get_translation("deleted") if showdel else ""}', + '`', + message.chat.title, + ], + ), + delete_after_send=True, + delete_orig=True, + ) + + +@sedenify(pattern='^.zombies', private=False) +def zombie_accounts(message): + args = extract_args(message).lower() + chat = message.chat + count = 0 + msg = f'`{get_translation("zombiesNoAccount")}`' + + if args != 'clean': + edit(message, f'`{get_translation("zombiesFind")}`') + for i in message._client.get_chat_members(chat.id): + if i.user.is_deleted: + count += 1 + sleep(1) + if count > 0: + msg = get_translation('zombiesFound', ['**', '`', count]) + return edit(message, msg) + + if not is_admin(message): + edit(message, f'`{get_translation("adminUsage")}`') + return message.continue_propagation() + + edit(message, f'`{get_translation("zombiesRemove")}`') + count = 0 + users = 0 + + for i in message._client.get_chat_members(chat.id): + if i.user.is_deleted: + try: + chat.ban_member(i.user.id) + except UserAdminInvalid: + count -= 1 + users += 1 + except BaseException: + return edit(message, f'`{get_translation("zombiesError")}`') + chat.unban_member(i.user.id) + count += 1 + + if count > 0: + msg = get_translation('zombiesResult', ['**', '`', count]) + + if users > 0: + msg = get_translation('zombiesResult2', ['**', '`', count, users]) + + edit(message, msg) + sleep(2) + message.delete() + + send_log(get_translation('zombiesLog', ['**', '`', count, chat.title, chat.id])) + + +@sedenify(pattern='^.setgpic$', admin=True, private=False) +def set_group_photo(message): + reply = message.reply_to_message + photo = None + if ( + reply + and reply.media + and ( + reply.photo + or (reply.sticker and not reply.sticker.is_animated) + or (reply.document and 'image' in reply.document.mime_type) + ) + ): + photo = download_media_wc(reply, 'group_photo.jpg') + else: + edit(message, f'{get_translation("mediaInvalid")}`') + return + + if photo: + image = Image.open(photo) + width, height = image.size + maxSize = (640, 640) + ratio = min(maxSize[0] / width, maxSize[1] / height) + image = image.resize((int(width * ratio), int(height * ratio))) + new_photo = f'{get_download_dir()}/group_photo_new.png' + image.save(new_photo) + try: + chat = message.chat + chat.set_photo(photo=new_photo) + remove(photo) + remove(new_photo) + edit(message, f'`{get_translation("groupPicChanged")}`') + except PhotoCropSizeSmall: + edit(message, f'`{get_translation("ppSmall")}`') + except ImageProcessFailed: + edit(message, f'`{get_translation("ppError")}`') + except BaseException as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + else: + edit(message, f'`{get_translation("ppError")}`') + + +@sedenify(incoming=True, outgoing=False) +def mute_check(message): + try: + from sedenecem.sql import mute_sql as sql + except BaseException: + message.continue_propagation() + + chat = message.chat + user = message.from_user + muted = sql.is_muted(chat.id, user.id) + + if muted: + sleep(0.1) + message.delete() + + try: + chat.restrict_member(user.id, permissions=ChatPermissions()) + except BaseException: + pass + + message.continue_propagation() + + +HELP.update({'admin': get_translation('adminInfo')}) diff --git a/sedenbot/modules/blacklist.py b/sedenbot/modules/chat/blacklist.py similarity index 80% rename from sedenbot/modules/blacklist.py rename to sedenbot/modules/chat/blacklist.py index 17023f5..e39a40f 100644 --- a/sedenbot/modules/blacklist.py +++ b/sedenbot/modules/chat/blacklist.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,17 +8,25 @@ # from io import BytesIO -from re import escape, search, IGNORECASE +from re import IGNORECASE, escape, search from sedenbot import HELP, LOGS -from sedenecem.core import (edit, reply, send_log, reply_doc, - extract_args, sedenify, get_translation) +from sedenecem.core import ( + edit, + extract_args, + get_translation, + reply, + reply_doc, + sedenify, + send_log, +) def blacklist_init(): try: global sql from importlib import import_module + sql = import_module('sedenecem.sql.blacklist_sql') except Exception as e: sql = None @@ -57,8 +65,7 @@ def blacklist(message): message.delete() msg_removed = True except Exception: - reply( - message, f'`{get_translation("blacklistPermission")}`') + reply(message, f'`{get_translation("blacklistPermission")}`') sql.rm_from_blacklist(message.chat.id, snip.lower()) break @@ -66,8 +73,8 @@ def blacklist(message): message.continue_propagation() -@sedenify(pattern='^.addblacklist', compat=False) -def addblacklist(client, message): +@sedenify(pattern='^.addblacklist') +def addblacklist(message): if not sql: edit(message, f'`{get_translation("nonSqlMode")}`') return @@ -75,8 +82,9 @@ def addblacklist(client, message): if len(text) < 1: edit(message, f'`{get_translation("blacklistText")}`') return - to_blacklist = list(set(trigger.strip() - for trigger in text.split("\n") if trigger.strip())) + to_blacklist = list( + set(trigger.strip() for trigger in text.split("\n") if trigger.strip()) + ) for trigger in to_blacklist: sql.add_to_blacklist(message.chat.id, trigger.lower()) edit(message, get_translation('blacklistAddSuccess', ['**', '`', text])) @@ -99,8 +107,9 @@ def showblacklist(message): if len(OUT_STR) > 4096: with BytesIO(str.encode(OUT_STR)) as out_file: out_file.name = 'blacklist.text' - reply_doc(message, out_file, - caption=f'**{get_translation("blacklistChats")}**') + reply_doc( + message, out_file, caption=f'**{get_translation("blacklistChats")}**' + ) message.delete() else: edit(message, OUT_STR) @@ -115,8 +124,9 @@ def rmblacklist(message): if len(text) < 1: edit(message, f'`{get_translation("blacklistText")}`') return - to_unblacklist = list(set(trigger.strip() - for trigger in text.split("\n") if trigger.strip())) + to_unblacklist = list( + set(trigger.strip() for trigger in text.split("\n") if trigger.strip()) + ) successful = 0 for trigger in to_unblacklist: if sql.rm_from_blacklist(message.chat.id, trigger.lower()): diff --git a/sedenbot/modules/chat.py b/sedenbot/modules/chat/chat.py similarity index 85% rename from sedenbot/modules/chat.py rename to sedenbot/modules/chat/chat.py index 7f598be..2212510 100644 --- a/sedenbot/modules/chat.py +++ b/sedenbot/modules/chat/chat.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,15 +8,16 @@ # from time import sleep + from sedenbot import HELP, LOGS -from sedenecem.core import (edit, sedenify, extract_args, - send_log, get_translation) +from sedenecem.core import edit, extract_args, get_translation, sedenify, send_log def chat_init(): try: global sql from importlib import import_module + sql = import_module('sedenecem.sql.keep_read_sql') except Exception as e: sql = None @@ -61,18 +62,18 @@ def mutechat(message): send_log(get_translation('chatLog', [message.chat.id])) -@sedenify(incoming=True, compat=False) -def keep_read(client, message): +@sedenify(incoming=True, outgoing=False) +def keep_read(message): if message.from_user and message.from_user.is_self: message.continue_propagation() try: from sedenecem.sql.keep_read_sql import is_kread except BaseException: - return + message.continue_propagation() if is_muted(message.chat.id): - client.read_history(message.chat.id) + message._client.read_chat_history(message.chat.id) message.continue_propagation() @@ -80,8 +81,8 @@ def keep_read(client, message): @sedenify(pattern='^.call') def call_notes(message): try: - from sedenbot.modules.notes import get_note - from sedenbot.modules.snips import get_snip + from sedenbot.modules.chat.notes import get_note + from sedenbot.modules.chat.snips import get_snip except BaseException: edit(message, f'`{get_translation("nonSqlMode")}`') return diff --git a/sedenbot/modules/filters.py b/sedenbot/modules/chat/filters.py similarity index 79% rename from sedenbot/modules/filters.py rename to sedenbot/modules/chat/filters.py index 0f7f4e2..11f48d9 100644 --- a/sedenbot/modules/filters.py +++ b/sedenbot/modules/chat/filters.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,18 +7,27 @@ # All rights reserved. See COPYING, AUTHORS. # -from re import fullmatch, IGNORECASE +from re import IGNORECASE, fullmatch -from sedenbot import HELP, LOGS, LOG_ID -from sedenecem.core import (extract_args, sedenify, edit, get_messages, - reply_msg, reply, forward, send_log, - get_translation) +from sedenbot import HELP, LOG_ID, LOGS +from sedenecem.core import ( + edit, + extract_args, + forward, + get_messages, + get_translation, + reply, + reply_msg, + sedenify, + send_log, +) def filters_init(): try: global sql from importlib import import_module + sql = import_module('sedenecem.sql.filters_sql') except Exception as e: sql = None @@ -93,12 +102,10 @@ def add_filter(message): string = None msg_o = forward(msg, LOG_ID) if not msg_o: - edit( - message, f'`{get_translation("filterError")}`') + edit(message, f'`{get_translation("filterError")}`') return - msg_id = msg_o.message_id - send_log(get_translation( - 'filterLog', ['`', message.chat.id, keyword])) + msg_id = msg_o.id + send_log(get_translation('filterLog', ['`', message.chat.id, keyword])) else: edit(message, f'`{get_translation("wrongCommand")}`') @@ -122,6 +129,20 @@ def stop_filter(message): edit(message, get_translation('filterRemoved', ['**', '`', filt])) +@sedenify(pattern='^.stopall$') +def stop_filter_all(message): + try: + from sedenecem.sql.filters_sql import get_filters, remove_filter + except BaseException: + edit(message, f'`{get_translation("nonSqlMode")}`') + return + filters = get_filters(message.chat.id) + for filt in filters: + remove_filter(message.chat.id, filt.keyword) + filtwords = [i.keyword for i in filters] + edit(message, get_translation('filterRemoved', ['**', '`', ', '.join(filtwords)])) + + @sedenify(pattern='^.filters$') def filters(message): try: diff --git a/sedenbot/modules/chat/globals.py b/sedenbot/modules/chat/globals.py new file mode 100644 index 0000000..1146ab7 --- /dev/null +++ b/sedenbot/modules/chat/globals.py @@ -0,0 +1,314 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from time import sleep + +from pyrogram import enums +from pyrogram.types import ChatPermissions +from sedenbot import BRAIN, HELP, LOGS +from sedenbot.modules.chat.ban import get_reason +from sedenecem.core import ( + edit, + extract_args_split, + extract_user, + get_translation, + sedenify, + send_log, +) + + +def globals_init(): + try: + global sql, sql2 + from importlib import import_module + + sql = import_module('sedenecem.sql.gban_sql') + sql2 = import_module('sedenecem.sql.gmute_sql') + except Exception as e: + sql = None + sql2 = None + LOGS.warn(get_translation('globalsSqlLog')) + raise e + + +globals_init() + + +@sedenify(pattern='^.gban') +def gban_user(message): + reply = message.reply_to_message + edit(message, f'`{get_translation("banProcess")}`') + + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + reason = get_reason(message) + + try: + replied_user = reply.from_user + if replied_user.is_self: + return edit(message, f'`{get_translation("cannotBanMyself")}`') + except BaseException: + pass + + for user in find_user: + if user.id in BRAIN: + return edit( + message, + get_translation('brainError', ['`', '**', user.first_name, user.id]), + ) + try: + if sql.is_gbanned(user.id): + return edit(message, f'`{get_translation("alreadyBanned")}`') + sql.gban(user.id) + edit( + message, + get_translation( + 'gbanResult', + ['**', user.first_name, user.id, '`', reason if reason else ''], + ), + ) + try: + common_chats = message._client.get_common_chats(user.id) + for i in common_chats: + i.ban_member(user.id) + except BaseException: + pass + sleep(1) + send_log( + get_translation( + 'gbanLog', [user.first_name, user.id, '`', reason if reason else ''] + ) + ) + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + + +@sedenify(pattern='^.(ung|gun)ban') +def ungban_user(message): + args = extract_args_split(message) + if len(args) > 1: + return edit(message, f'`{get_translation("wrongCommand")}`') + reply = message.reply_to_message + edit(message, f'`{get_translation("unbanProcess")}`') + + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + try: + replied_user = reply.from_user + if replied_user.is_self: + return edit(message, f'`{get_translation("cannotUnbanMyself")}`') + except BaseException: + pass + + for user in find_user: + try: + if not sql.is_gbanned(user.id): + return edit(message, f'`{get_translation("alreadyUnbanned")}`') + sql.ungban(user.id) + + def find_me(dialog): + try: + return ( + dialog.chat.get_member(me_id).privileges + and dialog.chat.get_member( + me_id + ).privileges.can_restrict_members + ) + except BaseException: + return False + + def find_member(dialog): + try: + return ( + dialog.chat.get_member(user.id) + and dialog.chat.get_member(user.id).restricted_by + and dialog.chat.get_member(user.id).restricted_by.id == me_id + ) + except BaseException: + return False + + try: + dialogs = message._client.get_dialogs() + me_id = message._client.me.id + chats = [ + dialog.chat + for dialog in dialogs + if ( + dialog.chat.type + in [enums.ChatType.SUPERGROUP, enums.ChatType.GROUP] + and find_me(dialog) + and find_member(dialog) + ) + ] + for chat in chats: + chat.unban_member(user.id) + except BaseException: + pass + edit( + message, + get_translation('unbanResult', ['**', user.first_name, user.id, '`']), + ) + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + + +@sedenify(pattern='^.listgban$') +def gbanlist(message): + users = sql.gbanned_users() + if not users: + return edit(message, f'`{get_translation("listEmpty")}`') + gban_list = f'**{get_translation("gbannedUsers")}**\n' + count = 0 + for i in users: + count += 1 + gban_list += f'**{count} -** `{i.sender}`\n' + return edit(message, gban_list) + + +@sedenify(incoming=True, outgoing=False) +def gban_check(message): + user = message.from_user + if sql.is_gbanned(user.id): + try: + chat = message.chat + chat.ban_member(user.id) + except BaseException: + pass + + message.continue_propagation() + + +@sedenify(pattern='^.gmute') +def gmute_user(message): + reply = message.reply_to_message + edit(message, f'`{get_translation("muteProcess")}`') + + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + reason = get_reason(message) + + try: + replied_user = reply.from_user + if replied_user.is_self: + return edit(message, f'`{get_translation("cannotMuteMyself")}`') + except BaseException: + pass + + for user in find_user: + if user.id in BRAIN: + return edit( + message, + get_translation('brainError', ['`', '**', user.first_name, user.id]), + ) + try: + if sql2.is_gmuted(user.id): + return edit(message, f'`{get_translation("alreadyMuted")}`') + sql2.gmute(user.id) + edit( + message, + get_translation( + 'gmuteResult', + ['**', user.first_name, user.id, '`', reason if reason else ''], + ), + ) + try: + common_chats = message._client.get_common_chats(user.id) + for i in common_chats: + i.restrict_member(user.id, permissions=ChatPermissions()) + except BaseException: + pass + sleep(1) + send_log( + get_translation( + 'gmuteLog', + [user.first_name, user.id, '`', reason if reason else ''], + ) + ) + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + + +@sedenify(pattern='^.(ung|gun)mute') +def ungmute_user(message): + args = extract_args_split(message) + if len(args) > 1: + return edit(message, f'`{get_translation("wrongCommand")}`') + reply = message.reply_to_message + edit(message, f'`{get_translation("unmuteProcess")}`') + + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + try: + replied_user = reply.from_user + if replied_user.is_self: + return edit(message, f'`{get_translation("cannotUnmuteMyself")}`') + except BaseException: + pass + + for user in find_user: + try: + if not sql2.is_gmuted(user.id): + return edit(message, f'`{get_translation("alreadyUnmuted")}`') + sql2.ungmute(user.id) + try: + common_chats = message._client.get_common_chats(user.id) + for i in common_chats: + i.unban_member(user.id) + except BaseException: + pass + edit( + message, + get_translation('unmuteResult', ['**', user.first_name, user.id, '`']), + ) + except Exception as e: + edit(message, get_translation('banError', ['`', '**', e])) + return + + +@sedenify(pattern='^.listgmute$') +def gmutelist(message): + users = sql2.gmuted_users() + if not users: + return edit(message, f'`{get_translation("listEmpty")}`') + gmute_list = f'**{get_translation("gmutedUsers")}**\n' + count = 0 + for i in users: + count += 1 + gmute_list += f'**{count} -** `{i.sender}`\n' + return edit(message, gmute_list) + + +@sedenify(incoming=True, outgoing=False) +def gmute_check(message): + user = message.from_user + if sql2.is_gmuted(user.id): + sleep(0.1) + message.delete() + + try: + chat = message.chat + chat.restrict_member(user.id, permissions=ChatPermissions()) + except BaseException: + pass + + message.continue_propagation() + + +HELP.update({'globals': get_translation('globalsInfo')}) diff --git a/sedenbot/modules/chat/locks.py b/sedenbot/modules/chat/locks.py new file mode 100644 index 0000000..d720fab --- /dev/null +++ b/sedenbot/modules/chat/locks.py @@ -0,0 +1,122 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from pyrogram.types import ChatPermissions +from sedenbot import HELP +from sedenecem.core import edit, get_translation, parse_cmd, sedenify + + +@sedenify(pattern='^.(un|)lock', private=False, admin=True) +def lock_unlock_chat(message): + text = (message.text or message.caption).replace(r'\s+', ' ').split(' ', 1) + + unlock = parse_cmd(text[0])[:2] == 'un' + if len(text) < 2: + edit(message, f"`{get_translation('wrongCommand')}`") + return + + args = text[1].lower() + + msg = None + media = None + other = None + webprev = None + gpoll = None + adduser = None + cpin = None + changeinfo = None + if args == 'msg': + msg = unlock + usage = get_translation('lockMsg') + elif args == 'media': + media = unlock + usage = get_translation('lockMedia') + elif args == 'other': + other = unlock + usage = get_translation('lockOther') + elif args == 'web': + webprev = unlock + usage = get_translation('lockWeb') + elif args == 'poll': + gpoll = unlock + usage = get_translation('lockPoll') + elif args == 'invite': + adduser = unlock + usage = get_translation('lockInvite') + elif args == 'pin': + cpin = unlock + usage = get_translation('lockPin') + elif args == 'info': + changeinfo = unlock + usage = get_translation('lockInformation') + elif args == 'all': + msg = unlock + media = unlock + other = unlock + webprev = unlock + gpoll = unlock + adduser = unlock + cpin = unlock + changeinfo = unlock + usage = get_translation('lockAll') + else: + if not args: + edit( + message, + get_translation('locksUnlockNoArgs' if usage else 'locksLockNoArgs'), + ) + return + else: + edit(message, get_translation('lockError', ['`', args])) + return + + chat = message._client.get_chat(message.chat.id) + + msg = get_on_none(msg, chat.permissions.can_send_messages) + media = get_on_none(media, chat.permissions.can_send_media_messages) + other = get_on_none(other, chat.permissions.can_send_other_messages) + webprev = get_on_none(webprev, chat.permissions.can_add_web_page_previews) + gpoll = get_on_none(gpoll, chat.permissions.can_send_polls) + adduser = get_on_none(adduser, chat.permissions.can_invite_users) + cpin = get_on_none(cpin, chat.permissions.can_pin_messages) + changeinfo = get_on_none(changeinfo, chat.permissions.can_change_info) + + try: + message._client.set_chat_permissions( + message.chat.id, + ChatPermissions( + can_send_messages=msg, + can_send_media_messages=media, + can_send_other_messages=other, + can_add_web_page_previews=webprev, + can_send_polls=gpoll, + can_change_info=changeinfo, + can_invite_users=adduser, + can_pin_messages=cpin, + ), + ) + edit( + message, + get_translation( + 'locksUnlockSuccess' if unlock else 'locksLockSuccess', ['`', usage] + ), + ) + except BaseException as e: + edit(message, get_translation('lockPerm', ['`', '**', str(e)])) + return + + +def get_on_none(item, defval): + if item is None: + return defval + + return item + + +HELP.update({'locks': get_translation('lockInfo')}) diff --git a/sedenbot/modules/notes.py b/sedenbot/modules/chat/notes.py similarity index 88% rename from sedenbot/modules/notes.py rename to sedenbot/modules/chat/notes.py index 8228974..56371f9 100644 --- a/sedenbot/modules/notes.py +++ b/sedenbot/modules/chat/notes.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,15 +7,24 @@ # All rights reserved. See COPYING, AUTHORS. # -from sedenbot import HELP, LOGS, LOG_ID -from sedenecem.core import (extract_args, sedenify, edit, get_messages, - reply_msg, forward, send_log, get_translation) +from sedenbot import HELP, LOG_ID, LOGS +from sedenecem.core import ( + edit, + extract_args, + forward, + get_messages, + get_translation, + reply_msg, + sedenify, + send_log, +) def notes_init(): try: global sql from importlib import import_module + sql = import_module('sedenecem.sql.notes_sql') except Exception as e: sql = None @@ -68,12 +77,10 @@ def save_note(message): string = None msg_o = forward(msg, LOG_ID) if not msg_o: - edit( - message, f'`{get_translation("noteError")}`') + edit(message, f'`{get_translation("noteError")}`') return - msg_id = msg_o.message_id - send_log(get_translation( - 'notesLog', ['`', message.chat.id, keyword])) + msg_id = msg_o.id + send_log(get_translation('notesLog', ['`', message.chat.id, keyword])) else: edit(message, f'`{get_translation("wrongCommand")}`') diff --git a/sedenbot/modules/chat/pmpermit.py b/sedenbot/modules/chat/pmpermit.py new file mode 100644 index 0000000..269721f --- /dev/null +++ b/sedenbot/modules/chat/pmpermit.py @@ -0,0 +1,247 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from pyrogram import enums +from pyrogram.raw.functions.messages import ReportSpam +from pyrogram.raw.types import InputPeerUser +from sedenbot import HELP, LOGS, PM_AUTO_BAN, PM_MSG_COUNT, PM_UNAPPROVED, TEMP_SETTINGS +from sedenbot.modules.chat.chat import is_muted +from sedenecem.core import edit, get_translation, reply, sedenify, send_log +from sqlalchemy.exc import IntegrityError + +# ========================= CONSTANTS ============================ +UNAPPROVED_MSG = PM_UNAPPROVED or get_translation('pmpermitMessage', ['`']) +# ================================================================= + + +def pmpermit_init(): + try: + global sql + from importlib import import_module + + sql = import_module('sedenecem.sql.pm_permit_sql') + except Exception as e: + sql = None + LOGS.warn(get_translation('pmpermitSqlLog')) + raise e + + +pmpermit_init() + + +@sedenify( + incoming=True, + outgoing=True, + disable_edited=True, + disable_notify=True, + group=False, + bot=False, +) +def permitpm(message): + if not PM_AUTO_BAN: + message.continue_propagation() + + if auto_accept(message) or message.from_user.is_self: + message.continue_propagation() + + if message.chat.id != 777000: + try: + from sedenecem.sql.pm_permit_sql import is_approved + except BaseException: + message.continue_propagation() + + apprv = is_approved(message.chat.id) + notifsoff = is_muted(-1) + + if not apprv and message.text != UNAPPROVED_MSG: + if message.chat.id in TEMP_SETTINGS['PM_LAST_MSG']: + prevmsg = TEMP_SETTINGS['PM_LAST_MSG'][message.chat.id] + if message.text != prevmsg: + for i in _find_unapproved_msg(message, message.chat.id): + i.delete() + if TEMP_SETTINGS['PM_COUNT'][message.chat.id] < (PM_MSG_COUNT - 1): + ret = reply(message, UNAPPROVED_MSG) + TEMP_SETTINGS['PM_LAST_MSG'][message.chat.id] = ret.text + else: + ret = reply(message, UNAPPROVED_MSG) + if ret.text: + TEMP_SETTINGS['PM_LAST_MSG'][message.chat.id] = ret.text + + if notifsoff: + message._client.read_chat_history(message.chat.id) + + if message.chat.id not in TEMP_SETTINGS['PM_COUNT']: + TEMP_SETTINGS['PM_COUNT'][message.chat.id] = 1 + else: + TEMP_SETTINGS['PM_COUNT'][message.chat.id] = ( + TEMP_SETTINGS['PM_COUNT'][message.chat.id] + 1 + ) + + if TEMP_SETTINGS['PM_COUNT'][message.chat.id] > (PM_MSG_COUNT - 1): + reply(message, f'`{get_translation("pmpermitBlock")}`') + + try: + del TEMP_SETTINGS['PM_COUNT'][message.chat.id] + del TEMP_SETTINGS['PM_LAST_MSG'][message.chat.id] + except BaseException: + pass + + message._client.block_user(message.chat.id) + peer: InputPeerUser = message._client.resolve_peer(message.chat.id) + message._client.invoke(ReportSpam(peer=peer)) + + send_log( + get_translation( + 'pmpermitLog', [message.chat.first_name, message.chat.id] + ) + ) + + message.continue_propagation() + + +def auto_accept(message): + self_user = message._client.me + if message.chat.id not in [self_user.id, 777000]: + try: + from sedenecem.sql.pm_permit_sql import approve, is_approved + except BaseException: + return False + + chat = message.chat + if is_approved(chat.id): + return True + + for msg in message._client.get_chat_history(chat.id, limit=1): + # chat.id in TEMP_SETTINGS['PM_LAST_MSG'] + # and msg.text != UNAPPROVED_MSG + # and + + if msg.from_user.id == self_user.id: + try: + del TEMP_SETTINGS['PM_COUNT'][chat.id] + del TEMP_SETTINGS['PM_LAST_MSG'][chat.id] + except BaseException: + pass + + try: + approve(chat.id) + for i in _find_unapproved_msg(message, chat.id): + i.delete() + send_log( + get_translation('pmAutoAccept', [chat.first_name, chat.id]) + ) + return True + except BaseException: + pass + + return False + + +@sedenify(outgoing=True, pattern='^.notifoff$') +def notifoff(message): + try: + from sedenecem.sql.keep_read_sql import kread + except BaseException: + edit(message, f'`{get_translation("nonSqlMode")}`') + return + + kread(str(-1)) + edit(message, f'`{get_translation("pmNotifOff")}`') + + +@sedenify(outgoing=True, pattern='^.notifon$') +def notifon(message): + try: + from sedenecem.sql.keep_read_sql import unkread + except BaseException: + edit(message, f'`{get_translation("nonSqlMode")}`') + return + + unkread(str(-1)) + edit(message, f'`{get_translation("pmNotifOn")}`') + + +@sedenify(outgoing=True, pattern='^.approve$') +def approvepm(message): + try: + from sedenecem.sql.pm_permit_sql import approve + except BaseException: + edit(message, f'`{get_translation("nonSqlMode")}`') + return + + if message.reply_to_message: + reply = message.reply_to_message + replied_user = reply.from_user + if replied_user.is_self: + edit(message, f'`{get_translation("cannotApproveMyself")}`') + return + aname = replied_user.id + name0 = str(replied_user.first_name) + uid = replied_user.id + else: + aname = message.chat + if not aname.type == enums.ChatType.PRIVATE: + edit(message, f'`{get_translation("pmApproveError")}`') + return + name0 = aname.first_name + uid = aname.id + + try: + approve(uid) + edit(message, get_translation('pmApproveSuccess', [name0, uid, '`'])) + send_log(get_translation('pmApproveLog', [name0, uid])) + for i in _find_unapproved_msg(message, message.chat.id): + i.delete() + except IntegrityError: + edit(message, f'`{get_translation("pmApproveError2")}`') + return + + +@sedenify(outgoing=True, pattern="^.disapprove$") +def disapprovepm(message): + try: + from sedenecem.sql.pm_permit_sql import dissprove + except BaseException: + edit(message, f'`{get_translation("nonSqlMode")}`') + return + + if message.reply_to_message: + reply = message.reply_to_message + replied_user = reply.from_user + if replied_user.is_self: + edit(message, f'`{get_translation("cannotDisapproveMyself")}`') + return + aname = replied_user.id + name0 = str(replied_user.first_name) + uid = replied_user.id + else: + aname = message.chat + if not aname.type == enums.ChatType.PRIVATE: + edit(message, f'`{get_translation("pmApproveError")}`') + return + name0 = aname.first_name + uid = aname.id + + dissprove(uid) + + edit(message, get_translation('pmDisapprove', [name0, uid, '`'])) + + send_log(get_translation('pmDisapprove', [name0, uid, '`'])) + + +def _find_unapproved_msg(message, chat_id): + try: + return message._.search_messages( + chat_id, from_user='me', limit=10, query=UNAPPROVED_MSG + ) + except BaseException: + return [] + + +HELP.update({'pmpermit': get_translation('pmpermitInfo')}) diff --git a/sedenbot/modules/chat/profile.py b/sedenbot/modules/chat/profile.py new file mode 100644 index 0000000..57b0678 --- /dev/null +++ b/sedenbot/modules/chat/profile.py @@ -0,0 +1,244 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from os import remove +from time import sleep + +from PIL import Image +from pyrogram import enums +from pyrogram.errors import UsernameOccupied +from pyrogram.raw.functions.account import UpdateProfile, UpdateStatus, UpdateUsername +from pyrogram.raw.functions.channels import GetAdminedPublicChannels +from sedenbot import HELP, TEMP_SETTINGS +from sedenecem.core import ( + download_media_wc, + edit, + extract_args, + get_download_dir, + get_translation, + sedenify, + send_log, +) + +# ====================== CONSTANT =============================== +ALWAYS_ONLINE = 'offline' +# =============================================================== + + +@sedenify(pattern='^.reserved$') +def reserved_channels(message): + channels = message._client.invoke(GetAdminedPublicChannels()) + out = '' + for chat in channels.chats: + out += f'{chat.title}\n@{chat.username}\n\n' + edit(message, out) + + +@sedenify(pattern='^.name') +def set_name(message): + newname = extract_args(message) + if ' ' not in newname: + firstname = newname + lastname = '' + else: + namesplit = newname.split(' ', 1) + firstname = namesplit[0] + lastname = namesplit[1] + + message._client.invoke(UpdateProfile(first_name=firstname, last_name=lastname)) + edit(message, f'`{get_translation("nameOk")}`') + + +@sedenify(pattern='^.setpfp$') +def set_profilepic(message): + reply = message.reply_to_message + photo = None + if ( + reply + and reply.media + and ( + reply.photo + or (reply.sticker and not reply.sticker.is_animated) + or (reply.document and 'image' in reply.document.mime_type) + ) + ): + photo = download_media_wc(reply, 'profile_photo.jpg') + else: + edit(message, f'`{get_translation("mediaInvalid")}`') + + if photo: + image = Image.open(photo) + width, height = image.size + maxSize = (640, 640) + ratio = min(maxSize[0] / width, maxSize[1] / height) + image = image.resize((int(width * ratio), int(height * ratio))) + new_photo = f'{get_download_dir()}/profile_photo_new.png' + image.save(new_photo) + message._client.set_profile_photo(photo=new_photo) + remove(photo) + remove(new_photo) + edit(message, f'`{get_translation("ppChanged")}`') + else: + edit(message, f'`{get_translation("ppError")}`') + + +@sedenify(pattern='^.delpfp') +def remove_profilepic(message): + group = extract_args(message) + if group == 'all': + lim = 0 + elif group.isdigit(): + lim = int(group) + else: + lim = 1 + + count = 0 + for photo in message._client.get_chat_photos('me', limit=lim): + message._client.delete_profile_photos(photo.file_id) + count += 1 + edit(message, f'`{get_translation("ppDeleted", [count])}`') + + +@sedenify(pattern='^.setbio') +def set_bio(message): + newbio = extract_args(message) + message._client.invoke(UpdateProfile(about=newbio)) + edit(message, f'`{get_translation("bioSuccess")}`') + + +@sedenify(pattern='^.username') +def set_username(message): + newusername = extract_args(message) + try: + message._client.invoke(UpdateUsername(username=newusername)) + edit(message, f'`{get_translation("usernameSuccess")}`') + except UsernameOccupied: + edit(message, f'`{get_translation("usernameTaken")}`') + + +@sedenify(pattern='^.block$') +def block_pm(message): + reply = message.reply_to_message + if reply: + replied_user = reply.from_user + if replied_user.is_self: + edit(message, f'`{get_translation("cannotBlockMyself")}`') + return + aname = replied_user.id + name0 = str(replied_user.first_name) + uid = replied_user.id + else: + aname = message.chat + if not aname.type == enums.ChatType.PRIVATE: + edit(message, f'`{get_translation("pmApproveError")}`') + return + name0 = aname.first_name + uid = aname.id + + message._client.block_user(uid) + + edit(message, f'`{get_translation("pmBlocked")}`') + + try: + from sedenecem.sql.pm_permit_sql import dissprove + + dissprove(uid) + except BaseException: + pass + + send_log(get_translation('pmBlockedLog', [name0, uid])) + + +@sedenify(pattern='^.unblock$') +def unblock_pm(message): + reply = message.reply_to_message + if reply: + replied_user = reply.from_user + if replied_user.is_self: + edit(message, f'`{get_translation("cannotUnblockMyself")}`') + return + name0 = str(replied_user.first_name) + uid = replied_user.id + message._client.unblock_user(uid) + edit(message, f'`{get_translation("pmUnblocked")}`') + + send_log(get_translation('pmUnblockedLog', [name0, replied_user.id])) + else: + edit(message, f'`{get_translation("pmUnblockedUsage")}`') + + +@sedenify(pattern='^.online') +def always_online(message): + args = extract_args(message) + offline = ALWAYS_ONLINE in TEMP_SETTINGS + if args == 'disable': + if offline: + del TEMP_SETTINGS[ALWAYS_ONLINE] + edit(message, f'`{get_translation("alwaysOnlineOff")}`') + return + else: + edit(message, f'`{get_translation("alwaysOnlineOff2")}`') + return + elif offline: + edit(message, f'`{get_translation("alwaysOnline2")}`') + return + + TEMP_SETTINGS[ALWAYS_ONLINE] = True + + edit(message, f'`{get_translation("alwaysOnline")}`') + + while ALWAYS_ONLINE in TEMP_SETTINGS: + try: + message._client.invoke(UpdateStatus(offline=False)) + sleep(5) + except BaseException: + return + + +@sedenify(pattern='^.stats$') +def user_stats(message): + edit(message, f'`{get_translation("processing")}`') + chats = 0 + channels = 0 + groups = 0 + sgroups = 0 + pms = 0 + bots = 0 + unread = 0 + user = [] + for i in message._client.get_dialogs(): + chats += 1 + if i.chat.type == enums.ChatType.CHANNEL: + channels += 1 + elif i.chat.type == enums.ChatType.GROUP: + groups += 1 + elif i.chat.type == enums.ChatType.SUPERGROUP: + sgroups += 1 + else: + pms += 1 + user.append(i.chat.id) + + if i.unread_messages_count > 0: + unread += 1 + + users = message._client.get_users(user) + for i in users: + if i.is_bot: + bots += 1 + + edit( + message, + get_translation( + 'statsResult', + ['**', '`', chats, channels, groups, sgroups, bots, pms, unread], + ), + ) + + +HELP.update({'profile': get_translation('profileInfo')}) diff --git a/sedenbot/modules/purge.py b/sedenbot/modules/chat/purge.py similarity index 60% rename from sedenbot/modules/purge.py rename to sedenbot/modules/chat/purge.py index fe2527a..a05a728 100644 --- a/sedenbot/modules/purge.py +++ b/sedenbot/modules/chat/purge.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,77 +8,77 @@ # from time import sleep -from pyrogram.errors import FloodWait +from pyrogram.errors import FloodWait from sedenbot import HELP -from sedenecem.core import (extract_args, sedenify, edit, - send_log, reply, get_translation) +from sedenecem.core import ( + edit, + extract_args, + get_translation, + reply, + sedenify, + send_log, +) -@sedenify(pattern='^.purge$', compat=False, admin=True) -def purge(client, message): +@sedenify(pattern='^.purge$', admin=True) +def purge(message): msg = message.reply_to_message if msg: - itermsg = client.iter_history( - message.chat.id, - offset_id=msg.message_id, - reverse=True) + itermsg = list(range(msg.id, message.id)) else: edit(message, f'`{get_translation("purgeUsage")}`') return count = 0 - for message in itermsg: + for i in itermsg: try: count = count + 1 - client.delete_messages(message.chat.id, message.message_id) + message._client.delete_messages(chat_id=message.chat.id, message_ids=i, revoke=True) except FloodWait as e: sleep(e.x) except Exception as e: edit(message, get_translation('purgeError', ['`', '**', e])) return - done = reply( - message, get_translation( - 'purgeResult', [ - '**', '`', str(count)])) + done = reply(message, get_translation('purgeResult', ['**', '`', str(count)])) send_log(get_translation('purgeLog', ['**', '`', str(count)])) sleep(2) + message.delete() done.delete() -@sedenify(pattern='^.purgeme', compat=False) -def purgeme(client, message): +@sedenify(pattern='^.purgeme') +def purgeme(message): + me = message._client.me count = extract_args(message) if not count.isdigit(): return edit(message, f'`{get_translation("purgemeUsage")}`') i = 1 - itermsg = client.get_history(message.chat.id) + itermsg = message._client.get_chat_history(message.chat.id) for message in itermsg: if i > int(count) + 1: break - i = i + 1 - message.delete() + if message.from_user.id == me.id: + i = i + 1 + message.delete() - smsg = reply( - message, get_translation( - 'purgeResult', [ - '**', '`', str(count)])) + smsg = reply(message, get_translation('purgeResult', ['**', '`', str(count)])) send_log(get_translation('purgeLog', ['**', '`', str(count)])) sleep(2) i = 1 smsg.delete() -@sedenify(pattern='^.del$', compat=False, admin=True) -def delete(client, message): +@sedenify(pattern='^.del$', admin=True) +def delete(message): msg_src = message.reply_to_message if msg_src: if msg_src.from_user.id: try: - client.delete_messages(message.chat.id, msg_src.message_id) + message._client.delete_messages(message.chat.id, msg_src.id) message.delete() send_log(f'`{get_translation("delResultLog")}`') except BaseException: diff --git a/sedenbot/modules/snips.py b/sedenbot/modules/chat/snips.py similarity index 88% rename from sedenbot/modules/snips.py rename to sedenbot/modules/chat/snips.py index 28f43cd..dd1ec5a 100644 --- a/sedenbot/modules/snips.py +++ b/sedenbot/modules/chat/snips.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,15 +7,24 @@ # All rights reserved. See COPYING, AUTHORS. # -from sedenbot import HELP, LOGS, LOG_ID -from sedenecem.core import (extract_args, sedenify, edit, get_messages, - reply_msg, forward, send_log, get_translation) +from sedenbot import HELP, LOG_ID, LOGS +from sedenecem.core import ( + edit, + extract_args, + forward, + get_messages, + get_translation, + reply_msg, + sedenify, + send_log, +) def snips_init(): try: global sql from importlib import import_module + sql = import_module('sedenecem.sql.snips_sql') except Exception as e: sql = None @@ -50,12 +59,10 @@ def save_snip(message): string = None msg_o = forward(msg, LOG_ID) if not msg_o: - edit( - message, f'`{get_translation("snipError")}`') + edit(message, f'`{get_translation("snipError")}`') return - msg_id = msg_o.message_id - send_log(get_translation( - 'snipsLog', ['`', message.chat.id, keyword])) + msg_id = msg_o.id + send_log(get_translation('snipsLog', ['`', message.chat.id, keyword])) else: edit(message, f'`{get_translation("wrongCommand")}`') diff --git a/sedenbot/modules/chat/spamwatch.py b/sedenbot/modules/chat/spamwatch.py new file mode 100644 index 0000000..8821458 --- /dev/null +++ b/sedenbot/modules/chat/spamwatch.py @@ -0,0 +1,56 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from pyrogram import enums +from sedenbot import BRAIN, HELP, SPAMWATCH_KEY +from sedenecem.core import get_translation, is_admin_myself, reply, sedenify, send_log + +from spamwatch import Client as SpamWatch + + +class SWClient: + spamwatch_client = SpamWatch(SPAMWATCH_KEY) if SPAMWATCH_KEY else None + + +@sedenify( + outgoing=False, + incoming=True, + disable_edited=True, + disable_notify=True, +) +def spamwatch_action(message): + if not SWClient.spamwatch_client: + message.continue_propagation() + + uid = message.from_user.id + if uid in BRAIN: + message.continue_propagation() + + ban_status = SWClient.spamwatch_client.get_ban(uid) + if not ban_status: + message.continue_propagation() + + if is_admin_myself(message.chat): + text = get_translation('spamWatchBan', [message.from_user.first_name, uid]) + + if message.chat.type == enums.ChatType.PRIVATE: + reply(message, text) + message._client.block_user(uid) + else: + myself = message.chat.get_member('me') + if myself.privileges and myself.privileges.can_restrict_members: + message.chat.ban_member(uid) + reply(message, text) + else: + return + + send_log(text) + + +HELP.update({'spamwatch': get_translation('spamWatchInfo')}) diff --git a/sedenbot/modules/chat/stickers.py b/sedenbot/modules/chat/stickers.py new file mode 100644 index 0000000..74ab1ae --- /dev/null +++ b/sedenbot/modules/chat/stickers.py @@ -0,0 +1,295 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from random import choice +from time import time + +from PIL import Image +from pyrogram.errors import YouBlockedUser +from pyrogram.raw.functions.messages import GetStickerSet +from pyrogram.raw.types import InputStickerSetShortName +from sedenbot import HELP, PACKNAME, PACKNICK, TEMP_SETTINGS +from sedenecem.core import ( + PyroConversation, + download_media_wc, + edit, + extract_args, + get_download_dir, + get_translation, + reply_doc, + sedenify, + sticker_resize, + video_convert, +) + +# ================= CONSTANT ================= +DIZCILIK = [get_translation(f'kangstr{i+1}') for i in range(0, 12)] +# ================= CONSTANT ================= + + +@sedenify(pattern='^.(d[ıi]zla|kang)') +def kang(message): + myacc = message._client.me + kanger = myacc.username or myacc.first_name + if myacc.username: + kanger = f'@{kanger}' + args = extract_args(message) + + reply = message.reply_to_message + if not reply: + edit(message, f'`{get_translation("stickerUsage")}`') + return + + anim = False + video = False + media = None + chat = 'Stickers' + + if reply.photo or reply.video or reply.animation or reply.document or reply.sticker: + edit(message, f'`{choice(DIZCILIK)}`') + anim = reply.sticker and reply.sticker.is_animated + video = ( + reply.animation or reply.video or reply.sticker and reply.sticker.is_video + ) + media = download_media_wc(reply, sticker_orig=anim or video) + else: + edit(message, f'`{get_translation("stickerError")}`') + return + + if not reply.sticker: + try: + if ( + reply.video + or reply.animation + or (reply.document and 'video' in reply.document.mime_type) + ): + media = video_convert(media) + video = True + else: + media = sticker_resize(media) + except BaseException: + edit(message, f'`{get_translation("stickerError")}`') + return + + if len(args) < 1: + args = '1' + + emoji = reply.sticker.emoji if reply.sticker and reply.sticker.emoji else '🤤' + pack = 1 + + for item in args.split(): + if item.isdigit(): + pack = int(item) + args = args.replace(item, '').strip() + else: + emoji = args.strip() + + ptime = time() + pname = f'PNAME_{ptime}' + pnick = f'PNICK_{ptime}' + + name_suffix = ( + ('_anim', ' (Animated)') + if anim + else ('_video', ' (Video)') + if video + else ('', '') + ) + + TEMP_SETTINGS[pname] = ( + PACKNAME.replace(' ', '') + if PACKNAME + else f'a{myacc.id}_by_{myacc.username}_{pack}{name_suffix[0]}' + ) + TEMP_SETTINGS[f'{pname}_TEMPLATE'] = f'a{myacc.id}_by_{myacc.username}_' + TEMP_SETTINGS[pnick] = ( + PACKNICK or f'{kanger}\'s UserBot pack {pack}{name_suffix[1]}' + ) + TEMP_SETTINGS[f'{pnick}_TEMPLATE'] = f'{kanger}\'s UserBot pack ' + + limit = '50' if anim or video else '120' + + def pack_created(pname): + try: + set_name = InputStickerSetShortName(short_name=TEMP_SETTINGS[pname]) + set = GetStickerSet(stickerset=set_name, hash=0) + message._client.invoke(query=set) + return True + except BaseException: + return False + + def create_new(conv, pack, pname, pnick): + cmd = f'/new{"animated" if anim else "video" if video else "pack"}' + + try: + send_recv(conv, cmd) + except Exception as e: + raise e + msg = send_recv(conv, TEMP_SETTINGS[pnick]) + if 'Invalid pack selected.' in msg.text: + pack += 1 + TEMP_SETTINGS[ + pname + ] = f"{TEMP_SETTINGS[f'{pname}_TEMPLATE']}{pack}{name_suffix[0]}" + TEMP_SETTINGS[ + pnick + ] = f"{TEMP_SETTINGS[f'{pnick}_TEMPLATE']}{pack}{name_suffix[1]}" + return create_new(conv, pack, pname, pnick) + msg = send_recv(conv, media, doc=True) + if 'Sorry' in msg.text: + edit(message, f'`{get_translation("stickerError")}`') + return + send_recv(conv, emoji) + send_recv(conv, '/publish') + if anim or video: + send_recv(conv, f'<{TEMP_SETTINGS[pnick]}>') + send_recv(conv, '/skip') + ret = send_recv(conv, TEMP_SETTINGS[pname]) + while 'already taken' in ret.text: + pack += 1 + TEMP_SETTINGS[ + pname + ] = f"{TEMP_SETTINGS[f'{pname}_TEMPLATE']}{pack}{name_suffix[0]}" + TEMP_SETTINGS[ + pnick + ] = f"{TEMP_SETTINGS[f'{pnick}_TEMPLATE']}{pack}{name_suffix[1]}" + ret = send_recv(conv, TEMP_SETTINGS[pname]) + return True + + def add_exist(conv, pack, pname, pnick): + try: + send_recv(conv, '/addsticker') + except Exception as e: + raise e + + status = send_recv(conv, TEMP_SETTINGS[pname]) + + if limit in status.text: + pack += 1 + TEMP_SETTINGS[ + pname + ] = f"{TEMP_SETTINGS[f'{pname}_TEMPLATE']}{pack}{name_suffix[0]}" + TEMP_SETTINGS[ + pnick + ] = f"{TEMP_SETTINGS[f'{pnick}_TEMPLATE']}{pack}{name_suffix[1]}" + edit(message, get_translation('packFull', ['`', '**', str(pack)])) + if pack_created(pname): + return add_exist(conv, pack, pname, pnick) + else: + return create_new(conv, pack, pname, pnick) + + status = send_recv(conv, media, doc=True) + if ('Sorry' or ('duration is too long' or 'File is too big')) in status.text: + edit(message, get_translation('botError', ['`', '**', chat])) + return + send_recv(conv, emoji) + send_recv(conv, '/done') + return True + + with PyroConversation(message, chat) as conv: + try: + send_recv(conv, '/cancel') + except YouBlockedUser: + return edit(message, get_translation('unblockChat', ['**', '`', chat])) + if pack_created(pname): + ret = add_exist(conv, pack, pname, pnick) + if not ret: + return + else: + create_new(conv, pack, pname, pnick) + + edit(message, get_translation('stickerAdded', ['`', TEMP_SETTINGS[pname]])) + del TEMP_SETTINGS[pname] + del TEMP_SETTINGS[pnick] + del TEMP_SETTINGS[f'{pname}_TEMPLATE'] + del TEMP_SETTINGS[f'{pnick}_TEMPLATE'] + + +def send_recv(conv, msg, doc=False): + if doc: + conv.send_doc(msg) + else: + conv.send_msg(msg) + return conv.recv_msg() + + +@sedenify(pattern='^.getsticker') +def getsticker(message): + args = extract_args(message) + reply = message.reply_to_message + if not reply or not reply.sticker: + edit(message, f'`{get_translation("replySticker")}`') + return + + video = False + photo = False + + if reply.sticker and reply.sticker.is_animated or reply.sticker.is_video: + video = download_media_wc(reply) + else: + photo = download_media_wc(reply, f'{get_download_dir()}/sticker.png') + image = Image.open(photo) + photo = f'{get_download_dir()}/sticker.png' + image.save(photo) + + reply_doc( + reply, + video or photo, + caption=f'**Sticker ID:** `{reply.sticker.file_id}' + f'`\n**Emoji**: `{reply.sticker.emoji or get_translation("notSet")}`' + if args == '-v' + else '', + delete_after_send=True, + ) + message.delete() + + +@sedenify(pattern='.packinfo$') +def packinfo(message): + reply = message.reply_to_message + if not reply: + edit(message, f'`{get_translation("packinfoError")}`') + return + + if not reply.sticker: + edit(message, f'`{get_translation("packinfoError2")}`') + return + + edit(message, f'`{get_translation("processing")}`') + + get_stickerset = message._client.invoke( + GetStickerSet( + stickerset=InputStickerSetShortName(short_name=reply.sticker.set_name), + hash=0, + ) + ) + pack_emojis = [] + for document_sticker in get_stickerset.packs: + if document_sticker.emoticon not in pack_emojis: + pack_emojis.append(document_sticker.emoticon) + + out = get_translation( + 'packinfoResult', + [ + '**', + '`', + get_stickerset.set.title, + get_stickerset.set.short_name, + '✅' if get_stickerset.set.official else '❌', + '✅' if get_stickerset.set.archived else '❌', + '✅' if get_stickerset.set.animated else '❌', + get_stickerset.set.count, + ' '.join(pack_emojis), + ], + ) + + edit(message, out) + + +HELP.update({'stickers': get_translation('stickerInfo')}) diff --git a/sedenbot/modules/deezer.py b/sedenbot/modules/deezer.py deleted file mode 100644 index 3484204..0000000 --- a/sedenbot/modules/deezer.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from deethon import Session - -from sedenbot import HELP, DEEZER_TOKEN -from sedenecem.core import (sedenify, extract_args, edit, - get_translation, reply_audio) - - -@sedenify(pattern='^.deezer') -def deezermusic(message): - if not DEEZER_TOKEN: - return edit(message, f'`{get_translation("deezerArlMissing")}`') - args = extract_args(message) - url = args - edit(message, f'`{get_translation("processing")}`') - if not url: - return edit(message, f'`{get_translation("wrongCommand")}`') - - try: - deezer = Session(DEEZER_TOKEN) - except Exception as e: - return edit(message, get_translation('banError', ['`', '**', e])) - - try: - if 'deezer' in url: - if 'track' in url: - track = deezer.download(url, bitrate='MP3_320') - edit(message, f'`{get_translation("uploadMedia")}`') - reply_audio(message, track, delete_orig=True) - elif 'album' in url: - album = deezer.download(url, bitrate='MP3_320') - edit(message, f'`{get_translation("uploadMedia")}`') - for track in album: - reply_audio(message, track, delete_orig=True) - except Exception as e: - return edit(message, get_translation('banError', ['`', '**', e])) - - -HELP.update({'deezer': get_translation('deezerInfo')}) diff --git a/sedenbot/modules/dogbin.py b/sedenbot/modules/dogbin.py deleted file mode 100644 index 36e32f8..0000000 --- a/sedenbot/modules/dogbin.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from os import remove -from requests import get, post, exceptions - -from sedenbot import HELP, DOWNLOAD_DIRECTORY -from sedenecem.core import edit, extract_args, sedenify, get_translation - -DOGBIN_URL = 'https://del.dog/' - - -@sedenify(pattern=r'^.paste', compat=False) -def paste(client, message): - dogbin_final_url = '' - match = extract_args(message) - reply_id = message.reply_to_message - - if not match and not reply_id: - edit(message, f'`{get_translation("dogbinUsage")}`') - return - - if match: - dogbin = match - elif reply_id: - dogbin = (message.reply_to_message) - if dogbin.media: - downloaded_file_name = client.download_media( - dogbin, - DOWNLOAD_DIRECTORY, - ) - m_list = None - with open(downloaded_file_name, 'rb') as fd: - m_list = fd.readlines() - dogbin = '' - for m in m_list: - dogbin += m.decode('UTF-8') + '\r' - remove(downloaded_file_name) - else: - dogbin = dogbin.dogbin - - edit(message, f'`{get_translation("dogbinPasting")}`') - resp = post(DOGBIN_URL + 'documents', data=dogbin.encode('utf-8')) - - if resp.status_code == 200: - response = resp.json() - key = response['key'] - dogbin_final_url = DOGBIN_URL + key - - if response['isUrl']: - reply_text = get_translation( - 'dogbinPasteResult2', [ - '`', dogbin_final_url, f'{DOGBIN_URL}v/{key}']) - else: - reply_text = get_translation( - 'dogbinPasteResult', ['`', dogbin_final_url]) - else: - reply_text = f'`{get_translation("dogbinReach")}`' - - edit(message, reply_text, preview=False) - - -@sedenify(outgoing=True, pattern="^.getpaste") -def getpaste(message): - textx = message.reply_to_message - dogbin = extract_args(message) - edit(message, f'`{get_translation("dogbinContent")}`') - - if textx: - dogbin = str(textx.dogbin) - - format_normal = f'{DOGBIN_URL}' - format_view = f'{DOGBIN_URL}v/' - - if dogbin.startswith(format_view): - dogbin = dogbin[len(format_view):] - elif dogbin.startswith(format_normal): - dogbin = dogbin[len(format_normal):] - elif dogbin.startswith('del.dog/'): - dogbin = dogbin[len('del.dog/'):] - else: - edit(message, f'`{get_translation("dogbinUrlError")}`') - return - - resp = get(f'{DOGBIN_URL}raw/{dogbin}') - - try: - resp.raise_for_status() - except exceptions.HTTPError as HTTPErr: - edit(message, get_translation('dogbinError', [str(HTTPErr)])) - return - except exceptions.Timeout as TimeoutErr: - edit(message, get_translation('dogbinTimeOut', [str(TimeoutErr)])) - return - except exceptions.TooManyRedirects as RedirectsErr: - edit(message, get_translation( - 'dogbinTooManyRedirects', [str(RedirectsErr)])) - return - - reply_text = get_translation('dogbinResult', ['`', resp.text]) - - edit(message, reply_text) - - -HELP.update({'dogbin': get_translation('dogbinInfo')}) diff --git a/sedenbot/modules/ecem.py b/sedenbot/modules/ecem.py deleted file mode 100644 index 662a3af..0000000 --- a/sedenbot/modules/ecem.py +++ /dev/null @@ -1,237 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from random import choice - -from sedenbot import HELP -from sedenecem.core import edit, sedenify, get_translation -# ================= CONSTANT ================= -ECEM_STRINGS = [ - "Çocukluk aşkımsın", - "Erkek arkadaşlarımın arkadaşlarının adlarını bilmem normal bir şey", - "Şu an acele işim var, daha sonra çekilsek olmaz mı? Zaten yine geleceğim.", - "Yok kızmadım", "Bunun farkına vardıysan benim için artık problem yok", - "Bundan sonra artık hayatında Ecem diye biri yok!", - "Yağmurlu havada sweatini veren arkadaşlarınız olsun 👅", - "Yalnız çiçek açar mısın? Yalnız, çiçek.", - "Affetmiyorum, annemi çağıracağım. O gereğini yapacak.", "Saçımla oynama", - "Saçımla oynamamanı söylediğim halde oynuyorsun. Arkamdan git lütfen.", - "Ben gelmiyorum bedene falan, kenarda ölmeyi bekleyeceğim", - "Düşman değiliz sadece onun olduğu yerde rahat edemem", - "Git arkamdan, istemiyorum seni", - "Murat Boz şarkıları beni hiç eğlendirmiyor", - "Tövbe tövbe kapatın şunu çarpılacağız", "Üç kızım olsun istiyorum", - "Eğer onunla evlenirsem nikah şahidim sen ol", - "Ehehehe çok komik, hadi defolun gidin", "Gidip onunla konuşsana?", - "Ne oldu sana? Anlatmak ister misin?", - "Lütfen anlat işte. Çok mu özel ki?", - "Hadi gel erkeklerin yanına geçelim", "İyi misin?", "Teşekkür ederim 😊", - "Daha sonra yine geleceğim, o zaman çekiliriz, olmaz mı?", - "Grubu dağıtıyoruz gençler", - "Grubu dağıtıyoruz gençler??? Ne ara dedim onu ya ben?", - "Allah belanı vermesin", "Hadi yönetici sensin, dağıt grubu", - "İyi akşamlar", "Her şeyi ben söylüyorum, biraz da siz söyleyin", - "Ben onu esprisine demiştim diyette değilim", - "Sizde var mı bilmiyorum sorim dedim birkaç kişiye daha sordum, çıktı alabilme şansın var mı?", - "Üçgende çıkmış sorular, 3. site", "Eğer olmadıysa gerek kalmadı", - "Yok yani, ben buldum birinden", "Evet evet hallettim ben sağol", - "Atim sen de kontrol et bir, o mu diye", - "Neyse sen çıkarttığını da getirirsin olmadı", "Kim ne dedi sana?", - "Onlar benim kardeşim gibi olduğu için sahipleniyorlar", - "Kötü bir durum yok", - "Bir anda yapınca rahatsız oldum ama sonra sana zaten sorun yok dedim", - "Onlar da biliyor, ondan tepki vermiştir", "Sorun yok yani", "Rica ederim", - "Teşekkür ederim kusura bakma telefonum bozuktu yeni görebildim 😊", - "Evet. Yalan borcum mu var size? Evet, yalan borcum mu var? Yalan borcum mu var?", - "Ne malsın ya", "Testleri bitirmeyin Allah aşkına", - "Sen mesajı silsen de bildirimde gözüküyor", "O ne demişti ki?", - "Ben birine bir şey yazmştım, sonra sildim. Ne yazdın dedi, söylemedim. Sonra yazdığım şeyi bildirimden açıp bana yazdı.", - "Mzkddkslslldldldldld", - "Toplu olarak yapıyoruz değil mi, hiç hoş olmayan şeyler çıkmasın sonra.", - "Lan sen konuşma", "Abi yalan değil ki, yetişmiyor", - "Ya hep mi korktuğum başıma gelir ya?", "Bende şans olsa zaten", - "Ben oyuna falan katılamam", - "Tövbe, hep olmaması gereken sınıfları söylüyorsun", - "Neyse ben konuşmuyorum sizinle zaten", "Ne uzattınız ya, değişmiş işte", - "Sen sus, seninle konuşan yok", "Çok özledim 😔 ♥️", - "Saygı, minnet ve özlemle", - "Canım, dayım, her şeyim yolun açık olsun askerim ♥️", "#NoFilter", - "Yorgunum ve ağrılar", "Sıkıldım ya, konuşun. Söz valla terslemeyeceğim.", - "Çok kaptırmış kendini o", - "Okul mal o zaman, o kadar yanlışla 11. olduysam", - "Kime diyorsunuz, ben bir şey anlamadım", "Sen hani hastaydın Lan", - "Sen ne diyon ya sksödödödöföfçgçgçhçhş", - "Hiç bu kadar güldüğümü hatırlamıyorum", - "Kaç dakika uğraştın, gerizekalı ya", - "Lan sus sende sabahtan beri Ecem Ecem dmdkdmdmdmföfögö", - "Evde gerizekalı gibi gülüyorum susun artık", "Tövbe yarabbim", - "Üstüme gitmeyin şu saatlerde benim", "Sakn ol raki", - "5 saattir anlamaya çalışıyor", "Yeter, gidin yatın", "Sus lan mal", - "Gahahaha diye gülenin dediğine bak", "Yarın hiç hoş olmayacaksın", - "Sinirliyim zaten, uğraşmayın benimle sjdkdkkdmfgm", - "Bu arada yüzümü telefon gözükmesin diye öyle tutmuyordum sebebini bilmiyorum djjd", - "Doğum günü kızıyım ben şşş. Nasıl bir değişiklik bekliyordunuz dkdkdk", - "İnternetten baksana", "Müzik dersi hangi gün? Hocaya söz vermiştim.", - "Hem arkamdan kaşar diyorsun, hem de kardeşim diyorsun", - "Böyle böyle yürüyor ya ahahahahahahaha", "Her an'ımda", "BFF♥️", - "Yine, yeni, yeniden ♥️", - "Buraya sığdıramayacağım milyon tane iyi anımız var, hep de olsun.", - "İyi ki doğdun kız kardeşim, seni çok seviyorum ♥️", - "Best gün ilan ediyorum ♥️", - "Beraber uyuyoruz ama yine de sen bilirsin sjskskksksks️", "Özledik be️", - "Ula 😔", "Konum yeterli", "Yazdan kalmalarla avunmaya devam", - "Kanka ben zaten tetikteyim ağlamak için yapma bak yanarız️", - "Ben de çok özledim nolacak şimdi", - "Kajsjskskskskdksksdsjjsksks istemiyom bırak beni️", - "O olsaydı iki güldürür kendime gelirdim kafayı yicem dayanamıyorum️", - "Hemde nasılll", "😔 her seferinde düşürür", "Yaz gelsin artıkkkkk", - "YERİM SENİ", "Sen niye beni sinirlendiriyorsun 😠😠", "Sensin 1.55", - "Bak aferin nasıl biliyor", - "Gevezelik yapma sjsjkskskskskkslslsksksksklsks", "Güldür güldür", - "Allah allah çok özledim bir şaşırdım", "Ne demek 2 yıl geçmiş", - "Her şeyi anladım da Çin ne sjdjdkmdmxmxmmxmxkc", "Her yere atacam", - "Işık bulursam 37478383 tane fotoğraf çekilirim mutlaka", - "Aşkım olduğunu söylemiş miydim ??", "Ya salak mısın sjdjdkdkkdkdkdkdkd", - "Bana bu kadar kilo aldıran hayat size neler yapmaz - '2018", - "Hatırla çabuk", "Ayyyyyyy 😳 ♥️", - "Biz bunu hangi kafayla çekilmişiz asksksksllslsldkd", - "Asıl kesmeseydim o zaman küserdik sjskkdkdksksmdk", - "Olay benim kilomdan buraya nasıl geldi", - "Kanka beni bir yıldır görmüyorsun ya hani", "Salın artık bizi", "Çatalla", - "Diyete başlıyorum konu kapanmıştır", "Bakılır neden olmasınn", - "Main storyden vazgeçilmiştir", - "Bilen bilir mutlu olduğum dönemler - işte anca 2018 -", - "Yorum yapmıyorum artıkkk", "Az dalgası dönmedi şu aynanın 👅", "Hadi bay", - "Aşırı aşırı aşırı özledim", "Hikayede kalmasın", - "Bulduğu her yerde fotoğraf çekmeyen de ne bilim", "Nasıl orada havalar", - "Sana sahip olduğum için çok şanslıyım", - "En acilinden bu günlere geri dönebilir miyiz", "Buralar artık benim", - "Seni ve uzun saçlarımı özledim 😔", "Sonunda kauvştuk ♥️", - "Sizi bile özledim 😔", "Allahım yine çok mutluyum", - "1 yıl önce mutluymuşum", "Yaaaaaa 😔 ♥️", "HER ŞEYİİİM ❤️💜🧡🖤💗💙💖💕💝💛💘♥💓", - "Hikayede kalmasın serisinden devam", - "Abi ben niye her fotoğrafta farklı çıkıyorum", "Benim küçük sevgilim ♥️", - "Göz altı morluklarım ve ben bunları hak etmedik", - "Sjskdksksjdksl gergin olmadığım tek bir saniye yok", - "Gözlerimden yorgunluk akmadığı bir gün bile yok", - "Yaz geri gelsin ve biz her gün içelim", - "Köpeğin yılışıklığı yüzünden kavga ettiğimiz günlerden biri djjdjdjd", - "Bu da senin için skskkdkdkdlskskd", "Ne demiş kumarda kazanan", - "SJSKKDKSKSKSKS DÜNYANIN EN DELİ ARKADAŞLARI BENDE iyi ki varsınız ♥️♥️♥️♥️", - "Canlarım asla normal duramazlar ♥️♥️♥️♥️", "Anasının kuzusuuuu", - "Barbie değil harbi", "Özledim be bu zamanları", - "9. sınıf, canım İzmir 😔 ♥️", - "377383843. Deneme sanırım neysss bugünlük yeterr", "Çokçokçok özledimm", - "Post olmaya hak kazandı", "Birthday baby ♥️♥️", - "Daha haklı bir tweet görmedim", - "Bu dediğime kendim bile inanamıyorum ama çok özlediiim ♥️♥️♥️", - "Sanırım eski saçımı özledim", "10 yıl da geçse aynı espriye devamke", - "Başladık yine", "Sosyal medyaya sjskskskdkkdkdkdk", "Bir ara ben de", - "Yeter laaan", "Sjskjdkdkskskskskkskks", - "Ben de nerde kaldın diye merak ettim", "Ararsın bu günleri", "GN", "gn", - "Dünyanın en tatlı varlığı", "Ya hahahahahahahahaha o benim tek aşkım", - "Hadi İzmire", "Buyrun Seden hanım", - "Arkada yeni farketmem djskskskskskkssksk", "Okulun güzel yanı", - "Kes önce benim mesajlarıma bak sen", "Sonunda kavuştuk", - "Kuzeniz çünkü 👅😘♥", "Şşş", "Abartmaya bayılıyorsun", "İg", - "BARIŞALIM ARTIK", "Aşkım ♥♥♥", "Geceden kalan", - "Oooo kanka büyük laf soktun o nası laf sokmak", "Canımmsınn♥", - "Djdkdkkd çektirenler utansın", "Bebeğiiiiim♥♥♥♥♥", "Goodnighttttt♥♥♥♥♥", - "Aşk hayatımın özeti", "Bağışıklık yapınca böyle oluyor", "Aşkımmm♥♥♥♥♥", - "İyi gecelerrr", "Tecrübe konuşuyor", "Ben de 😔😔♥♥", - "Sevdaaaa çiçeğiim♥♥♥", "Bu da senin için skskkdkdkdlskskd", - "Ksnkskzksls ♥♥♥♥", "Hasta ve yorgun", "Ksjsksksksksks", "Canlarımmm♥♥♥♥", - "Msmsksks", "Bir konserimiz daha yok mu ama", "Aşkımaşkımm♥♥", "Aynen", - "Ya defol git djdkdmfmgöögmdmdmdmd", - "Lan tamam bin pişman ettiniz ya sjskdkkskskslsmskldmdlddk", - "Seri üzgün sjkdkdkdksls", "Bir oyun kazandık onu da çok gördünüz", - "Benim sayemde", - "Bütün maçları bana sorup oynuyon sonra bir de benim privimde artisli yapıyorsun ", - "Ksksksksksksksksks", "SHJSJSKDKDKSKDK", - "Yeter uyuyun hadi dersiniz var sabah", - "kdkdkdkdkdkdmdmdmdm lan sınıfta öksürmekten canım çıktı bu kadarı da fazla sjskkfksks", - "Tamam", - "Sjkdkdkdkdlskdkxkdkdkdld şela en son bir kus istersen demişti ama yine de sen bilirsin", - "SJDKFKLGLDKDKDLSLDKDKDKDLDLDLLD", "Toplu igg", "Hatırım kalır", - "Bir dahakine kafamı kırmayın ama", "NSSNMDMDMDMSMSMDM", - "Nsnsmsmdmdmdmskkdms", "İyi kiii♥♥♥♥", - "Senin sesin yüzünden sesi kapalı attım haykırmışsın arkadaş gibi arkadaş kskskskskkdmsksmsmsmmdmddk", - "Dağa taşa yazacan yakında bir olsaydık şu sınavı skskdkdkksksks", - "Düşünsene yarın hoca sayıları değiştiriyor", - "Kalırım sjjdjfjdkskskdkdkdkkdksks", - "Olay beraber okumak değilmiş abi beraber geçirdiğimiz ortammış", - "Skskskksks sadece bunlar var elimdee ♥♥", "Kordonun dili olsa da konuşsa", - "Bende serisi var sjsjskdmmsksks", "Abartttttt", - "Babuş açı iyi bir de biz de zayıftık bir zamanlar", "Tabi koçç", - "Yuh 2 sene önceydi o", "Aslan 2 ay sonra barıştık biz onun üzerinden", - "Djdjdkkdkdkdkd çünkü o Seden", "Uğraşmaaa", "Yayaya♥♥♥", - "Birthday baby♥♥ -acil fotoğraf @sedenogen", - "Sjsjdjjsksksks özlediğime değil ÇOK özlediğime", "Yaaaaa", - "Deme bak gider boyatırım", "Sen kesinlikle benim ilerideki çocuğumsun", - "Ayyyyyyy♥♥", "Bu fotoğrafta çöktüğümü fark etmişimdir", - "Current mood: DAY OFF!", "Sıkıntıdan patlamama ramak kaldı", - "Bir bihter ziyagil değildik ama biz de çok acı çektik sjjsjsjskd", - "Efektten bıkana kadar devam", "Şov başlasın dedi", - "Bir pijamamı bir de seni♥", "Yeni yıla evde tek başına girmek mi 0/10", - "Seri yorgun", "10/10", "Black is my favorite color", - "Çok şükür bugün de yorgunluktan öldük", "Best couple", "İggg♥", - "Definitely Tired", "Good night", "Saç kestirmek net pişmanlıktır", - "Güne puanım 0", "Teyzoşuuuum♥", "Aşşkııım♥♥♥", "Sensin en güzelllll♥♥♥", - "Bebeğiiim♥", "Bir kere ya bir kere güzel bir şey at", "Canımm♥", - "Aşkımmm♥", "Ben de seni çokk seviyorum birtanem♥", - "Emoji yanlış oldu sanırım 👅 ♥", "Teşekkürler ♥", "Birtanemm♥", - "Hem de çokk♥", "En özell♥", "Balll♥", "Seviyorum seniii♥", - "Psikolojik tedavi için dm", "Çözüyormuş gibi çek knk", - "Yalan söyleme dakika sayıyordun bitsin diye", - "Seninle arkada marş söyleyip çerez yemeği özledim", - "Gözümü galatasaray marşıyla açıyordum sjsjdkdkdkkdkd", - "Aferin böyle yola gel sjskkfskdkfdkkdkddk", "Gn priv ailemm♥️♥️♥️", - "Uyumuyorum çaktırma sjskkskdkdkd", "Kara gözlüm ölesim var", - "Bu bağlantıyı kimse anlamayacak sjsksksksksk", "Yapma yanarız", - "skamaksksksksksks", "inşallahh bence de görelim artık", - "hem de nasılllll", "Konumumuz belli", - "Herkes böyle bacı bulamaz şanslıyım tabi 👅 ♥️♥️♥️♥️", - "En değerlilerim misiniz nesinizz????", "😳♥️♥️", - "Pardon da neyin var acabaaa", "Bebekkkk gibisin aşkımm", - "Şşş sus yoksa inanırım", "İyi geceler canlarım️♥♥♥", - "Sizi seveni üzün, düzene uyun", "Tanıyamadım", "Hangi sınıf", - "Okuldan kimse takip etmiyor?", "12 dahil takip ettiğim kişiler var", - "Ama seninle hiçbiri takipleşmiyor", "Kavuştukkkk♥", "Kuzucuğumla💘", - "Bu özlemin tarifi yok♥", "Mood.", "Sondaki gülüşe düşmeyen de ne bilim", - "Herkes bu kadar bencil olmak zorunda mı?", "❔", "Bekle dedi gitti 👅♥", - "Bu da burda kalsın", "SENİİİN👅", "Aşşşşşkkıım", "💗💗", - "Ah Seden'im... küçük civcivim", "💓💓💓💓", - "Özlemini tişörtünle gideriyorum.", "Sus ya sen çok farklısın sanki", - "Seden'e derdimi anlatıyorumdur. Seden:", "Canımın içiiiii 💙", "😘😘", - "Anlık sinir krizleri geçirildi", - "Çevrimiçi olup olmadığımızı test ediyoruzdur #whatsapp yaktın bizi", - "Djjdkdkmdöödööd", "Ben almadım Seden almış", - "Hocam lütfen bir sonraki dersimize olsun", - "Biraz da şerefsiz arkadaşlarımızı paylaşalım ♥️♥️♥️♥️", - "sadece Doğum günümde yazıyorsunuz", "Yalansa yalan de djjdjdkdkksks", - "👅♥️♥️♥️", "SJSJJDJDKSKSKSKKSKSKDK yok artık hala mı", - "Bütün okul anladı @CiyanogenOneTeams ona yâr olmayacağımı anlamadı Jsjsjdkskskskskskl", - "KSKDKDKDKDKSKKSKDKDKKD", - "LAN HATIRLATMA sjjdjdjdkdkdkdkdk bir fotoğraf çekilelim mi NE SJSKSKSKSK", - "Yemin ederim daha fazla sevenini görmedim jdjdkdkdkd"] -# ================= CONSTANT ================= -'''Copyright (c) @Sedenogen | 2020''' - - -@sedenify(pattern='^.ecem$') -def ecemify(message): - ecem(message) - - -def ecem(message): - # Ecem'in sözlüğü - edit(message, choice(ECEM_STRINGS)) - - -HELP.update({'ecem': get_translation('ecemInfo')}) diff --git a/sedenbot/modules/effects.py b/sedenbot/modules/effects.py deleted file mode 100644 index 9f220b8..0000000 --- a/sedenbot/modules/effects.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from os import path, remove -from subprocess import Popen - -from sedenbot import HELP -from sedenecem.core import (edit, sedenify, download_media_wc, reply_voice, - extract_args, reply_doc, get_translation) - - -@sedenify(pattern='^.earrape') -def earrape(message): - args = extract_args(message).split(' ', 1) - reply = message.reply_to_message - earrape = 'earrape' - if path.isfile(earrape): - remove(earrape) - - util = args[0].lower() - if util == 'mp4': - if not(reply.video or reply.video_note or ( - reply.document and 'video' in reply.document.mime_type)): - edit(message, f'`{get_translation("wrongMedia")}`') - else: - edit(message, f'`{get_translation("applyEarrape")}`') - media = download_media_wc(reply, earrape) - process = Popen(['ffmpeg', - '-i', - f'{media}', - '-af', - 'acrusher=.1:1:64:0:log', - f'{media}.mp4']) - final, _ = process.communicate() - edit(message, f'`{get_translation("uploadMedia")}`') - reply_doc( - message, - f'{media}.mp4', - delete_after_send=True, - delete_orig=True) - remove(media) - elif util == 'mp3': - if not(reply.video or reply.video_note or ( - reply.audio or reply.voice or ( - reply.document and 'video' in reply.document.mime_type))): - edit(message, f'`{get_translation("wrongMedia")}`') - else: - edit(message, f'`{get_translation("applyEarrape")}`') - media = download_media_wc(reply, earrape) - process = Popen(['ffmpeg', - '-i', - f'{media}', - '-af', - 'acrusher=.1:1:64:0:log', - f'{media}.mp3']) - final, _ = process.communicate() - edit(message, f'`{get_translation("uploadMedia")}`') - reply_voice(message, f'{media}.mp3', delete_orig=True) - remove(media) - remove(f'{media}.mp3') - else: - edit(message, f'`{get_translation("wrongCommand")}`') - return - - -@sedenify(pattern='^.nightcore') -def nightcore(message): - reply = message.reply_to_message - nightcore = 'nightcore' - if path.isfile(nightcore): - remove(nightcore) - - if not(reply.audio or reply.voice or ( - reply.document and 'audio' in reply.document.mime_type)): - edit(message, f'`{get_translation("wrongMedia")}`') - else: - edit(message, f'`{get_translation("applyNightcore")}`') - media = download_media_wc(reply, file_name=nightcore) - process = Popen(['ffmpeg', - '-i', - f'{media}', - '-af', - 'asetrate=44100*1.16,aresample=44100,atempo=1', - f'{media}.mp3']) - final, _ = process.communicate() - edit(message, f'`{get_translation("uploadMedia")}`') - reply_voice(message, f'{media}.mp3') - remove(media) - remove(f'{media}.mp3') - message.delete() - - -@sedenify(pattern='^.slowedtoperfection') -def slowedtoperfection(message): - reply = message.reply_to_message - slowedtoperfection = 'slowedtoperfection' - if path.isfile(slowedtoperfection): - remove(slowedtoperfection) - - if not(reply.audio or reply.voice or ( - reply.document and 'audio' in reply.document.mime_type)): - edit(message, f'`{get_translation("wrongMedia")}`') - else: - edit(message, f'`{get_translation("applySlowedtoperfection")}`') - media = download_media_wc(reply, file_name=slowedtoperfection) - process = Popen(['ffmpeg', - '-i', - f'{media}', - '-af', - 'aecho=1.0:0.7:20:0.5,asetrate=44100*0.84,aresample=44100,atempo=1', - f'{media}.mp3']) - final, _ = process.communicate() - edit(message, f'`{get_translation("uploadMedia")}`') - reply_voice(message, f'{media}.mp3') - remove(media) - remove(f'{media}.mp3') - message.delete() - - -HELP.update({'effects': get_translation('effectsInfo')}) diff --git a/sedenbot/modules/ezanvakti.py b/sedenbot/modules/ezanvakti.py deleted file mode 100644 index 3e07ef0..0000000 --- a/sedenbot/modules/ezanvakti.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from re import sub, DOTALL -from functools import reduce -from requests import get -from bs4 import BeautifulSoup - -from sedenbot import HELP -from sedenecem.core import edit, extract_args, sedenify, get_translation - - -@sedenify(pattern='^.ezanvakti') -def ezanvakti(message): - konum = extract_args(message).lower() - if len(konum) < 1: - edit(message, f'`{get_translation("ezanvaktiKonum")}`') - return - - try: - knum = find_loc(konum) - if knum < 0: - raise ValueError - request = get(f'https://namazvakitleri.diyanet.gov.tr/tr-TR/{knum}') - result = BeautifulSoup(request.text, 'html.parser') - except BaseException: - edit(message, f'`{get_translation("ezanvaktiErrorInfo", [konum])}`') - return - - res1 = result.body.findAll('div', {'class': ['body-content']}) - res1 = res1[0].findAll('script') - res1 = sub( - r'|\r|{.*?}|\[.*?\]|\n ', '', str(res1[0]), - flags=DOTALL) - res1 = sub('\n\n', '\n', res1)[:-1].split('\n') - - def get_val(st): - return [i.split('=')[1].replace('"', '').strip() - for i in st[:-1].split(';')] - - res2 = get_val(res1[1]) - res3 = get_val(res1[2]) - - vakitler = get_translation( - 'ezanvaktiShowInfo', - ['**', '`', res2[1], - res3[0], - res3[1], - res3[2], - res3[3], - res3[4], - res3[5]]) - - edit(message, vakitler) - - -def find_loc(konum): - if konum.isdigit(): - plaka = int(konum) - if plaka > 0 and plaka < 82: - return int(sehirler[plaka - 1].split()[2]) - else: - return -1 - else: - di = {'ç': 'c', 'ğ': 'g', 'ı': 'i', 'ö': 'o', 'ş': 's', 'ü': 'u'} - konum = reduce(lambda x, y: x.replace(y, di[y]), di, konum) - sehir_ad = [s.split()[1].lower() for s in sehirler] - try: - index = sehir_ad.index(konum) - return int(sehirler[index].split()[2]) - except BaseException: - return -1 - - -sehirler = [ - '01 Adana 9146', '02 Adiyaman 9158', '03 Afyonkarahisar 9167', - '04 Agri 9185', '05 Amasya 9198', '06 Ankara 9206', '07 Antalya 9225', - '08 Artvin 9246', '09 Aydin 9252', '10 Balikesir 9270', '11 Bilecik 9297', - '12 Bingol 9303', '13 Bitlis 9311', '14 Bolu 9315', '15 Burdur 9327', - '16 Bursa 9335', '17 Canakkale 9352', '18 Cankiri 9359', '19 Corum 9370', - '20 Denizli 9392', '21 Diyarbakir 9402', '22 Edirne 9419', - '23 Elazig 9432', '24 Erzincan 9440', '25 Erzurum 9451', - '26 Eskisehir 9470', '27 Gaziantep 9479', '28 Giresun 9494', - '29 Gumushane 9501', '30 Hakkari 9507', '31 Hatay 20089', - '32 Isparta 9528', '33 Mersin 9737', '34 Istanbul 9541', '35 Izmir 9560', - '36 Kars 9594', '37 Kastamonu 9609', '38 Kayseri 9620', - '39 Kirklareli 9638', '40 Kirsehir 9646', '41 Kocaeli 9654', - '42 Konya 9676', '43 Kutahya 9689', '44 Malatya 9703', '45 Manisa 9716', - '46 Kahramanmaras 9577', '47 Mardin 9726', '48 Mugla 9747', '49 Mus 9755', - '50 Nevsehir 9760', '51 Nigde 9766', '52 Ordu 9782', '53 Rize 9799', - '54 Sakarya 9807', '55 Samsun 9819', '56 Siirt 9839', '57 Sinop 9847', - '58 Sivas 9868', '59 Tekirdag 9879', '60 Tokat 9887', '61 Trabzon 9905', - '62 Tunceli 9914', '63 Sanliurfa 9831', '64 Usak 9919', '65 Van 9930', - '66 Yozgat 9949', '67 Zonguldak 9955', '68 Aksaray 9193', - '69 Bayburt 9295', '70 Karaman 9587', '71 Kirikkale 9635', - '72 Batman 9288', '73 Sirnak 9854', '74 Bartin 9285', '75 Ardahan 9238', - '76 Igdir 9522', '77 Yalova 9935', '78 Karabuk 9581', '79 Kilis 9629', - '80 Osmaniye 9788', '81 Duzce 9414'] - -HELP.update({ - "ezanvakti": - ".ezanvakti <şehir> \ - \nKullanım: Belirtilen şehir için namaz vakitlerini gösterir. \ - \nÖrnek: .ezanvakti istanbul veya .ezanvakti 34" -}) diff --git a/sedenbot/modules/fun/carbon_text.py b/sedenbot/modules/fun/carbon_text.py new file mode 100644 index 0000000..36767d4 --- /dev/null +++ b/sedenbot/modules/fun/carbon_text.py @@ -0,0 +1,85 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from os import path, remove +from time import sleep +from urllib.parse import quote_plus + +from gtts import gTTS +from selenium.webdriver.common.by import By + +from sedenbot import HELP, SEDEN_LANG +from sedenecem.core import ( + edit, + extract_args, + extract_args_split, + get_translation, + get_webdriver, + reply_doc, + reply_voice, + sedenify, + send_log, +) + +CARBONLANG = 'auto' +TTS_LANG = SEDEN_LANG +TRT_LANG = SEDEN_LANG + + +@sedenify(pattern='^.crblang') +def carbonlang(message): + global CARBONLANG + CARBONLANG = extract_args(message) + edit(message, get_translation('carbonLang', ['**', CARBONLANG])) + + +@sedenify(pattern='^.carbon') +def carbon(message): + match = extract_args(message) + if len(match) < 1: + edit(message, f'`{get_translation("wrongCommand")}`') + return + edit(message, f'`{get_translation("processing")}`') + reply = message.reply_to_message + pcode = message.text + if pcode[8:]: + pcode = str(pcode[8:]) + elif reply: + pcode = str(reply.message) + code = quote_plus(pcode) + global CARBONLANG + CARBON = f'https://carbon.now.sh/?l={CARBONLANG}&code={code}' + edit(message, f'`{get_translation("processing")}\n%25`') + if path.isfile('./carbon.png'): + remove('./carbon.png') + driver = get_webdriver() + driver.get(CARBON) + edit(message, f'`{get_translation("processing")}\n%50`') + driver.command_executor._commands['send_command'] = ( + 'POST', + '/session/$sessionId/chromium/send_command', + ) + driver.find_element(By.XPATH, "//button[contains(text(),'Export')]").click() + edit(message, f'`{get_translation("processing")}\n%75`') + while not path.isfile('./carbon.png'): + sleep(0.5) + edit(message, f'`{get_translation("processing")}\n%100`') + file = './carbon.png' + edit(message, f'`{get_translation("carbonUpload")}`') + reply_doc( + reply if reply else message, + file, + caption=get_translation('carbonResult'), + delete_after_send=True, + ) + message.delete() + driver.quit() + + +HELP.update({'carbon': get_translation('carbonInfo')}) diff --git a/sedenbot/modules/colors.py b/sedenbot/modules/fun/colors.py similarity index 82% rename from sedenbot/modules/colors.py rename to sedenbot/modules/fun/colors.py index ba1947b..cb40a4e 100644 --- a/sedenbot/modules/colors.py +++ b/sedenbot/modules/fun/colors.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,10 +8,8 @@ # from PIL import Image, ImageColor - from sedenbot import HELP -from sedenecem.core import (edit, reply_img, extract_args, - sedenify, get_translation) +from sedenecem.core import edit, extract_args, get_translation, reply_img, sedenify @sedenify(pattern='^.color') @@ -32,7 +30,8 @@ def color(message): 'sedencik.png', caption=input_str, delete_file=True, - delete_orig=True) + delete_orig=True, + ) else: edit(message, f'`{get_translation("colorsUsage")}`') diff --git a/sedenbot/modules/deepfry.py b/sedenbot/modules/fun/deepfry.py similarity index 65% rename from sedenbot/modules/deepfry.py rename to sedenbot/modules/fun/deepfry.py index 9915adc..69fd444 100644 --- a/sedenbot/modules/deepfry.py +++ b/sedenbot/modules/fun/deepfry.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -9,16 +9,21 @@ from os import remove from random import randint, uniform -from PIL import Image, ImageEnhance, ImageOps +from PIL import Image, ImageEnhance, ImageOps from sedenbot import HELP -from sedenecem.core import (edit, reply_img, sedenify, parse_cmd, - download_media_wc, get_translation) +from sedenecem.core import ( + download_media_wc, + edit, + get_translation, + parse_cmd, + reply_img, + sedenify, +) @sedenify(pattern='^.(deepf|f)ry') def deepfry(message): - text = (message.text or message.caption).split(' ', 1) fry = parse_cmd(text[0]) == 'fry' @@ -45,19 +50,19 @@ def deepfry(message): edit(message, f'`{get_translation("deepfryError")}`') return else: - edit(message, get_translation('deepfryNoPic', - ['`', f'{"f" if fry else "deepf"}ry'])) + edit( + message, + get_translation('deepfryNoPic', ['`', f'{"f" if fry else "deepf"}ry']), + ) return # Download Media edit(message, f'`{get_translation("deepfryDownload")}`') image_file = download_media_wc(reply, 'image.png') image = Image.open(image_file) - remove(image_file) # Apply effect to media - edit(message, get_translation( - 'deepfryApply', ['`', f'{"" if fry else "deep"}'])) + edit(message, get_translation('deepfryApply', ['`', f'{"" if fry else "deep"}'])) for _ in range(frycount): image = deepfry_media(image, fry) @@ -65,7 +70,9 @@ def deepfry(message): image.save(fried_io, 'JPEG') fried_io.close() - reply_img(message, 'image.jpeg', delete_file=True, delete_orig=True) + reply_img(reply or message, 'image.jpeg', delete_file=True) + remove(image_file) + message.delete() def deepfry_media(img: Image, fry: bool) -> Image: @@ -73,33 +80,33 @@ def deepfry_media(img: Image, fry: bool) -> Image: if fry: colors = ( (randint(50, 200), randint(40, 170), randint(40, 190)), - (randint(190, 255), randint(170, 240), randint(180, 250)) + (randint(190, 255), randint(170, 240), randint(180, 250)), ) - # Resim formatı ayarla + # Set image format img = img.copy().convert('RGB') width, height = img.width, img.height - temp_num = uniform(.8, .9) if fry else .75 - img = img.resize((int(width ** temp_num), - int(height ** temp_num)), - resample=Image.LANCZOS) + temp_num = uniform(0.8, 0.9) if fry else 0.75 + img = img.resize( + (int(width**temp_num), int(height**temp_num)), resample=Image.LANCZOS + ) - temp_num = uniform(.85, .95) if fry else .88 - img = img.resize((int(width ** temp_num), - int(height ** temp_num)), - resample=Image.BILINEAR) + temp_num = uniform(0.85, 0.95) if fry else 0.88 + img = img.resize( + (int(width**temp_num), int(height**temp_num)), resample=Image.BILINEAR + ) - temp_num = uniform(.89, .98) if fry else .9 - img = img.resize((int(width ** temp_num), - int(height ** temp_num)), - resample=Image.BICUBIC) + temp_num = uniform(0.89, 0.98) if fry else 0.9 + img = img.resize( + (int(width**temp_num), int(height**temp_num)), resample=Image.BICUBIC + ) img = img.resize((width, height), resample=Image.BICUBIC) temp_num = randint(3, 7) if fry else 4 img = ImageOps.posterize(img, temp_num) - # Renk yerleşimi oluştur + # Create a color scheme overlay = img.split()[0] temp_num = uniform(1.0, 2.0) if fry else 2 @@ -109,13 +116,11 @@ def deepfry_media(img: Image, fry: bool) -> Image: overlay = ImageEnhance.Brightness(overlay).enhance(temp_num) overlay = ImageOps.colorize( - overlay, - colors[0] if fry else (254, 0, 2), - colors[1] if fry else (255, 255, 15) + overlay, colors[0] if fry else (254, 0, 2), colors[1] if fry else (255, 255, 15) ) - # Kırmızı ve sarıyı ana görüntüye yerleştir ve keskinleştir - temp_num = uniform(0.1, 0.4) if fry else .75 + # Place red and yellow in image and sharpen + temp_num = uniform(0.1, 0.4) if fry else 0.75 img = Image.blend(img, overlay, temp_num) temp_num = randint(5, 300) if fry else 100 @@ -134,8 +139,11 @@ def check_media(reply_message): data = True elif reply_message.document: name = reply_message.document.file_name - if name and '.' in name and name[name.find( - '.') + 1:] in ['png', 'jpg', 'jpeg', 'webp']: + if ( + name + and '.' in name + and name[name.find('.') + 1 :] in ['png', 'jpg', 'jpeg', 'webp'] + ): data = True return data diff --git a/sedenbot/modules/fun/effects.py b/sedenbot/modules/fun/effects.py new file mode 100644 index 0000000..560a03c --- /dev/null +++ b/sedenbot/modules/fun/effects.py @@ -0,0 +1,142 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from os import path, remove + +from sedenbot import HELP +from sedenecem.core import ( + download_media_wc, + edit, + extract_args_split, + get_status_out, + get_translation, + reply_audio, + reply_video, + sedenify, +) + + +@sedenify(pattern='^.earrape') +def earrape(message): + args = extract_args_split(message) + reply = message.reply_to_message + + if args: + util = args[0].lower() + if util == 'mp4': + if not ( + reply + and ( + reply.video + or reply.video_note + or (reply.document and 'video' in reply.document.mime_type) + ) + ): + edit(message, f'`{get_translation("wrongMedia")}`') + else: + edit(message, f'`{get_translation("applyEarrape")}`') + media = download_media_wc(reply) + get_status_out( + f'ffmpeg -i {media} -af acrusher=.1:1:64:0:log {media}.mp4' + ) + edit(message, f'`{get_translation("uploadMedia")}`') + reply_video(reply or message, f'{media}.mp4', delete_file=True) + remove(media) + message.delete() + elif util == 'mp3': + if not ( + reply + and ( + reply.video + or reply.video_note + or ( + reply.audio + or reply.voice + or (reply.document and 'video' in reply.document.mime_type) + ) + ) + ): + edit(message, f'`{get_translation("wrongMedia")}`') + else: + edit(message, f'`{get_translation("applyEarrape")}`') + media = download_media_wc(reply) + get_status_out( + f'ffmpeg -i {media} -af acrusher=.1:1:64:0:log {media}.mp3' + ) + edit(message, f'`{get_translation("uploadMedia")}`') + reply_audio(reply or message, f'{media}.mp3', delete_file=True) + remove(media) + message.delete() + else: + edit(message, f'`{get_translation("wrongCommand")}`') + return + + +@sedenify(pattern='^.nightcore$') +def nightcore(message): + # Copyright (c) @kisekinopureya | 2021 + reply = message.reply_to_message + + if not ( + reply + and ( + reply.audio + or reply.voice + or (reply.document and 'audio' in reply.document.mime_type) + ) + ): + edit(message, f'`{get_translation("wrongMedia")}`') + else: + edit(message, f'`{get_translation("applyNightcore")}`') + media = download_media_wc(reply) + + filename = f'{media}.mp3' + if path.exists(filename): + remove(filename) + get_status_out( + f'ffmpeg -i {media} -af asetrate=44100*1.16,aresample=44100,atempo=1 {filename}' + ) + edit(message, f'`{get_translation("uploadMedia")}`') + reply_audio(reply or message, f'{media}.mp3', delete_file=True) + remove(media) + message.delete() + + +@sedenify(pattern='^.slowedtoperfection$') +def slowedtoperfection(message): + # Copyright (c) @kisekinopureya | 2021 + reply = message.reply_to_message + + if not ( + reply + and ( + reply.audio + or reply.voice + or (reply.document and 'audio' in reply.document.mime_type) + ) + ): + edit(message, f'`{get_translation("wrongMedia")}`') + else: + edit(message, f'`{get_translation("applySlowedtoperfection")}`') + media = download_media_wc(reply) + + filename = f'{media}.mp3' + if path.exists(filename): + remove(filename) + + get_status_out( + f'ffmpeg -i {media} -af aecho=1.0:0.7:20:0.5,asetrate=44100*0.84,aresample=44100,atempo=1 {filename}' + ) + edit(message, f'`{get_translation("uploadMedia")}`') + reply_audio(reply or message, f'{media}.mp3', delete_file=True) + remove(media) + message.delete() + + +HELP.update({'effects': get_translation('effectsInfo')}) diff --git a/sedenbot/modules/lastfm.py b/sedenbot/modules/fun/lastfm.py similarity index 77% rename from sedenbot/modules/lastfm.py rename to sedenbot/modules/fun/lastfm.py index dc077be..a943358 100644 --- a/sedenbot/modules/lastfm.py +++ b/sedenbot/modules/fun/lastfm.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -9,11 +9,10 @@ from re import sub from urllib.parse import quote -from pylast import User, LastFMNetwork, md5 +from pylast import LastFMNetwork, User, md5 from sedenbot import HELP, environ -from sedenecem.core import sedenify, edit, get_translation - +from sedenecem.core import edit, get_translation, sedenify # =================== CONSTANT =================== LASTFM_API = environ.get('LASTFM_API', None) @@ -22,10 +21,12 @@ LASTFM_PASSWORD_PLAIN = environ.get('LASTFM_PASSWORD', None) LASTFM_PASS = md5(LASTFM_PASSWORD_PLAIN) if LASTFM_API and LASTFM_SECRET and LASTFM_USERNAME and LASTFM_PASS: - lastfm = LastFMNetwork(api_key=LASTFM_API, - api_secret=LASTFM_SECRET, - username=LASTFM_USERNAME, - password_hash=LASTFM_PASS) + lastfm = LastFMNetwork( + api_key=LASTFM_API, + api_secret=LASTFM_SECRET, + username=LASTFM_USERNAME, + password_hash=LASTFM_PASS, + ) else: lastfm = None # ================================================ @@ -36,9 +37,8 @@ def last_fm(message): edit(message, f'`{get_translation("processing")}`') if not lastfm: return edit( - message, get_translation( - 'lastfmApiMissing', [ - '**', '`']), preview=False) + message, get_translation('lastfmApiMissing', ['**', '`']), preview=False + ) playing = User(LASTFM_USERNAME, lastfm).get_now_playing() username = f'https://www.last.fm/user/{LASTFM_USERNAME}' @@ -47,14 +47,13 @@ def last_fm(message): rectrack = quote(f'{playing}') rectrack = sub('^', 'https://open.spotify.com/search/', rectrack) output = get_translation( - 'lastfmProcess', [ - LASTFM_USERNAME, username, '__', playing, rectrack, '`', tags]) + 'lastfmProcess', + [LASTFM_USERNAME, username, '__', playing, rectrack, '`', tags], + ) else: recent = User(LASTFM_USERNAME, lastfm).get_recent_tracks(limit=5) playing = User(LASTFM_USERNAME, lastfm).get_now_playing() - output = get_translation( - 'lastfmProcess2', [ - LASTFM_USERNAME, username, '__']) + output = get_translation('lastfmProcess2', [LASTFM_USERNAME, username, '__']) for i, track in enumerate(recent): printable = artist_and_song(track) tags = gettags(track) diff --git a/sedenbot/modules/lyrics.py b/sedenbot/modules/fun/lyrics.py similarity index 69% rename from sedenbot/modules/lyrics.py rename to sedenbot/modules/fun/lyrics.py index ac213af..a506f24 100644 --- a/sedenbot/modules/lyrics.py +++ b/sedenbot/modules/fun/lyrics.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,23 +8,20 @@ # from lyricsgenius import Genius - -from sedenbot import HELP, GENIUS_TOKEN -from sedenecem.core import (edit, reply_doc, extract_args, - sedenify, get_translation) +from sedenbot import GENIUS_TOKEN, HELP +from sedenecem.core import edit, extract_args, get_translation, reply_doc, sedenify @sedenify(pattern='^.lyrics') def lyrics(message): args = extract_args(message) - if r"-" in args: + if r'-' in args: pass else: - edit(message, f'`{get_translation("lyricsError")}`') - return + return edit(message, f'`{get_translation("lyricsError")}`') if not GENIUS_TOKEN: - edit(message, f'`{get_translation("geniusToken")}`') + return edit(message, f'`{get_translation("geniusToken")}`') else: genius = Genius(GENIUS_TOKEN) try: @@ -52,12 +49,15 @@ def lyrics(message): if len(songs.lyrics) > 4096: edit(message, f'`{get_translation("lyricsOutput")}`') with open('lyrics.txt', 'w+') as f: - f.write(get_translation('lyricsQuery', [ - '', '', artist, song, songs.lyrics])) + f.write( + get_translation('lyricsQuery', ['', '', artist, song, songs.lyrics]) + ) reply_doc(message, 'lyrics.txt', delete_after_send=True) else: - edit(message, get_translation('lyricsQuery', [ - '**', '`', artist, song, songs.lyrics])) + edit( + message, + get_translation('lyricsQuery', ['**', '`', artist, song, songs.lyrics]), + ) return diff --git a/sedenbot/modules/fun/memes.py b/sedenbot/modules/fun/memes.py new file mode 100644 index 0000000..687c454 --- /dev/null +++ b/sedenbot/modules/fun/memes.py @@ -0,0 +1,992 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from io import BytesIO +from random import choice, getrandbits, randint +from re import sub +from textwrap import wrap +from time import sleep +from pyrogram import enums + +from cowpy import cow +from PIL import Image, ImageDraw, ImageFont +from requests import get +from sedenbot import HELP +from sedenecem.core import ( + download_media_wc, + edit, + extract_args, + extract_args_split, + get_download_dir, + get_translation, + parse_cmd, + reply_img, + reply_sticker, + sedenify, +) + +# ================= CONSTANT ================= +ZALGS = [ + [ + '̖', + ' ̗', + ' ̘', + ' ̙', + ' ̜', + ' ̝', + ' ̞', + ' ̟', + ' ̠', + ' ̤', + ' ̥', + ' ̦', + ' ̩', + ' ̪', + ' ̫', + ' ̬', + ' ̭', + ' ̮', + ' ̯', + ' ̰', + ' ̱', + ' ̲', + ' ̳', + ' ̹', + ' ̺', + ' ̻', + ' ̼', + ' ͅ', + ' ͇', + ' ͈', + ' ͉', + ' ͍', + ' ͎', + ' ͓', + ' ͔', + ' ͕', + ' ͖', + ' ͙', + ' ͚', + ' ', + ], + [ + ' ̍', + ' ̎', + ' ̄', + ' ̅', + ' ̿', + ' ̑', + ' ̆', + ' ̐', + ' ͒', + ' ͗', + ' ͑', + ' ̇', + ' ̈', + ' ̊', + ' ͂', + ' ̓', + ' ̈́', + ' ͊', + ' ͋', + ' ͌', + ' ̃', + ' ̂', + ' ̌', + ' ͐', + ' ́', + ' ̋', + ' ̏', + ' ̽', + ' ̉', + ' ͣ', + ' ͤ', + ' ͥ', + ' ͦ', + ' ͧ', + ' ͨ', + ' ͩ', + ' ͪ', + ' ͫ', + ' ͬ', + ' ͭ', + ' ͮ', + ' ͯ', + ' ̾', + ' ͛', + ' ͆', + ' ̚', + ], + [ + ' ̕', + ' ̛', + ' ̀', + ' ́', + ' ͘', + ' ̡', + ' ̢', + ' ̧', + ' ̨', + ' ̴', + ' ̵', + ' ̶', + ' ͜', + ' ͝', + ' ͞', + ' ͟', + ' ͠', + ' ͢', + ' ̸', + ' ̷', + ' ͡', + ], +] + +EMOJIS = [ + '😂', + '😂', + '👌', + '✌', + '💞', + '👍', + '👌', + '💯', + '🎶', + '👀', + '😂', + '👓', + '👏', + '👐', + '🍕', + '💥', + '🍴', + '💦', + '💦', + '🍑', + '🍆', + '😩', + '😏', + '👉👌', + '👀', + '👅', + '😩', + '🚰', + '♿', +] + +UWUS = [ + '(・`ω´・)', + ';;w;;', + 'owo', + 'UwU', + '>w<', + '^w^', + r'\(^o\) (/o^)/', + '( ^ _ ^)∠☆', + '(ô_ô)', + '~:o', + ';-;', + '(*^*)', + '(>_', + '(♥_♥)', + '*(^O^)*', + '((+_+))', +] + +REACTS = [ + 'ʘ‿ʘ', + 'ヾ(-_- )ゞ', + '(っ˘ڡ˘ς)', + '(´ж`ς)', + '( ಠ ʖ̯ ಠ)', + '(° ͜ʖ͡°)╭∩╮', + '(ᵟຶ︵ ᵟຶ)', + '(งツ)ว', + 'ʚ(•`', + '(っ▀¯▀)つ', + '(◠﹏◠)', + '( ͡ಠ ʖ̯ ͡ಠ)', + '( ఠ ͟ʖ ఠ)', + '(∩`-´)⊃━☆゚.*・。゚', + '(⊃。•́‿•̀。)⊃', + '(._.)', + '{•̃_•̃}', + '(ᵔᴥᵔ)', + '♨_♨', + '⥀.⥀', + 'ح˚௰˚づ ', + '(҂◡_◡)', + '(っ•́。•́)♪♬', + '◖ᵔᴥᵔ◗ ♪ ♫ ', + '(☞゚ヮ゚)☞', + '[¬º-°]¬', + '(Ծ‸ Ծ)', + '(•̀ᴗ•́)و ̑̑', + 'ヾ(´〇`)ノ♪♪♪', + "(ง'̀-'́)ง", + 'ლ(•́•́ლ)', + 'ʕ •́؈•̀ ₎', + '♪♪ ヽ(ˇ∀ˇ )ゞ', + 'щ(゚Д゚щ)', + '( ˇ෴ˇ )', + '눈_눈', + '(๑•́ ₃ •̀๑) ', + '( ˘ ³˘)♥ ', + 'ԅ(≖‿≖ԅ)', + '♥‿♥', + '◔_◔', + '⁽⁽ଘ( ˊᵕˋ )ଓ⁾⁾', + '乁( ◔ ౪◔)「 ┑( ̄Д  ̄)┍', + '( ఠൠఠ )ノ', + '٩(๏_๏)۶', + '┌(ㆆ㉨ㆆ)ʃ', + 'ఠ_ఠ', + '(づ。◕‿‿◕。)づ', + '(ノಠ ∩ಠ)ノ彡( \\o°o)\\', + '“ヽ(´▽`)ノ”', + '༼ ༎ຶ ෴ ༎ຶ༽', + '。゚( ゚இ‸இ゚)゚。', + '(づ ̄ ³ ̄)づ', + '(⊙.☉)7', + 'ᕕ( ᐛ )ᕗ', + 't(-_-t)', + '(ಥ⌣ಥ)', + 'ヽ༼ ಠ益ಠ ༽ノ', + '༼∵༽ ༼⍨༽ ༼⍢༽ ༼⍤༽', + 'ミ●﹏☉ミ', + '(⊙_◎)', + '¿ⓧ_ⓧﮌ', + 'ಠ_ಠ', + '(´・_・`)', + 'ᕦ(ò_óˇ)ᕤ', + '⊙﹏⊙', + '(╯°□°)╯︵ ┻━┻', + r'¯\_(⊙︿⊙)_/¯', + '٩◔̯◔۶', + '°‿‿°', + 'ᕙ(⇀‸↼‶)ᕗ', + '⊂(◉‿◉)つ', + 'V•ᴥ•V', + 'q(❂‿❂)p', + 'ಥ_ಥ', + 'ฅ^•ﻌ•^ฅ', + 'ಥ﹏ಥ', + '( ^_^)o自自o(^_^ )', + 'ಠ‿ಠ', + 'ヽ(´▽`)/', + 'ᵒᴥᵒ#', + '( ͡° ͜ʖ ͡°)', + '┬─┬ ノ( ゜-゜ノ)', + 'ヽ(´ー`)ノ', + '☜(⌒▽⌒)☞', + 'ε=ε=ε=┌(;*´Д`)ノ', + '(╬ ಠ益ಠ)', + '┬─┬⃰͡ (ᵔᵕᵔ͜ )', + '┻━┻ ︵ヽ(`Д´)ノ︵ ┻━┻', + r'¯\_(ツ)_/¯', + 'ʕᵔᴥᵔʔ', + '(`・ω・´)', + 'ʕ•ᴥ•ʔ', + 'ლ(`ー´ლ)', + 'ʕʘ̅͜ʘ̅ʔ', + '( ゚Д゚)', + r'¯\(°_o)/¯', + '(。◕‿◕。)', +] + +RUNS = [get_translation(f'runstr{i+1}') for i in range(0, 48)] + +SHGS = [ + '┐(´д`)┌', + '┐(´~`)┌', + '┐(´ー`)┌', + '┐( ̄ヘ ̄)┌', + '╮(╯∀╰)╭', + '╮(╯_╰)╭', + '┐(´д`)┌', + '┐(´∀`)┌', + 'ʅ(́◡◝)ʃ', + '┐(゚~゚)┌', + "┐('д')┌", + '┐(‘~`;)┌', + 'ヘ(´-`;)ヘ', + '┐( -“-)┌', + 'ʅ(´◔౪◔)ʃ', + 'ヽ(゜~゜o)ノ', + 'ヽ(~~~ )ノ', + '┐(~ー~;)┌', + '┐(-。ー;)┌', + r'¯\_(ツ)_/¯', + r'¯\_(⊙_ʖ⊙)_/¯', + r'¯\_༼ ಥ ‿ ಥ ༽_/¯', + '乁( ⁰͡ Ĺ̯ ⁰͡ ) ㄏ', +] + +CRYS = [ + 'أ‿أ', + '╥﹏╥', + '(;﹏;)', + '(ToT)', + '(┳Д┳)', + '(ಥ﹏ಥ)', + '(;へ:)', + '(T_T)', + '(πーπ)', + '(T▽T)', + '(⋟﹏⋞)', + '(iДi)', + '(´Д⊂ヽ', + '(;Д;)', + '(>﹏<)', + '(TдT)', + '(つ﹏⊂)', + '༼☯﹏☯༽', + '(ノ﹏ヽ)', + '(ノAヽ)', + '(╥_╥)', + '(T⌓T)', + '(༎ຶ⌑༎ຶ)', + '(☍﹏⁰)。', + '(ಥ_ʖಥ)', + '(つд⊂)', + '(≖͞_≖̥)', + '(இ﹏இ`。)', + '༼ಢ_ಢ༽', + '༼ ༎ຶ ෴ ༎ຶ༽', +] + +XDA_STRINGS = [ + 'sur', + 'Sir', + 'bro', + 'yes', + 'no', + 'bolte', + 'bolit', + 'bholit', + 'volit', + 'mustah', + 'fap', + 'lit', + 'lmao', + 'iz', + 'jiosim', + 'ijo', + 'nut', + 'workz', + 'workang', + 'flashabl zip', + 'bateri', + 'bacup', + 'bad englis', + 'sar', + 'treble wen', + 'gsi', + 'fox bag', + 'bag fox', + 'fine', + 'bast room', + 'fax', + 'trable', + 'kenzo', + 'plz make room', + 'andreid pai', + 'when', + 'port', + 'mtk', + 'send moni', + 'bad rom', + 'dot', + 'rr', + 'linage', + 'arrows', + 'kernal', + 'meme12', + 'bruh', + 'imail', + 'email', + 'plaka', + 'evox', +] +# ================= CONSTANT ================= + + +@sedenify(pattern=r'^.(\w+)say') +def cowsay(message): + ext = message.text.split(' ', 1) + arg = parse_cmd(ext[0]) + arg = arg[: arg.find('say')] + textx = message.reply_to_message + if textx and textx.text: + text = textx.text + elif len(ext) > 1: + text = ext[1] + else: + edit(message, f'`{get_translation("wrongCommand")}`') + return + + if arg == 'cow' or arg not in cow.COWACTERS: + arg = 'default' + + cheese = cow.get_cow(arg) + cheese = cheese() + + edit(message, f"`{cheese.milk(text).replace('`', '´')}`") + + +@sedenify(pattern='^:/$') +def kek(message): + uio = ['/', '\\'] + for i in range(1, 15): + sleep(0.3) + edit(message, f':{uio[i % len(uio)]}') + + +@sedenify(pattern='^.cry$') +def cry(message): + edit(message, choice(CRYS)) + + +@sedenify(pattern='^.cp') +def copypasta(message): + textx = message.reply_to_message + copypasta = extract_args(message) + + if len(copypasta) > 0: + pass + elif textx: + copypasta = textx.text + else: + edit(message, f'`{get_translation("cpUsage")}`') + return + + reply_text = choice(EMOJIS) + b_char = choice(copypasta).lower() + for owo in copypasta: + if owo == ' ': + reply_text += choice(EMOJIS) + elif owo in EMOJIS: + reply_text += owo + reply_text += choice(EMOJIS) + elif owo.lower() == b_char: + reply_text += '🅱️' + else: + if bool(getrandbits(1)): + reply_text += owo.upper() + else: + reply_text += owo.lower() + reply_text += choice(EMOJIS) + edit(message, reply_text) + + +@sedenify(pattern='^.vapor') +def vapor(message): + reply_text = [] + textx = message.reply_to_message + vapor = extract_args(message) + if len(vapor) > 0: + pass + elif textx: + vapor = textx.text + else: + edit(message, f'`{get_translation("vaporUsage")}`') + return + + for charac in vapor: + if 0x21 <= ord(charac) <= 0x7F: + reply_text.append(chr(ord(charac) + 0xFEE0)) + elif ord(charac) == 0x20: + reply_text.append(chr(0x3000)) + else: + reply_text.append(charac) + + edit(message, ''.join(reply_text)) + + +@sedenify(pattern='^.str') +def stretch(message): + textx = message.reply_to_message + stretch = extract_args(message) + if len(stretch) > 0: + pass + elif textx: + stretch = textx.text + else: + edit(message, f'`{get_translation("strUsage")}`') + return + + count = randint(3, 10) + reply_text = sub(r'([aeiouAEIOUaeiouAEIOUаеиоуюяыэё])', (r'\1' * count), stretch) + edit(message, reply_text) + + +@sedenify(pattern='^.zal') +def zalgofy(message): + reply_text = [] + textx = message.reply_to_message + zalgofy = extract_args(message) + if len(zalgofy) > 0: + pass + elif textx: + zalgofy = textx.text + else: + edit(message, f'`{get_translation("zalUsage")}`') + return + + for charac in zalgofy: + if not charac.isalpha(): + reply_text.append(charac) + continue + + for _ in range(0, 3): + charac += choice(ZALGS[randint(0, 2)]).strip() + + reply_text.append(charac) + + edit(message, ''.join(reply_text)) + + +@sedenify(pattern='^.owo') +def owo(message): + textx = message.reply_to_message + owo = extract_args(message) + if len(owo) > 0: + pass + elif textx: + owo = textx.text + else: + edit(message, f'`{get_translation("owoUsage")}`') + return + + reply_text = sub(r'(r|l)', 'w', owo) + reply_text = sub(r'(R|L)', 'W', reply_text) + reply_text = sub(r'n([aeiou])', r'ny\1', reply_text) + reply_text = sub(r'N([aeiouAEIOU])', r'Ny\1', reply_text) + reply_text = sub(r'\!+', ' ' + choice(UWUS), reply_text) + reply_text = reply_text.replace('ove', 'uv') + reply_text += ' ' + choice(UWUS) + edit(message, reply_text) + + +@sedenify(pattern='^.mock') +def mock(message): + reply_text = [] + textx = message.reply_to_message + mock = extract_args(message) + if len(mock): + pass + elif textx: + mock = textx.text + else: + edit(message, f'`{get_translation("mockUsage")}`') + return + + for charac in mock: + if charac.isalpha() and randint(0, 1): + to_app = charac.upper() if charac.islower() else charac.lower() + reply_text.append(to_app) + else: + reply_text.append(charac) + + edit(message, ''.join(reply_text)) + + +@sedenify(pattern='^.clap') +def clap(message): + textx = message.reply_to_message + clap = extract_args(message) + if clap: + pass + elif textx: + clap = textx.text + else: + edit(message, f'`{get_translation("clapUsage")}`') + return + reply_text = '👏 ' + reply_text += clap.replace(' ', ' 👏 ') + reply_text += ' 👏' + edit(message, reply_text) + + +@sedenify(pattern='^.lfy') +def lfy(message): + textx = message.reply_to_message + qry = extract_args(message) + if qry: + query = str(qry) + elif textx: + query = textx + else: + edit(message, f'`{get_translation("wrongCommand")}`') + return + query = query.message + query_encoded = query.replace(' ', '+') + lfy_url = f'http://lmgtfy.com/?s=g&iie=1&q={query_encoded}' + payload = {'format': 'json', 'url': lfy_url} + r = get('http://is.gd/create.php', params=payload) + edit( + message, + f'`{get_translation("lfyResult")}`' f"\n[{query}]({r.json()['shorturl']})", + ) + + +@sedenify(pattern='.action') +def set_action(message): + ACTIONS = { + 'typing': enums.ChatAction.TYPING, + 'photo': enums.ChatAction.UPLOAD_PHOTO, + 'rec_video': enums.ChatAction.RECORD_VIDEO, + 'video': enums.ChatAction.UPLOAD_VIDEO, + 'rec_audio': enums.ChatAction.RECORD_AUDIO, + 'audio': enums.ChatAction.UPLOAD_AUDIO, + 'document': enums.ChatAction.UPLOAD_DOCUMENT, + 'location': enums.ChatAction.FIND_LOCATION, + 'rec_videonote': enums.ChatAction.RECORD_VIDEO_NOTE, + 'videonote': enums.ChatAction.UPLOAD_VIDEO_NOTE, + 'game': enums.ChatAction.PLAYING, + 'contact': enums.ChatAction.CHOOSE_CONTACT, + 'speaking': enums.ChatAction.SPEAKING, + 'import_history': enums.ChatAction.IMPORT_HISTORY, + 'sticker': enums.ChatAction.CHOOSE_STICKER, + 'cancel': enums.ChatAction.CANCEL, + } + args = extract_args_split(message) + scam_action = None + scam_time = None + + if len(args) == 1: + try: + scam_action = ACTIONS[args[0].lower()] + except KeyError: + try: + scam_time = int(args[0]) + except: + pass + elif len(args) == 2: + try: + scam_action = ACTIONS[args[0].lower()] + scam_time = int(args[1]) + except KeyError: + try: + scam_time = int(args[0]) + except: + pass + except ValueError: + pass + else: + edit(message, f'{get_translation("wrongCommand")}') + return + + if scam_action is None: + scam_action = choice(list(ACTIONS.values())) + + if scam_time is None: + scam_time = randint(30, 60) + + try: + if scam_time > 0: + message.delete() + while scam_time > 0: + message.reply_chat_action(scam_action) + sleep(2) + scam_time = scam_time - 2 + except BaseException: + return + + +@sedenify(pattern='^.type') +def type(message): + textx = message.reply_to_message + type = extract_args(message) + if type: + pass + elif textx: + type = textx.text + else: + edit(message, f'`{get_translation("wrongCommand")}`') + return + typing_symbol = '|' + old_text = '' + edit(message, typing_symbol) + sleep(0.3) + for character in type: + old_text = old_text + '' + character + typing_text = old_text + '' + typing_symbol + edit(message, typing_text) + sleep(0.03) + edit(message, old_text) + sleep(0.03) + + +@sedenify(pattern='^[Ss]krrt$') +def skrrt(message): + t = f'{(message.text or message.caption)[0]}krrt' + for j in range(16): + t = f'{t[:-1]}rt' + edit(message, t) + + +@sedenify(pattern='^[Oo]of$') +def oof(message): + t = f'{(message.text or message.caption)[0]}of' + for j in range(16): + t = f'{t[:-1]}of' + edit(message, t) + + +@sedenify(pattern='^.10iq$') +def iqless(message): + edit( + message, + 'DÜÜÜT DÜÜÜTT AÇ YOLU AÇÇ HADİ ASLAN PARÇASI YOLU AÇ \n' + 'HADİ BAK ENGELLİ BEKLİYO BURDA HADİ DÜÜÜTTT ♿️ BAK \n' + 'SİNİRLENDİ ARKADAŞ HADİ YOLU AÇ HADİİ DÜÜÜT DÜÜTT BİİİPP \n' + 'HADİ BE HIZLI OLL DÜÜÜTT BİİİPPP ♿️♿️ BAK HIZLANDI ENGELLİ \n' + 'KARDEŞİMİZ SERİ KÖZ GETİR SERİ DÜÜÜTT DÜÜÜT DÜÜÜÜTTTTT \n' + 'BİİİİPPP BİİİİİPPP DÜÜÜTTT ♿️♿️♿️♿️ BAK ARTIYO SAYILARI \n' + 'AÇTIN MI YOLU AÇMADIN PÜÜÜÜ REZİİİLL DÜÜÜÜTTT ♿️♿️♿️ \n' + '♿️♿️♿️ BAK KALABALIKLASTI BAK DELI GELIYOR DELIRDI DELI \n' + 'AC YOLU DUTDUTDURURURUDUTTT♿️♿️♿️♿️♿️♿️♿️♿️♿️ \n' + '♿️♿️♿️♿️♿️KAFAYI YEDI BUNLAR AC LAAAAN YOLU', + ) + + +@sedenify(pattern='^.mizah$') +def mizahshow(message): + edit( + message, + '⚠️⚠️⚠️MmMmMmMizahh Şoww😨😨😨😨😱😱😱😱😱 \n' + '😱😱⚠️⚠️ 😂😂😂😂😂😂😂😂😂😂😂😂😂😂😱😵 \n' + '😂😂👍👍👍👍👍👍👍👍👍👍👍👍👍 MiZah \n' + 'ŞeLaLesNdEn b1r yUdm aLdım✔️✔️✔️✔️ \n' + 'AHAHAHAHAHAHHAHAHAHAHAHAHAHAHAHAHAHHAHAHAHAHA \n' + 'HAHAHAHAHAHAHHAHAHAHAHAHAHA😂😂😂😂😂😂😂😂 \n' + '😂 KOMİK LAN KOMİİİK \n' + 'heLaL LaN ✔️✔️✔️✔️✔️✔️✔️✔️👏👏👏👏👏👏👏👏 \n' + '👏 EfSaNe mMmMiZah şooooovv 👏👏👏👏👏😂😂😂😂 \n' + '😂😂😂😂😂😂⚠️ \n' + '💯💯💯💯💯💯💯💯💯 \n' + 'KNK AYNI BİİİZ 😂😂😂👏👏 \n' + '💯💯⚠️⚠️♿️AÇ YOLU POST SAHİBİ VE ONU ♿️SAVUNANLAR \n' + 'GELIYOR ♿️♿️ DÜÜTT♿️ \n' + 'DÜÜÜÜT♿️DÜÜT♿️💯💯⚠️ \n' + '♿️KOMİİİK ♿️ \n' + 'CJWJCJWJXJJWDJJQUXJAJXJAJXJWJFJWJXJAJXJWJXJWJFIWIXJQJJQJASJAXJ \n' + 'AJXJAJXJJAJXJWJFWJJFWIIFIWICIWIFIWICJAXJWJFJEICIIEICIEIFIWICJSXJJS \n' + 'CJEIVIAJXBWJCJIQICIWJX💯💯💯💯💯💯😂😂😂😂😂😂😂 \n' + '😂⚠️😂😂😂😂😂😂⚠️⚠️⚠️😂😂😂😂♿️♿️♿️😅😅 \n' + '😅😂👏💯⚠️👏♿️🚨', + ) + + +@sedenify(pattern='^.h$') +def h(message): + edit( + message, + '⠀⠀⠀⠀⠀⠀⠀⢀⠀⠂⠂⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠠⠀⠀⠀⠀⠀\n' + '⠀⠀⠀⠀⠀⠄⠈⠐⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢂⠀⠀⠀⠀\n' + '⠀⠀⠀⠀⡐⠀⠀⠀⠀⠀⠀⠀⢡⠀⠀⠀⠀⠀⠀⠀⠀⠀⠊⠀⠀⢸⠀⠀⠀⠀\n' + '⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠀⠀⠀⠑⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀\n' + '⠀⠀⠀⠀⠀⠀⠀⠀⠉⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⢣⠀⠀⠀\n' + '⠀⠀⠀⢸⠀⠀⠀⡜⠀⡆⠀⠀⠀⢀⣲⠀⠀⠀⠀⠀⠀⠴⠀⠀⡇⠀⠀⡀⠀⠀\n' + '⠀⠀⠀⡜⠀⠀⠁⠀⠀⠘⠀⠀⠀⠀⠀⢘⣄⠀⠀⠀⡜⣀⠀⢠⠉⠀⠀⢠⠀⠀\n' + '⠀⠀⠀⣄⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⢠⠀⠈⠛⠛⠒⡀⠀⡇⠀⡄⠀⠈⠀⠀\n' + '⠀⠀⠀⢒⠀⠀⡱⠀⠀⠀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠇⠀⠈⠀⠀⠀⠀\n' + '⠀⠀⠀⢸⠀⢠⠀⠀⠀⢸⠀⠀⠀⠀⢀⠀⠙⠁⠀⠁⣉⠊⠀⡆⠀⠀⠈⠀⡅⠀\n' + '⠀⠀⠀⠀⡀⠈⠀⠀⠀⠃⠀⠀⠀⠀⡌⠈⠀⠑⠃⠋⠀⠀⠀⡇⠀⠀⠀⠀⢠⠀\n' + '⠀⠀⠀⠀⠘⠀⠈⡀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⡀⠈⠀\n' + '⠀⠀⠀⠀⠀⣂⢀⢸⠀⢱⢀⣤⢀⠀⠃⠀⠀⠀⠀⢂⠀⠀⠂⠂⠀⠀⠀⣘⡈⡀\n' + '⠀⠀⠀⠀⠀⠀⠠⠹⠓⢸⠀⠀⢀⠓⠀⠀⠀⠀⠀⡞⢀⠀⢀⠀⠀⠀⠐⢹⠂⠀\n' + '⠀⠀⠀⠀⠀⠀⠀⠈⠛⠇⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠂⠁⠀⠀⠀⠀⠀⠀⠀\n' + '⠀⠀⠀⠀⠀⠀⠀⠀⢰⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀\n' + '⠀⠀⠀⠀⠀⠀⠀⠀⡌⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠊⠀⠀⠀⠀⠀⠀⠀⠀\n' + '⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⡠⠀⠀⠀⠀⠀⠀⡆⠀⢰⠀⠀⠀⠀⠀⠀⠀⠀⠀\n' + '⠀⠀⠀⠀⠀⠀⠀⠊⠠⠂⠉⢤⣀⠀⠀⠀⠀⢠⠀⠐⠣⠠⢤⠀⠀⠀⠀⠀⠀⠀\n' + '⠀⠀⠀⠀⠀⠀⠀⠁⠂⠤⠼⠓⠓⠒⠀⠀⠀⠈⠂⠀⠀⠀⠂⠚⠁⠀⠀⠀⠀⠀', + ) + + +@sedenify(pattern='^.(amogu|su)s') +def amogus(message): + args = extract_args(message) + reply = message.reply_to_message + if args: + pass + elif reply: + if not reply.text: + return edit(message, f'`{get_translation("wrongCommand")}`') + args = reply.text + else: + edit(message, f'`{get_translation("wrongCommand")}`') + return + + edit(message, f"`{get_translation('processing')}`") + + arr = randint(1, 12) + fontsize = 100 + FONT_FILE = 'sedenecem/fonts/OpenSans.ttf' + # https://github.com/KeyZenD/AmongUs + url = 'https://raw.githubusercontent.com/KeyZenD/AmongUs/master/' + font = ImageFont.truetype(FONT_FILE, size=int(fontsize)) + + imposter = Image.open(BytesIO(get(f'{url}{arr}.png').content)) + text_ = '\n'.join(['\n'.join(wrap(part, 30)) for part in args.split('\n')]) + w, h = ImageDraw.Draw(Image.new('RGB', (1, 1))).multiline_textsize( + text_, font, stroke_width=2 + ) + text = Image.new('RGBA', (w + 40, h + 40)) + ImageDraw.Draw(text).multiline_text( + (15, 15), text_, '#FFF', font, stroke_width=2, stroke_fill='#000' + ) + w = imposter.width + text.width + 30 + h = max(imposter.height, text.height) + image = Image.new('RGBA', (w, h)) + image.paste(imposter, (0, h - imposter.height), imposter) + image.paste(text, (w - text.width, 0), text) + image.thumbnail((512, 512)) + + output = BytesIO() + output.name = 'sus.webp' + image.save(output, 'WebP') + output.seek(0) + + reply_sticker(reply or message, output) + message.delete() + + +@sedenify(pattern='^.gay') +def gay_calculator(message): + args = extract_args(message) + reply = message.reply_to_message + random = randint(0, 100) + + try: + replied_user = reply.from_user + except BaseException: + pass + + if random: + if args: + return edit(message, f'**{get_translation("gayString", [args, random])}**') + if reply: + if replied_user.is_self: + edit(message, f'**{get_translation("gayString3", [random])}**') + else: + return edit(message, f'**{get_translation("gayString2", [random])}**') + edit(message, f'**{get_translation("gayString3", [random])}**') + + +@sedenify(pattern='^.(r(eact|un)|shg|xda)$') +def react_shg_run_xda(message): + options = message.text[1:] + if options == 'react': + options_list = REACTS + elif options == 'shg': + options_list = SHGS + elif options == 'run': + options_list = RUNS + elif options == 'xda': # Quotes taken from friendly-telegram (https://gitlab.com/friendly-telegram) + options_list = XDA_STRINGS + else: + return + edit(message, choice(options_list)) + + +@sedenify(pattern='^.f (.*)') +def payf(message): + paytext = extract_args(message) + pay = ( + f'{paytext * 8}\n{paytext * 8}\n{paytext * 2}\n{paytext * 2}' + f'\n{paytext * 2}\n{paytext * 6}\n{paytext * 6}\n{paytext * 2}' + f'\n{paytext * 2}\n{paytext * 2}\n{paytext * 2}\n{paytext * 2}' + ) + edit(message, pay) + + +@sedenify(pattern='.mem') +def meme_maker(message): + args = extract_args(message).upper().split(',') + reply = message.reply_to_message + font = 'sedenecem/fonts/impact.ttf' + if len(args) == 2: + top, bottom = args[0], args[1] + else: + bottom = args[0 if args[1] == '' else 1] + + if ( + reply + and reply.media + and ( + reply.photo + or (reply.sticker and not reply.sticker.is_animated) + or (reply.document and 'image' in reply.document.mime_type) + ) + ): + media = download_media_wc(reply, f'{get_download_dir()}/image.jpg') + image = Image.open(media) + draw = ImageDraw.Draw(image) + width, height = image.size + estimated_font_size = find_font_size(''.join(args), font, image, 1) + + text_font = ImageFont.truetype(font, estimated_font_size) + text_per_line = width // text_font.size + top_text = wrap(top, width=(text_per_line + 5)) + bottom_text = wrap(bottom, width=(text_per_line + 5)) + y = 10 + for text in top_text: + text_width, text_height = text_font.getsize(text) + x = (width - text_width) / 2 + draw.text( + (x, y), + text, + fill='white', + font=text_font, + stroke_width=3, + stroke_fill='black', + ) + y += text_height + y = height - text_height * len(bottom_text) - 15 + for text in bottom_text: + text_width, text_height = text_font.getsize(text) + x = (width - text_width) / 2 + draw.text( + (x, y), + text, + fill='white', + font=text_font, + stroke_width=3, + stroke_fill='black', + ) + y += text_height + + image.convert('RGB').save(media, 'JPEG') + reply_img(reply or message, media, delete_file=True) + message.delete() + else: + edit(message, 'Lütfen bir resim yanıtlayın.') + + +def get_text_size(text, image, font): + im = Image.new('RGB', (image.width, image.height)) + draw = ImageDraw.Draw(im) + return draw.textsize(text, font) + + +def find_font_size(text, font, image, target_width_ratio): + # https://stackoverflow.com/a/66091387 + tested_font_size = 100 + tested_font = ImageFont.truetype(font, tested_font_size) + observed_width, observed_height = get_text_size(text, image, tested_font) + estimated_font_size = ( + tested_font_size / (observed_width / image.width) * target_width_ratio + ) + return round(estimated_font_size) + + +HELP.update({'memes': get_translation('memesInfo')}) diff --git a/sedenbot/modules/quotly.py b/sedenbot/modules/fun/quotly.py similarity index 81% rename from sedenbot/modules/quotly.py rename to sedenbot/modules/fun/quotly.py index 98b88ab..960e07a 100644 --- a/sedenbot/modules/quotly.py +++ b/sedenbot/modules/fun/quotly.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,14 +8,14 @@ # from time import sleep -from pyrogram.errors import YouBlockedUser +from pyrogram.errors import YouBlockedUser from sedenbot import HELP -from sedenecem.core import sedenify, edit, get_translation, PyroConversation +from sedenecem.core import PyroConversation, edit, get_translation, sedenify -@sedenify(pattern='^.q$', compat=False) -def quotly(client, message): +@sedenify(pattern='^.q$') +def quotly(message): reply = message.reply_to_message if reply and (reply.text or reply.photo or reply.sticker): edit(message, f'`{get_translation("makeQuote")}`') @@ -26,7 +26,7 @@ def quotly(client, message): sleep(1) chat = 'QuotLyBot' - with PyroConversation(client, chat) as conv: + with PyroConversation(message, chat) as conv: response = None try: conv.forward_msg(reply) diff --git a/sedenbot/modules/rgb.py b/sedenbot/modules/fun/rgb.py similarity index 74% rename from sedenbot/modules/rgb.py rename to sedenbot/modules/fun/rgb.py index 0b3aa85..8e3f17f 100644 --- a/sedenbot/modules/rgb.py +++ b/sedenbot/modules/fun/rgb.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,24 +8,22 @@ # from io import BytesIO -from os import remove from random import randint from textwrap import wrap from PIL import Image, ImageChops, ImageDraw, ImageFont - from sedenbot import HELP -from sedenecem.core import (extract_args, sedenify, edit, - send_sticker, get_translation) +from sedenecem.core import edit, extract_args, get_translation, reply_sticker, sedenify -@sedenify(pattern='^.rgb', compat=False) -def sticklet(client, message): +@sedenify(pattern='^.rgb') +def sticklet(message): R = randint(0, 256) G = randint(0, 256) B = randint(0, 256) sticktext = extract_args(message) + reply = message.reply_to_message if len(sticktext) < 1: edit(message, f'`{get_translation("wrongCommand")}`') @@ -41,7 +39,7 @@ def sticklet(client, message): draw = ImageDraw.Draw(image) fontsize = 230 - FONT_FILE = 'sedenecem/fonts/GoogleSans.ttf' + FONT_FILE = 'sedenecem/fonts/OpenSans.ttf' font = ImageFont.truetype(FONT_FILE, size=fontsize) @@ -52,8 +50,9 @@ def sticklet(client, message): font = ImageFont.truetype(FONT_FILE, size=int(fontsize)) width, height = draw.multiline_textsize(sticktext, font=font) - draw.multiline_text(((512 - width) / 2, (512 - height) / 2), - sticktext, font=font, fill=(R, G, B)) + draw.multiline_text( + ((512 - width) / 2, (512 - height) / 2), sticktext, font=font, fill=(R, G, B) + ) image_stream = BytesIO() image_stream.name = 'image.webp' @@ -69,12 +68,8 @@ def trim(im): image.save(image_stream, 'WebP') image_stream.seek(0) - send_sticker(client, message.chat, image_stream) + reply_sticker(reply or message, image_stream, delete_file=True) message.delete() - try: - remove(image_stream.name) - except BaseException: - pass HELP.update({'rgb': get_translation('rgbInfo')}) diff --git a/sedenbot/modules/spammer.py b/sedenbot/modules/fun/spammer.py similarity index 67% rename from sedenbot/modules/spammer.py rename to sedenbot/modules/fun/spammer.py index dd0d7f4..c3ecda4 100644 --- a/sedenbot/modules/spammer.py +++ b/sedenbot/modules/fun/spammer.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -10,9 +10,18 @@ from threading import Event from sedenbot import HELP -from sedenecem.core import (edit, reply, reply_img, send_log, extract_args, - extract_args_arr, sedenify, get_translation, - spam_allowed, increment_spam_count) +from sedenecem.core import ( + edit, + extract_args, + extract_args_split, + get_translation, + increment_spam_count, + reply, + reply_img, + sedenify, + send_log, + spam_allowed, +) @sedenify(pattern='^.tspam') @@ -26,8 +35,8 @@ def tspam(message): if not spam_allowed(): return - for metin in tspam.replace(' ', ''): - reply(message, metin) + for text in tspam.replace(' ', ''): + reply(message, text) count = increment_spam_count() if not count: break @@ -51,12 +60,12 @@ def spam(message): if not spam_allowed(): return - miktar = int(arr[0]) - metin = spam.replace(arr[0], '', 1).strip() - for i in range(0, miktar): - reply(message, metin) - count = increment_spam_count() - if not count: + count = int(arr[0]) + text = spam.replace(arr[0], '', 1).strip() + for i in range(0, count): + reply(message, text) + limit = increment_spam_count() + if not limit: break send_log(get_translation('spamLog')) @@ -64,7 +73,7 @@ def spam(message): @sedenify(pattern='^.picspam') def picspam(message): - arr = extract_args_arr(message) + arr = extract_args_split(message) if len(arr) < 2 or not arr[0].isdigit(): edit(message, f'`{get_translation("spamWrong")}`') return @@ -73,12 +82,12 @@ def picspam(message): if not spam_allowed(): return - miktar = int(arr[0]) - link = arr[1] - for i in range(0, miktar): - reply_img(message, link) - count = increment_spam_count() - if not count: + count = int(arr[0]) + url = arr[1] + for i in range(0, count): + reply_img(message, url) + limit = increment_spam_count() + if not limit: break send_log(get_translation('picspamLog')) @@ -86,14 +95,14 @@ def picspam(message): @sedenify(pattern='^.delayspam') def delayspam(message): - """Copyright (c) @ReversedPosix | 2020""" + # Copyright (c) @ReversedPosix | 2020-2022 delayspam = extract_args(message) arr = delayspam.split() if len(arr) < 3 or not arr[0].isdigit() or not arr[1].isdigit(): edit(message, f'`{get_translation("spamWrong")}`') return - gecikme = int(arr[0]) - miktar = int(arr[1]) + delay = int(arr[0]) + count = int(arr[1]) spam_message = delayspam.replace(arr[0], '', 1) spam_message = spam_message.replace(arr[1], '', 1).strip() message.delete() @@ -102,12 +111,12 @@ def delayspam(message): return delaySpamEvent = Event() - for i in range(0, miktar): + for i in range(0, count): if i != 0: - delaySpamEvent.wait(gecikme) + delaySpamEvent.wait(delay) reply(message, spam_message) - count = increment_spam_count() - if not count: + limit = increment_spam_count() + if not limit: break send_log(get_translation('delayspamLog')) diff --git a/sedenbot/modules/gban.py b/sedenbot/modules/gban.py deleted file mode 100644 index 3002a77..0000000 --- a/sedenbot/modules/gban.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from time import sleep - -from sedenbot import BRAIN -from sedenecem.sql import gban_sql as sql -from sedenecem.core import (edit, sedenify, send_log, reply, - extract_args, get_translation) - - -@sedenify(pattern='^.gban', compat=False) -def gban_user(client, message): - args = extract_args(message) - reply = message.reply_to_message - edit(message, f'`{get_translation("banProcess")}`') - if args: - try: - user = client.get_users(args) - except Exception: - edit(message, f'`{get_translation("banFailUser")}`') - return - elif reply: - user_id = reply.from_user.id - user = client.get_users(user_id) - user_id = user.id - else: - edit(message, f'`{get_translation("banFailUser")}`') - return - - try: - replied_user = reply.from_user - if replied_user.is_self: - return edit(message, f'`{get_translation("cannotBanMyself")}`') - except BaseException: - pass - - if user.id in BRAIN: - return edit( - message, get_translation( - 'brainError', [ - '`', '**', user.first_name, user.id])) - - try: - if sql.is_gbanned(user.id): - return edit(message, f'`{get_translation("alreadyBanned")}`') - chat_id = message.chat.id - sql.gban(user.id) - edit( - message, get_translation( - 'gbanResult', [ - '**', user.first_name, user.id, '`'])) - sleep(1) - send_log( - get_translation( - 'gbanLog', - ['**', user.first_name, user.id, '`'])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(pattern='^.ungban', compat=False) -def ungban_user(client, message): - args = extract_args(message) - reply = message.reply_to_message - edit(message, f'`{get_translation("unbanProcess")}`') - if args: - try: - user = client.get_users(args) - except Exception: - edit(message, f'`{get_translation("banFailUser")}`') - return - elif reply: - user_id = reply.from_user.id - user = client.get_users(user_id) - user_id = user.id - else: - edit(message, f'`{get_translation("banFailUser")}`') - return - - try: - replied_user = reply.from_user - if replied_user.is_self: - return edit(message, f'`{get_translation("cannotUnbanMyself")}`') - except BaseException: - pass - - try: - if not sql.is_gbanned(user.id): - return edit(message, f'`{get_translation("alreadyUnbanned")}`') - chat_id = message.chat.id - sql.ungban(user.id) - client.unban_chat_member(chat_id, user.id) - edit( - message, get_translation( - 'unbanResult', [ - '**', user.first_name, user.id, '`'])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(incoming=True, outgoing=False, compat=False) -def gban_check(client, message): - gbanned = sql.is_gbanned(message.from_user.id) - - if gbanned: - try: - user_id = message.from_user.id - chat_id = message.chat.id - client.kick_chat_member(chat_id, user_id) - except BaseException as e: - send_log(get_translation('banError', ['`', '**', e])) - - message.continue_propagation() diff --git a/sedenbot/modules/gmute.py b/sedenbot/modules/gmute.py deleted file mode 100644 index ecc9c82..0000000 --- a/sedenbot/modules/gmute.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from time import sleep - -from pyrogram.types import ChatPermissions - -from sedenbot import BRAIN -from sedenecem.sql import gmute_sql as sql -from sedenecem.core import (edit, sedenify, send_log, - extract_args, get_translation) - - -@sedenify(pattern='^.gmute', compat=False) -def gmute_user(client, message): - args = extract_args(message) - reply = message.reply_to_message - edit(message, f'`{get_translation("muteProcess")}`') - if len(args): - try: - user = client.get_users(args) - except Exception: - edit(message, f'`{get_translation("banFailUser")}`') - return - elif reply: - user_id = reply.from_user.id - user = client.get_users(user_id) - user_id = user.id - else: - edit(message, f'`{get_translation("banFailUser")}`') - return - - try: - replied_user = reply.from_user - if replied_user.is_self: - return edit(message, f'`{get_translation("cannotMuteMyself")}`') - except BaseException: - pass - - if user.id in BRAIN: - return edit( - message, get_translation( - 'brainError', [ - '`', '**', user.first_name, user.id])) - - try: - if sql.is_gmuted(user.id): - return edit(message, f'`{get_translation("alreadyMuted")}`') - sql.gmute(user.id) - edit( - message, get_translation( - 'gmuteResult', [ - '**', user.first_name, user.id, '`'])) - sleep(1) - send_log(get_translation('gmuteLog', ['**', user.first_name, user.id])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(pattern='^.ungmute', compat=False) -def ungmute_user(client, message): - args = extract_args(message) - reply = message.reply_to_message - edit(message, f'`{get_translation("unmuteProcess")}`') - if len(args): - try: - user = client.get_users(args) - except Exception: - edit(message, f'`{get_translation("banFailUser")}`') - return - elif reply: - user_id = reply.from_user.id - user = client.get_users(user_id) - user_id = user.id - else: - edit(message, f'`{get_translation("banFailUser")}`') - return - - try: - replied_user = reply.from_user - if replied_user.is_self: - return edit(message, f'`{get_translation("cannotUnmuteMyself")}`') - except BaseException: - pass - - try: - if not sql.is_gmuted(user.id): - return edit(message, f'`{get_translation("alreadyUnmuted")}`') - sql.ungmute(user.id) - edit( - message, get_translation( - 'unmuteResult', [ - '**', user.first_name, user.id, '`'])) - except Exception as e: - edit(message, get_translation('banError', ['`', '**', e])) - return - - -@sedenify(incoming=True, outgoing=False, compat=False) -def gmute_check(client, message): - gmuted = sql.is_gmuted(message.from_user.id) - - if gmuted: - sleep(0.1) - message.delete() - - try: - user_id = message.from_user.id - chat_id = message.chat.id - client.restrict_chat_member(chat_id, user_id, ChatPermissions()) - except BaseException: - pass - - message.continue_propagation() diff --git a/sedenbot/modules/hash.py b/sedenbot/modules/hash.py deleted file mode 100644 index 3eea36e..0000000 --- a/sedenbot/modules/hash.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from subprocess import PIPE -from subprocess import run as runapp -from pybase64 import b64encode, b64decode - -from sedenbot import HELP -from sedenecem.core import (edit, reply_doc, extract_args, - sedenify, get_translation) - - -@sedenify(pattern='^.hash') -def hash(message): - hashtxt_ = extract_args(message) - if len(hashtxt_) < 1: - edit(message, f'`{get_translation("wrongCommand")}`') - return - hashtxt = open('hash.txt', 'w+') - hashtxt.write(hashtxt_) - hashtxt.close() - md5 = runapp(['md5sum', 'hash.txt'], stdout=PIPE) - md5 = md5.stdout.decode() - sha1 = runapp(['sha1sum', 'hash.txt'], stdout=PIPE) - sha1 = sha1.stdout.decode() - sha256 = runapp(['sha256sum', 'hash.txt'], stdout=PIPE) - sha256 = sha256.stdout.decode() - sha512 = runapp(['sha512sum', 'hash.txt'], stdout=PIPE) - runapp(['rm', 'hash.txt'], stdout=PIPE) - sha512 = sha512.stdout.decode() - - def rem_filename(st): - return st[:st.find(' ')] - - ans = (f'Text: `{hashtxt_}`' - f'\nMD5: `{rem_filename(md5)}`' - f'\nSHA1: `{rem_filename(sha1)}`' - f'\nSHA256: `{rem_filename(sha256)}`' - f'\nSHA512: `{rem_filename(sha512)}`') - if len(ans) > 4096: - hashfile = open('hash.txt', 'w+') - hashfile.write(ans) - hashfile.close() - reply_doc(message, - 'hash.txt', - caption=f'`{get_translation("outputTooLarge")}`') - runapp(['rm', 'hash.txt'], stdout=PIPE) - message.delete() - else: - edit(message, ans) - - -@sedenify(pattern='^.base64') -def base64(message): - argv = extract_args(message) - args = argv.split(' ', 1) - if len(args) < 2 or args[0] not in ['en', 'de']: - edit(message, f'`{get_translation("wrongCommand")}`') - return - args[1] = args[1].replace('`', '') - if args[0] == 'en': - lething = str(b64encode(bytes(args[1], 'utf-8')))[2:] - edit(message, f'Input: `{args[1]}`\nEncoded: `{lething[:-1]}`') - else: - lething = str(b64decode(bytes(args[1], 'utf-8')))[2:] - edit(message, f'Input: `{args[1]}`\nDecoded: `{lething[:-1]}`') - - -HELP.update({'base64': get_translation('base64Info')}) -HELP.update({'hash': get_translation('hashInfo')}) diff --git a/sedenbot/modules/locks.py b/sedenbot/modules/locks.py deleted file mode 100644 index 63d9d4b..0000000 --- a/sedenbot/modules/locks.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from pyrogram.types import ChatPermissions - -from sedenbot import HELP -from sedenecem.core import edit, sedenify, get_translation, parse_cmd - - -@sedenify(pattern=r'^.(un|)lock', compat=False, private=False, admin=True) -def lock(client, message): - text = (message.text or message.caption).replace(r'\s+', ' ').split(' ', 1) - - unlock = parse_cmd(text[0])[:2] == 'un' - if len(text) < 2: - edit(message, f"`{get_translation('wrongCommand')}`") - return - - kilit = text[1].lower() - - msg = None - media = None - sticker = None - gif = None - gamee = None - ainline = None - webprev = None - gpoll = None - adduser = None - cpin = None - changeinfo = None - if kilit == 'msg': - msg = unlock - kullanim = get_translation('lockMsg') - elif kilit == 'media': - media = unlock - kullanim = get_translation('lockMedia') - elif kilit == 'gif': - gif = unlock - sticker = gif - kullanim = get_translation('lockGif') - elif kilit == 'game': - gamee = unlock - kullanim = get_translation('lockGame') - elif kilit == 'inline': - ainline = unlock - kullanim = get_translation('lockInline') - elif kilit == 'web': - webprev = unlock - kullanim = get_translation('lockWeb') - elif kilit == 'poll': - gpoll = unlock - kullanim = get_translation('lockPoll') - elif kilit == 'invite': - adduser = unlock - kullanim = get_translation('lockInvite') - elif kilit == 'pin': - cpin = unlock - kullanim = get_translation('lockPin') - elif kilit == 'info': - changeinfo = unlock - kullanim = get_translation('lockInformation') - elif kilit == 'all': - msg = unlock - media = unlock - gif = unlock - gamee = unlock - ainline = unlock - webprev = unlock - gpoll = unlock - adduser = unlock - cpin = unlock - changeinfo = unlock - kullanim = get_translation('lockAll') - else: - if not kilit: - edit( - message, get_translation( - 'locksUnlockNoArgs' if unlock else 'locksLockNoArgs')) - return - else: - edit(message, get_translation('lockError', ['`', kilit])) - return - - kilitle = client.get_chat(message.chat.id) - - msg = get_on_none(msg, kilitle.permissions.can_send_messages) - media = get_on_none(media, kilitle.permissions.can_send_media_messages) - sticker = get_on_none(sticker, kilitle.permissions.can_send_stickers) - gif = get_on_none(gif, kilitle.permissions.can_send_animations) - gamee = get_on_none(gamee, kilitle.permissions.can_send_games) - ainline = get_on_none(ainline, kilitle.permissions.can_use_inline_bots) - webprev = get_on_none( - webprev, kilitle.permissions.can_add_web_page_previews) - gpoll = get_on_none(gpoll, kilitle.permissions.can_send_polls) - adduser = get_on_none(adduser, kilitle.permissions.can_invite_users) - cpin = get_on_none(cpin, kilitle.permissions.can_pin_messages) - changeinfo = get_on_none(changeinfo, kilitle.permissions.can_change_info) - - try: - client.set_chat_permissions(message.chat.id, ChatPermissions( - can_send_messages=msg, - can_send_media_messages=media, - can_send_stickers=sticker, - can_send_animations=gif, - can_send_games=gamee, - can_use_inline_bots=ainline, - can_add_web_page_previews=webprev, - can_send_polls=gpoll, - can_change_info=changeinfo, - can_invite_users=adduser, - can_pin_messages=cpin - )) - edit( - message, get_translation( - 'locksUnlockSuccess' if unlock else 'locksLockSuccess', [ - '`', kullanim])) - except BaseException as e: - edit(message, get_translation('lockPerm', ['`', '**', str(e)])) - return - - -def get_on_none(item, defval): - if item is None: - return defval - - return item - - -HELP.update({'locks': get_translation('lockInfo')}) diff --git a/sedenbot/modules/lovers.py b/sedenbot/modules/lovers.py deleted file mode 100644 index e722274..0000000 --- a/sedenbot/modules/lovers.py +++ /dev/null @@ -1,272 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# -# For NaytSeyd ️️❤️ Saniye -# - - -from time import sleep -from random import choice -from sedenecem.core import edit, sedenify -# ================= CONSTANT ================= -SANIYE_STRINGS = [ - "blyrm🥺", "hallo", "nbr ex svglm", "evt🥺", "tamam askm", "hosgeldin askm", - "handicap", "jeg elsker dig", "ja", "ben uyuyorum, ig bebeler", "tmm cool", - "cool bruh", "bn varim🥺", "kstm", "@NightShade slk amd", "sefsz amd", - "amd skm", "ezk amd", "eeee😒", "bu ne lan Vsjjsbsjs", "uuuuu", - "gelin kuzucuklrm", "amedi skm", "ex svglilrm", - "svglm olmak istyenlr dm gelsn", "ikinizden ayriliyrm", "ayb krcm🥺", - "ve ii gclr bkr ❤️", "sn kmsn", "ekle bnie", "pika pikaaaaaaa", - "gule gule kullan", "cnku tg skm", "banane smk", "bkr skm", - "slk bkre dedim", "slm gzlm", "iimsn", "kayiplarda", - "snie gørdm dha ii oldm", "hello askm", "slk baris", "karima sg deme", - "sen haketmistin..", "kiyamam ki", "bana sg demen... 🥺", "hyr yalan", - "sen uzuyon", "beni cok uzuyorsun", "ecm skm", - "tmm küs, gece dc glmcm ozmn", "iyi gecelr 🌸", "yengenim", "eed askm", - "yemek yiyip gelcm bebislerm", "sapikmisin", "bana tapacaksin burda", - "nzbdbshajahahhq yazik", "komik olan ne", "belki gørdum nrdn biliyn", - "pjmani skm", "gle beraber uyuyalm", "ama uykm var🥺", "tm cok coolsn", - "bi susarmisiniz", "oglum sikrirtme kendini", "ingilizceni skm", - "buna gulecekmiydim", "søv ona", "@NightShade bana yavsiyo bu🥺", - "saniyeman ol", "eed puskuvut", "kutlay benim askim, ona yuruyemezsin😠", - "@NightShade amd😠", "bnie hep uzuyo🥺", "shhshshsha", - "bn derim senin yerine", "kanka deme lazim olr", - "seni gece ariyacam, kacta musaitsin", "ppn cko hos. svglm olrmsn", - "amd aglsn", "hiiiiio, happy bday ona ozaman", - "bildigim kadariyla u were tek kid", "jesus christ", - "o neydi lan zbnsshjaka", "tamam ozaman afettim🥺", - "alisdim artik beni uzmene...", "resimlerini siliyom...", - "seni cko øzledim❤️", "@NightShade 💞", - "bana dc girin diyorsunuz, beni takmiyorsunuz srfszlr", "kalbime gir", - "hadi see u", "bu gece de ben konusamam 🥺", - "bn cikiom tgden birazdan, instageamdan yazarsn", - "sn iyi degilken bende iyi olamam 🥺", "nazar degdi :(", "snie øzldm", - "bende sana atmistim, sende stickers yaptin :(", "yeterince cool degilsn", - "wowoowowowow", "uwuwuwuwuwu", "elni at"] -# ================= CONSTANT ================= - - -@sedenify(pattern='^.saniye') -def saniyeify(message): - saniye(message) - - -def saniye(message): - edit(message, choice(SANIYE_STRINGS)) - - -@sedenify(pattern='^.saniş$') -def sanis(message): - animation_interval = 0.1 - animation_ttl = range(0, 100) - edit( - message, - '[Saniş](tg://user?id=623847224) 💘 [NaytSeyd](tg://user?id=551728027)') - animation_chars = [ - "`◼️◼️◼️◼️◼️\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◼️◼️◼️◼️◼️`", - "`◼️◼️◼️◼️◼️\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◼️◼️◼️◼️◼️`", - "`◼️❤️◼️◼️◼️\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◼️◼️◼️❤️◼️`", - "`◼️◼️◼️❤️◼️\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◼️❤️◼️◼️◼️`", - "`◼️◼️◼️◼️◼️\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◼️◼️◼️◼️◼️`", - "`◼️◼️◼️◼️◼️\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◼️◼️◼️◼️◼️`", - "`◼️❤️◼️◼️◼️\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◼️◼️◼️❤️◼️`", - "`◼️◼️◼️❤️◼️\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◼️❤️◼️◼️◼️`", - "`◼️◼️◼️◼️◼️\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◼️◼️◼️◼️◼️`", - "`◼️◼️◼️◼️◼️\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◼️◼️◼️◼️◼️`", - "`◼️❤️◼️◼️◼️\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◼️◼️◼️❤️◼️`", - "`◼️◼️◼️❤️◼️\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◼️❤️◼️◼️◼️`", - "`◼️◼️◼️◼️◼️\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◼️◼️◼️◼️◼️`", - "`◼️◼️◼️◼️◼️\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◼️◼️◼️◼️◼️`", - "`◼️❤️◼️◼️◼️\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◼️◼️◼️❤️◼️`", - "`◼️◼️◼️❤️◼️\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◼️❤️◼️◼️◼️`", - ] - for i in animation_ttl: - sleep(animation_interval) - edit(message, animation_chars[i % len(animation_chars)]) - - -@sedenify(pattern='^.snş$') -def sns(message): - animation_interval = 0.5 - animation_ttl = range(0, 7) - edit(message, "Saniş ❤️") - animation_chars = [ - "S_", - "SA_", - "SAN_", - "SANİ_", - "SANİŞ_", - "SANİŞ❤_", - "[Saniş](tg://user?id=623847224) 💘 [NaytSeyd](tg://user?id=551728027)", - ] - for i in animation_ttl: - sleep(animation_interval) - edit(message, animation_chars[i % len(animation_chars)]) - - -@sedenify(pattern='^.naytsaniş$') -def naytsanis(message): - animation_interval = 1 - animation_ttl = range(0, 6) - edit( - message, - '[Saniş](tg://user?id=623847224) 💘 [NaytSeyd](tg://user?id=551728027)') - animation_chars = [ - "`⠀⠀⠀⣠⣶⡾⠏⠉⠙⠳⢦⡀⠀⠀⠀⢠⠞⠉⠙⠲⡀⠀\n ⠀⣴⠿⠏⠀⠀⠀⠀⠀ ⢳⡀⠀⡏⠀⠀⠀ ⠀⢷\n⢠⣟⣋⡀⢀⣀⣀⡀⠀⣀⡀⣧⠀⢸⠀⠀⠀ ⠀ ⡇\n⢸⣯⡭⠁⠸⣛⣟⠆⡴⣻⡲⣿ ⣸ NaytSeyd ⡇\n ⣟⣿⡭⠀⠀⠀⠀⠀⢱⠀⠀ ⣿ ⢹⠀ ⡇\n ⠙⢿⣯⠄⠀⠀⠀❤️⠀⠀⡿ ⠀⡇⠀⠀⠀⠀ ⡼\n⠀⠀⠀⠹⣶⠆⠀⠀⠀⠀⠀⡴⠃ ⠘⠤⣄⣠⠞⠀\n⠀⠀⠀⠀⢸⣷⡦⢤⡤⢤⣞⣁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⢀⣤⣴⣿⣏⠁⠀⠀⠸⣏⢯⣷⣖⣦⡀⠀⠀⠀⠀⠀⠀\n⢀⣾⣽⣿⣿⣿⣿⠛⢲⣶⣾⢉⡷⣿⣿⠵⣿⠀⠀⠀⠀⠀⠀\n⣼⣿⠍⠉⣿⡭⠉⠙⢺⣇⣼⡏⠀⠀ ⠀⣄⢸⠀⠀⠀⠀⠀⠀`", - "`⠀⠀⠀⣠⣶⡾⠏⠉⠙⠳⢦⡀⠀⠀⠀⢠⠞⠉⠙⠲⡀⠀\n ⠀⣴⠿⠏⠀⠀⠀⠀⠀ ⠀⢳⡀⠀⡏⠀⠀⠀ ⠀⢷\n⢠⣟⣋⡀⢀⣀⣀⡀⠀⣀⡀⣧⠀⢸⠀⠀⠀ ⡇\n⢸⣯⡭⠁⠸⣛⣟⠆⡴⣻⡲⣿ ⣸ Saniş ⡇\n ⣟⣿⡭⠀⠀⠀⠀⠀⢱⠀⠀ ⣿ ⢹⠀ ⡇\n ⠙⢿⣯⠄⠀⠀❤️⠀⠀⡿ ⠀⡇⠀⠀⠀⠀ ⡼\n⠀⠀⠀⠹⣶⠆⠀⠀⠀⠀⠀⡴⠃ ⠘⠤⣄⣠⠞⠀\n⠀⠀⠀⠀⢸⣷⡦⢤⡤⢤⣞⣁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⢀⣤⣴⣿⣏⠁⠀⠀⠸⣏⢯⣷⣖⣦⡀⠀⠀⠀⠀⠀⠀\n⢀⣾⣽⣿⣿⣿⣿⠛⢲⣶⣾⢉⡷⣿⣿⠵⣿⠀⠀⠀⠀⠀⠀\n⣼⣿⠍⠉⣿⡭⠉⠙⢺⣇⣼⡏⠀⠀ ⠀⣄⢸⠀⠀⠀⠀⠀⠀`", - "`⠀⠀⠀⣠⣶⡾⠏⠉⠙⠳⢦⡀⠀⠀⠀⢠⠞⠉⠙⠲⡀⠀\n ⠀⣴⠿⠏⠀⠀ ⠀⢳⡀⠀⡏⠀⠀ ⠀⢷\n⢠⣟⣋⡀⢀⣀⣀⡀⠀⣀⡀⣧⠀⢸⠀⠀⠀⠀ ⡇\n⢸⣯⡭⠁⠸⣛⣟⠆⡴⣻⡲⣿ ⣸ NaytSeyd ⡇\n ⣟⣿⡭⠀⠀⠀⠀⠀⢱⠀⠀ ⣿ ⢹⠀ ⡇\n ⠙⢿⣯⠄⠀⠀❤️⠀⠀⡿ ⠀⡇⠀⠀⠀⠀ ⡼\n⠀⠀⠀⠹⣶⠆⠀⠀⠀⠀⠀⡴⠃ ⠘⠤⣄⣠⠞⠀\n⠀⠀⠀⠀⢸⣷⡦⢤⡤⢤⣞⣁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⢀⣤⣴⣿⣏⠁⠀⠀⠸⣏⢯⣷⣖⣦⡀⠀⠀⠀⠀⠀⠀\n⢀⣾⣽⣿⣿⣿⣿⠛⢲⣶⣾⢉⡷⣿⣿⠵⣿⠀⠀⠀⠀⠀⠀\n⣼⣿⠍⠉⣿⡭⠉⠙⢺⣇⣼⡏⠀⠀ ⠀⣄⢸⠀⠀⠀⠀⠀⠀`", - "`⠀⠀⠀⣠⣶⡾⠏⠉⠙⠳⢦⡀⠀⠀⠀⢠⠞⠉⠙⠲⡀⠀\n ⠀⣴⠿⠏⠀⠀ ⠀⢳⡀⠀⡏⠀⠀ ⠀⢷\n⢠⣟⣋⡀⢀⣀⣀⡀⠀⣀⡀⣧⠀⢸⠀ ⠀ ⡇\n⢸⣯⡭⠁⠸⣛⣟⠆⡴⣻⡲⣿ ⣸ Saniş ⡇\n ⣟⣿⡭⠀⠀⠀⠀⠀⢱⠀ ⣿ ⢹⠀ ⡇\n ⠙⢿⣯⠄⠀⠀⠀❤️ ⠀⠀⡿ ⠀⡇⠀⠀⠀⠀ ⡼\n⠀⠀⠀⠹⣶⠆⠀⠀⠀⠀⠀⡴⠃ ⠘⠤⣄⣠⠞⠀\n⠀⠀⠀⠀⢸⣷⡦⢤⡤⢤⣞⣁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⢀⣤⣴⣿⣏⠁⠀⠀⠸⣏⢯⣷⣖⣦⡀⠀⠀⠀⠀⠀⠀\n⢀⣾⣽⣿⣿⣿⣿⠛⢲⣶⣾⢉⡷⣿⣿⠵⣿⠀⠀⠀⠀⠀⠀\n⣼⣿⠍⠉⣿⡭⠉⠙⢺⣇⣼⡏⠀⠀ ⠀⣄⢸⠀⠀⠀⠀⠀⠀`", - "`⠀⠀⠀⣠⣶⡾⠏⠉⠙⠳⢦⡀⠀⠀⠀⢠⠞⠉⠙⠲⡀⠀\n ⠀⣴⠿⠏⠀⠀⠀⠀⠀ ⢳⡀⠀⡏⠀⠀ ⠀⢷\n⢠⣟⣋⡀⢀⣀⣀⡀⠀⣀⡀⣧⠀⢸⠀⠀ ⠀ ⡇\n⢸⣯⡭⠁⠸⣛⣟⠆⡴⣻⡲⣿ ⣸ NaytSeyd ⡇\n ⣟⣿⡭⠀⠀⠀⠀⠀⢱⠀⠀ ⣿ ⢹⠀ ⡇\n ⠙⢿⣯⠄⠀⠀❤️ ⠀⡿ ⠀⡇⠀⠀⠀⠀ ⡼\n⠀⠀⠀⠹⣶⠆⠀⠀⠀⠀⠀⡴⠃ ⠘⠤⣄⣠⠞⠀\n⠀⠀⠀⠀⢸⣷⡦⢤⡤⢤⣞⣁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⢀⣤⣴⣿⣏⠁⠀⠀⠸⣏⢯⣷⣖⣦⡀⠀⠀⠀⠀⠀⠀\n⢀⣾⣽⣿⣿⣿⣿⠛⢲⣶⣾⢉⡷⣿⣿⠵⣿⠀⠀⠀⠀⠀⠀\n⣼⣿⠍⠉⣿⡭⠉⠙⢺⣇⣼⡏⠀⠀ ⠀⣄⢸⠀⠀⠀⠀⠀⠀`", - "`⠀⠀⠀⣠⣶⡾⠏⠉⠙⠳⢦⡀⠀⠀⠀⢠⠞⠉⠙⠲⡀⠀\n ⠀⣴⠿⠏⠀⠀⠀⠀⠀ ⠀⢳⡀⠀⡏⠀⠀ ⠀⢷\n⢠⣟⣋⡀⢀⣀⣀⡀⠀⣀⡀⣧⠀⢸⠀ ⠀ ⡇\n⢸⣯⡭⠁⠸⣛⣟⠆⡴⣻⡲⣿ ⣸ Saniş ⡇\n ⣟⣿⡭⠀⠀⠀⠀⠀⢱⠀ ⣿ ⢹⠀ ⡇\n ⠙⢿⣯⠄⠀⠀❤️⠀⠀⡿ ⠀⡇⠀⠀⠀⠀ ⡼\n⠀⠀⠀⠹⣶⠆⠀⠀⠀⠀⠀⡴⠃ ⠘⠤⣄⣠⠞⠀\n⠀⠀⠀⠀⢸⣷⡦⢤⡤⢤⣞⣁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⢀⣤⣴⣿⣏⠁⠀⠀⠸⣏⢯⣷⣖⣦⡀⠀⠀⠀⠀⠀⠀\n⢀⣾⣽⣿⣿⣿⣿⠛⢲⣶⣾⢉⡷⣿⣿⠵⣿⠀⠀⠀⠀⠀⠀\n⣼⣿⠍⠉⣿⡭⠉⠙⢺⣇⣼⡏⠀⠀ ⠀⣄⢸⠀⠀⠀⠀⠀⠀`", ] - for i in animation_ttl: - sleep(animation_interval) - edit(message, animation_chars[i % len(animation_chars)]) - - -@sedenify(pattern='^.❤️$') -def sanisveamed(message): - edit( - message, "⠀⠀`.-----. .-----. `\n" - r"` / '..' \ `\n" - "`| | `\n" - "`| Ahmet | `\n" - r"` \ Saniş / `\n" - r"` \ / `\n" - r"` '\ /' `\n" - r"` '\ /' `\n" - r"` '\ /' `\n" - r"` '\/' `\n" - " [Saniş](tg://user?id=623847224) 💘 [NaytSeyd](tg://user?id=551728027)") diff --git a/sedenbot/modules/lydia.py b/sedenbot/modules/lydia.py deleted file mode 100644 index 16f725e..0000000 --- a/sedenbot/modules/lydia.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from time import sleep -from coffeehouse.lydia import LydiaAI -from coffeehouse.api import API - -from sedenbot import HELP, LOGS, LYDIA_APIKEY -from sedenecem.core import sedenify, edit, reply, get_translation - - -def lydia_init(): - try: - global sql - from importlib import import_module - sql = import_module('sedenecem.sql.lydia_sql') - except Exception as e: - sql = None - LOGS.warn(get_translation('lydiaSqlLog')) - raise e - - -lydia_init() - -ACC_LYDIA = {} - -if LYDIA_APIKEY: - api_key = LYDIA_APIKEY - api_client = API(api_key) - lydia = LydiaAI(api_client) - - -@sedenify(pattern='^.repcf') -def repcf(message): - if not LYDIA_APIKEY: - return edit( - message, get_translation( - 'lydiaMissingApi', [ - '**', '`']), preview=False) - edit(message, f'`{get_translation("processing")}`') - try: - session = lydia.create_session() - reply = message.reply_to_message - msg = reply.text - text_rep = session.think_thought(msg) - edit(message, get_translation('lydiaResult', ['**', text_rep])) - except Exception as e: - edit(message, str(e)) - - -@sedenify(pattern='^.addcf') -def addcf(message): - if not LYDIA_APIKEY: - return edit( - message, get_translation( - 'lydiaMissingApi', [ - '**', '`']), preview=False) - edit(message, f'`{get_translation("processing")}`') - sleep(3) - reply_msg = message.reply_to_message - if reply_msg: - session = lydia.create_session() - session_id = session.id - if not reply_msg.from_user.id: - return edit(message, f'`{get_translation("lydiaError")}`') - ACC_LYDIA.update({(message.chat.id & reply_msg.from_user.id): session}) - edit( - message, - get_translation( - 'lydiaResult2', - ['**', '`', str(reply_msg.from_user.id), - str(message.chat.id)])) - else: - edit(message, f'`{get_translation("lydiaError2")}`') - - -@sedenify(pattern='^.remcf') -def remcf(message): - if not LYDIA_APIKEY: - return edit( - message, get_translation( - 'lydiaMissingApi', [ - '**', '`']), preview=False) - edit(message, f'`{get_translation("processing")}`') - sleep(3) - reply_msg = message.reply_to_message - try: - del ACC_LYDIA[message.chat.id & reply_msg.from_user.id] - edit( - message, - get_translation( - 'lydiaResult3', - ['**', '`', str(reply_msg.from_user.id), - str(message.chat.id)])) - except Exception: - edit(message, f'`{get_translation("lydiaError3")}`') - - -@sedenify(incoming=True, - outgoing=False, - disable_edited=True, - disable_notify=True) -def user(message): - user_text = message.text - try: - session = ACC_LYDIA[message.chat.id & message.from_user.id] - msg = message.text - message.reply_chat_action('typing') - text_rep = session.think_thought(msg) - wait_time = 0 - for i in range(len(text_rep)): - wait_time = wait_time + 0.1 - sleep(wait_time) - reply(message, text_rep) - except BaseException: - pass - - message.continue_propagation() - - -HELP.update({'lydia': get_translation('lydiaInfo')}) diff --git a/sedenbot/modules/memes.py b/sedenbot/modules/memes.py deleted file mode 100644 index 1e5f851..0000000 --- a/sedenbot/modules/memes.py +++ /dev/null @@ -1,492 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from time import sleep - -from re import sub -from random import choice, getrandbits, randint -from cowpy import cow -from requests import get - -from sedenbot import HELP -from sedenecem.core import (edit, extract_args, sedenify, - get_translation, parse_cmd) -# ================= CONSTANT ================= -ZALGS = [['̖', ' ̗', ' ̘', ' ̙', ' ̜', ' ̝', ' ̞', ' ̟', ' ̠', ' ̤', ' ̥', - ' ̦', ' ̩', ' ̪', ' ̫', ' ̬', ' ̭', ' ̮', ' ̯', ' ̰', ' ̱', ' ̲', - ' ̳', ' ̹', ' ̺', ' ̻', ' ̼', ' ͅ', ' ͇', ' ͈', ' ͉', ' ͍', ' ͎', - ' ͓', ' ͔', ' ͕', ' ͖', ' ͙', ' ͚', ' '], - [' ̍', ' ̎', ' ̄', ' ̅', ' ̿', ' ̑', ' ̆', ' ̐', ' ͒', ' ͗', ' ͑', - ' ̇', ' ̈', ' ̊', ' ͂', ' ̓', ' ̈́', ' ͊', ' ͋', ' ͌', ' ̃', ' ̂', - ' ̌', ' ͐', ' ́', ' ̋', ' ̏', ' ̽', ' ̉', ' ͣ', ' ͤ', ' ͥ', ' ͦ', - ' ͧ', ' ͨ', ' ͩ', ' ͪ', ' ͫ', ' ͬ', ' ͭ', ' ͮ', ' ͯ', ' ̾', ' ͛', - ' ͆', ' ̚'], - [' ̕', ' ̛', ' ̀', ' ́', ' ͘', ' ̡', ' ̢', ' ̧', ' ̨', ' ̴', ' ̵', - ' ̶', ' ͜', ' ͝', ' ͞', ' ͟', ' ͠', ' ͢', ' ̸', ' ̷', ' ͡']] - -EMOJIS = ['😂', '😂', '👌', '✌', '💞', '👍', '👌', '💯', '🎶', '👀', - '😂', '👓', '👏', '👐', '🍕', '💥', '🍴', '💦', '💦', - '🍑', '🍆', '😩', '😏', '👉👌', '👀', '👅', '😩', '🚰', - '♿'] - -UWUS = ['(・`ω´・)', ';;w;;', 'owo', 'UwU', '>w<', '^w^', r'\(^o\) (/o^)/', - '( ^ _ ^)∠☆', '(ô_ô)', '~:o', ';-;', '(*^*)', '(>_', '(♥_♥)', - '*(^O^)*', '((+_+))'] - -REACTS = ['ʘ‿ʘ', 'ヾ(-_- )ゞ', '(っ˘ڡ˘ς)', '(´ж`ς)', '( ಠ ʖ̯ ಠ)', '(° ͜ʖ͡°)╭∩╮', - '(ᵟຶ︵ ᵟຶ)', '(งツ)ว', 'ʚ(•`', '(っ▀¯▀)つ', '(◠﹏◠)', '( ͡ಠ ʖ̯ ͡ಠ)', - '( ఠ ͟ʖ ఠ)', '(∩`-´)⊃━☆゚.*・。゚', '(⊃。•́‿•̀。)⊃', '(._.)', '{•̃_•̃}', - '(ᵔᴥᵔ)', '♨_♨', '⥀.⥀', 'ح˚௰˚づ ', '(҂◡_◡)', '(っ•́。•́)♪♬', - '◖ᵔᴥᵔ◗ ♪ ♫ ', '(☞゚ヮ゚)☞', '[¬º-°]¬', '(Ծ‸ Ծ)', '(•̀ᴗ•́)و ̑̑', - 'ヾ(´〇`)ノ♪♪♪', "(ง'̀-'́)ง", 'ლ(•́•́ლ)', 'ʕ •́؈•̀ ₎', '♪♪ ヽ(ˇ∀ˇ )ゞ', - 'щ(゚Д゚щ)', '( ˇ෴ˇ )', '눈_눈', '(๑•́ ₃ •̀๑) ', '( ˘ ³˘)♥ ', - 'ԅ(≖‿≖ԅ)', '♥‿♥', '◔_◔', '⁽⁽ଘ( ˊᵕˋ )ଓ⁾⁾', - '乁( ◔ ౪◔)「 ┑( ̄Д  ̄)┍', '( ఠൠఠ )ノ', '٩(๏_๏)۶', '┌(ㆆ㉨ㆆ)ʃ', - 'ఠ_ఠ', '(づ。◕‿‿◕。)づ', '(ノಠ ∩ಠ)ノ彡( \\o°o)\\', '“ヽ(´▽`)ノ”', - '༼ ༎ຶ ෴ ༎ຶ༽', '。゚( ゚இ‸இ゚)゚。', '(づ ̄ ³ ̄)づ', '(⊙.☉)7', 'ᕕ( ᐛ )ᕗ', - 't(-_-t)', '(ಥ⌣ಥ)', 'ヽ༼ ಠ益ಠ ༽ノ', '༼∵༽ ༼⍨༽ ༼⍢༽ ༼⍤༽', 'ミ●﹏☉ミ', - '(⊙_◎)', '¿ⓧ_ⓧﮌ', 'ಠ_ಠ', '(´・_・`)', 'ᕦ(ò_óˇ)ᕤ', '⊙﹏⊙', - '(╯°□°)╯︵ ┻━┻', r'¯\_(⊙︿⊙)_/¯', '٩◔̯◔۶', '°‿‿°', 'ᕙ(⇀‸↼‶)ᕗ', - '⊂(◉‿◉)つ', 'V•ᴥ•V', 'q(❂‿❂)p', 'ಥ_ಥ', 'ฅ^•ﻌ•^ฅ', 'ಥ﹏ಥ', - '( ^_^)o自自o(^_^ )', 'ಠ‿ಠ', 'ヽ(´▽`)/', 'ᵒᴥᵒ#', '( ͡° ͜ʖ ͡°)', - '┬─┬ ノ( ゜-゜ノ)', 'ヽ(´ー`)ノ', '☜(⌒▽⌒)☞', 'ε=ε=ε=┌(;*´Д`)ノ', '(╬ ಠ益ಠ)', - '┬─┬⃰͡ (ᵔᵕᵔ͜ )', '┻━┻ ︵ヽ(`Д´)ノ︵ ┻━┻', r'¯\_(ツ)_/¯', 'ʕᵔᴥᵔʔ', - '(`・ω・´)', 'ʕ•ᴥ•ʔ', 'ლ(`ー´ლ)', 'ʕʘ̅͜ʘ̅ʔ', '( ゚Д゚)', r'¯\(°_o)/¯', - '(。◕‿◕。)'] - -RUNS = [get_translation(f'runstr{i+1}') for i in range(0, 48)] - -SHGS = ['┐(´д`)┌', '┐(´~`)┌', '┐(´ー`)┌', '┐( ̄ヘ ̄)┌', '╮(╯∀╰)╭', '╮(╯_╰)╭', - '┐(´д`)┌', '┐(´∀`)┌', 'ʅ(́◡◝)ʃ', '┐(゚~゚)┌', "┐('д')┌", '┐(‘~`;)┌', - 'ヘ(´-`;)ヘ', '┐( -“-)┌', 'ʅ(´◔౪◔)ʃ', 'ヽ(゜~゜o)ノ', 'ヽ(~~~ )ノ', - '┐(~ー~;)┌', '┐(-。ー;)┌', r'¯\_(ツ)_/¯', r'¯\_(⊙_ʖ⊙)_/¯', - r'¯\_༼ ಥ ‿ ಥ ༽_/¯', '乁( ⁰͡ Ĺ̯ ⁰͡ ) ㄏ'] - -CRYS = ['أ‿أ', '╥﹏╥', '(;﹏;)', '(ToT)', '(┳Д┳)', '(ಥ﹏ಥ)', '(;へ:)', - '(T_T)', '(πーπ)', '(T▽T)', '(⋟﹏⋞)', '(iДi)', '(´Д⊂ヽ', - '(;Д;)', '(>﹏<)', '(TдT)', '(つ﹏⊂)', '༼☯﹏☯༽', '(ノ﹏ヽ)', '(ノAヽ)', - '(╥_╥)', '(T⌓T)', '(༎ຶ⌑༎ຶ)', '(☍﹏⁰)。', '(ಥ_ʖಥ)', '(つд⊂)', '(≖͞_≖̥)', - '(இ﹏இ`。)', '༼ಢ_ಢ༽', '༼ ༎ຶ ෴ ༎ຶ༽'] - -XDA_STRINGS = ['sur', 'Sir', 'bro', 'yes', 'no', 'bolte', 'bolit', - 'bholit', 'volit', 'mustah', 'fap', 'lit', 'lmao', - 'iz', 'jiosim', 'ijo', 'nut', 'workz', 'workang', - 'flashabl zip', 'bateri', 'bacup', 'bad englis', - 'sar', 'treble wen', 'gsi', 'fox bag', 'bag fox', - 'fine', 'bast room', 'fax', 'trable', 'kenzo', - 'plz make room', 'andreid pai', 'when', 'port', - 'mtk', 'send moni', 'bad rom', 'dot', 'rr', 'linage', - 'arrows', 'kernal', 'meme12', 'bruh', 'imail', - 'email', 'plaka', 'evox'] -# ================= CONSTANT ================= - - -@sedenify(pattern=r'^.(\w+)say') -def cowsay(message): - ext = message.text.split(' ', 1) - arg = parse_cmd(ext[0]) - arg = arg[:arg.find('say')] - textx = message.reply_to_message - if textx and textx.text: - text = textx.text - elif len(ext) > 1: - text = ext[1] - else: - edit(message, f'`{get_translation("wrongCommand")}`') - return - - if arg == 'cow' or arg not in cow.COWACTERS: - arg = 'default' - - cheese = cow.get_cow(arg) - cheese = cheese() - - edit(message, f"`{cheese.milk(text).replace('`', '´')}`") - - -@sedenify(pattern='^:/$') -def kek(message): - uio = ['/', '\\'] - for i in range(1, 15): - sleep(0.3) - edit(message, f':{uio[i % len(uio)]}') - - -@sedenify(pattern='^.cry$') -def cry(message): - edit(message, choice(CRYS)) - - -@sedenify(pattern='^.cp') -def copypasta(message): - textx = message.reply_to_message - copypasta = extract_args(message) - - if len(copypasta) > 0: - pass - elif textx: - copypasta = textx.text - else: - edit(message, f'`{get_translation("cpUsage")}`') - return - - reply_text = choice(EMOJIS) - b_char = choice(copypasta).lower() - for owo in copypasta: - if owo == ' ': - reply_text += choice(EMOJIS) - elif owo in EMOJIS: - reply_text += owo - reply_text += choice(EMOJIS) - elif owo.lower() == b_char: - reply_text += '🅱️' - else: - if bool(getrandbits(1)): - reply_text += owo.upper() - else: - reply_text += owo.lower() - reply_text += choice(EMOJIS) - edit(message, reply_text) - - -@sedenify(pattern='^.vapor') -def vapor(message): - reply_text = [] - textx = message.reply_to_message - vapor = extract_args(message) - if len(vapor) > 0: - pass - elif textx: - vapor = textx.text - else: - edit(message, f'`{get_translation("vaporUsage")}`') - return - - for charac in vapor: - if 0x21 <= ord(charac) <= 0x7F: - reply_text.append(chr(ord(charac) + 0xFEE0)) - elif ord(charac) == 0x20: - reply_text.append(chr(0x3000)) - else: - reply_text.append(charac) - - edit(message, ''.join(reply_text)) - - -@sedenify(pattern='^.str') -def stretch(message): - textx = message.reply_to_message - stretch = extract_args(message) - if len(stretch) > 0: - pass - elif textx: - stretch = textx.text - else: - edit(message, f'`{get_translation("strUsage")}`') - return - - count = randint(3, 10) - reply_text = sub(r'([aeiouAEIOUaeiouAEIOUаеиоуюяыэё])', (r'\1' * count), - stretch) - edit(message, reply_text) - - -@sedenify(pattern='^.zal') -def zalgofy(message): - reply_text = [] - textx = message.reply_to_message - zalgofy = extract_args(message) - if len(zalgofy) > 0: - pass - elif textx: - zalgofy = textx.text - else: - edit(message, f'`{get_translation("zalUsage")}`') - return - - for charac in zalgofy: - if not charac.isalpha(): - reply_text.append(charac) - continue - - for _ in range(0, 3): - charac += choice(ZALGS[randint(0, 2)]).strip() - - reply_text.append(charac) - - edit(message, ''.join(reply_text)) - - -@sedenify(pattern='^.owo') -def owo(message): - textx = message.reply_to_message - owo = extract_args(message) - if len(owo) > 0: - pass - elif textx: - owo = textx.text - else: - edit(message, f'`{get_translation("owoUsage")}`') - return - - reply_text = sub(r'(r|l)', 'w', owo) - reply_text = sub(r'(R|L)', 'W', reply_text) - reply_text = sub(r'n([aeiou])', r'ny\1', reply_text) - reply_text = sub(r'N([aeiouAEIOU])', r'Ny\1', reply_text) - reply_text = sub(r'\!+', ' ' + choice(UWUS), reply_text) - reply_text = reply_text.replace('ove', 'uv') - reply_text += ' ' + choice(UWUS) - edit(message, reply_text) - - -@sedenify(pattern='^.mock') -def mock(message): - reply_text = [] - textx = message.reply_to_message - mock = extract_args(message) - if mock: - pass - elif textx: - mock = textx.text - else: - edit(message, f'`{get_translation("mockUsage")}`') - return - - for charac in mock: - if charac.isalpha() and randint(0, 1): - to_app = charac.upper() if charac.islower() else charac.lower() - reply_text.append(to_app) - else: - reply_text.append(charac) - - edit(message, ''.join(reply_text)) - - -@sedenify(pattern='^.clap') -def clap(message): - textx = message.reply_to_message - clap = extract_args(message) - if clap: - pass - elif textx: - clap = textx.text - else: - edit(message, f'`{get_translation("clapUsage")}`') - return - reply_text = '👏 ' - reply_text += clap.replace(' ', ' 👏 ') - reply_text += ' 👏' - edit(message, reply_text) - - -@sedenify(pattern='^.lfy') -def lfy(message): - textx = message.reply_to_message - qry = extract_args(message) - if qry: - query = str(qry) - elif textx: - query = textx - else: - edit(message, f'`{get_translation("wrongCommand")}`') - return - query = query.message - query_encoded = query.replace(' ', '+') - lfy_url = f'http://lmgtfy.com/?s=g&iie=1&q={query_encoded}' - payload = {'format': 'json', 'url': lfy_url} - r = get('http://is.gd/create.php', params=payload) - edit(message, f'`{get_translation("lfyResult")}`' - f"\n[{query}]({r.json()['shorturl']})") - - -@sedenify(pattern=r'.scam', compat=False) -def scam(client, message): - options = [ - 'typing', - 'upload_photo', - 'record_video', - 'upload_video', - 'record_audio', - 'upload_audio', - 'upload_document', - 'find_location', - 'record_video_note', - 'upload_video_note', - 'choose_contact', - 'playing'] - input_str = extract_args(message) - args = input_str.split() - if len(args) == 0: - scam_action = choice(options) - scam_time = randint(30, 60) - elif len(args) == 1: - try: - scam_action = str(args[0]).lower() - scam_time = randint(30, 60) - except ValueError: - scam_action = choice(options) - scam_time = int(args[0]) - elif len(args) == 2: - scam_action = str(args[0]).lower() - scam_time = int(args[1]) - else: - edit(message, f'`{get_translation("wrongCommand")}`') - return - try: - if scam_time > 0: - chat_id = message.chat.id - message.delete() - client.send_chat_action(chat_id, scam_action) - sleep(scam_time) - except BaseException: - return - - -@sedenify(pattern='^.type') -def type(message): - textx = message.reply_to_message - type = extract_args(message) - if type: - pass - elif textx: - type = textx.text - else: - edit(message, f'`{get_translation("wrongCommand")}`') - return - typing_symbol = '|' - old_text = '' - edit(message, typing_symbol) - sleep(0.3) - for character in type: - old_text = old_text + '' + character - typing_text = old_text + '' + typing_symbol - edit(message, typing_text) - sleep(0.03) - edit(message, old_text) - sleep(0.03) - - -@sedenify(pattern='^[Ss]krrt$') -def skrrt(message): - t = f'{(message.text or message.caption)[0]}krrt' - for j in range(16): - t = f'{t[:-1]}rt' - edit(message, t) - - -@sedenify(pattern='^[Oo]of$') -def oof(message): - t = f'{(message.text or message.caption)[0]}of' - for j in range(16): - t = f'{t[:-1]}of' - edit(message, t) - - -@sedenify(pattern='^.10iq$') -def iqless(message): - edit(message, - 'DÜÜÜT DÜÜÜTT AÇ YOLU AÇÇ HADİ ASLAN PARÇASI YOLU AÇ \n' - 'HADİ BAK ENGELLİ BEKLİYO BURDA HADİ DÜÜÜTTT ♿️ BAK \n' - 'SİNİRLENDİ ARKADAŞ HADİ YOLU AÇ HADİİ DÜÜÜT DÜÜTT BİİİPP \n' - 'HADİ BE HIZLI OLL DÜÜÜTT BİİİPPP ♿️♿️ BAK HIZLANDI ENGELLİ \n' - 'KARDEŞİMİZ SERİ KÖZ GETİR SERİ DÜÜÜTT DÜÜÜT DÜÜÜÜTTTTT \n' - 'BİİİİPPP BİİİİİPPP DÜÜÜTTT ♿️♿️♿️♿️ BAK ARTIYO SAYILARI \n' - 'AÇTIN MI YOLU AÇMADIN PÜÜÜÜ REZİİİLL DÜÜÜÜTTT ♿️♿️♿️ \n' - '♿️♿️♿️ BAK KALABALIKLASTI BAK DELI GELIYOR DELIRDI DELI \n' - 'AC YOLU DUTDUTDURURURUDUTTT♿️♿️♿️♿️♿️♿️♿️♿️♿️ \n' - '♿️♿️♿️♿️♿️KAFAYI YEDI BUNLAR AC LAAAAN YOLU') - - -@sedenify(pattern='^.mizah$') -def mizahshow(message): - edit( - message, '⚠️⚠️⚠️MmMmMmMizahh Şoww😨😨😨😨😱😱😱😱😱 \n' - '😱😱⚠️⚠️ 😂😂😂😂😂😂😂😂😂😂😂😂😂😂😱😵 \n' - '😂😂👍👍👍👍👍👍👍👍👍👍👍👍👍 MiZah \n' - 'ŞeLaLesNdEn b1r yUdm aLdım✔️✔️✔️✔️ \n' - 'AHAHAHAHAHAHHAHAHAHAHAHAHAHAHAHAHAHHAHAHAHAHA \n' - 'HAHAHAHAHAHAHHAHAHAHAHAHAHA😂😂😂😂😂😂😂😂 \n' - '😂 KOMİK LAN KOMİİİK \n' - 'heLaL LaN ✔️✔️✔️✔️✔️✔️✔️✔️👏👏👏👏👏👏👏👏 \n' - '👏 EfSaNe mMmMiZah şooooovv 👏👏👏👏👏😂😂😂😂 \n' - '😂😂😂😂😂😂⚠️ \n' - '💯💯💯💯💯💯💯💯💯 \n' - 'KNK AYNI BİİİZ 😂😂😂👏👏 \n' - '💯💯⚠️⚠️♿️AÇ YOLU POST SAHİBİ VE ONU ♿️SAVUNANLAR \n' - 'GELIYOR ♿️♿️ DÜÜTT♿️ \n' - 'DÜÜÜÜT♿️DÜÜT♿️💯💯⚠️ \n' - '♿️KOMİİİK ♿️ \n' - 'CJWJCJWJXJJWDJJQUXJAJXJAJXJWJFJWJXJAJXJWJXJWJFIWIXJQJJQJASJAXJ \n' - 'AJXJAJXJJAJXJWJFWJJFWIIFIWICIWIFIWICJAXJWJFJEICIIEICIEIFIWICJSXJJS \n' - 'CJEIVIAJXBWJCJIQICIWJX💯💯💯💯💯💯😂😂😂😂😂😂😂 \n' - '😂⚠️😂😂😂😂😂😂⚠️⚠️⚠️😂😂😂😂♿️♿️♿️😅😅 \n' - '😅😂👏💯⚠️👏♿️🚨') - - -@sedenify(pattern='^.h$') -def h(message): - edit(message, - '⠀⠀⠀⠀⠀⠀⠀⢀⠀⠂⠂⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠠⠀⠀⠀⠀⠀\n' - '⠀⠀⠀⠀⠀⠄⠈⠐⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢂⠀⠀⠀⠀\n' - '⠀⠀⠀⠀⡐⠀⠀⠀⠀⠀⠀⠀⢡⠀⠀⠀⠀⠀⠀⠀⠀⠀⠊⠀⠀⢸⠀⠀⠀⠀\n' - '⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠀⠀⠀⠑⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀\n' - '⠀⠀⠀⠀⠀⠀⠀⠀⠉⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⢣⠀⠀⠀\n' - '⠀⠀⠀⢸⠀⠀⠀⡜⠀⡆⠀⠀⠀⢀⣲⠀⠀⠀⠀⠀⠀⠴⠀⠀⡇⠀⠀⡀⠀⠀\n' - '⠀⠀⠀⡜⠀⠀⠁⠀⠀⠘⠀⠀⠀⠀⠀⢘⣄⠀⠀⠀⡜⣀⠀⢠⠉⠀⠀⢠⠀⠀\n' - '⠀⠀⠀⣄⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⢠⠀⠈⠛⠛⠒⡀⠀⡇⠀⡄⠀⠈⠀⠀\n' - '⠀⠀⠀⢒⠀⠀⡱⠀⠀⠀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠇⠀⠈⠀⠀⠀⠀\n' - '⠀⠀⠀⢸⠀⢠⠀⠀⠀⢸⠀⠀⠀⠀⢀⠀⠙⠁⠀⠁⣉⠊⠀⡆⠀⠀⠈⠀⡅⠀\n' - '⠀⠀⠀⠀⡀⠈⠀⠀⠀⠃⠀⠀⠀⠀⡌⠈⠀⠑⠃⠋⠀⠀⠀⡇⠀⠀⠀⠀⢠⠀\n' - '⠀⠀⠀⠀⠘⠀⠈⡀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⡀⠈⠀\n' - '⠀⠀⠀⠀⠀⣂⢀⢸⠀⢱⢀⣤⢀⠀⠃⠀⠀⠀⠀⢂⠀⠀⠂⠂⠀⠀⠀⣘⡈⡀\n' - '⠀⠀⠀⠀⠀⠀⠠⠹⠓⢸⠀⠀⢀⠓⠀⠀⠀⠀⠀⡞⢀⠀⢀⠀⠀⠀⠐⢹⠂⠀\n' - '⠀⠀⠀⠀⠀⠀⠀⠈⠛⠇⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠂⠁⠀⠀⠀⠀⠀⠀⠀\n' - '⠀⠀⠀⠀⠀⠀⠀⠀⢰⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀\n' - '⠀⠀⠀⠀⠀⠀⠀⠀⡌⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠊⠀⠀⠀⠀⠀⠀⠀⠀\n' - '⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⡠⠀⠀⠀⠀⠀⠀⡆⠀⢰⠀⠀⠀⠀⠀⠀⠀⠀⠀\n' - '⠀⠀⠀⠀⠀⠀⠀⠊⠠⠂⠉⢤⣀⠀⠀⠀⠀⢠⠀⠐⠣⠠⢤⠀⠀⠀⠀⠀⠀⠀\n' - '⠀⠀⠀⠀⠀⠀⠀⠁⠂⠤⠼⠓⠓⠒⠀⠀⠀⠈⠂⠀⠀⠀⠂⠚⠁⠀⠀⠀⠀⠀') - - -@sedenify(pattern='^.react$') -def react(message): - edit(message, choice(REACTS)) - - -@sedenify(pattern='^.shg$') -def shg(message): - edit(message, choice(SHGS)) - - -@sedenify(pattern='^.run$') -def run(message): - edit(message, choice(RUNS)) - - -@sedenify(pattern='^.xda$') -def xda(message): - edit(message, choice(XDA_STRINGS)) - - -''' -Copyright (c) @NaytSeyd, Quotes taken -from friendly-telegram (https://gitlab.com/friendly-telegram) | 2020 -''' - - -@sedenify(pattern='^.f (.*)') -def payf(message): - paytext = extract_args(message) - pay = f'{paytext * 8}\n{paytext * 8}\n{paytext * 2}\n{paytext * 2}' \ - f'\n{paytext * 2}\n{paytext * 6}\n{paytext * 6}\n{paytext * 2}' \ - f'\n{paytext * 2}\n{paytext * 2}\n{paytext * 2}\n{paytext * 2}' - edit(message, pay) - - -HELP.update({'memes': get_translation('memesInfo')}) diff --git a/sedenbot/modules/misc.py b/sedenbot/modules/misc.py deleted file mode 100644 index d2ef1ba..0000000 --- a/sedenbot/modules/misc.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from random import choice - -from sedenbot import HELP, SUPPORT_GROUP -from sedenecem.core import (edit, reply, extract_args, - sedenify, get_translation) - - -@sedenify(pattern='^.random') -def random(message): - items = extract_args(message, False) - args = items.split() - if len(args) < 2: - edit(message, f'`{get_translation("randomUsage")}`') - return - - edit(message, get_translation( - 'randomResult', ['**', '`', items, choice(args)])) - - -@sedenify(pattern='^.chatid$', private=False) -def chatid(message): - edit( - message, - get_translation('chatidResult', ['`', str(message.chat.id)])) - - -@sedenify(pattern='^.id$') -def userid(message): - reply = message.reply_to_message - if reply: - if not reply.forward_from: - user_id = reply.from_user.id - if reply.from_user.username: - name = f'**@{reply.from_user.username}**' - else: - name = f'**[{reply.from_user.first_name}](tg://user?id={reply.from_user.id})**' - else: - user_id = reply.forward_from.id - if reply.forward_from.username: - name = f'**@{reply.forward_from.username}**' - else: - name = f'**[{reply.forward_from.first_name}](tg://user?id={reply.forward_from.id})**' - edit( - message, get_translation( - 'useridResult', [ - '**', name, '`', user_id])) - else: - edit(message, f'`{get_translation("wrongCommand")}`') - - -@sedenify(pattern='^.kickme$', compat=False, private=False) -def kickme(client, message): - edit(message, f'`{get_translation("kickmeResult")}`') - client.leave_chat(message.chat.id, 'me') - - -@sedenify(pattern='^.support$') -def support(message): - edit(message, get_translation('supportResult', [SUPPORT_GROUP]), - preview=False) - - -@sedenify(pattern='^.founder') -def founder(message): - edit(message, get_translation('founderResult', ['`', '**']), preview=False) - - -@sedenify(pattern='^.readme$') -def readme(message): - edit( - message, - '[Seden README.md](https://github.com/TeamDerUntergang/' - 'Telegram-SedenUserBot/blob/seden/README.md)', - preview=False) - - -@sedenify(pattern='^.repo$') -def repo(message): - edit( - message, - '[Seden Repo](https://github.com/TeamDerUntergang/' - 'Telegram-SedenUserBot)', - preview=False) - - -@sedenify(pattern='^.repeat') -def repeat(message): - '''Copyright (c) Gegham Zakaryan | 2019''' - args = extract_args(message).split(' ', 1) - if len(args) < 2: - edit(message, f'`{get_translation("wrongCommand")}`') - return - cnt, txt = args - if not cnt.isdigit(): - edit(message, f'`{get_translation("wrongCommand")}`') - return - replyCount = int(cnt) - toBeRepeated = txt - - replyText = f'{toBeRepeated}\n' - - for i in range(0, replyCount - 1): - replyText += f'{toBeRepeated}\n' - - edit(message, replyText) - - -@sedenify(pattern='^.crash$') -def crash(message): - edit(message, f'`{get_translation("testLogId")}`') - raise Exception(get_translation('testException')) - - -@sedenify(pattern='^.tagall$', compat=False, private=False) -def tagall(client, message): - msg = '@tag' - chat = message.chat.id - length = 0 - for member in client.iter_chat_members(chat): - if length < 4092: - msg += f'[\u2063](tg://user?id={member.user.id})' - length += 1 - reply(message, msg, delete_orig=True) - - -@sedenify(pattern='^.report$', compat=False, private=False) -def report_admin(client, message): - msg = '@admin' - chat = message.chat.id - for member in client.iter_chat_members(chat, filter='administrators'): - msg += f'[\u2063](tg://user?id={member.user.id})' - re_msg = message.reply_to_message - reply(re_msg if re_msg else message, msg) - message.delete() - - -HELP.update({'misc': get_translation('miscInfo')}) diff --git a/sedenbot/modules/autopp.py b/sedenbot/modules/miscs/autopp.py similarity index 72% rename from sedenbot/modules/autopp.py rename to sedenbot/modules/miscs/autopp.py index 43969dd..95b60ae 100644 --- a/sedenbot/modules/autopp.py +++ b/sedenbot/modules/miscs/autopp.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,23 +7,28 @@ # All rights reserved. See COPYING, AUTHORS. # +from datetime import datetime from os import path, remove from time import sleep -from datetime import datetime + from PIL import Image, ImageDraw, ImageFont from requests import get - -from sedenbot import HELP, AUTO_PP, TEMP_SETTINGS, LOGS -from sedenecem.core import (extract_args, sedenify, edit, - get_translation, download_media_wc) +from sedenbot import AUTO_PP, HELP, LOGS, TEMP_SETTINGS +from sedenecem.core import ( + download_media_wc, + edit, + extract_args, + get_translation, + sedenify, +) # =================== CONSTANT =================== KEY_AUTOPP = 'autopic' # ================================================ -@sedenify(pattern='^.autopp', compat=False) -def autopic(client, message): +@sedenify(pattern='^.autopp') +def autopic(message): args = extract_args(message) autopic = KEY_AUTOPP in TEMP_SETTINGS if args == 'disable': @@ -42,7 +47,7 @@ def autopic(client, message): edit(message, f'`{get_translation("autoppProcess")}`') - FONT_FILE = 'sedenecem/fonts/GoogleSans.ttf' + FONT_FILE = 'sedenecem/fonts/OpenSans.ttf' downloaded_file_name = 'oldpp.png' photo = 'newpp.png' @@ -55,12 +60,12 @@ def autopic(client, message): load.write(get(AUTO_PP).content) else: try: - profile_photo = client.get_profile_photos('me', limit=1) - downloaded_file_name = download_media_wc( - profile_photo[0], downloaded_file_name) + for i in message._client.get_chat_photos('me', limit=1): + downloaded_file_name = download_media_wc( + i.file_id, downloaded_file_name + ) except BaseException: - edit(message, f'`{get_translation("autoppConfig")}`') - return + return edit(message, f'`{get_translation("autoppConfig")}`') edit(message, f'`{get_translation("autoppResult")}`') @@ -73,9 +78,12 @@ def autopic(client, message): size = drawn_text.multiline_textsize(current_time, font=fnt) drawn_text.text( ((img.width - size[0]) / 2, (img.height - size[1])), - current_time, font=fnt, fill=(255, 255, 255)) + current_time, + font=fnt, + fill=(255, 255, 255), + ) img.save(photo) - client.set_profile_photo(photo=photo) + message._client.set_profile_photo(photo=photo) remove(photo) sleep(60) except BaseException: diff --git a/sedenbot/modules/direct_link.py b/sedenbot/modules/miscs/direct_link.py similarity index 70% rename from sedenbot/modules/direct_link.py rename to sedenbot/modules/miscs/direct_link.py index 74f3f67..2729804 100644 --- a/sedenbot/modules/direct_link.py +++ b/sedenbot/modules/miscs/direct_link.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,18 +7,27 @@ # All rights reserved. See COPYING, AUTHORS. # -from re import findall, sub from json import JSONDecodeError +from re import findall, sub from urllib.parse import unquote, urlparse + from bs4 import BeautifulSoup -from requests import get, Session +from requests import Session, get +from selenium.webdriver.common.by import By from sedenbot import HELP -from sedenecem.core import (edit, extract_args, sedenify, - get_webdriver, get_translation) - - -@sedenify(pattern=r'^.direct') +from sedenecem.core import ( + edit, + extract_args, + get_translation, + get_webdriver, + reply_doc, + sedenify, + useragent, +) + + +@sedenify(pattern='^.direct') def direct(message): edit(message, f'`{get_translation("processing")}`') textx = message.reply_to_message @@ -38,7 +47,7 @@ def check(url, items, starts=False): return url.startswith(items) if starts else items in url for item in items: - if (url.startswith(item) if starts else item in url): + if url.startswith(item) if starts else item in url: return True return False @@ -53,9 +62,11 @@ def check(url, items, starts=False): reply += f'`{get_translation("directUrlNotFound")}`\n' continue try: - if check(link, 'zippyshare.com'): + if check(link, ['drive.google.com', 'docs.google.com']): + reply += gdrive(link, message) + elif check(link, 'zippyshare.com'): reply += zippy_share(link) - elif check(link, 'yadi.sk'): + elif check(link, ['yadi.sk', 'disk.yandex.com']): reply += yandex_disk(link) elif check(link, 'mediafire.com'): reply += mediafire(link) @@ -74,18 +85,40 @@ def check(url, items, starts=False): edit(message, reply, preview=False) +def gdrive(link: str, message) -> str: + reply = '' + url_id = link.split('/')[5] + dl_url = f'https://drive.usercontent.google.com/download?id={url_id}&export=download&confirm=t' + headers = {'user-agent': useragent()} + reply_doc( + message, 'cookies.txt', caption=get_translation("directGdriveCookieUsage") + ) + reply += get_translation("directGdriveCookie") + cookies = { + '__Secure-1PSID': 'g.a000hQgeotAjF0s4Bo1rG0uA-Rs--F4uvWXHlSTgXrmA1YgNa-70LASRphA1f_pHqKS5DVTCuwACgYKA' + 'boSAQASFQHGX2Mi1y83cqTGHara3fsXiu43ZBoVAUF8yKrxezWJON0xdZvCEZj1KgQP0076' + } + response = get(url=dl_url, headers=headers, cookies=cookies, stream=True) + page = BeautifulSoup(response.content, 'html.parser') + info = page.find('span', {'class': 'uc-name-size'}).text + uuid = page.find('input', {'name': 'uuid'}).get('value') + at = page.find('input', {'name': 'at'}).get('value') + dl_url += f'&uuid={uuid}&at={at}' + reply += f'[{info}]({dl_url})\n' + return reply + + def zippy_share(link: str) -> str: reply = '' driver = get_webdriver() driver.get(link) - left = driver.find_element_by_xpath('//div[contains(@class, "left")]') - font = left.find_elements_by_xpath('.//font') + left = driver.find_element(By.XPATH, '//div[contains(@class, "left")]') + font = left.find_elements(By.XPATH, './/font') name = font[2].text size = font[4].text - button = driver.find_element_by_xpath('//a[contains(@id, "dlbutton")]') + button = driver.find_element(By.XPATH, '//a[contains(@id, "dlbutton")]') link = button.get_attribute('href') - reply += '{}\n'.format(get_translation('directZippyLink', - [name, size, link])) + reply += '{}\n'.format(get_translation('directZippyLink', [name, size, link])) driver.quit() return reply @@ -118,21 +151,24 @@ def sourceforge(link: str) -> str: file_path = findall(r'files(.*)/download', link)[0] reply = f"Mirrors for __{file_path.split('/')[-1]}__\n" project = findall(r'projects?/(.*?)/files', link)[0] - mirrors = f'https://sourceforge.net/settings/mirror_choices?' \ + mirrors = ( + f'https://sourceforge.net/settings/mirror_choices?' f'projectname={project}&filename={file_path}' + ) page = BeautifulSoup(get(mirrors).content, 'html.parser') info = page.find('ul', {'id': 'mirrorList'}).findAll('li') for mirror in info[1:]: name = findall(r'\((.*)\)', mirror.text.strip())[0] - dl_url = f'https://{mirror["id"]}.dl.sourceforge.net/project/{project}/{file_path}' + dl_url = ( + f'https://{mirror["id"]}.dl.sourceforge.net/project/{project}/{file_path}' + ) reply += f'[{name}]({dl_url}) ' return reply def osdn(link: str) -> str: osdn_link = 'https://osdn.net' - page = BeautifulSoup( - get(link, allow_redirects=True).content, 'html.parser') + page = BeautifulSoup(get(link, allow_redirects=True).content, 'html.parser') info = page.find('a', {'class': 'mirror_link'}) link = unquote(osdn_link + info['href']) reply = f"Mirrors for __{link.split('/')[-1]}__\n" @@ -161,13 +197,12 @@ def github(link: str) -> str: def androidfilehost(link: str) -> str: fid = findall(r'\?fid=[0-9]+', link)[0] session = Session() - user_agent = useragent() - headers = {'user-agent': user_agent} + headers = {'user-agent': useragent()} res = session.get(link, headers=headers, allow_redirects=True) headers = { 'origin': 'https://androidfilehost.com', 'accept-language': 'en-US,en;q=0.9', - 'user-agent': user_agent, + 'user-agent': useragent(), 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 'x-mod-sbb-ctype': 'xhr', 'accept': '*/*', @@ -175,11 +210,7 @@ def androidfilehost(link: str) -> str: 'authority': 'androidfilehost.com', 'x-requested-with': 'XMLHttpRequest', } - data = { - 'submit': 'submit', - 'action': 'getdownloadmirrors', - 'fid': f'{fid}' - } + data = {'submit': 'submit', 'action': 'getdownloadmirrors', 'fid': f'{fid}'} mirrors = None reply = f'URL: {link}\n' error = f'`{get_translation("mirrorError")}`\n' @@ -188,7 +219,8 @@ def androidfilehost(link: str) -> str: 'https://androidfilehost.com/libs/otf/mirrors.otf.php', headers=headers, data=data, - cookies=res.cookies) + cookies=res.cookies, + ) mirrors = req.json()['MIRRORS'] except (JSONDecodeError, TypeError): reply += error @@ -202,16 +234,4 @@ def androidfilehost(link: str) -> str: return reply -def useragent(): - req = get('https://user-agents.net/random') - soup = BeautifulSoup(req.text, 'html.parser') - agent = soup.find('article') - if agent: - agent = agent.find('li') - if agent: - return agent.find('a').text.replace('"', '') - - return 'Googlebot/2.1 (+http://www.google.com/bot.html)' - - HELP.update({'direct': get_translation('directInfo')}) diff --git a/sedenbot/modules/miscs/ezanvakti.py b/sedenbot/modules/miscs/ezanvakti.py new file mode 100644 index 0000000..7ad17a7 --- /dev/null +++ b/sedenbot/modules/miscs/ezanvakti.py @@ -0,0 +1,242 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from datetime import datetime, timedelta +from functools import reduce +from re import DOTALL, sub + +from bs4 import BeautifulSoup +from requests import get + +from sedenbot import HELP +from sedenecem.core import edit, extract_args, sedenify + + +@sedenify(pattern='^.ezan(|vakti)') +def ezanvakti(message): + konum = extract_args(message).lower() + if len(konum) < 1: + return edit(message, '`Lütfen komutun yanına bir şehir belirtin.`') + + try: + result = get_result(konum) + except BaseException: + return edit(message, f'`{konum} için bir bilgi bulunamadı.`') + res1 = result.body.find('div', {'class': 'body-content'}) + res1 = res1.find('script') # type: ignore + res1 = sub( + r'|\r|{.*?}|\[.*?\]|\n ', '', str(res1), flags=DOTALL + ) + res1 = sub('\n\n', '\n', res1)[:-1].split('\n') + + def get_val(st): + return [i.split('=')[1].replace('"', '').strip() for i in st[:-1].split(';')] + + res2 = get_val(res1[1]) + res3 = get_val(res1[2]) + + vakitler = ( + '**Diyanet Namaz Vakitleri**\n\n' + + f'📍 **Yer:** `{res2[1]}`\n\n' + + f'🏙 **İmsak:** `{res3[0]}`\n' + + f'🌅 **Güneş:** `{res3[1]}`\n' + + f'🌇 **Öğle:** `{res3[2]}`\n' + + f'🌆 **İkindi:** `{res3[3]}`\n' + + f'🌃 **Akşam:** `{res3[4]}`\n' + + f'🌌 **Yatsı:** `{res3[5]}`' + ) + + edit(message, vakitler) + + +@sedenify(pattern='^.ramazan') +def ramazan(message): + konum = extract_args(message).lower() + if len(konum) < 1: + return edit(message, '`Lütfen komutun yanına bir şehir belirtin.`') + + try: + result = get_result(konum) + except BaseException: + return edit(message, f'`{konum} için bir bilgi bulunamadı.`') + res1 = result.body.find('div', {'class': 'body-content'}) + res1 = res1.find('script') + res1 = sub( + r'|\r|{.*?}|\[.*?\]|\n ', '', str(res1), flags=DOTALL + ) + res1 = sub('\n\n', '\n', res1)[:-1].split('\n') + + def get_val(st): + return [i.split('=')[1].replace('"', '').strip() for i in st[:-1].split(';')] + + res2 = get_val(res1[1]) + res3 = get_val(res1[2]) + + sahur_vakti, iftar_vakti, teravih_vakti = res3[0], res3[4], res3[5] + + def get_remaining_time(vakt): + vakt_time = datetime.strptime(vakt, '%H:%M') + current_time = datetime.now() + + if current_time < vakt_time: + time_left = vakt_time - current_time + else: + tomorrow = current_time + timedelta(days=1) + tomorrow_date = datetime( + tomorrow.year, + tomorrow.month, + tomorrow.day, + vakt_time.hour, + vakt_time.minute, + ) + time_left = tomorrow_date - current_time + + hours_left = time_left.seconds // 3600 + minutes_left = (time_left.seconds % 3600) // 60 + + if hours_left == 0: + return f'{vakt} ({minutes_left} dakika kaldı)' + elif minutes_left == 0: + return f'{vakt} ({hours_left} saat kaldı)' + else: + return f'{vakt} ({hours_left} saat {minutes_left} dakika kaldı)' + + sahur = get_remaining_time(sahur_vakti) + iftar = get_remaining_time(iftar_vakti) + teravih = get_remaining_time(teravih_vakti) + + vakitler = ( + '**Diyanet Ramazan Vakitleri**\n\n' + + f'📍 **Yer:** `{res2[1]}`\n\n' + + (f'🏙 **Sahur:** `{sahur}`\n') + + (f'🌃 **İftar:** `{iftar}`\n') + + (f'🌌 **Teravih:** `{teravih}`\n\n') + + '**Hayırlı Ramazanlar**' + ) + + edit(message, vakitler) + + +def find_loc(konum): + if konum.isdigit(): + plaka = int(konum) + if plaka > 0 and plaka < 82: + return int(sehirler[plaka - 1].split()[2]) + else: + return -1 + else: + di = {'ç': 'c', 'ğ': 'g', 'ı': 'i', 'ö': 'o', 'ş': 's', 'ü': 'u'} + konum = reduce(lambda x, y: x.replace(y, di[y]), di, konum) + sehir_ad = [s.split()[1].lower() for s in sehirler] + try: + index = sehir_ad.index(konum) + return int(sehirler[index].split()[2]) + except BaseException: + return -1 + + +def get_result(konum): + knum = find_loc(konum) + if knum < 0: + raise ValueError + request = get(f'https://namazvakitleri.diyanet.gov.tr/tr-TR/{knum}') + return BeautifulSoup(request.content, 'html.parser') + + +sehirler = [ + '01 Adana 9146', + '02 Adiyaman 9158', + '03 Afyonkarahisar 9167', + '04 Agri 9185', + '05 Amasya 9198', + '06 Ankara 9206', + '07 Antalya 9225', + '08 Artvin 9246', + '09 Aydin 9252', + '10 Balikesir 9270', + '11 Bilecik 9297', + '12 Bingol 9303', + '13 Bitlis 9311', + '14 Bolu 9315', + '15 Burdur 9327', + '16 Bursa 9335', + '17 Canakkale 9352', + '18 Cankiri 9359', + '19 Corum 9370', + '20 Denizli 9392', + '21 Diyarbakir 9402', + '22 Edirne 9419', + '23 Elazig 9432', + '24 Erzincan 9440', + '25 Erzurum 9451', + '26 Eskisehir 9470', + '27 Gaziantep 9479', + '28 Giresun 9494', + '29 Gumushane 9501', + '30 Hakkari 9507', + '31 Hatay 20089', + '32 Isparta 9528', + '33 Mersin 9737', + '34 Istanbul 9541', + '35 Izmir 9560', + '36 Kars 9594', + '37 Kastamonu 9609', + '38 Kayseri 9620', + '39 Kirklareli 9638', + '40 Kirsehir 9646', + '41 Kocaeli 9654', + '42 Konya 9676', + '43 Kutahya 9689', + '44 Malatya 9703', + '45 Manisa 9716', + '46 Kahramanmaras 9577', + '47 Mardin 9726', + '48 Mugla 9747', + '49 Mus 9755', + '50 Nevsehir 9760', + '51 Nigde 9766', + '52 Ordu 9782', + '53 Rize 9799', + '54 Sakarya 9807', + '55 Samsun 9819', + '56 Siirt 9839', + '57 Sinop 9847', + '58 Sivas 9868', + '59 Tekirdag 9879', + '60 Tokat 9887', + '61 Trabzon 9905', + '62 Tunceli 9914', + '63 Sanliurfa 9831', + '64 Usak 9919', + '65 Van 9930', + '66 Yozgat 9949', + '67 Zonguldak 9955', + '68 Aksaray 9193', + '69 Bayburt 9295', + '70 Karaman 9587', + '71 Kirikkale 9635', + '72 Batman 9288', + '73 Sirnak 9854', + '74 Bartin 9285', + '75 Ardahan 9238', + '76 Igdir 9522', + '77 Yalova 9935', + '78 Karabuk 9581', + '79 Kilis 9629', + '80 Osmaniye 9788', + '81 Duzce 9414', +] + +HELP.update( + { + "ezanvakti": ".ezanvakti <şehir> \ + \nKullanım: Belirtilen şehir için namaz vakitlerini gösterir. \ + \nÖrnek: .ezanvakti istanbul" + } +) diff --git a/sedenbot/modules/horeke.py b/sedenbot/modules/miscs/horeke.py similarity index 76% rename from sedenbot/modules/horeke.py rename to sedenbot/modules/miscs/horeke.py index 3067efb..f821adc 100644 --- a/sedenbot/modules/horeke.py +++ b/sedenbot/modules/miscs/horeke.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,18 +7,26 @@ # All rights reserved. See COPYING, AUTHORS. # -from os import execl, getpid -from sys import executable, argv -from requests import get from math import floor +from os import execl, getpid, kill +from signal import SIGINT +from sys import argv, executable + from heroku3 import from_key +from requests import get -from sedenbot import HELP, HEROKU_KEY, HEROKU_APPNAME -from sedenecem.core import (edit, sedenify, get_translation, - send_log, reply_doc) +from sedenbot import HELP, HEROKU_APPNAME, HEROKU_KEY +from sedenecem.core import ( + edit, + get_translation, + reply_doc, + sedenify, + send_log, + useragent, +) -@sedenify(pattern='^.(quo|ko)ta$') +@sedenify(pattern='^.(qu|k)ota$') def dyno(message): if not HEROKU_KEY: edit(message, f"`{get_translation('notHeroku')}`") @@ -32,7 +40,8 @@ def dyno(message): if not HEROKU_APPNAME: edit( message, - f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`') + f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`', + ) for app in heroku_applications: if app.name == HEROKU_APPNAME: @@ -42,20 +51,21 @@ def dyno(message): if heroku_app is None: edit( message, - f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`') + f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`', + ) return acc_id = heroku.account().id headers = { - 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36', + 'User-Agent': useragent(), 'Authorization': f'Bearer {HEROKU_KEY}', 'Accept': 'application/vnd.heroku+json; version=3.account-quotas', } req = get( - f'https://api.heroku.com/accounts/{acc_id}/actions/get-quota', - headers=headers) + f'https://api.heroku.com/accounts/{acc_id}/actions/get-quota', headers=headers + ) if req.status_code != 200: edit(message, f"`{get_translation('covidError')}`") @@ -97,31 +107,34 @@ def get_app_quota(): message, get_translation( 'herokuQuotaInfo', - ['`', '**', - get_translation( - 'herokuQuotaInHM', [acc_total_hrs, acc_total_min]), - get_translation( - 'herokuQuotaInHM', [acc_used_hrs, acc_used_min]), - acc_quota_percent, - get_translation( - 'herokuQuotaInHM', [acc_remaining_hrs, acc_remaining_min]), - acc_quota_rem_percent, - get_translation( - 'herokuQuotaInHM', [app_quota_hrs, app_quota_min]), - app_quota_percent])) - - -@sedenify(pattern='^.(restart|yb)$', compat=False) -def _restart(client, message): - return restart(client, message) - - -@sedenify(pattern='^.d(restart|yb)$', compat=False) -def _drestart(client, message): - return restart(client, message, dyno=True) - - -def restart(client, message, dyno=False): + [ + '`', + '**', + get_translation('herokuQuotaInHM', [acc_total_hrs, acc_total_min]), + get_translation('herokuQuotaInHM', [acc_used_hrs, acc_used_min]), + acc_quota_percent, + get_translation( + 'herokuQuotaInHM', [acc_remaining_hrs, acc_remaining_min] + ), + acc_quota_rem_percent, + get_translation('herokuQuotaInHM', [app_quota_hrs, app_quota_min]), + app_quota_percent, + ], + ), + ) + + +@sedenify(pattern='^.(re(star|boo)t|yb)$') +def _restart(message): + return restart(message) + + +@sedenify(pattern='^.d(re(star|boo)t|yb)$') +def _drestart(message): + return restart(message, dyno=True) + + +def restart(message, dyno=False): send_log(get_translation('restartLog')) def std_off(): @@ -145,7 +158,8 @@ def std_ret(): if not HEROKU_APPNAME: edit( message, - f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`') + f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`', + ) std_ret() return @@ -157,7 +171,8 @@ def std_ret(): if heroku_app is None: edit( message, - f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`') + f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`', + ) std_ret() return @@ -167,15 +182,14 @@ def std_ret(): dynos[0].restart() -@sedenify(pattern='^.(shutdown|kapat)$', compat=False) -def shutdown(client, message): +@sedenify(pattern='^.(shutdown|kapat)$') +def shutdown(message): edit(message, f'`{get_translation("shutdown")}`') send_log(get_translation('shutdownLog')) def std_off(): try: - from subprocess import getoutput - getoutput(f'kill -7 {getpid()}') + kill(getpid(), SIGINT) except Exception: pass @@ -189,7 +203,8 @@ def std_off(): if not HEROKU_APPNAME: edit( message, - f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`') + f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`', + ) std_off() return @@ -201,11 +216,11 @@ def std_off(): if heroku_app is None: edit( message, - f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`') + f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`', + ) std_off() return - std_off() heroku_app.scale_formation_process('seden', 0) @@ -223,7 +238,8 @@ def dyno_logs(message): if not HEROKU_APPNAME: edit( message, - f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`') + f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`', + ) for app in heroku_applications: if app.name == HEROKU_APPNAME: @@ -233,7 +249,8 @@ def dyno_logs(message): if heroku_app is None: edit( message, - f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`') + f'`{get_translation("updateHerokuVariables", ["HEROKU_APPNAME "])}`', + ) return filename = 'seden_heroku_log.txt' diff --git a/sedenbot/modules/miscs/misc.py b/sedenbot/modules/miscs/misc.py new file mode 100644 index 0000000..e9a7283 --- /dev/null +++ b/sedenbot/modules/miscs/misc.py @@ -0,0 +1,245 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from os import remove +from random import choice +from subprocess import PIPE +from subprocess import run as runapp +from pyrogram import enums + +from image_to_ascii import ImageToAscii +from pybase64 import b64decode, b64encode +from sedenbot import HELP, SUPPORT_GROUP +from sedenecem.core import ( + download_media_wc, + edit, + extract_args, + extract_user, + get_translation, + reply, + reply_doc, + sedenify, +) + + +@sedenify(pattern='^.random') +def random(message): + items = extract_args(message, False) + args = items.split() + if len(args) < 2: + edit(message, f'`{get_translation("randomUsage")}`') + return + + edit(message, get_translation('randomResult', ['**', '`', items, choice(args)])) + + +@sedenify(pattern='^.chatid$', private=False) +def get_chat_id(message): + edit(message, get_translation('chatidResult', ['`', str(message.chat.id)])) + + +@sedenify(pattern='^.invitelink$', admin=True, private=False) +def get_invite_link(message): + chat = message._client.get_chat(message.chat.id) + try: + url = chat.invite_link + edit(message, url, preview=False) + except BaseException: + pass + + +@sedenify(pattern='^.id$') +def get_user_id(message): + reply = message.reply_to_message + if reply: + if not reply.forward_from: + user_id = reply.from_user.id + if reply.from_user.username: + name = f'**@{reply.from_user.username}**' + else: + name = f'**[{reply.from_user.first_name}](tg://user?id={reply.from_user.id})**' + else: + user_id = reply.forward_from.id + if reply.forward_from.username: + name = f'**@{reply.forward_from.username}**' + else: + name = f'**[{reply.forward_from.first_name}](tg://user?id={reply.forward_from.id})**' + edit(message, get_translation('useridResult', ['**', name, '`', user_id])) + else: + edit(message, f'`{get_translation("wrongCommand")}`') + + +@sedenify(pattern='^.kickme$', private=False) +def kick_me(message): + edit(message, f'`{get_translation("kickmeResult")}`') + message.chat.leave() + + +@sedenify(pattern='^.support$') +def support(message): + edit(message, get_translation('supportResult', [SUPPORT_GROUP]), preview=False) + + +@sedenify(pattern='^.founder') +def founder(message): + edit(message, get_translation('founderResult', ['`', '**']), preview=False) + + +@sedenify(pattern='^.repeat') +def repeat(message): + # Copyright (c) Gegham Zakaryan | 2019 + args = extract_args(message).split(' ', 1) + if len(args) < 2: + edit(message, f'`{get_translation("wrongCommand")}`') + return + cnt, txt = args + if not cnt.isdigit(): + edit(message, f'`{get_translation("wrongCommand")}`') + return + replyCount = int(cnt) + toBeRepeated = txt + + replyText = f'{toBeRepeated}\n' + + for i in range(0, replyCount - 1): + replyText += f'{toBeRepeated}\n' + + edit(message, replyText) + + +@sedenify(pattern='^.crash$') +def crash(message): + edit(message, f'`{get_translation("testLogId")}`') + raise Exception(get_translation('testException')) + + +@sedenify(pattern='^.tagall$', private=False) +def tag_all_users(message): + msg = '@tag' + chat = message.chat.id + length = 0 + for member in message._client.get_chat_members(chat): + if length < 4092: + msg += f'[\u2063](tg://user?id={member.user.id})' + length += 1 + reply(message, msg, delete_orig=True) + + +@sedenify(pattern='^.report$', private=False) +def report_admin(message): + msg = '@admin' + chat = message.chat.id + for member in message._client.get_chat_members( + chat, filter=enums.ChatMembersFilter.ADMINISTRATORS + ): + msg += f'[\u2063](tg://user?id={member.user.id})' + re_msg = message.reply_to_message + reply(re_msg if re_msg else message, msg) + message.delete() + + +@sedenify(pattern='^.hash') +def hash(message): + hashtxt_ = extract_args(message) + if len(hashtxt_) < 1: + edit(message, f'`{get_translation("wrongCommand")}`') + return + hashtxt = open('hash.txt', 'w+') + hashtxt.write(hashtxt_) + hashtxt.close() + md5 = runapp(['md5sum', 'hash.txt'], stdout=PIPE) + md5 = md5.stdout.decode() + sha1 = runapp(['sha1sum', 'hash.txt'], stdout=PIPE) + sha1 = sha1.stdout.decode() + sha256 = runapp(['sha256sum', 'hash.txt'], stdout=PIPE) + sha256 = sha256.stdout.decode() + sha512 = runapp(['sha512sum', 'hash.txt'], stdout=PIPE) + runapp(['rm', 'hash.txt'], stdout=PIPE) + sha512 = sha512.stdout.decode() + + def rem_filename(st): + return st[: st.find(' ')] + + ans = ( + f'Text: `{hashtxt_}`' + f'\nMD5: `{rem_filename(md5)}`' + f'\nSHA1: `{rem_filename(sha1)}`' + f'\nSHA256: `{rem_filename(sha256)}`' + f'\nSHA512: `{rem_filename(sha512)}`' + ) + if len(ans) > 4096: + hashfile = open('hash.txt', 'w+') + hashfile.write(ans) + hashfile.close() + reply_doc(message, 'hash.txt', caption=f'`{get_translation("outputTooLarge")}`') + runapp(['rm', 'hash.txt'], stdout=PIPE) + message.delete() + else: + edit(message, ans) + + +@sedenify(pattern='^.base64') +def base64(message): + argv = extract_args(message) + args = argv.split(' ', 1) + if len(args) < 2 or args[0] not in ['en', 'de']: + edit(message, f'`{get_translation("wrongCommand")}`') + return + args[1] = args[1].replace('`', '') + if args[0] == 'en': + lething = str(b64encode(bytes(args[1], 'utf-8')))[2:] + edit(message, f'Input: `{args[1]}`\nEncoded: `{lething[:-1]}`') + else: + lething = str(b64decode(bytes(args[1], 'utf-8')))[2:] + edit(message, f'Input: `{args[1]}`\nDecoded: `{lething[:-1]}`') + + +@sedenify(pattern='^.ascii$') +def img_to_ascii(message): + reply = message.reply_to_message + edit(message, f'`{get_translation("processing")}`') + if not reply: + edit(message, f'`{get_translation("wrongCommand")}`') + return + + if not ( + reply.photo + or (reply.sticker and not reply.sticker.is_animated) + or (reply.document and 'image' in reply.document.mime_type) + ): + edit(message, f'`{get_translation("wrongMedia")}`') + else: + media = download_media_wc(reply, file_name='ascii.png') + ImageToAscii(imagePath=media, outputFile="output.txt") + reply_doc(reply, 'output.txt', delete_after_send=True) + message.delete() + remove(media) + + +@sedenify(pattern='^.mention') +def mention_user(message): + args = extract_args(message) + arr = args.split(' ', 1) + find_user = extract_user(message) + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + for user in find_user: + name = ( + user.first_name.replace('\u2060', '') if user.first_name else user.username + ) + if message.reply_to_message: + edit(message, f'[{args or name}](tg://user?id={user.id})') + elif args: + if len(arr) > 1: + edit(message, f'[{arr[1]}](tg://user?id={user.id})') + else: + edit(message, f'[{args}](tg://user?id={user.id})') + + +HELP.update({'misc': get_translation('miscInfo')}) diff --git a/sedenbot/modules/ocr.py b/sedenbot/modules/miscs/ocr.py similarity index 76% rename from sedenbot/modules/ocr.py rename to sedenbot/modules/miscs/ocr.py index f150413..061f113 100644 --- a/sedenbot/modules/ocr.py +++ b/sedenbot/modules/miscs/ocr.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,18 +7,20 @@ # All rights reserved. See COPYING, AUTHORS. # -from os import remove, path, makedirs -from requests import post +from os import remove +from requests import post from sedenbot import HELP, OCR_APIKEY -from sedenecem.core import (edit, extract_args, sedenify, - get_translation, download_media_wc) +from sedenecem.core import ( + download_media_wc, + edit, + extract_args, + get_translation, + sedenify, +) -def ocr_file(filename, - language='eng', - overlay=False, - api_key=OCR_APIKEY): +def ocr_file(filename, language='eng', overlay=False, api_key=OCR_APIKEY): payload = { 'isOverlayRequired': overlay, @@ -38,9 +40,10 @@ def ocr_file(filename, def ocr(message): if not OCR_APIKEY: return edit( - message, get_translation( - 'ocrApiMissing', [ - '**', 'OCR Space', '`']), preview=False) + message, + get_translation('ocrApiMissing', ['**', 'OCR Space', '`']), + preview=False, + ) match = extract_args(message) reply = message.reply_to_message diff --git a/sedenbot/modules/miscs/paste.py b/sedenbot/modules/miscs/paste.py new file mode 100644 index 0000000..646d02f --- /dev/null +++ b/sedenbot/modules/miscs/paste.py @@ -0,0 +1,80 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from requests import get, post +from requests.exceptions import HTTPError, Timeout, TooManyRedirects + +from sedenbot import HELP +from sedenecem.core import edit, extract_args, get_translation, sedenify + + +@sedenify(pattern='^.paste') +def pastebin(message): + paste = extract_args(message, line=False) + reply = message.reply_to_message + edit(message, f'`{get_translation("processing")}`') + + url = "https://dpaste.org/api/" + + if reply: + if not reply.text: + return edit(message, f'`{get_translation("pasteErr")}`') + paste = reply.text + + try: + r = post( + url=url, + data={ + 'content': paste.encode('utf-8'), + 'lexer': '_text', + 'expires': '3600', + }, + ) + except BaseException as e: + raise e + + try: + resp = r.text + out = resp.replace('"', '') + return edit(message, out, preview=False) + except BaseException as e: + raise e + + +@sedenify(pattern='^.getpaste') +def get_hastebin_text(message): + args = extract_args(message) + reply = message.reply_to_message + edit(message, f'`{get_translation("processing")}`') + + if reply: + args = reply.text + + if args.startswith('https://dpaste.org/'): + args = args[len('https://dpaste.org/') :] + elif args.startswith('dpaste.org/'): + args = args[len('dpaste.org/') :] + else: + return edit(message, f'`{get_translation("wrongURL")}`') + + resp = get(f'https://dpaste.org/{args}/raw') + + try: + resp.raise_for_status() + except HTTPError as err: + return edit(message, get_translation('banError', ['`', '**', err])) + except Timeout as err: + return edit(message, get_translation('banError', ['`', '**', err])) + except TooManyRedirects as err: + return edit(message, get_translation('banError', ['`', '**', err])) + + edit(message, get_translation('getPasteOut', ['`', resp.text])) + + +HELP.update({'pastebin': get_translation('pasteInfo')}) diff --git a/sedenbot/modules/miscs/remove_bg.py b/sedenbot/modules/miscs/remove_bg.py new file mode 100644 index 0000000..7a46b1a --- /dev/null +++ b/sedenbot/modules/miscs/remove_bg.py @@ -0,0 +1,73 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from os import path, remove + +from PIL import Image +from removebg import RemoveBg +from sedenbot import HELP, RBG_APIKEY +from sedenecem.core import ( + download_media_wc, + edit, + get_download_dir, + get_translation, + reply_doc, + sedenify, +) + + +@sedenify(pattern='^.rbg$') +def rbg(message): + if not RBG_APIKEY: + return edit( + message, + get_translation('rbgApiMissing', ['**', 'Remove.BG', '`']), + preview=False, + ) + reply = message.reply_to_message + + if ( + reply + and reply.media + and ( + reply.photo + or (reply.sticker and not reply.sticker.is_animated) + or (reply.document and 'image' in reply.document.mime_type) + ) + ): + edit(message, f'`{get_translation("processing")}`') + else: + edit(message, f'`{get_translation("rbgUsage")}`') + return + + IMG_PATH = f'{get_download_dir()}/image.png' + + if path.exists(IMG_PATH): + remove(IMG_PATH) + download_media_wc(reply, IMG_PATH) + edit(message, f'`{get_translation("rbgProcessing")}`') + + if reply.sticker and not reply.sticker.is_animated: + image = Image.open(IMG_PATH) + IMG_PATH = f'{get_download_dir()}/image.png' + image.save(IMG_PATH) + + try: + remove_bg = RemoveBg(RBG_APIKEY, get_translation('rbgLog')) + remove_bg.remove_background_from_img_file(IMG_PATH) + rbg_img = f'{IMG_PATH}_no_bg.png' + reply_doc( + reply, rbg_img, caption=get_translation('rbgResult'), delete_after_send=True + ) + message.delete() + except Exception as e: + return edit(message, get_translation('banError', ['`', '**', e])) + + +HELP.update({'rbg': get_translation('rbgInfo')}) diff --git a/sedenbot/modules/reverse.py b/sedenbot/modules/miscs/reverse.py similarity index 72% rename from sedenbot/modules/reverse.py rename to sedenbot/modules/miscs/reverse.py index b4a989a..ccb6142 100644 --- a/sedenbot/modules/reverse.py +++ b/sedenbot/modules/miscs/reverse.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,28 +8,30 @@ # from os import path, remove -from re import findall, I, M -from urllib import request, parse -from requests import post, get -from PIL import Image -from bs4 import BeautifulSoup +from re import I, M, findall +from urllib import parse, request +from bs4 import BeautifulSoup +from PIL import Image from pyrogram.types import InputMediaPhoto - +from requests import get, post from sedenbot import HELP -from sedenecem.core import (sedenify, edit, reply_doc, extract_args, - download_media_wc, get_translation) +from sedenecem.core import ( + download_media_wc, + edit, + extract_args, + get_translation, + reply_doc, + sedenify, + useragent, +) opener = request.build_opener() -useragent = '''Mozilla/5.0 (Linux; Android 9; -SM-G960F Build/PPR1.180610.011; wv) -AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 -Chrome/78.0.3904.70 Mobile Safari/537.36''' -opener.addheaders = [('User-agent', useragent)] +opener.addheaders = [('User-agent', useragent())] -@sedenify(pattern=r'^.reverse$', compat=False) -def reverse(client, message): +@sedenify(pattern='^.reverse$') +def reverse(message): photo = 'reverse.png' if path.isfile(photo): remove(photo) @@ -53,13 +55,8 @@ def reverse(client, message): image.close() # https://stackoverflow.com/questions/23270175/google-reverse-image-search-using-post-request#28792943 searchUrl = 'https://www.google.com/searchbyimage/upload' - multipart = { - 'encoded_image': (photo, open(photo, 'rb')), - 'image_content': '' - } - response = post(searchUrl, - files=multipart, - allow_redirects=False) + multipart = {'encoded_image': (photo, open(photo, 'rb')), 'image_content': ''} + response = post(searchUrl, files=multipart, allow_redirects=False) fetchUrl = response.headers['Location'] if response != 400: @@ -68,15 +65,13 @@ def reverse(client, message): edit(message, f'`{get_translation("reverseGoogle")}`') return - remove(photo) - match = ParseSauce(fetchUrl + - '&preferences?hl=en&fg=1#languages') + #remove(photo) + match = ParseSauce(fetchUrl + '&preferences?hl=en&fg=1#languages') guess = match['best_guess'] imgspage = match['similar_images'] if guess and imgspage: - edit(message, get_translation( - "reverseResult", [guess, fetchUrl, '`'])) + edit(message, get_translation("reverseResult", [guess, fetchUrl, '`'])) else: edit(message, f'`{get_translation("reverseError2")}`') return @@ -96,8 +91,7 @@ def reverse(client, message): file.close() yeet.append(InputMediaPhoto(n)) reply_doc(message, yeet) - edit(message, get_translation( - "reverseResult", [guess, fetchUrl, imgspage])) + edit(message, get_translation("reverseResult", [guess, fetchUrl, imgspage])) def ParseSauce(googleurl): @@ -109,8 +103,9 @@ def ParseSauce(googleurl): try: for similar_image in soup.findAll('input', {'class': 'gLFyf'}): - url = 'https://www.google.com/search?tbm=isch&q=' + \ - parse.quote_plus(similar_image.get('value')) + url = 'https://www.google.com/search?tbm=isch&q=' + parse.quote_plus( + similar_image.get('value') + ) results['similar_images'] = url except BaseException: pass diff --git a/sedenbot/modules/miscs/spotify_api.py b/sedenbot/modules/miscs/spotify_api.py new file mode 100644 index 0000000..bf20e06 --- /dev/null +++ b/sedenbot/modules/miscs/spotify_api.py @@ -0,0 +1,223 @@ +# Copyright (C) 2020-2024 TeamDerUntergang +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from concurrent.futures import ThreadPoolExecutor +from glob import glob +from os import cpu_count, mkdir, path, remove, rmdir +from threading import Lock +from urllib.request import urlretrieve +from zipfile import ZipFile + +from pyrogram import enums +from requests import get +from sedenbot import HELP +from sedenecem.core import ( + edit, + extract_args_split, + get_translation, + reply_audio, + reply_doc, + reply_img, + sedenify, +) +from spotipy import Spotify, SpotifyClientCredentials +from yt_dlp import YoutubeDL + + +class Spotipy: + def __init__(self): + self.spotify_dir() + self.sp = Spotify(auth_manager=SpotifyClientCredentials()) + + def spotify_dir(self): + if path.exists('Spotify'): + rmdir('Spotify') + mkdir('Spotify') + else: + mkdir('Spotify') + + def search_track(self, name: str): + ydl_opts = { + 'outtmpl': f'Spotify/{name}.%(ext)s', + 'format': 'bestaudio/best', + 'addmetadata': True, + 'writethumbnail': True, + 'prefer_ffmpeg': True, + "extractaudio": True, + 'geo_bypass': True, + 'nocheckcertificate': True, + 'cachedir': False, + 'default_search': 'ytsearch', + 'noplaylist': True, + 'postprocessors': [ + { + 'key': 'FFmpegExtractAudio', + 'preferredcodec': 'mp3', + 'preferredquality': '320', + }, + {'key': 'EmbedThumbnail'}, + {'key': 'FFmpegMetadata'}, + ], + 'quiet': True, + 'logtostderr': False, + } + + with YoutubeDL(ydl_opts) as ydl: + ydl.download([name]) + + def fetch_track(self, message, playlist_url: str, is_zip=False): + fullname = [] + offset = 0 + + edit(message, f'`{get_translation("downloadMedia")}`') + + while True: + fields = "items.track.name,items.track.artists.name,total" + playlist = self.sp.playlist_items( + playlist_url, + fields=fields, + offset=offset, + additional_types=['track'], + ) + total = playlist['total'] + + for item in playlist['items']: + if item['track'] is None: + offset += 1 + continue + + track = item['track']['name'] + artist = item['track']['artists'][0]['name'] + name = f'{artist} - {track}' + fullname.append(name) + + offset += 1 + + if total == offset: + break + self.download_track(message, fullname, is_zip=is_zip) + + def download_track(self, message, song_list: list, is_zip): + lock = Lock() + with lock: + with ThreadPoolExecutor(min(32, cpu_count() or 0) + 4) as executor: + executor.map(self.search_track, song_list) + + if is_zip: + zipfile = ZipFile('Spotify/playlist.zip', 'w') + for song in glob('Spotify/*.mp3'): + zipfile.write(song) + remove(song) + zipfile.close() + + edit(message, f'`{get_translation("uploadingZip")}`') + reply_doc( + message, + 'Spotify/playlist.zip', + delete_orig=True, + delete_after_send=True, + ) + else: + for song in glob('Spotify/*.mp3'): + reply_audio(message, song, delete_orig=True, delete_file=True) + + def show_playlist(self, username=None): + global counter + counter = 0 + users_playlists = '\n' + + if username: + result = self.sp.user_playlists(username, limit=50) + else: + result = self.sp.user_playlists(username=self.username) + + for item in result['items']: + pl_name = item['name'] + users_playlists += f'▪️ {pl_name}\n' + counter += 1 + msg = f'{users_playlists}' + return msg + + def show_users_detail( + self, + username, + message, + ): + if username == None: + return edit(message, (get_translation("invalidUsername"))) + else: + r = get(f"https://open.spotify.com/user/{username}") + if r.status_code == 404: + edit(message, get_translation("userNotFound", ['**', '`', username])) + + else: + user = self.sp.user(username) + profile_photo = [i['url'] for i in user['images']] + if profile_photo: + r = urlretrieve("".join(profile_photo), 'Spotify/pfp.png') + else: + profile_photo = None + pass + + out = get_translation( + 'spotifyResult', + [ + '**', + '`', + username, + user['external_urls']['spotify'], + self.show_playlist(username), + counter, + ], + ) + + media_perm = True + if message.chat.type in [ + enums.ChatType.SUPERGROUP, + enums.ChatType.GROUP, + ]: + perm = message.chat.permissions + media_perm = perm.can_send_media_messages + + if profile_photo and media_perm: + reply_img( + message, + photo='Spotify/pfp.png', + caption=out, + delete_file=True, + delete_orig=True, + ) + else: + edit(message, out, preview=False) + + +@sedenify(pattern='^.spoti(|fy)') +def spotify_download(message): + spotify = Spotipy() + args = extract_args_split(message) + + if len(args) == 2 and args[0] == 'dl' and not args[1] == 'zip': + url = args[1] + spotify.fetch_track(message, url) + + elif len(args) == 3 and args[0] == 'dl' and args[1] == 'zip': + url = args[2] + spotify.fetch_track(message, url, is_zip=True) + + elif len(args) == 2 and args[0] == 'show': + username = args[1] + if len(username) == 1: + edit(message, f'`{get_translation("invalidUsername")}`') + else: + spotify.show_users_detail(username=username, message=message) + else: + edit(message, f'`{get_translation("invalidProcess")}`') + + +HELP.update({'spotify': get_translation('spotifyInfo')}) diff --git a/sedenbot/modules/pmpermit.py b/sedenbot/modules/pmpermit.py deleted file mode 100644 index 133f689..0000000 --- a/sedenbot/modules/pmpermit.py +++ /dev/null @@ -1,240 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from sqlalchemy.exc import IntegrityError - -from sedenbot.modules.chat import is_muted -from sedenbot import (PM_COUNT, HELP, PM_AUTO_BAN, LOGS, - PM_LAST_MSG, PM_UNAPPROVED, PM_MSG_COUNT) -from sedenecem.core import (sedenify, send_log, me, - edit, reply, get_translation) -# ========================= CONSTANTS ============================ -UNAPPROVED_MSG = PM_UNAPPROVED or get_translation('pmpermitMessage', ['`']) -# ================================================================= - - -def pmpermit_init(): - try: - global sql - from importlib import import_module - sql = import_module('sedenecem.sql.pm_permit_sql') - except Exception as e: - sql = None - LOGS.warn(get_translation('pmpermitSqlLog')) - raise e - - -pmpermit_init() - - -@sedenify(incoming=True, outgoing=False, disable_edited=True, - disable_notify=True, group=False, compat=False, bot=False) -def permitpm(client, message): - if message.from_user and message.from_user.is_self: - message.continue_propagation() - - if not PM_AUTO_BAN: - message.continue_propagation() - else: - if auto_accept(client, message): - return - - self_user = me[0] - if message.chat.id not in [self_user.id, 777000]: - try: - from sedenecem.sql.pm_permit_sql import is_approved - except BaseException: - pass - - apprv = is_approved(message.chat.id) - notifsoff = is_muted(-1) - - if not apprv and message.text != UNAPPROVED_MSG: - if message.chat.id in PM_LAST_MSG: - prevmsg = PM_LAST_MSG[message.chat.id] - if message.text != prevmsg: - for message in _find_unapproved_msg( - client, message.chat.id): - message.delete() - if PM_COUNT[message.chat.id] < (PM_MSG_COUNT - 1): - ret = reply(message, UNAPPROVED_MSG) - PM_LAST_MSG[message.chat.id] = ret.text - else: - ret = reply(message, UNAPPROVED_MSG) - if ret.text: - PM_LAST_MSG[message.chat.id] = ret.text - - if notifsoff: - client.read_history(message.chat.id) - - if message.chat.id not in PM_COUNT: - PM_COUNT[message.chat.id] = 1 - else: - PM_COUNT[message.chat.id] = PM_COUNT[message.chat.id] + 1 - - if PM_COUNT[message.chat.id] > (PM_MSG_COUNT - 1): - reply(message, f'`{get_translation("pmpermitBlock")}`') - - try: - del PM_COUNT[message.chat.id] - del PM_LAST_MSG[message.chat.id] - except BaseException: - pass - - client.block_user(message.chat.id) - - send_log( - get_translation( - 'pmpermitLog', [ - message.chat.first_name, message.chat.id])) - - message.continue_propagation() - - -def auto_accept(client, message): - self_user = me[0] - if message.chat.id not in [self_user.id, 777000]: - try: - from sedenecem.sql.pm_permit_sql import approve, is_approved - except BaseException: - return False - - chat = message.chat - if is_approved(chat.id): - return True - - for msg in client.get_history(chat.id, limit=3): - if chat.id in PM_LAST_MSG and msg.text != PM_LAST_MSG[chat.id] and msg.from_user.is_self: - try: - del PM_COUNT[chat.id] - del PM_LAST_MSG[chat.id] - except BaseException: - pass - - try: - approve(chat.id) - for message in _find_unapproved_msg(client, chat.id): - message.delete() - send_log( - get_translation( - 'pmAutoAccept', [ - chat.first_name, chat.id])) - return True - except BaseException: - pass - - return False - - -@sedenify(outgoing=True, pattern='^.notifoff$') -def notifoff(message): - try: - from sedenecem.sql.keep_read_sql import kread - except BaseException: - edit(message, f'`{get_translation("nonSqlMode")}`') - return - - kread(str(-1)) - edit(message, f'`{get_translation("pmNotifOff")}`') - - -@sedenify(outgoing=True, pattern='^.notifon$') -def notifon(message): - try: - from sedenecem.sql.keep_read_sql import unkread - except BaseException: - edit(message, f'`{get_translation("nonSqlMode")}`') - return - - unkread(str(-1)) - edit(message, f'`{get_translation("pmNotifOn")}`') - - -@sedenify(outgoing=True, pattern='^.approve$', compat=False) -def approvepm(client, message): - try: - from sedenecem.sql.pm_permit_sql import approve - except BaseException: - edit(message, f'`{get_translation("nonSqlMode")}`') - return - - if message.reply_to_message: - reply = message.reply_to_message - replied_user = reply.from_user - if replied_user.is_self: - edit(message, f'`{get_translation("cannotApproveMyself")}`') - return - aname = replied_user.id - name0 = str(replied_user.first_name) - uid = replied_user.id - else: - aname = message.chat - if not aname.type == 'private': - edit(message, f'`{get_translation("pmApproveError")}`') - return - name0 = aname.first_name - uid = aname.id - - try: - approve(uid) - for message in _find_unapproved_msg(client, message.chat.id): - message.delete() - except IntegrityError: - edit(message, f'`{get_translation("pmApproveError2")}`') - return - - edit(message, get_translation('pmApproveSuccess', [name0, uid, '`'])) - - send_log(get_translation('pmApproveLog', [name0, uid])) - - -@sedenify(outgoing=True, pattern="^.disapprove$") -def disapprovepm(message): - try: - from sedenecem.sql.pm_permit_sql import dissprove - except BaseException: - edit(message, f'`{get_translation("nonSqlMode")}`') - return - - if message.reply_to_message: - reply = message.reply_to_message - replied_user = reply.from_user - if replied_user.is_self: - edit(message, f'`{get_translation("cannotDisapproveMyself")}`') - return - aname = replied_user.id - name0 = str(replied_user.first_name) - uid = replied_user.id - else: - aname = message.chat - if not aname.type == 'private': - edit(message, f'`{get_translation("pmApproveError")}`') - return - name0 = aname.first_name - uid = aname.id - - dissprove(uid) - - edit(message, get_translation('pmDisapprove', [name0, uid, '`'])) - - send_log(get_translation('pmDisapprove', [name0, uid, '`'])) - - -def _find_unapproved_msg(client, chat_id): - try: - return client.search_messages( - chat_id, - from_user='me', - limit=10, - query=UNAPPROVED_MSG) - except BaseException: - return [] - - -HELP.update({'pmpermit': get_translation('pmpermitInfo')}) diff --git a/sedenbot/modules/profile.py b/sedenbot/modules/profile.py deleted file mode 100644 index 62ec521..0000000 --- a/sedenbot/modules/profile.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from os import remove - -from pyrogram.errors import UsernameOccupied -from pyrogram.raw.functions import channels, account - -from sedenbot import HELP -from sedenecem.core import (edit, extract_args, sedenify, send_log, - get_translation, download_media_wc) -# ====================== CONSTANT =============================== -INVALID_MEDIA = get_translation('mediaInvalid') -PP_CHANGED = get_translation('ppChanged') -PP_ERROR = get_translation('ppError') - -BIO_SUCCESS = get_translation('bioSuccess') -NAME_OK = get_translation('nameOk') - -USERNAME_SUCCESS = get_translation('usernameSuccess') -USERNAME_TAKEN = get_translation('usernameTaken') -# =============================================================== - - -@sedenify(pattern='^.reserved$', compat=False) -def reserved(client, message): - sonuc = client.send(channels.GetAdminedPublicChannels()) - mesaj = '' - for channel_obj in sonuc.chats: - mesaj += f'{channel_obj.title}\n@{channel_obj.username}\n\n' - edit(message, mesaj) - - -@sedenify(pattern='^.name', compat=False) -def name(client, message): - newname = extract_args(message) - if ' ' not in newname: - firstname = newname - lastname = '' - else: - namesplit = newname.split(' ', 1) - firstname = namesplit[0] - lastname = namesplit[1] - - client.send(account.UpdateProfile( - first_name=firstname, last_name=lastname)) - edit(message, f'`{NAME_OK}`') - - -@sedenify(pattern='^.setpfp$', compat=False) -def set_profilepic(client, message): - reply = message.reply_to_message - photo = None - if (reply and reply.media and (reply.photo or ( - reply.document and 'image' in reply.document.mime_type))): - photo = download_media_wc(reply, 'profile_photo.jpg') - else: - edit(message, f'`{INVALID_MEDIA}`') - - if photo: - client.set_profile_photo(photo=photo) - remove(photo) - edit(message, f'`{PP_CHANGED}`') - else: - edit(message, f'`{PP_ERROR}`') - - -@sedenify(pattern=r'^.delpfp', compat=False) -def remove_profilepic(client, message): - group = message.text[8:] - if group == 'all': - lim = 0 - elif group.isdigit(): - lim = int(group) - else: - lim = 1 - - count = 0 - for photo in client.iter_profile_photos('me', limit=lim): - client.delete_profile_photos(photo.file_id) - count += 1 - edit(message, f'`{count} adet profil fotoğrafı silindi.`') - - -@sedenify(pattern='^.setbio', compat=False) -def setbio(client, message): - newbio = extract_args(message) - client.send(account.UpdateProfile(about=newbio)) - edit(message, BIO_SUCCESS) - - -@sedenify(pattern='^.username', compat=False) -def username(client, message): - newusername = extract_args(message) - try: - client.send(account.UpdateUsername(username=newusername)) - edit(message, f'`{USERNAME_SUCCESS}`') - except UsernameOccupied: - edit(message, f'`{USERNAME_TAKEN}`') - - -@sedenify(pattern='^.block$', compat=False) -def blockpm(client, message): - if message.reply_to_message: - reply = message.reply_to_message - replied_user = reply.from_user - if replied_user.is_self: - edit(message, f'`{get_translation("cannotBlockMyself")}`') - return - aname = replied_user.id - name0 = str(replied_user.first_name) - uid = replied_user.id - else: - aname = message.chat - if not aname.type == 'private': - edit(message, f'`{get_translation("pmApproveError")}`') - return - name0 = aname.first_name - uid = aname.id - - client.block_user(uid) - - edit(message, f'`{get_translation("pmBlocked")}`') - - try: - from sedenecem.sql.pm_permit_sql import dissprove - dissprove(uid) - except BaseException: - pass - - send_log(get_translation('pmBlockedLog', [name0, uid])) - - -@sedenify(pattern='^.unblock$', compat=False) -def unblockpm(client, message): - if message.reply_to_message: - reply = message.reply_to_message - replied_user = reply.from_user - if replied_user.is_self: - edit(message, f'`{get_translation("cannotUnblockMyself")}`') - return - name0 = str(replied_user.first_name) - uid = replied_user.id - client.unblock_user(uid) - edit(message, f'`{get_translation("pmUnblocked")}`') - - send_log(get_translation('pmUnblockedLog', [name0, replied_user.id])) - else: - edit(message, f'`{get_translation("pmUnblockedUsage")}`') - - -HELP.update({'profile': get_translation('profileInfo')}) diff --git a/sedenbot/modules/qrcode.py b/sedenbot/modules/qrcode.py deleted file mode 100644 index b3eec08..0000000 --- a/sedenbot/modules/qrcode.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from os import remove -from qrcode import QRCode, constants -from barcode import get -from barcode.writer import ImageWriter -from urllib3 import PoolManager -from bs4 import BeautifulSoup - -from sedenbot import HELP -from sedenecem.core import (extract_args, sedenify, edit, reply_doc, - download_media_wc, get_translation) - - -@sedenify(pattern=r'^.decode$') -def parseqr(message): - reply = message.reply_to_message - if not reply: - return edit(message, f'`{get_translation("wrongCommand")}`') - - if not(reply.photo or reply.sticker or ( - reply.document and 'image' in reply.document.mime_type)): - edit(message, f'`{get_translation("wrongCommand")}`') - return - - downloaded_file_name = download_media_wc(reply) - - dw = open(downloaded_file_name, 'rb') - files = {'f': dw.read()} - t_response = None - - try: - http = PoolManager() - t_response = http.request( - 'POST', 'https://zxing.org/w/decode', fields=files) - t_response = t_response.data - http.clear() - dw.close() - except BaseException: - pass - - remove(downloaded_file_name) - if not t_response: - edit(message, f'`{get_translation("decodeFail")}`') - return - try: - soup = BeautifulSoup(t_response, 'html.parser') - qr_contents = soup.find_all('pre')[0].text - edit(message, qr_contents) - except BaseException: - edit(message, f'`{get_translation("decodeFail")}`') - - -@sedenify(pattern=r'^.barcode') -def barcode(message): - input_str = extract_args(message) - usage = get_translation('barcodeUsage', ['**', '`']) - reply = message.reply_to_message - if len(input_str) < 1 and not reply: - edit(message, usage) - return - edit(message, f'`{get_translation("processing")}`') - if reply: - if reply.media: - downloaded_file_name = download_media_wc(reply) - media_list = None - with open(downloaded_file_name, 'rb') as file: - media_list = file.readlines() - qrmsg = '' - for media in media_list: - qrmsg += media.decode('UTF-8') + '\r\n' - remove(downloaded_file_name) - else: - qrmsg = reply - else: - qrmsg = input_str - - bar_code_type = 'code128' - try: - bar_code_mode_f = get(bar_code_type, qrmsg, writer=ImageWriter()) - filename = bar_code_mode_f.save(bar_code_type) - reply_doc(message, filename, delete_after_send=True) - except Exception as e: - edit(message, str(e)) - return - message.delete() - - -@sedenify(pattern=r'^.makeqr') -def makeqr(message): - input_str = extract_args(message) - usage = get_translation('makeqrUsage', ['**', '`']) - reply = message.reply_to_message - if len(input_str) < 1 and not reply: - edit(message, usage) - return - edit(message, f'`{get_translation("processing")}`') - if reply: - if reply.media: - downloaded_file_name = download_media_wc(reply) - media_list = None - with open(downloaded_file_name, 'rb') as file: - media_list = file.readlines() - qrmsg = '' - for media in media_list: - qrmsg += media.decode('UTF-8') + '\r\n' - remove(downloaded_file_name) - else: - qrmsg = reply - else: - qrmsg = input_str - - try: - qr = QRCode( - version=1, - error_correction=constants.ERROR_CORRECT_L, - box_size=10, - border=4) - qr.add_data(qrmsg) - qr.make(fit=True) - img = qr.make_image(fill_color='black', back_color='white') - img.save('img_file.webp', 'PNG') - reply_doc(message, 'img_file.webp', delete_after_send=True) - except Exception as e: - edit(message, str(e)) - return - message.delete() - - -HELP.update({'qrcode': get_translation('makeqrInfo')}) -HELP.update({'barcode': get_translation('barcodeInfo')}) diff --git a/sedenbot/modules/remove_bg.py b/sedenbot/modules/remove_bg.py deleted file mode 100644 index bb0c3d0..0000000 --- a/sedenbot/modules/remove_bg.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from os import path, remove -from removebg import RemoveBg - -from sedenbot import HELP, RBG_APIKEY, DOWNLOAD_DIRECTORY -from sedenecem.core import (sedenify, edit, reply_doc, - get_translation, download_media_wc) - - -@sedenify(pattern='^.rbg$') -def rbg(message): - if not RBG_APIKEY: - return edit( - message, get_translation( - 'rbgApiMissing', [ - '**', 'Remove.BG', '`']), preview=False) - reply = message.reply_to_message - - if reply and ( - reply.photo or ( - reply.document and 'image' in reply.document.mime_type)): - edit(message, f'`{get_translation("processing")}`') - else: - edit(message, f'`{get_translation("rbgUsage")}`') - return - - IMG_PATH = f'{DOWNLOAD_DIRECTORY}/image.png' - - if path.exists(IMG_PATH): - remove(IMG_PATH) - download_media_wc(reply, IMG_PATH) - edit(message, f'`{get_translation("rbgProcessing")}`') - try: - remove_bg = RemoveBg(RBG_APIKEY, f'{get_translation("rbgLog")}') - remove_bg.remove_background_from_img_file(IMG_PATH) - rbg_img = IMG_PATH + '_no_bg.png' - reply_doc(reply, rbg_img, - caption=get_translation('rbgResult')) - message.delete() - except Exception as e: - return edit(message, get_translation('banError', ['`', '**', e])) - - -HELP.update({'rbg': get_translation('rbgInfo')}) diff --git a/sedenbot/modules/sangmata.py b/sedenbot/modules/sangmata.py deleted file mode 100644 index 0de7828..0000000 --- a/sedenbot/modules/sangmata.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from pyrogram.errors import YouBlockedUser - -from sedenbot import HELP -from sedenecem.core import sedenify, edit, get_translation, PyroConversation - - -@sedenify(pattern='^.sangmata$', compat=False) -def sangmata(client, message): - reply = message.reply_to_message - if reply and reply.text: - edit(message, f'`{get_translation("processing")}`') - else: - edit(message, f'`{get_translation("replyMessage")}`') - return - - chat = 'SangMataInfo_bot' - - with PyroConversation(client, chat) as conv: - response = None - try: - conv.forward_msg(reply) - response = conv.recv_msg() - except YouBlockedUser: - edit(message, get_translation('unblockChat', ['**', '`', chat])) - return - except Exception as e: - raise e - - if not response: - edit(message, f'`{get_translation("answerFromBot")}`') - elif response.text and response.text.startswith('Forward'): - edit(message, f'`{get_translation("privacySettings")}`') - else: - edit(message, response.text) - - -HELP.update({'sangmata': get_translation('sangmataInfo')}) diff --git a/sedenbot/modules/scrapers.py b/sedenbot/modules/scrapers.py deleted file mode 100644 index 3947432..0000000 --- a/sedenbot/modules/scrapers.py +++ /dev/null @@ -1,507 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from os import remove, path -from time import sleep -from re import findall, sub -from urllib.parse import quote_plus -from urllib.error import HTTPError -from mimetypes import guess_type -from urbandict import define -from wikipedia import set_lang, summary -from wikipedia.exceptions import DisambiguationError, PageError -from gtts import gTTS -from gtts.lang import tts_langs -from googletrans import LANGUAGES, Translator -from emoji import get_emoji_regexp -from requests import get -from bs4 import BeautifulSoup - -from pyrogram.types import InputMediaPhoto - -from traceback import format_exc - -from sedenbot import HELP, SEDEN_LANG -from sedenecem.core import (sedenify, edit, send_log, reply_doc, reply_voice, - extract_args, get_webdriver, get_translation) - -CARBONLANG = 'auto' -TTS_LANG = SEDEN_LANG -TRT_LANG = SEDEN_LANG - - -@sedenify(pattern='^.crblang') -def carbonlang(message): - global CARBONLANG - CARBONLANG = extract_args(message) - edit(message, get_translation('carbonLang', ['**', CARBONLANG])) - - -@sedenify(pattern='^.carbon') -def carbon(message): - match = extract_args(message) - if len(match) < 1: - edit(message, f'`{get_translation("wrongCommand")}`') - return - edit(message, f'`{get_translation("processing")}`') - textx = message.reply_to_message - pcode = message.text - if pcode[8:]: - pcode = str(pcode[8:]) - elif textx: - pcode = str(textx.message) - code = quote_plus(pcode) - global CARBONLANG - CARBON = f'https://carbon.now.sh/?l={CARBONLANG}&code={code}' - edit(message, f'`{get_translation("processing")}\n%25`') - if path.isfile('./carbon.png'): - remove('./carbon.png') - driver = get_webdriver() - driver.get(CARBON) - edit(message, f'`{get_translation("processing")}\n%50`') - driver.command_executor._commands['send_command'] = ( - 'POST', '/session/$sessionId/chromium/send_command') - driver.find_element_by_xpath("//button[contains(text(),'Export')]").click() - edit(message, f'`{get_translation("processing")}\n%`75') - while not path.isfile('./carbon.png'): - sleep(0.5) - edit(message, f'`{get_translation("processing")}\n%100`') - file = './carbon.png' - edit(message, f'`{get_translation("carbonUpload")}`') - reply_doc(message, file, caption=get_translation('carbonResult'), - delete_orig=True, delete_after_send=True) - driver.quit() - - -@sedenify(pattern='^.img') -def img(message): - query = extract_args(message) - lim = findall(r'lim=\d+', query) - try: - lim = lim[0] - lim = lim.replace('lim=', '') - query = query.replace('lim=' + lim[0], '') - lim = int(lim) - if lim > 10: - lim = 10 - except IndexError: - lim = 3 - - if len(query) < 1: - edit(message, f'`{get_translation("imgUsage")}`') - return - edit(message, f'`{get_translation("processing")}`') - - url = f'https://www.google.com/search?tbm=isch&q={query}&gbv=2&sa=X&biw=1920&bih=1080' - driver = get_webdriver() - driver.get(url) - count = 1 - files = [] - for i in driver.find_elements_by_xpath( - '//div[contains(@class,"isv-r PNCib MSM1fd BUooTd")]'): - i.click() - try_count = 0 - while len(element := driver.find_elements_by_xpath( - '//img[contains(@class,"n3VNCb") and contains(@src,"http")]')) < 1 and try_count < 20: - try_count += 1 - sleep(.1) - if len(element) < 1: - continue - link = element[0].get_attribute('src') - filename = f'result_{count}.jpg' - try: - with open(filename, 'wb') as result: - result.write(get(link).content) - ftype = guess_type(filename) - if not ftype[0] or ftype[0].split( - '/')[1] not in ['png', 'jpg', 'jpeg']: - remove(filename) - continue - except Exception: - continue - files.append(InputMediaPhoto(filename)) - sleep(1) - driver.find_elements_by_xpath( - '//a[contains(@class,"hm60ue")]')[0].click() - count += 1 - if lim < count: - break - sleep(1) - - driver.quit() - - reply_doc(message, files, delete_orig=True, delete_after_send=True) - - -@sedenify(pattern=r'^.google') -def google(message): - match = extract_args(message) - if len(match) < 1: - edit(message, f'`{get_translation("wrongCommand")}`') - return - page = findall(r"page=\d+", match) - try: - page = page[0] - page = page.replace('page=', '') - match = match.replace('page=' + page[0], '') - page = int(page) - except BaseException: - page = 1 - msg = do_gsearch(match, page) - edit(message, get_translation('googleResult', ['**', '`', match, msg]), - preview=False) - - send_log(get_translation('googleLog', [match])) - - -def do_gsearch(query, page): - - def find_page(num): - if num < 1: - num = 1 - return (num - 1) * 10 - - def parse_key(keywords): - return keywords.replace(' ', '+') - - def replacer(st): - return sub( - r'[`\*_]', - '', - st).replace( - '\n', - ' ').replace( - '(', - '〈').replace( - ')', - '〉').replace( - '!', - 'ⵑ').strip() - - def link_replacer(link): - rep = {'(': '%28', ')': '%29', '[': '%5B', ']': '%5D', '%': '½'} - for i in rep.keys(): - link = link.replace(i, rep[i]) - return link - - def get_result(res): - link = res.findAll('a', {'class': ['fuLhoc', 'ZWRArf']})[0] - href = f"https://google.com{link_replacer(link['href'])}" - title = link.findAll('span', {'class': ['CVA68e', 'qXLe6d']})[0].text - title = replacer(title) - desc = res.findAll('span', {'class': ['qXLe6d', 'FrIlee']})[-1].text - desc = replacer(desc) - if len(desc.strip()) < 1: - desc = get_translation('googleDesc') - return f'[{title}]({href})\n{desc}' - - query = parse_key(query) - page = find_page(page) - req = get( - f'https://www.google.com/search?q={query}&gbv=1&sei=2oR3X4nhGY611fAP_5-EkAw&start={find_page(page)}', - headers={ - 'User-Agent': 'Mozilla/5.0 (compatible; Konqueror/2.2-12; Linux)', - 'Content-Type': 'text/html'}) - soup = BeautifulSoup(req.text, 'html.parser') - res1 = soup.findAll('div', {'class': ['ezO2md']}) - - def is_right_class(res): - ret = res.find('span', {'class': ['qXLe6d', 'dXDvrc']}) - - if not ret: - return False - - ret = ret.parent - - return ret.name == 'a' and 'fuLhoc' in ret['class'] - - res1 = [res for res in res1 if is_right_class(res)] - - out = '' - for i in range(0, len(res1)): - res = res1[i] - try: - out += f'{i+1} - {get_result(res)}\n\n' - except Exception: - print(format_exc()) - print(res) - pass - - return out - - -@sedenify(pattern='^.d(uckduck|d)go') -def ddgo(message): - query = extract_args(message) - if len(query) < 1: - edit(message, f'`{get_translation("wrongCommand")}`') - return - req = get( - f'https://duckduckgo.com/lite?q={query}', - headers={ - 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64)' - 'AppleWebKit/537.36 (KHTML, like Gecko)' - 'Chrome/81.0.4044.138 Safari/537.36', - 'Content-Type': 'text/html'}) - soup = BeautifulSoup(req.text, 'html.parser') - res1 = soup.findAll('table', {'border': 0}) - res1 = res1[-1].findAll('tr') - - edit(message, get_translation('sedenQuery', [ - '**', '`', query, do_ddsearch(query, res1)]), preview=False) - send_log(get_translation('ddgoLog', [query])) - - -def do_ddsearch(query, res1): - def splitter(res): - subs = [] - tlist = None - comp = False - for i in range(len(res)): - item = res[i] - if res3 := item.find('a', {'class': ['result-link']}): - if comp: - subs.append(tlist) - comp = True - tlist = [] - tlist.append(res3) - elif res4 := item.find('td', {'class': ['result-snippet']}): - tlist.append(res4) - subs.append(tlist) - if len(subs) > 9: - break - return subs - - res1 = splitter(res1) - - out = '' - for i in range(len(res1)): - item = res1[i] - link = item[0] - ltxt = link.text.replace('|', '-').replace('...', '').strip() - desc = (item[1].text.strip() - if len(item) > 1 - else f'{get_translation("ddgoDesc")}') - out += (f'{i+1}-[{ltxt}]({link["href"]})\n{desc}\n\n') - - return out - - -@sedenify(pattern='^.ud') -def urbandictionary(message): - match = extract_args(message) - if len(match) < 1: - edit(message, f'`{get_translation("wrongCommand")}`') - return - edit(message, f'`{get_translation("processing")}`') - query = extract_args(message) - try: - define(query) - except HTTPError: - edit(message, get_translation('udResult', ['**', query])) - return - mean = define(query) - deflen = sum(len(i) for i in mean[0]['def']) - exalen = sum(len(i) for i in mean[0]['example']) - meanlen = deflen + exalen - if int(meanlen) >= 0: - if int(meanlen) >= 4096: - edit(message, f'`{get_translation("outputTooLarge")}`') - file = open('urbandictionary.txt', 'w+') - file.write('Query: ' + query + '\n\nMeaning: ' + mean[0]['def'] + - '\n\n' + 'Örnek: \n' + mean[0]['example']) - file.close() - reply_doc(message, 'urbandictionary.txt', - caption=f'`{get_translation("outputTooLarge")}`') - if path.exists('urbandictionary.txt'): - remove('urbandictionary.txt') - message.delete() - return - edit(message, get_translation('sedenQueryUd', [ - '**', '`', query, mean[0]['def'], mean[0]['example']])) - else: - edit(message, get_translation('udNoResult', ['**', query])) - - -@sedenify(pattern=r'^.wiki') -def wiki(message): - match = extract_args(message) - if len(match) < 1: - edit(message, f'`{get_translation("wrongCommand")}`') - return - set_lang(SEDEN_LANG) - match = extract_args(message) - try: - summary(match) - except DisambiguationError as error: - edit(message, get_translation('wikiError', [error])) - return - except PageError as pageerror: - edit(message, get_translation('wikiError2', [pageerror])) - return - result = summary(match) - if len(result) >= 4096: - file = open('wiki.txt', 'w+') - file.write(result) - file.close() - reply_doc(message, 'wiki.txt', - caption=f'`{get_translation("outputTooLarge")}`') - if path.exists('wiki.txt'): - remove('wiki.txt') - return - edit(message, get_translation('sedenQuery', ['**', '`', match, result])) - - send_log(get_translation('wikiLog', ['`', match])) - - -@sedenify(pattern=r'^.tts') -def tts(message): - textx = message.reply_to_message - ttsx = extract_args(message) - if ttsx: - pass - elif textx: - ttsx = textx.text - else: - edit(message, f'`{get_translation("ttsUsage")}`') - return - - try: - gTTS(ttsx, lang=TTS_LANG) - except AssertionError: - edit(message, f'`{get_translation("ttsBlank")}`') - return - except ValueError: - edit(message, f'`{get_translation("ttsNoSupport")}`') - return - except RuntimeError: - edit(message, f'{get_translation("ttsError")}') - return - tts = gTTS(ttsx, lang=TTS_LANG) - tts.save('h.mp3') - with open('h.mp3', 'rb') as audio: - linelist = list(audio) - linecount = len(linelist) - if linecount == 1: - tts = gTTS(ttsx, lang=TTS_LANG) - tts.save('h.mp3') - with open('h.mp3', 'r'): - reply_voice(message, 'h.mp3', delete_orig=True) - remove('h.mp3') - - send_log(get_translation('ttsLog')) - - -@sedenify(pattern=r'^.trt') -def trt(message): - translator = Translator() - textx = message.reply_to_message - trt = extract_args(message) - if trt: - pass - elif textx: - trt = textx.text - else: - edit(message, f'{get_translation("trtUsage")}') - return - - try: - reply_text = translator.translate(deEmojify(trt), dest=TRT_LANG) - except ValueError: - edit(message, f'{get_translation("trtError")}') - return - - source_lan = LANGUAGES[f'{reply_text.src.lower()}'] - transl_lan = LANGUAGES[f'{reply_text.dest.lower()}'] - reply_text = '{}\n\n{}'.format( - get_translation( - 'transHeader', - ['**', '`', source_lan.title(), - transl_lan.title()]), - reply_text.text) - - edit(message, reply_text) - - send_log(get_translation( - 'trtLog', [source_lan.title(), transl_lan.title()])) - - -def deEmojify(inputString): - return get_emoji_regexp().sub(u'', inputString) - - -@sedenify(pattern='^.lang') -def lang(message): - arr = extract_args(message).split(' ', 1) - - if len(arr) != 2: - edit(message, f'`{get_translation("wrongCommand")}`') - return - - util = arr[0].lower() - arg = arr[1].lower() - if util == 'trt': - scraper = get_translation('scraper1') - global TRT_LANG - if arg in LANGUAGES: - TRT_LANG = arg - LANG = LANGUAGES[arg] - else: - edit(message, get_translation('scraperTrt', ['`', LANGUAGES])) - return - elif util == 'tts': - scraper = get_translation('scraper2') - global TTS_LANG - if arg in tts_langs(): - TTS_LANG = arg - LANG = tts_langs()[arg] - else: - edit(message, get_translation('scraperTts', ['`', tts_langs()])) - return - edit(message, get_translation( - 'scraperResult', ['`', scraper, LANG.title()])) - - send_log(get_translation('scraperLog', ['`', scraper, LANG.title()])) - - -@sedenify(pattern='^.currency') -def currency(message): - input_str = extract_args(message) - input_sgra = input_str.split(' ') - if len(input_sgra) == 3: - try: - number = float(input_sgra[0]) - currency_from = input_sgra[1].upper() - currency_to = input_sgra[2].upper() - request_url = f'https://api.exchangeratesapi.io/latest?base={currency_from}' - current_response = get(request_url).json() - if currency_to in current_response['rates']: - current_rate = float(current_response['rates'][currency_to]) - rebmun = round(number * current_rate, 2) - edit( - message, - f'**{number} {currency_from} = {rebmun} {currency_to}**') - else: - edit(message, get_translation('currencyError')) - except Exception as e: - edit(message, str(e)) - else: - edit(message, get_translation('syntaxError')) - return - - -HELP.update({'img': get_translation('imgInfo')}) -HELP.update({'currency': get_translation('currencyInfo')}) -HELP.update({'carbon': get_translation('carbonInfo')}) -HELP.update({'google': get_translation('googleInfo')}) -HELP.update({'duckduckgo': get_translation('ddgoInfo')}) -HELP.update({'wiki': get_translation('wikiInfo')}) -HELP.update({'ud': get_translation('udInfo')}) -HELP.update({'tts': get_translation('ttsInfo')}) -HELP.update({'trt': get_translation('trtInfo')}) diff --git a/sedenbot/modules/seden.py b/sedenbot/modules/seden.py deleted file mode 100644 index 2751185..0000000 --- a/sedenbot/modules/seden.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from collections import OrderedDict - -from sedenbot import HELP -from sedenecem.core import (edit, reply, extract_args, - sedenify, get_translation) - - -@sedenify(pattern='^.seden') -def seden(message): - seden = extract_args(message).lower() - cmds = OrderedDict(sorted(HELP.items())) - if len(seden) > 0: - if seden in cmds: - edit(message, str(cmds[seden])) - else: - edit(message, f'**{get_translation("sedenUsage")}**') - else: - edit(message, get_translation('sedenUsage2', ['**', '`'])) - metin = f'{get_translation("sedenShowLoadedModules", ["**", "`", len(cmds)])}\n' - for item in cmds: - metin += f'• `{item}`\n' - reply(message, metin) diff --git a/sedenbot/modules/speedtest.py b/sedenbot/modules/speedtest.py deleted file mode 100644 index 33aaf30..0000000 --- a/sedenbot/modules/speedtest.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from datetime import datetime -from speedtest import Speedtest - -from sedenbot import HELP -from sedenecem.core import (extract_args, sedenify, edit, - reply_doc, get_translation) - - -@sedenify(pattern='^.speedtest') -def speed_test(message): - input_str = extract_args(message) - as_text = False - if input_str == 'text': - as_text = True - edit(message, f'`{get_translation("speedtest")}`') - start = datetime.now() - spdtst = Speedtest() - spdtst.get_best_server() - spdtst.download() - spdtst.upload() - end = datetime.now() - ms = (end - start).microseconds / 1000 - response = spdtst.results.dict() - download_speed = response.get('download') - upload_speed = response.get('upload') - ping_time = response.get('ping') - client_infos = response.get('client') - i_s_p = client_infos.get('isp') - i_s_p_rating = client_infos.get('isprating') - try: - response = spdtst.results.share() - speedtest_image = response - if as_text: - edit(message, - get_translation('speedtestResultText', - ['**', - ms, - convert_from_bytes(download_speed), - convert_from_bytes(upload_speed), - ping_time, - i_s_p, - i_s_p_rating, - ''])) - else: - reply_doc( - message, speedtest_image, caption=get_translation( - 'speedtestResultDoc', [ - '**', ms]), delete_after_send=True, delete_orig=True) - except Exception as exc: - edit(message, - get_translation('speedtestResultText', - ['**', - ms, - convert_from_bytes(download_speed), - convert_from_bytes(upload_speed), - ping_time, - i_s_p, - i_s_p_rating, - f'ERROR: {str(exc)}'])) - - -def convert_from_bytes(size): - power = 2**10 - _ = 0 - units = { - 0: '', - 1: 'kilobytes', - 2: 'megabytes', - 3: 'gigabytes', - 4: 'terabytes' - } - while size > power: - size /= power - _ += 1 - return f'{round(size, 2)} {units[_]}' - - -HELP.update({'speedtest': get_translation('speedtestInfo')}) diff --git a/sedenbot/modules/stickers.py b/sedenbot/modules/stickers.py deleted file mode 100644 index d6e4f91..0000000 --- a/sedenbot/modules/stickers.py +++ /dev/null @@ -1,200 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from random import choice -from requests import get - -from pyrogram.raw.functions.messages import GetStickerSet -from pyrogram.raw.types import InputStickerSetShortName - -from sedenbot import HELP, PACKNAME, PACKNICK -from sedenecem.core import ( - sedenify, - edit, - me, - download_media_wc, - reply_doc, - get_translation, - extract_args, - PyroConversation, - sticker_resize as resizer) -# ================= CONSTANT ================= -DIZCILIK = [get_translation(f'kangstr{i+1}') for i in range(0, 12)] -# ================= CONSTANT ================= - - -@sedenify(pattern='^.(d[ıi]zla|kang)', compat=False) -def kang(client, message): - myacc = me[0] - kanger = myacc.username or myacc.first_name - if myacc.username: - kanger = f'@{kanger}' - args = extract_args(message) - - reply = message.reply_to_message - if not reply: - edit(message, f'`{get_translation("stickerUsage")}`') - return - - anim = False - media = None - - if(reply.photo or reply.document or reply.sticker): - edit(message, f'`{choice(DIZCILIK)}`') - anim = reply.sticker and reply.sticker.is_animated - media = download_media_wc(reply, sticker_orig=anim) - else: - edit(message, f'`{get_translation("stickerError")}`') - return - - if len(args) < 1: - args = 1 - - emoji = '🤤' - - if ' ' in str(args): - emoji, args = args.split(' ', 1) - - pack = 1 if not str(args).isdigit() else int(args) - - pname = PACKNAME.replace( - ' ', '') if PACKNAME else f'a{myacc.id}_by_{myacc.username}_{pack}' - pnick = PACKNICK or f"{kanger}'s UserBot pack {pack}" - - limit = '50' if anim else '120' - - def pack_created(): - created = get(f'https://telegram.me/addstickers/{pname}') - created = (('A Telegram user has created the ' - 'Sticker Set') not in created.text) - return created - - def create_new(conv, pack): - cmd = f'/new{"animated" if anim else "pack"}' - - try: - send_recv(conv, cmd) - except Exception as e: - raise e - msg = send_recv(conv, pnick) - if msg.text == 'Invalid pack selected.': - pack += 1 - return create_new(conv) - msg = send_recv(conv, media, doc=True) - if 'Sorry, the file type is invalid.' in msg.text: - edit(message, f'`{get_translation("stickerError")}`') - return - send_recv(conv, emoji) - send_recv(conv, '/publish') - if anim: - send_recv(conv, f'<{pnick}>') - send_recv(conv, '/skip') - send_recv(conv, pname) - - def add_exist(conv, pack, pname, pnick): - try: - send_recv(conv, '/addsticker') - except Exception as e: - raise e - - status = send_recv(conv, pname) - - if limit in status.text: - pack += 1 - pname = f'a{myacc.id}_by_{myacc.username}_{pack}' - pnick = f"{kanger}'s UserBot pack {pack}" - edit(message, get_translation('packFull', ['`', '**', str(pack)])) - return add_exist(conv, pack, pname, pnick) - - send_recv(conv, media, doc=True) - send_recv(conv, emoji) - send_recv(conv, '/done') - return True - - if anim: - pname += '_anim' - pnick += ' (Animated)' - else: - if not reply.sticker: - media = resizer(media) - - with PyroConversation(client, 'Stickers') as conv: - if pack_created(): - ret = add_exist(conv, pack, pname, pnick) - if not ret: - return - else: - create_new(conv, pack) - - edit(message, get_translation('stickerAdded', ['`', pname])) - - -def send_recv(conv, msg, doc=False): - if doc: - conv.send_doc(msg) - else: - conv.send_msg(msg) - return conv.recv_msg() - - -@sedenify(pattern='^.getsticker$') -def getsticker(message): - reply = message.reply_to_message - if not reply or not reply.sticker: - edit(message, f'`{get_translation("replySticker")}`') - return - - photo = download_media_wc(reply) - - reply_doc( - reply, - photo, - caption=f'**Sticker ID:** `{reply.sticker.file_id}' - f'`\n**Emoji**: `{reply.sticker.emoji}`', - delete_after_send=True, - delete_orig=True) - - -@sedenify(pattern='.packinfo$', compat=False) -def packinfo(client, message): - reply = message.reply_to_message - if not reply: - edit(message, f'`{get_translation("packinfoError")}`') - return - - if not reply.sticker: - edit(message, f'`{get_translation("packinfoError2")}`') - return - - edit(message, f'`{get_translation("processing")}`') - - get_stickerset = client.send( - GetStickerSet( - stickerset=InputStickerSetShortName( - short_name=reply.sticker.set_name))) - pack_emojis = [] - for document_sticker in get_stickerset.packs: - if document_sticker.emoticon not in pack_emojis: - pack_emojis.append(document_sticker.emoticon) - - out = get_translation('packinfoResult', - ['**', - '`', - get_stickerset.set.title, - get_stickerset.set.short_name, - get_stickerset.set.official, - get_stickerset.set.archived, - get_stickerset.set.animated, - get_stickerset.set.count, - ' '.join(pack_emojis)]) - - edit(message, out) - - -HELP.update({'stickers': get_translation('stickerInfo')}) diff --git a/sedenbot/modules/system.py b/sedenbot/modules/system.py deleted file mode 100644 index 2420924..0000000 --- a/sedenbot/modules/system.py +++ /dev/null @@ -1,225 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from datetime import datetime -from shutil import which -from getpass import getuser -from operator import add, sub, mul, truediv, pow, xor, neg -from ast import (Add, Sub, Mult, Div, Pow, BitXor, USub, - parse, Num, BinOp, UnaryOp) - -from pyrogram.raw.functions.help import GetNearestDc - -from sedenbot.modules.lovers import saniye -from sedenbot.modules.ecem import ecem -from sedenbot import (HELP, ALIVE_MSG, CHANNEL, - BOT_VERSION, HOSTNAME, USER) -from sedenecem.core import (edit, reply, reply_doc, send_log, - extract_args, sedenify, get_translation) -# ================= CONSTANT ================= -KULLANICIMESAJI = ALIVE_MSG or f"`{get_translation('sedenAlive')}`" -# ============================================ - - -@sedenify(pattern='^.neofetch$') -def neofetch(message): - try: - from subprocess import PIPE, Popen - islem = Popen( - ['neofetch', f'HOSTNAME={HOSTNAME}', f'USER={USER}', '--stdout'], - stdout=PIPE, stderr=PIPE) - sonuc, _ = islem.communicate() - edit(message, sonuc.decode(), parse=None) - except BaseException: - edit(message, f'`{get_translation("neofetchNotFound")}`') - - -@sedenify(pattern='^.botver$') -def botver(message): - if which('git'): - from subprocess import PIPE, Popen - degisiklik = Popen(['git', 'rev-list', '--all', '--count'], - stdout=PIPE, stderr=PIPE, universal_newlines=True) - sonuc, _ = degisiklik.communicate() - - edit(message, - get_translation('sedenShowBotVersion', - ['**', - '`', - 'Seden UserBot', - CHANNEL, - BOT_VERSION, - sonuc]), - preview=False) - else: - edit(message, f'`{get_translation("sedenGitNotFound")}`') - - -@sedenify(pattern='^.pip') -def pip3(message): - pipmodule = extract_args(message) - if len(pipmodule) > 0: - edit(message, f'`{get_translation("pipSearch")}`') - pipsorgu = f"pip3 search {pipmodule}" - from subprocess import PIPE, Popen - islem = Popen(pipsorgu.split(), stdout=PIPE, - stderr=PIPE, universal_newlines=True) - sonuc, _ = islem.communicate() - - if sonuc: - if len(sonuc) > 4096: - edit(message, f'`{get_translation("outputTooLarge")}`') - file = open('pip3.txt', 'w+') - file.write(sonuc) - file.close() - reply_doc(message, 'pip3.txt', delete_after_send=True) - return - edit(message, get_translation( - 'sedenQuery', ['**', '`', pipsorgu, sonuc])) - else: - edit(message, get_translation('sedenQuery', [ - '**', '`', pipsorgu, get_translation('sedenZeroResults')])) - else: - edit(message, f'`{get_translation("pipHelp")}`') - - -@sedenify(pattern='^.ping$') -def ping(message): - basla = datetime.now() - edit(message, '`Pong!`') - bitir = datetime.now() - sure = (bitir - basla).microseconds / 1000 - edit(message, f'`Pong!\n{sure}ms`') - - -@sedenify(pattern='^.alive$') -def alive(message): - if KULLANICIMESAJI.lower() == 'ecem': - ecem(message) - return - elif KULLANICIMESAJI.lower() == 'saniye': - saniye(message) - return - edit(message, f'{KULLANICIMESAJI}') - - -@sedenify(pattern='^.echo') -def echo(message): - args = extract_args(message) - if len(args) > 0: - message.delete() - reply(message, args) - else: - edit(message, f'`{get_translation("echoHelp")}`') - - -@sedenify(pattern='^.dc$', compat=False) -def dc(client, message): - sonuc = client.send(GetNearestDc()) - - edit(message, get_translation('sedenNearestDC', [ - '**', '`', sonuc.country, sonuc.nearest_dc, sonuc.this_dc])) - - -@sedenify(pattern='^.term') -def terminal(message): - command = extract_args(message) - - if len(command) < 1: - edit(message, f'`{get_translation("termUsage")}`') - return - - curruser = getuser() - try: - from os import geteuid - uid = geteuid() - except ImportError: - uid = 0 - - if not command: - edit(message, f'`{get_translation("termHelp")}`') - return - - sonuc = f'`{get_translation("termNoResult")}`' - try: - from subprocess import getoutput - sonuc = getoutput(command) - except BaseException: - pass - - if len(sonuc) > 4096: - output = open('output.txt', 'w+') - output.write(sonuc) - output.close() - reply_doc(message, 'output.txt', - caption=f'`{get_translation("outputTooLarge")}`', - delete_after_send=True) - return - - edit( - message, - f'`{curruser}:~{"#" if uid == 0 else "$"} {command}\n{sonuc}`') - - send_log(get_translation('termLog', [command])) - - -@sedenify(pattern='^.eval') -def eval(message): - args = extract_args(message) - if len(args) < 1: - edit(message, f'`{get_translation("evalUsage")}`') - return - - try: - evaluation = safe_eval(args) - if evaluation: - if isinstance(evaluation, str): - if len(evaluation) >= 4096: - file = open('output.txt', 'w+') - file.write(evaluation) - file.close() - reply_doc(message, - 'output.txt', - caption=f'`{get_translation("outputTooLarge")}`', - delete_after_send=True) - return - edit(message, get_translation( - 'sedenQuery', ['**', '`', args, evaluation])) - else: - edit(message, get_translation('sedenQuery', [ - '**', '`', args, get_translation('sedenErrorResult')])) - except Exception as err: - edit(message, get_translation( - 'sedenQuery', ['**', '`', args, str(err)])) - - send_log(get_translation('evalLog', [args])) - - -operators = {Add: add, Sub: sub, Mult: mul, - Div: truediv, Pow: pow, BitXor: xor, - USub: neg} - - -def safe_eval(expr): - expr = expr.lower().replace('x', '*').replace(' ', '') - return str(_eval(parse(expr, mode='eval').body)) - - -def _eval(node): - if isinstance(node, Num): - return node.n - elif isinstance(node, BinOp): - return operators[type(node.op)](_eval(node.left), _eval(node.right)) - elif isinstance(node, UnaryOp): - return operators[type(node.op)](_eval(node.operand)) - else: - raise TypeError(f'`{get_translation("safeEval")}`') - - -HELP.update({'system': get_translation('systemInfo')}) diff --git a/sedenbot/modules/android.py b/sedenbot/modules/tools/android.py similarity index 59% rename from sedenbot/modules/android.py rename to sedenbot/modules/tools/android.py index 5bdd7e2..3c43c82 100644 --- a/sedenbot/modules/android.py +++ b/sedenbot/modules/tools/android.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang +# Copyright (C) 2020-2024 TeamDerUntergang # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,35 +7,60 @@ # All rights reserved. See COPYING, AUTHORS. # -from re import sub +from datetime import datetime from json import loads +from re import sub from urllib.parse import urlencode + from bs4 import BeautifulSoup from requests import get -from datetime import datetime - -from sedenbot import HELP, VALID_PROXY_URL -from sedenecem.core import edit, extract_args, sedenify, get_translation - -GITHUB = 'https://github.com' +from sedenbot import HELP +from sedenecem.core import ( + ProxyHandler, + edit, + extract_args, + get_translation, + sedenify, + useragent, +) + +@sedenify(pattern='^.k(ernel)?su$') +def kernelsu(message): + kernelsu_url = 'https://api.github.com/repos/tiann/KernelSU/releases' + try: + response = get(kernelsu_url) + data = response.json() + releases = sorted(data, key=lambda x: x['created_at'], reverse=True)[:3] + out = f'**{get_translation("ksuReleases")}**\n' + for release in releases: + tag_name = release['tag_name'] + assets = release['assets'] + apk_assets = [asset for asset in assets if asset['name'].endswith('.apk')] + if apk_assets: + latest_apk = apk_assets[0] + apk_name = latest_apk['name'] + apk_download_url = latest_apk['browser_download_url'] + out += f'`{tag_name}:` [{apk_name}]({apk_download_url})\n' + edit(message, out, preview=False) + except Exception: + pass @sedenify(pattern='^.magisk$') def magisk(message): magisk_dict = { - 'Stable': - 'https://raw.githubusercontent.com/topjohnwu/' - 'magisk_files/master/stable.json', - 'Beta': - 'https://raw.githubusercontent.com/topjohnwu/' - 'magisk_files/master/beta.json'} + 'Stable': 'https://raw.githubusercontent.com/topjohnwu/' + 'magisk-files/master/stable.json', + 'Beta': 'https://raw.githubusercontent.com/topjohnwu/' + 'magisk-files/master/beta.json', + 'Canary': 'https://raw.githubusercontent.com/topjohnwu/' + 'magisk-files/master/canary.json', + } releases = f'**{get_translation("magiskReleases")}**\n' for name, release_url in magisk_dict.items(): try: data = get(release_url).json() - releases += f'`{name}:` [ZIP v{data["magisk"]["version"]}]({data["magisk"]["link"]}) | ' \ - f'[APK v{data["app"]["version"]}]({data["app"]["link"]}) | ' \ - f'[Uninstaller]({data["uninstaller"]["link"]})\n' + releases += f'`{name}:` [APK v{data["magisk"]["version"]}]({data["magisk"]["link"]})\n' except BaseException: pass edit(message, releases, preview=False) @@ -44,12 +69,14 @@ def magisk(message): @sedenify(pattern='^.phh') def phh(message): get_phh = get( - 'https://api.github.com/repos/phhusson/treble_experimentations/releases/latest').json() + 'https://api.github.com/repos/phhusson/treble_experimentations/releases/latest' + ).json() search = extract_args(message) releases = '{}\n'.format( get_translation( - 'androidPhhHeader', [ - '`', "{} ".format(search) if len(search) > 0 else ""])) + 'androidPhhHeader', ['`', "{} ".format(search) if len(search) > 0 else ""] + ) + ) count = 0 for i in range(len(get_phh)): try: @@ -69,7 +96,48 @@ def phh(message): edit(message, releases, preview=False) -@sedenify(pattern=r'^.device') +def format_size(size_in_bytes): + if size_in_bytes >= 1e9: + return '{:.2f} GB'.format(size_in_bytes / 1e9) + else: + return '{:.2f} MB'.format(size_in_bytes / 1e6) + + +@sedenify(pattern='^.l(ineage(os)?|os)') +def get_lineageos(message): + args = extract_args(message).lower() + + if len(args) < 1: + return edit(message, f'`{get_translation("wrongCommand")}`') + else: + edit(message, f'`{get_translation("processing")}`') + + req = get( + f'https://download.lineageos.org/api/v1/{args}/nightly/*', + headers={'User-Agent': useragent()}, + ).json() + + response = req['response'] + + if response: + build = response[0] + time = datetime.utcfromtimestamp(int(build['datetime'])).strftime('%Y-%m-%d') + filename = build['filename'] + size = format_size(int(build['size'])) + build_url = build['url'] + version = build['version'] + else: + return edit(message, get_translation('losNoBuild', ['**', '`', args])) + + edit( + message, + get_translation( + 'losBuild', ['**', '`', args, filename, build_url, size, version, time] + ), + ) + + +@sedenify(pattern='^.device') def device(message): textx = message.reply_to_message codename = extract_args(message) @@ -80,25 +148,26 @@ def device(message): else: edit(message, f'`{get_translation("deviceUsage")}`') return - data = loads(get('https://raw.githubusercontent.com/androidtrackers/' - 'certified-android-devices/master/by_device.json').text) + data = loads( + get( + 'https://raw.githubusercontent.com/androidtrackers/' + 'certified-android-devices/master/by_device.json' + ).text + ) results = data.get(codename) if results: - reply = "{}\n".format( - get_translation( - 'deviceSearch', [ - '**', codename])) + reply = "{}\n".format(get_translation('deviceSearch', ['**', codename])) for item in results: - reply += get_translation('deviceSearchResultChild', - ['**', item['brand'], - item['name'], - item['model']]) + reply += get_translation( + 'deviceSearchResultChild', + ['**', item['brand'], item['name'], item['model']], + ) else: reply = get_translation('deviceError', ['`', codename]) edit(message, reply) -@sedenify(pattern=r'^.codename') +@sedenify(pattern='^.codename') def codename(message): textx = message.reply_to_message arr = extract_args(message) @@ -114,29 +183,38 @@ def codename(message): else: edit(message, f'`{get_translation("codenameUsage")}`') return - data = loads(get('https://raw.githubusercontent.com/androidtrackers/' - 'certified-android-devices/master/by_brand.json').text) + data = loads( + get( + 'https://raw.githubusercontent.com/androidtrackers/' + 'certified-android-devices/master/by_brand.json' + ).text + ) devices_lower = {k.lower(): v for k, v in data.items()} devices = devices_lower.get(brand) if not devices: reply = get_translation('codenameError', ['`', device]) else: - results = [i for i in devices if device.lower( - ) in i['name'].lower() or device.lower() in i['model'].lower()] + results = [ + i + for i in devices + if device.lower() in i['name'].lower() + or device.lower() in i['model'].lower() + ] if results: reply = f'{get_translation("codenameSearch", ["**", brand, device])}\n' if len(results) > 8: results = results[:8] for item in results: - reply += get_translation('codenameSearchResultChild', - ['**', item['device'], - item['name'], item['model']]) + reply += get_translation( + 'codenameSearchResultChild', + ['**', item['device'], item['name'], item['model']], + ) else: reply = get_translation('codenameError', ['`', device]) edit(message, reply) -@sedenify(pattern=r'^.twrp') +@sedenify(pattern='^.twrp') def twrp(message): textx = message.reply_to_message device = extract_args(message) @@ -159,11 +237,12 @@ def twrp(message): size = page.find('span', {'class': 'filesize'}).text date = page.find('em').text.strip() reply = get_translation( - 'twrpResult', ['**', '__', device, dl_file, dl_link, size, date]) + 'twrpResult', ['**', '__', device, dl_file, dl_link, size, date] + ) edit(message, reply) -@sedenify(pattern=r'^.o(range|)f(ox|rp)') +@sedenify(pattern='^.o(range|)f(ox|rp)') def ofox(message): if len(args := extract_args(message)) < 1: edit(message, f'`{get_translation("ofrpUsage")}`') @@ -176,8 +255,11 @@ def ofox(message): releases = ofrp_get_packages(args) if len(releases.releases) < 1: - edit(message, get_translation('ofrpNotFound', ['`', args, OFOX_REPO]), - preview=False) + edit( + message, + get_translation('ofrpNotFound', ['`', args, OFOX_REPO]), + preview=False, + ) return out = '' @@ -193,33 +275,32 @@ def ofox(message): edit(message, f'**OrangeFox Recovery ({args}):**\n{out}') -@sedenify(pattern=r'^.specs') +@sedenify(pattern='^.specs') def specs(message): args = extract_args(message) if len(args) < 1: edit(message, f'`{get_translation("specsUsage")}`') return - edit(message, f'`{get_translation("fetchProxy")}`') - proxy = get_random_proxy() - edit(message, f'`{get_translation("providedProxy")}`') + handler = ProxyHandler() + proxy = handler.use_proxy(message) link = find_device(args, proxy) if not link: edit(message, f'`{get_translation("specsError")}`') return - req = get(link, - headers={'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; ' - '+http://www.google.com/bot.html)'}, - proxies=proxy) + req = get( + link, + headers={'User-Agent': useragent()}, + proxies=proxy, + ) soup = BeautifulSoup(req.text, features='html.parser') def get_spec(query, key='data-spec', cls='td'): try: result = soup.find(cls, {key: query.split()}).text.strip() - result = get_translation('specsError2') if len( - result) < 1 else result + result = get_translation('specsError2') if len(result) < 1 else result return result except BaseException: return get_translation('specsError2') @@ -249,57 +330,64 @@ def get_spec(query, key='data-spec', cls='td'): sarus = sub(r'\s\s+', ', ', get_spec('sar-us')) sareu = sub(r'\s\s+', ', ', get_spec('sar-eu')) - edit(message, - get_translation('specsResult', - ['**', - '`', - title, - launch, - body, - sarus, - sareu, - os, - cpuname, - cpuchip, - gpuname, - storage, - stortyp, - dispsize, - dispres, - bcampx, - bcamft, - bcamvd, - fcampx, - fcamft, - fcamvd, - battery, - wlan, - bluetooth, - gps, - sensors, - link])) + edit( + message, + get_translation( + 'specsResult', + [ + '**', + '`', + title, + launch, + body, + sarus, + sareu, + os, + cpuname, + cpuchip, + gpuname, + storage, + stortyp, + dispsize, + dispres, + bcampx, + bcamft, + bcamvd, + fcampx, + fcamft, + fcamvd, + battery, + wlan, + bluetooth, + gps, + sensors, + link, + ], + ), + ) def find_device(query, proxy): - """@frknkrc44, GSMArena üzerinden cihaz bulma""" + """Find device from GSMArena by @frknkrc44""" raw_query = query.lower() def replace_query(query): - return urlencode({'sSearch': query}) + return urlencode({'sQuickSearch': 'yes', 'sName': query}) query = replace_query(raw_query) - req = get(f'https://www.gsmarena.com/res.php3?{query}', - headers={'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; ' - '+http://www.google.com/bot.html)'}, - proxies=proxy) + req = get( + f'https://www.gsmarena.com/results.php3?{query}', + headers={'User-Agent': useragent()}, + proxies=proxy, + ) soup = BeautifulSoup(req.text, features='html.parser') - if 'Too' in soup.find('title').text: # GSMArena geçici ban atarsa + if 'Too' in soup.find('title').text: # if temp banned by GSMArena return None res = soup.findAll('div', {'class': ['makers']}) - if not res or len(res) < 1: # hiçbir cihaz bulunamazsa + if not res or len(res) < 1: # if no device found return None res = res[0].findAll('li') @@ -307,71 +395,15 @@ def replace_query(query): for item in res: name = str(item.find('span')) name = sub('(<|', '', name) - if name[name.find('>') + 1:].lower() == raw_query or sub('|/>)', - ' ', name).lower() == raw_query: + if ( + name[name.find('>') + 1 :].lower() == raw_query + or sub('|/>)', ' ', name).lower() == raw_query + ): link = f"https://www.gsmarena.com/{item.find('a')['href']}" return link return None -def _xget_random_proxy(): - try_valid = tuple(VALID_PROXY_URL[0].split(':')) if len( - VALID_PROXY_URL) > 0 else None - if try_valid: - valid = _try_proxy(try_valid) - if valid[0] == 200 and "Too" not in valid[1]: - return try_valid - - head = { - 'Accept-Encoding': 'gzip, deflate, sdch', - 'Accept-Language': 'en-US,en;q=0.8', - 'User-Agent': 'ArabyBot (compatible; Mozilla/5.0; GoogleBot; FAST Crawler 6.4; http://www.araby.com;)', - 'Referer': 'https://www.google.com/search?q=sslproxies', - } - - req = get('https://sslproxies.org/', headers=head) - soup = BeautifulSoup(req.text, 'html.parser') - res = soup.find('table', {'id': 'proxylisttable'}).find('tbody') - res = res.findAll('tr') - for item in res: - infos = item.findAll('td') - ip = infos[0].text - port = infos[1].text - proxy = (ip, port) - if _try_proxy(proxy)[0] == 200: - return proxy - - return None - - -def _try_proxy(proxy): - try: - prxy = f'http://{proxy[0]}:{proxy[1]}' - req = get('https://www.gsmarena.com/', - proxies={'http': prxy, 'https': prxy}, timeout=1) - if req.status_code == 200: - return (200, req.text) - raise Exception - except BaseException: - return (404, None) - - -def get_random_proxy(): - proxy = _xget_random_proxy() - if not proxy: - return None - proxy = f'http://{proxy[0]}:{proxy[1]}' - VALID_PROXY_URL.clear() - VALID_PROXY_URL.append(proxy) - - proxy_dict = { - 'https': proxy, - 'http': proxy, - } - - return proxy_dict - - class OFRPDeviceInfo: def __init__(self, json, releases): if not json: @@ -392,12 +424,12 @@ def __init__(self, json): self.type = json['type'] self.device = json['device_id'] - date = datetime.utcfromtimestamp( - int(json['date'])).strftime('%d-%m-%Y %H:%M:%S') + date = datetime.utcfromtimestamp(int(json['date'])).strftime( + '%d-%m-%Y %H:%M:%S' + ) self.date = date - self.file_size = '{:,.2f} MB'.format( - int(json['size']) / float(1 << 20)) + self.file_size = '{:,.2f} MB'.format(int(json['size']) / float(1 << 20)) self.md5 = json['md5'] self.version = json['version'] @@ -423,7 +455,7 @@ def ofrp_get(url): try: head = { 'Accept-Language': 'en-US,en;q=0.8', - 'User-Agent': 'ArabyBot (compatible; Mozilla/5.0; GoogleBot; FAST Crawler 6.4; http://www.araby.com;)', + 'User-Agent': useragent(), 'Referer': 'https://orangefox.download/en', } req = get(url, headers=head) diff --git a/sedenbot/modules/tools/currency.py b/sedenbot/modules/tools/currency.py new file mode 100644 index 0000000..9c0532f --- /dev/null +++ b/sedenbot/modules/tools/currency.py @@ -0,0 +1,76 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from bs4 import BeautifulSoup +from requests import get + +from sedenbot import HELP +from sedenecem.core import edit, extract_args, get_translation, sedenify, useragent + + +@sedenify(pattern='^.currency') +def currency_convert(message): + input_str = extract_args(message) + input_sgra = input_str.split(' ') + if len(input_sgra) == 3: + try: + number = float(input_sgra[0]) + currency_from = input_sgra[1].upper() + currency_to = input_sgra[2].upper() + request_url = f'https://www.x-rates.com/calculator/?from={currency_from}&to={currency_to}&amount={number}' + current_response = get(request_url, headers={'User-Agent': useragent()}) + if current_response.status_code == 200: + soup = BeautifulSoup(current_response.text, 'html.parser') + rebmun = soup.find('span', {'class': 'ccOutputRslt'}) + result = rebmun.find('span') + result.extract() + edit(message, f'**{number} {currency_from} = {rebmun.text.strip()}**') + else: + edit(message, f'`{get_translation("currencyError")}`') + except Exception as e: + edit(message, str(e)) + else: + edit(message, f'`{get_translation("syntaxError")}`') + return + + +@sedenify(pattern='^.d[oö]viz') +def doviz(message): + req = get( + 'https://www.doviz.com/', + headers={'User-Agent': useragent()}, + ) + page = BeautifulSoup(req.content, 'html.parser') + res = page.find_all('div', {'class': 'item'}) + out = '**Güncel döviz kurları:**\n\n' + + for item in res: + name = item.find('span', {'class': 'name'}).text + value = item.find('span', {'class': 'value'}).text + + rate_elem = item.find( + 'div', {'class': ['change-rate status down', 'change-rate status up']} + ) + rate_class = rate_elem['class'][-1] if rate_elem else None + + changes_emoji = '' + if rate_class == 'down': + changes_emoji = '⬇️' + elif rate_class == 'up': + changes_emoji = '⬆️' + + if changes_emoji: + out += f'{changes_emoji} **{name}:** `{value}`\n' + else: + out += f'**{name}:** `{value}`\n' + + edit(message, out) + + +HELP.update({'currency': get_translation('currencyInfo')}) diff --git a/sedenbot/modules/tools/ddgo_search.py b/sedenbot/modules/tools/ddgo_search.py new file mode 100644 index 0000000..6e5d0d4 --- /dev/null +++ b/sedenbot/modules/tools/ddgo_search.py @@ -0,0 +1,83 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from bs4 import BeautifulSoup +from requests import get + +from sedenbot import HELP +from sedenecem.core import ( + edit, + extract_args, + get_translation, + sedenify, + send_log, + useragent, +) + + +@sedenify(pattern='^.d(uckduck|d)go') +def ddgo(message): + query = extract_args(message) + if len(query) < 1: + edit(message, f'`{get_translation("wrongCommand")}`') + return + req = get( + f'https://duckduckgo.com/lite?q={query}', + headers={ + 'User-Agent': useragent(), + 'Content-Type': 'text/html', + }, + ) + soup = BeautifulSoup(req.text, 'html.parser') + res1 = soup.findAll('table', {'border': 0}) + res1 = res1[-1].findAll('tr') + + match = do_ddsearch(res1) + edit( + message, + get_translation('googleResult', ['**', '`', query, match]), + preview=False, + ) + send_log(get_translation('ddgoLog', [query])) + + +def do_ddsearch(res1): + def splitter(res): + subs = [] + tlist = None + comp = False + for i in range(len(res)): + item = res[i] + if res3 := item.find('a', {'class': ['result-link']}): + if comp: + subs.append(tlist) + comp = True + tlist = [] + tlist.append(res3) + elif res4 := item.find('td', {'class': ['result-snippet']}): + tlist.append(res4) + subs.append(tlist) + if len(subs) > 9: + break + return subs + + res1 = splitter(res1) + + out = '' + for i in range(len(res1)): + item = res1[i] + link = item[0] + ltxt = link.text.replace('|', '-').replace('...', '').strip() + desc = item[1].text.strip() if len(item) > 1 else get_translation('ddgoDesc') + out += f'{i+1} - [{ltxt}]({link["href"]})\n{desc}\n\n' + + return out + + +HELP.update({'duckduckgo': get_translation('ddgoInfo')}) diff --git a/sedenbot/modules/tools/exif.py b/sedenbot/modules/tools/exif.py new file mode 100644 index 0000000..a7f726a --- /dev/null +++ b/sedenbot/modules/tools/exif.py @@ -0,0 +1,190 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from math import floor, sqrt +from os import path, remove + +from exifread import process_file + +from sedenbot import HELP +from sedenecem.core import download_media_wc, edit, get_translation, sedenify + + +@sedenify(pattern="^.exif$") +def exif_data(message): + reply = message.reply_to_message + google_coordinate = ["https://www.google.com/maps?q="] + edit(message, f'`{get_translation("exifProcess")}`') + + if reply: + data = check_media(reply) + + if not data: + edit(message, f'`{get_translation("exifError")}`') + return + + # Download Media + image_file = download_media_wc(reply, "image.jpg") + image = open(image_file, "rb") + remove(image_file) + + # Extract EXIF data + data = process_file(image) + + if not data: + edit(message, f'`{get_translation("exifError")}`') + return + + # Get EXIF tags + tag_keys = list(data.keys()) + tag_keys.sort() + + # Create dictionary for conversion functions + unit_dict = { + "EXIF ApertureValue": calculate_aperture, + "EXIF BrightnessValue": calculate_brightness, + "EXIF FNumber": calculate_fnumber, + "EXIF FocalLength": calculate_focal, + "EXIF ShutterSpeedValue": calculate_shutter, + "GPS GPSAltitude": calculate_altitude, + "GPS GPSLatitude": calculate_gps, + "GPS GPSLatitudeRef": calculate_latitude_ref, + "GPS GPSLongitude": calculate_gps, + "GPS GPSLongitudeRef": calculate_longitude_ref, + "JPEGThumbnail": handle_thumbnail, + } + + # Build EXIF data string + data_str = get_translation("exifLog") + for i in tag_keys: + if i in unit_dict.keys(): + converted = "" + + if not str(data[i]) in ['0', '0/0', '[0/0, 0/0, 0/0]']: + # GPS Data + if "GPS" == i.split(" ")[0]: + converted = unit_dict[i](data[i].printable, google_coordinate) + + # Thumbnail Image Data in Byte Array + elif type(data[i]) == bytes: + converted = unit_dict[i](data[i]) + + # Others + else: + converted = unit_dict[i](data[i].printable) + + data_str += "{0} : {1}\n".format(i, converted) + else: + data_str += "{0} : {1}\n".format(i, str(data[i])) + if len(google_coordinate) == 5: + google_coordinate.insert(0, get_translation("exifMaps")) + data_str += "".join(google_coordinate) + google_coordinate.insert(-1, "\n") + + edit(message, data_str) + + +def calculate_aperture(string): + if '/' in string: + division = string.split("/") + return "f/%.1f" % (sqrt(2.0) ** (int(division[0]) / int(division[1]))) + else: + return f'f/{float(string):.1f}' + + +def calculate_brightness(string): + division = string.split("/") + return "%.2f EV" % (int(division[0]) / int(division[1])) + + +def calculate_fnumber(string): + if '/' in string: + division = string.split("/") + return "f/%.1f" % (int(division[0]) / int(division[1])) + return f'f/{float(string):.1f}' + + +def calculate_focal(string): + if '/' in string: + division = string.split("/") + return "{0} mm".format(str(floor(int(division[0]) / int(division[1])))) + return f'{string} mm' + + +def calculate_shutter(string): + print(string) + if '/' in string: + division = string.split("/") + return "1/{0} sec".format( + str(floor(2 ** (int(division[0]) / int(division[1])))) + ) + return f"1/{int(string)} sec" + + +def calculate_altitude(string, google_coordinate): + ret = string + division = string.split("/") + if "/" in ret: + ret = "%.1f m" % (int(division[0]) / int(division[1])) + return ret + + +def calculate_gps(coord, google_coordinate): + coord_list = coord.split(",") + hour = int(coord_list[0].replace("[", "")) + minutes = coord_list[1].strip().split("/") + seconds = coord_list[2].strip().replace("]", "").split("/") + if len(minutes) > 1: + minutes = int(minutes[0]) / int(minutes[1]) + seconds = (minutes % 1) * 60 + else: + minutes = int(minutes[0]) + seconds = int(seconds[0]) / int(seconds[1]) + + google_coordinate.append(f"{hour}%C2%B0{floor(minutes)}'{seconds:.2f}%22") + return f"{hour}°{floor(minutes)}'{seconds:.2f}\"" + + +def calculate_latitude_ref(coord, google_coordinate): + google_coordinate.append(f"{coord.strip()}+") + return coord.strip() + + +def calculate_longitude_ref(coord, google_coordinate): + google_coordinate.append(coord.strip()) + return coord.strip() + + +def handle_thumbnail(img): + return "" + + +def check_media(reply_message): + data = False + + if reply_message and reply_message.media: + if reply_message.photo: + data = True + elif reply_message.sticker and not reply_message.sticker.is_animated: + data = True + elif reply_message.document: + name = reply_message.document.file_name + print(name) + print(name[name.find(".") + 1 :]) + if ( + name + and "." in name + and path.splitext(name)[1] in [".png", ".jpg", ".jpeg", ".webp"] + ): + data = True + + return data + + +HELP.update({"exif": get_translation("exifInfo")}) diff --git a/sedenbot/modules/tools/gdrive.py b/sedenbot/modules/tools/gdrive.py new file mode 100644 index 0000000..0c0e4dd --- /dev/null +++ b/sedenbot/modules/tools/gdrive.py @@ -0,0 +1,332 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from os import path, remove, replace +from re import match, search +from time import time +from urllib.parse import unquote + +from googleapiclient.discovery import build +from googleapiclient.http import MediaFileUpload, MediaIoBaseDownload +from httplib2 import Http +from oauth2client.client import FlowExchangeError, OAuth2WebServerFlow +from pyrogram.types import Message +from pySmartDL import SmartDL +from requests import get +from sedenbot import DRIVE_CLIENT, DRIVE_SECRET, GDRIVE_FOLDER_ID, HELP +from sedenecem.core import ( + download_media_wc, + edit, + extract_args, + extract_args_split, + get_translation, + reply_doc, + sedenify, +) +from sedenecem.sql.gdrive_sql import get, remove_, set + + +def extract_code(url) -> str: + if "error" in url: + return "" + if "%2F" in url: + url = unquote(url) + code = search("code=(\d\/.*)&", url)[1] + return code + + +class Progress: + def __init__(self, msg: Message, file_name, start_time): + self.msg = msg + self.file_name = file_name + self.start_time = start_time + + def download(self, current, total): + percentage = float(current * 100 / total) + if (curr_time := time()) - self.start_time > 5: + self.start_time = curr_time + self.msg.edit_text( + get_translation( + "pyrogramDown", + ["**", "`", self.file_name, f"½{round(percentage, 2)}"], + ) + ) + + def upload(self, current, total): + percentage = float(current * 100 / total) + if (curr_time := time()) - self.start_time > 5: + self.start_time = curr_time + self.msg.edit_text( + get_translation( + "pyrogramUp", + ["**", "`", self.file_name, f"½{round(percentage, 2)}"], + ) + ) + + +class Gdrive: + def __init__(self, message: Message): + self.message = message + self.dl_path = "." + self.service = build("drive", "v3", credentials=get(self.message.from_user.id)) + self.folder_mimetype = "application/vnd.google-apps.folder" + + def get_file_id(self, link): + file_id = search("d\/(.+)\/v", link).group(1) + return file_id + + def download_link(self, url): + try: + dl = SmartDL(urls=url, dest=self.dl_path, progress_bar=False) + dl.start(blocking=False) + start_time = time() + while not dl.isFinished(): + if dl.isFinished(): + break + if (curr_time := time()) - start_time > 5: + start_time = curr_time + edit( + self.message, + get_translation( + "gdriveDown", + [ + "**", + "`", + path.basename(dl.get_dest()), + round(dl.get_progress(), 2), + ], + ), + ) + return path.basename(dl.get_dest()) + except: + return edit(self.message, f"Bir Hatayla Karşılaşıldı") + + def upload_to_telegram(self, url): + file_id = self.get_file_id(url) + + file_info = ( + self.service.files() + .get(fileId=file_id, fields="size,name", supportsAllDrives=True) + .execute() + ) + size = file_info.get("size") + file_name = file_info.get("name") + + if int(size) > 4294967296 if self.message.from_user.is_premium else 2147483648: + return edit(self.message, get_translation("tgUpLimit", ["`"])) + else: + file_name = self.download_from_gdrive(url) + start_time = time() + progress = Progress(self.message, file_name, start_time) + + reply_doc( + self.message, + file_name, + progress=progress.upload, + ) + self.message.delete() + + def download_from_gdrive(self, link) -> str: + file_id = self.get_file_id(link) + + file_info = ( + self.service.files() + .get(fileId=file_id, fields="name,size", supportsAllDrives=True) + .execute() + ) + request = self.service.files().get_media(fileId=file_id, supportsAllDrives=True) + + fh = open(f'{file_info.get("name")}', "wb") + downloader = MediaIoBaseDownload( + fd=fh, request=request, chunksize=100 * 1024 * 1024 + ) + + done = False + while done is False: + status, done = downloader.next_chunk() + progress = edit( + self.message, + get_translation( + "gdriveDown", + [ + "**", + "`", + file_info.get("name"), + f"½{int(status.progress() * 100)}", + ], + ), + ) + edit( + self.message, + get_translation("gdriveDownComplete", ["**", "`", file_info.get("name")]), + ) + return file_info.get("name") + + def copy_file_gdrive(self, link): + + edit(self.message, get_translation('processing')) + file_id = self.get_file_id(link) + + meta = ( + self.service.files() + .get( + supportsAllDrives=True, + fileId=file_id, + fields="name,id,mimeType,size", + ) + .execute() + ) + + if meta.get('mimeType') == self.folder_mimetype: + return edit(self.message, f"`{get_translation('gdriveCopyErr')}`") + + body = {'parents': [GDRIVE_FOLDER_ID]} + + self.service.files().copy( + fileId=file_id, body=body, supportsAllDrives=True + ).execute() + + edit(self.message, get_translation('gdriveCopyFile')) + + def upload_to_gdrive(self, filename): + file_metadata = { + "name": filename, + "parents": [GDRIVE_FOLDER_ID], + } + + media = MediaFileUpload(filename, resumable=True, chunksize=100 * 1024 * 1024) + + file = self.service.files().create( + body=file_metadata, media_body=media, fields="id", supportsAllDrives=True + ) + response = None + start_time = time() + while response is None: + status, response = file.next_chunk() + if status: + if (curr_time := time()) - start_time > 5: + start_time = curr_time + edit( + self.message, + get_translation( + "gdriveUp", + ["**", "`", filename, f"½{int(status.progress() * 100)}"], + ), + ) + else: + edit( + self.message, + get_translation("gdriveUpComplete", ["**", "`", filename]), + ) + remove(filename) + + +flow = None + + +@sedenify(pattern="^.gauth") +def drive_auth(message): + global flow + user_id = message.from_user.id + args = extract_args_split(message) + + if len(args) == 0: + creds = get(user_id) + if creds is not None: + creds.refresh(Http()) + edit(message, f'`{get_translation("gdriveAuth")}`') + set(user_id, creds) + else: + OAUTH_SCOPE = "https://www.googleapis.com/auth/drive" + REDIRECT_URI = "http://localhost" + + flow = OAuth2WebServerFlow( + DRIVE_CLIENT, DRIVE_SECRET, OAUTH_SCOPE, redirect_uri=REDIRECT_URI + ) + auth_url = flow.step1_get_authorize_url() + edit( + message, + f'{get_translation("gauthURL", ["**","`"])} [Google Drive Auth URL]({auth_url})', + ) + + elif args[0] == "token": + url = args[1] + code = extract_code(url) + if not code: + return edit(message, f'`{get_translation("gauthTokenErr")}`') + if flow: + try: + creds = flow.step2_exchange(code) + set(user_id, creds) + edit(message, f'`{get_translation("gauthTokenSuccess")}`') + except FlowExchangeError: + edit( + message, + f'`{get_translation("gauthTokenInvalid")}`', + ) + flow = None + else: + edit(message, f'`{get_translation("gauthFirstRun")}`') + elif args[0] == "revoke": + remove_(user_id) + edit(message, f'`{get_translation("gauthTokenRevoke")}`') + else: + edit(message, get_translation("gdriveUsage")) + + +@sedenify(pattern="^.gupload") +def drive_upload(message): + + if get(message.from_user.id) is None: + return edit(message, f'`{get_translation("gauthFirstRun")}`') + + drive = Gdrive(message) + reply = message.reply_to_message + if reply and reply.document: + file_name = reply.document.file_name + + start_time = time() + progress = Progress(message, file_name, start_time) + + down_file = download_media_wc(reply, file_name, progress=progress.download) + replace(path.join("downloads", file_name), file_name) + + drive.upload_to_gdrive(file_name) + + else: + + args = extract_args(message) + if not args.startswith("https://" or "http://"): + return edit(message, f"`Geçerli bir url girin.`") + + is_drive = match("^https://drive.google.com", args) + if is_drive: + return drive.copy_file_gdrive(args) + else: + dl = drive.download_link(args) + + drive.upload_to_gdrive(dl) + + +@sedenify(pattern=".gdownload") +def gdownload(message): + if get(message.from_user.id) is None: + return edit(message, f'`{get_translation("gauthFirstRun")}`') + else: + pass + args = extract_args(message) + + if not match("^https://drive\.google\.com", args): + return edit(message, f'`{get_translation("onlySupportGdrive")}`') + + drive = Gdrive(message) + drive.upload_to_telegram(args) + + +HELP.update({"gdrive": get_translation("gdriveUsage")}) diff --git a/sedenbot/modules/git.py b/sedenbot/modules/tools/git_search.py similarity index 60% rename from sedenbot/modules/git.py rename to sedenbot/modules/tools/git_search.py index 9954c51..f7d126e 100644 --- a/sedenbot/modules/git.py +++ b/sedenbot/modules/tools/git_search.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,13 +8,13 @@ # from json import loads -from requests import get +from requests import get from sedenbot import HELP -from sedenecem.core import sedenify, edit, extract_args, get_translation +from sedenecem.core import edit, extract_args, get_translation, sedenify -@sedenify(pattern='^.github') +@sedenify(pattern='^.git(|hub)') def github(message): args = extract_args(message) @@ -42,7 +42,7 @@ def return_defval_onnull(jsonkey, defval): user_id = json.get('id', -1) - NULL_TEXT = f'{get_translation("gitNull")}' + NULL_TEXT = get_translation('gitNull') name = return_defval_onnull('name', NULL_TEXT) acc_type = return_defval_onnull('type', 'User') @@ -81,25 +81,33 @@ def get_repos(): out += f'{i}\n' return out - edit(message, f'**{get_translation("gitUserInfo", [login])}**\n\n' + - format_info(get_translation("gitUser"), user_id) + - format_info(get_translation("gitAccount"), acc_type) + - format_info(get_translation("gitName"), name) + - format_info(get_translation("gitCompany"), company) + - format_info(get_translation("gitWebsite"), blog) + - format_info(get_translation("gitLocation"), location) + - format_info(get_translation("gitMail"), email) + - format_info(get_translation("gitBio"), bio) + - format_info(get_translation("gitTwitter"), twitter) + - format_info(get_translation("gitTotalRepo"), repo_count) + - format_info(get_translation("gitTotalGist"), gist_count) + - ((format_info(get_translation("gitFollowers"), followers) + - format_info(get_translation("gitFollowing"), following)) - if acc_type == 'User' - else '') + - format_info(get_translation("gitCreationDate"), created) + - format_info(get_translation("gitDateOfUpdate"), updated) + - f'\n{get_translation("gitRepoList")}\n{get_repos()}', preview=False) + edit( + message, + f'**{get_translation("gitUserInfo", [login])}**\n\n' + + format_info(get_translation("gitUser"), user_id) + + format_info(get_translation("gitAccount"), acc_type) + + format_info(get_translation("gitName"), name) + + format_info(get_translation("gitCompany"), company) + + format_info(get_translation("gitWebsite"), blog) + + format_info(get_translation("gitLocation"), location) + + format_info(get_translation("gitMail"), email) + + format_info(get_translation("gitBio"), bio) + + format_info(get_translation("gitTwitter"), twitter) + + format_info(get_translation("gitTotalRepo"), repo_count) + + format_info(get_translation("gitTotalGist"), gist_count) + + ( + ( + format_info(get_translation("gitFollowers"), followers) + + format_info(get_translation("gitFollowing"), following) + ) + if acc_type == 'User' + else '' + ) + + format_info(get_translation("gitCreationDate"), created) + + format_info(get_translation("gitDateOfUpdate"), updated) + + f'**\n{get_translation("gitRepoList")}\n{get_repos()}**', + preview=False, + ) HELP.update({'git': get_translation('gitInfo')}) diff --git a/sedenbot/modules/tools/google_search.py b/sedenbot/modules/tools/google_search.py new file mode 100644 index 0000000..0f8fcdf --- /dev/null +++ b/sedenbot/modules/tools/google_search.py @@ -0,0 +1,126 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from random import choice +from re import findall, sub +from traceback import format_exc + +from bs4 import BeautifulSoup +from requests import get + +from sedenbot import HELP, SEDEN_LANG +from sedenecem.core import ( + edit, + extract_args, + get_translation, + google_domains, + sedenify, + send_log, + useragent, +) + + +@sedenify(pattern='^.google') +def google(message): + match = extract_args(message) + if len(match) < 1: + edit(message, f'`{get_translation("wrongCommand")}`') + return + page = findall(r"page=\d+", match) + try: + page = page[0] + page = page.replace('page=', '') + match = match.replace('page=' + page[0], '') + page = int(page) + except BaseException: + page = 1 + msg = do_gsearch(match, page) + edit( + message, get_translation('googleResult', ['**', '`', match, msg]), preview=False + ) + + send_log(get_translation('googleLog', [match])) + + +def do_gsearch(query, page): + def find_page(num): + if num < 1: + num = 1 + return (num - 1) * 10 + + def parse_key(keywords): + return keywords.replace(' ', '+') + + def replacer(st): + return ( + sub(r'[`\*_]', '', st) + .replace('\n', ' ') + .replace('(', '〈') + .replace(')', '〉') + .replace('!', 'ⵑ') + .strip() + ) + + def get_result(res): + link = res.find('a', href=True) + title = res.find('h3') + if title: + title = title.text + desc = res.find( + 'div', attrs={'class': ['VwiC3b', 'yXK7lf', 'MUxGbd', 'yDYNvb', 'lyLwlc']} + ) + if desc: + desc = desc.text + + if link and title and desc: + return f'[{replacer(title)}]({link["href"]})\n{desc or ""}' + + query = parse_key(query) + page = find_page(page) + temp = f'/search?q={query}&start={find_page(page)}&hl={SEDEN_LANG}' + + req = get( + f'https://{choice(google_domains)}{temp}', + headers={ + 'User-Agent': useragent(), + 'Content-Type': 'text/html', + }, + ) + + retries = 0 + while req.status_code != 200 and retries < 10: + retries += 1 + req = get( + f'https://{choice(google_domains)}{temp}', + headers={ + 'User-Agent': useragent(), + 'Content-Type': 'text/html', + }, + ) + + soup = BeautifulSoup(req.text, 'html.parser') + res1 = soup.find_all('div', attrs={'class': 'g'}) + + out = '' + count = 0 + for res in res1: + try: + result = get_result(res) + if result: + count += 1 + out += f'{count} - {result}\n\n' + except Exception: + print(format_exc()) + print(res) + pass + + return out + + +HELP.update({'goolag': get_translation('googleInfo')}) diff --git a/sedenbot/modules/tools/image_search.py b/sedenbot/modules/tools/image_search.py new file mode 100644 index 0000000..c2c1784 --- /dev/null +++ b/sedenbot/modules/tools/image_search.py @@ -0,0 +1,101 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from mimetypes import guess_type +from os import remove +from random import choice +from re import findall +from time import sleep + +from pyrogram.types import InputMediaPhoto +from requests import get +from selenium.webdriver.common.by import By +from sedenbot import HELP + +from sedenecem.core import ( + edit, + extract_args, + get_translation, + get_webdriver, + google_domains, + reply_doc, + sedenify, +) + + +@sedenify(pattern='^.img') +def img(message): + query = extract_args(message) + lim = findall(r'lim=\d+', query) + try: + lim = lim[0] + lim = lim.replace('lim=', '') + query = query.replace('lim=' + lim[0], '') + lim = int(lim) + if lim > 10: + lim = 10 + except IndexError: + lim = 3 + + if len(query) < 1: + edit(message, f'`{get_translation("imgUsage")}`') + return + edit(message, f'`{get_translation("processing")}`') + + url = f'https://{choice(google_domains)}/search?tbm=isch&q={query}&gbv=2&sa=X&biw=1920&bih=1080' + driver = get_webdriver() + driver.get(url) + count = 1 + files = [] + for i in driver.find_elements( + By.XPATH, '//div[contains(@class,"isv-r PNCib MSM1fd BUooTd")]' + ): + i.click() + try_count = 0 + while ( + len( + element := driver.find_elements( + By.XPATH, + '//img[contains(@class,"n3VNCb") and contains(@src,"http")]', + ) + ) + < 1 + and try_count < 20 + ): + try_count += 1 + sleep(0.1) + if len(element) < 1: + continue + link = element[0].get_attribute('src') + filename = f'result_{count}.jpg' + try: + with open(filename, 'wb') as result: + result.write(get(link).content) + ftype = guess_type(filename) + if not ftype[0] or ftype[0].split('/')[1] not in ['png', 'jpg', 'jpeg']: + remove(filename) + continue + except Exception: + continue + files.append(InputMediaPhoto(filename)) + sleep(1) + elements = driver.find_elements(By.XPATH, '//a[contains(@class,"hm60ue")]') + for element in elements: + element.click() + count += 1 + if lim < count: + break + sleep(1) + + driver.quit() + + reply_doc(message, files, delete_orig=True, delete_after_send=True) + + +HELP.update({'img': get_translation('imgInfo')}) diff --git a/sedenbot/modules/tools/imei_check.py b/sedenbot/modules/tools/imei_check.py new file mode 100644 index 0000000..244c30d --- /dev/null +++ b/sedenbot/modules/tools/imei_check.py @@ -0,0 +1,62 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from re import findall + +from pyrogram import enums +from requests import post + +from sedenbot import HELP +from sedenecem.core import edit, extract_args, get_translation, sedenify + + +@sedenify(pattern='^.imei(|check)') +def imeichecker(message): + imei = extract_args(message) + edit(message, f'`{get_translation("processing")}`') + if len(imei) != 15: + edit(message, f'`{get_translation("wrongCommand")}`') + return + try: + while True: + response = post( + f"https://m.turkiye.gov.tr/api2.php?p=imei-sorgulama&txtImei={imei}" + ).json() + if not response['data']['asyncFinished']: + continue + result = response['data'] + break + _marka = findall(r'Marka:(.+) Model', result['markaModel']) + _model = findall(r'Model Bilgileri:(.+)', result['markaModel']) + _pazaradi = findall(r'Pazar Adı:(.+) Marka', result['markaModel']) + marka = _marka[0].replace(',', '').strip() if _marka else None + model = _model[0].replace(',', '').strip() if _model else None + pazaradi = _pazaradi[0].replace(',', '').strip() if _pazaradi else None + reply_text = f"<b>Sorgu Tarihi:</b> <code>{result['sorguTarihi']}</code>\n\n" + reply_text += f"<b>IMEI:</b> <code>{result['imei'][:-5]+5*'*'}</code>\n" + reply_text += f"<b>Durum:</b> <code>{result['durum']}</code>\n" + reply_text += f"<b>Kaynak:</b> <code>{result['kaynak']}</code>\n" + reply_text += ( + f"<b>Pazar Adı:</b> <code>{pazaradi}</code>\n" + if pazaradi is not None + else "" + ) + reply_text += ( + f"<b>Marka:</b> <code>{marka}</code>\n" if marka is not None else "" + ) + reply_text += ( + f"<b>Model:</b> <code>{model}</code>\n\n" if model is not None else "" + ) + + edit(message, reply_text, parse=enums.parse_mode.ParseMode.HTML, preview=False) + except Exception as e: + raise e + + +HELP.update({'imeicheck': get_translation('imeiInfo')}) diff --git a/sedenbot/modules/tools/kargo_takip.py b/sedenbot/modules/tools/kargo_takip.py new file mode 100644 index 0000000..3edc5e1 --- /dev/null +++ b/sedenbot/modules/tools/kargo_takip.py @@ -0,0 +1,114 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from json import JSONDecodeError +from re import sub +from typing import Union + +from pyrogram import enums +from requests import post +from sedenbot import HELP +from sedenecem.core import ( + edit, + extract_args_split, + get_translation, + parse_cmd, + sedenify, +) + + +def format_datetime(datetime_str): + if datetime_str: + date_part, time_part = datetime_str.split('T') + formatted_datetime = f"{date_part} {time_part.replace(':', ':')}" + return formatted_datetime + else: + return None + + +def format_prefix(text): + if text: + if ":" in text: + formatted_text = sub(r':\s*', ':', text).split(':', 1)[-1].strip() + else: + formatted_text = text + else: + formatted_text = None + return formatted_text + + +def parseShipEntity(jsonEntity: dict) -> str: + if not jsonEntity['value'][0]['success']: + return '<code>Kargo bulunamadı</code>' + else: + text = ( + f"<b>Firma:</b> <code>{jsonEntity['value'][0]['value']['companyName']}</code>\n" + f"<b>Takip No:</b> <code>{jsonEntity['value'][0]['value']['barcode']}</code>\n" + f"<b>Durum:</b> <code>{jsonEntity['value'][0]['value']['statusDescription']}</code>\n" + f"<b>Gönderici:</b> <code>{format_prefix(jsonEntity['value'][0]['value']['sender']) or 'Bulunamadı'}</code>\n" + f"<b>Alıcı:</b> <code>{format_prefix(jsonEntity['value'][0]['value']['receiver']) or 'Bulunamadı'}</code>\n" + f"<b>Gönderim Yeri:</b> <code>{format_prefix(jsonEntity['value'][0]['value']['senderAddress']) or 'Bulunamadı'}</code>\n" + f"<b>Alım Yeri:</b> <code>{format_prefix(jsonEntity['value'][0]['value']['receiverAddress']) or 'Bulunamadı'}</code>\n" + f"<b>Gönderi Tarihi:</b> <code>{format_datetime(jsonEntity['value'][0]['value']['sendDate']) or 'Bulunamadı'}</code>\n" + f"<b>Teslim Tarihi:</b> <code>{format_datetime(jsonEntity['value'][0]['value']['deliveredDate']) or 'Bulunamadı'}</code>" + ) + if jsonEntity['value'][0]['value']['movement']: + last_movement = jsonEntity['value'][0]['value']['movement'][-1] + movements = ( + f"\n\n<b><u>Son hareketler</u></b>\n\n" + f"<code>Yer: {format_prefix(last_movement['externalLocation'])}</code>\n" + f"<code>Durum: {last_movement['title']}</code>\n" + f"<code>Tarih: {format_datetime(last_movement['date'])}</code>\n" + ) + text += movements + return text + + +def getShipEntity(company: str, trackId: Union[int, str]): + url = 'https://kargomnerede.com.tr/api/search-codes' + data = {"barcodes": [{"companyId": company, "code": trackId}]} + + try: + response = post(url, json=data) + return response.json() if response.json()['success'] else None + except JSONDecodeError: + return None + + +@sedenify(pattern='^.(hepsijet|yurti[cç]i|(s[uü]ra|pt)t|aras|mng|ups)') +def shippingTrack(message): + edit(message, f"`{get_translation('processing')}`") + trackId = extract_args_split(message) + comp = parse_cmd(message.text) + if not trackId or len(trackId) > 1: + edit(message, f"`{get_translation('wrongCommand')}`") + return + match comp.replace('ç', 'c').replace('ü', 'u'): + case 'yurtici': + kargo_data = getShipEntity(company='2', trackId=trackId[0]) + case 'aras': + kargo_data = getShipEntity(company='1', trackId=trackId[0]) + case 'ptt': + kargo_data = getShipEntity(company='4', trackId=trackId[0]) + case 'mng': + kargo_data = getShipEntity(company='3', trackId=trackId[0]) + case 'ups': + kargo_data = getShipEntity(company='5', trackId=trackId[0]) + case 'surat': + kargo_data = getShipEntity(company='6', trackId=trackId[0]) + case 'hepsijet': + kargo_data = getShipEntity(company='10', trackId=trackId[0]) + if kargo_data: + text = parseShipEntity(kargo_data) + edit(message, text, parse=enums.ParseMode.HTML) + return + edit(message, f"`{get_translation('shippingNoResult')}`") + + +HELP.update({'shippingtrack': get_translation('shippingTrack')}) diff --git a/sedenbot/modules/tools/profile_info.py b/sedenbot/modules/tools/profile_info.py new file mode 100644 index 0000000..a933cc2 --- /dev/null +++ b/sedenbot/modules/tools/profile_info.py @@ -0,0 +1,207 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from pyrogram import enums +from pyrogram.errors import PeerIdInvalid +from pyrogram.raw.functions.messages import GetOnlines +from sedenbot import BLACKLIST, BRAIN, HELP +from sedenecem.core import ( + download_media_wc, + edit, + extract_user, + extract_args, + get_translation, + reply_img, + sedenify, +) + + +@sedenify(pattern='^.whois') +def who_is(message): + find_user = extract_user(message) + reply = message.reply_to_message + media_perm = None + edit(message, f'`{get_translation("whoisProcess")}`') + + if len(find_user) < 1: + return edit(message, f'`{get_translation("banFailUser")}`') + + if message.chat.type in [enums.ChatType.SUPERGROUP, enums.ChatType.GROUP]: + perm = message.chat.permissions + media_perm = perm.can_send_media_messages + + if message.chat.type == enums.ChatType.PRIVATE: + media_perm = True + + for reply_user in find_user: + try: + reply_chat = message._client.get_chat(reply_user.id) + except Exception: + return edit(message, f'`{get_translation("whoisError")}`') + if reply_user or reply_chat is not None: + try: + user_photo = reply_user.photo.big_file_id + photo = download_media_wc(user_photo, 'photo.png') + except BaseException: + photo = None + pass + + first_name = reply_user.first_name or get_translation('notSet') + last_name = reply_user.last_name or get_translation('notSet') + username = ( + f'@{reply_user.username}' + if reply_user.username + else get_translation('notSet') + ) + user_id = reply_user.id + photos = message._client.get_chat_photos_count(user_id) + dc_id = reply_user.dc_id or get_translation('notSet') + bot = reply_user.is_bot + chats = len(message._client.get_common_chats(user_id)) + premium = reply_user.is_premium + bio = reply_chat.bio or get_translation('notSet') + status = reply_user.status + user_utils = UserUtils(BRAIN, BLACKLIST) + last_seen = user_utils.last_seen(bot, status) + sudo = user_utils.sudo_check(user_id) + blacklist = user_utils.blacklist_check(user_id) + + caption = get_translation( + 'whoisResult', + [ + '**', + '`', + first_name, + last_name, + username, + user_id, + photos, + dc_id, + chats, + '✅' if premium else '❌', + bio, + last_seen, + sudo if sudo else '', + blacklist if blacklist else '', + ], + ) + + if photo and media_perm: + reply_img(reply or message, photo, caption=caption, delete_file=True) + message.delete() + else: + return edit(message, caption) + + +class UserUtils: + def __init__(self, brain, blacklist): + self.brain = brain + self.blacklist = blacklist + + def last_seen(self, bot, status): + status_list = { + enums.UserStatus.ONLINE: get_translation('statusOnline'), + enums.UserStatus.OFFLINE: get_translation('statusOffline'), + enums.UserStatus.RECENTLY: get_translation('statusRecently'), + enums.UserStatus.LAST_WEEK: get_translation('statusWeek'), + enums.UserStatus.LAST_MONTH: get_translation('statusMonth'), + enums.UserStatus.LONG_AGO: get_translation('statusLong') + } + if bot: + return 'BOT' + elif status in status_list: + return status_list[status] + + def sudo_check(self, user_id): + if user_id in self.brain: + return get_translation('sudoCheck') + + def blacklist_check(self, user_id): + if user_id in self.blacklist: + return get_translation('blacklistCheck') + + +@sedenify(pattern='^.ginfo') +def get_chat_info(message): + args = extract_args(message) + reply = message.reply_to_message + chat_id = message.chat.id + media_perm = None + edit(message, f'`{get_translation("processing")}`') + + try: + reply_chat = message._client.get_chat(args or chat_id) + peer = message._client.resolve_peer(args or chat_id) + except PeerIdInvalid: + edit(message, f'`{get_translation("groupNotFound")}`') + return + + if message.chat.type in [enums.ChatType.SUPERGROUP, enums.ChatType.GROUP]: + perm = message.chat.permissions + media_perm = perm.can_send_media_messages + + try: + online_users = message._client.invoke(GetOnlines(peer=peer)) + online = online_users.onlines + except PeerIdInvalid: + edit(message, f'`{get_translation("groupNotFound")}`') + return + + try: + group_photo = reply_chat.photo.big_file_id + photo = download_media_wc(group_photo, 'photo.png') + except BaseException: + photo = None + pass + + title = reply_chat.title or get_translation('notSet') + username = ( + f'**@{reply_chat.username}**' + if reply_chat.username + else f'`{get_translation("notFound")}`' + ) + chat_id = reply_chat.id + dc_id = reply_chat.dc_id or get_translation('notFound') + group_type = reply_chat.type + sticker_pack = ( + f'**[Pack](https://t.me/addstickers/{reply_chat.sticker_set_name})**' + if reply_chat.sticker_set_name + else f'`{get_translation("notSet")}`' + ) + members = reply_chat.members_count + description = ( + f'\n{reply_chat.description}' + if reply_chat.description + else get_translation('notSet') + ) + + caption = get_translation( + 'groupinfoResult', + [ + '**', + '`', + title, + chat_id, + dc_id, + group_type, + members, + online, + sticker_pack, + username, + description, + ], + ) + if photo and media_perm: + reply_img(reply or message, photo, caption=caption, delete_file=True) + message.delete() + else: + edit(message, caption, preview=False) + + +HELP.update({'info': get_translation('groupInfo')}) diff --git a/sedenbot/modules/tools/qrcode.py b/sedenbot/modules/tools/qrcode.py new file mode 100644 index 0000000..a726c4b --- /dev/null +++ b/sedenbot/modules/tools/qrcode.py @@ -0,0 +1,127 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from os import remove + +from barcode import get +from barcode.writer import ImageWriter +from bs4 import BeautifulSoup +from PIL import Image +from sedenbot import HELP +from sedenecem.core import ( + download_media_wc, + edit, + extract_args, + get_download_dir, + get_translation, + reply_sticker, + sedenify, +) +from urllib3 import PoolManager + +from qrcode import QRCode, constants + + +@sedenify(pattern='^.decode$') +def parseqr(message): + reply = message.reply_to_message + if ( + reply + and reply.media + and ( + reply.photo + or (reply.sticker and not reply.sticker.is_animated) + or (reply.document and 'image' in reply.document.mime_type) + ) + ): + edit(message, f'`{get_translation("processing")}`') + else: + edit(message, f'`{get_translation("wrongCommand")}`') + return + + output = download_media_wc(reply, f'{get_download_dir()}/decode.png') + + if reply.sticker and not reply.sticker.is_animated: + image = Image.open(output) + output = f'{get_download_dir()}/decode.png' + image.save(output) + + dw = open(output, 'rb') + files = {'f': dw.read()} + t_response = None + + try: + http = PoolManager() + t_response = http.request('POST', 'https://zxing.org/w/decode', fields=files) + t_response = t_response.data + http.clear() + dw.close() + except BaseException: + pass + + remove(output) + if not t_response: + edit(message, f'`{get_translation("decodeFail")}`') + return + try: + soup = BeautifulSoup(t_response, 'html.parser') + qr_contents = soup.find_all('pre')[0].text + edit(message, qr_contents) + except BaseException: + edit(message, f'`{get_translation("decodeFail")}`') + + +@sedenify(pattern='^.barcode') +def barcode(message): + input_str = extract_args(message) + reply = message.reply_to_message + if len(input_str) < 1: + edit(message, get_translation('barcodeUsage', ['**', '`'])) + return + edit(message, f'`{get_translation("processing")}`') + try: + bar_code_mode_f = get('code128', input_str, writer=ImageWriter()) + filename = bar_code_mode_f.save('code128') + image = Image.open(filename) + filename = f'{get_download_dir()}/barcode.webp' + image.save(filename) + reply_sticker(reply or message, filename, delete_file=True) + message.delete() + remove('code128.png') + except Exception as e: + edit(message, str(e)) + return + + +@sedenify(pattern='^.makeqr') +def makeqr(message): + input_str = extract_args(message) + reply = message.reply_to_message + if len(input_str) < 1: + edit(message, get_translation('makeqrUsage', ['**', '`'])) + return + edit(message, f'`{get_translation("processing")}`') + try: + qr = QRCode( + version=1, error_correction=constants.ERROR_CORRECT_L, box_size=10, border=4 + ) + qr.add_data(input_str) + qr.make(fit=True) + img = qr.make_image(fill_color='black', back_color='white') + output = f'{get_download_dir()}/qrcode.webp' + img.save(output) + reply_sticker(reply or message, output, delete_file=True) + message.delete() + except Exception as e: + edit(message, str(e)) + return + + +HELP.update({'qrcode': get_translation('makeqrInfo')}) +HELP.update({'barcode': get_translation('barcodeInfo')}) diff --git a/sedenbot/modules/screencapture.py b/sedenbot/modules/tools/screencapture.py similarity index 77% rename from sedenbot/modules/screencapture.py rename to sedenbot/modules/tools/screencapture.py index 4017b11..0d6e75b 100644 --- a/sedenbot/modules/screencapture.py +++ b/sedenbot/modules/tools/screencapture.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,18 +7,25 @@ # All rights reserved. See COPYING, AUTHORS. # -from time import sleep from base64 import b64decode from re import match +from time import sleep from sedenbot import HELP -from sedenecem.core import (sedenify, edit, reply_doc, extract_args, - get_webdriver, get_translation) +from sedenecem.core import ( + edit, + extract_args, + get_translation, + get_webdriver, + reply_doc, + sedenify, +) -@sedenify(pattern=r'^.ss') -def ss(message): +@sedenify(pattern='^.ss') +def screenshot(message): input_str = extract_args(message) + reply = message.reply_to_message link_match = match(r'\bhttp(.*)?://.*\.\S+', input_str) if link_match: link = link_match.group() @@ -44,8 +51,7 @@ def ss(message): ) driver.set_window_size(width + 125, height + 125) wait_for = int(height / 1000) - edit( - message, f'`{get_translation("ssResult", [height, width, wait_for])}`') + edit(message, f'`{get_translation("ssResult", [height, width, wait_for])}`') sleep(wait_for) im_png = driver.get_screenshot_as_base64() driver.close() @@ -53,12 +59,8 @@ def ss(message): with open(name, 'wb') as out: out.write(b64decode(im_png)) edit(message, f'`{get_translation("ssUpload")}`') - reply_doc( - message, - name, - caption=input_str, - delete_after_send=True, - delete_orig=True) + reply_doc(reply or message, name, caption=input_str, delete_after_send=True) + message.delete() HELP.update({'ss': get_translation('ssInfo')}) diff --git a/sedenbot/modules/sed.py b/sedenbot/modules/tools/sed.py similarity index 79% rename from sedenbot/modules/sed.py rename to sedenbot/modules/tools/sed.py index fe99f26..b0ff555 100644 --- a/sedenbot/modules/sed.py +++ b/sedenbot/modules/tools/sed.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,18 +7,21 @@ # All rights reserved. See COPYING, AUTHORS. # -from re import match, sub, IGNORECASE, I +from re import IGNORECASE, I, match, sub from sre_constants import error as sre_err from sedenbot import HELP -from sedenecem.core import edit, sedenify, get_translation +from sedenecem.core import edit, get_translation, sedenify DELIMITERS = ('/', ':', '|', '_') def separate_sed(sed_string): - if (len(sed_string) > 3 and sed_string[3] in DELIMITERS - and sed_string.count(sed_string[3]) >= 2): + if ( + len(sed_string) > 3 + and sed_string[3] in DELIMITERS + and sed_string.count(sed_string[3]) >= 2 + ): delim = sed_string[3] start = counter = 4 while counter < len(sed_string): @@ -37,9 +40,12 @@ def separate_sed(sed_string): return None while counter < len(sed_string): - if (sed_string[counter] == '\\' and counter + 1 < len(sed_string) - and sed_string[counter + 1] == delim): - sed_string = sed_string[:counter] + sed_string[counter + 1:] + if ( + sed_string[counter] == '\\' + and counter + 1 < len(sed_string) + and sed_string[counter + 1] == delim + ): + sed_string = sed_string[:counter] + sed_string[counter + 1 :] elif sed_string[counter] == delim: replace_with = sed_string[start:counter] @@ -83,14 +89,13 @@ def sed(message): if 'i' in flags and 'g' in flags: text = sub(repl, repl_with, to_fix, flags=I).strip() elif 'i' in flags: - text = sub(repl, repl_with, to_fix, count=1, - flags=I).strip() + text = sub(repl, repl_with, to_fix, count=1, flags=I).strip() elif 'g' in flags: text = sub(repl, repl_with, to_fix).strip() else: text = sub(repl, repl_with, to_fix, count=1).strip() except sre_err: - edit(message, f'{get_translation("sedLearn")}') + edit(message, f'`{get_translation("sedLearn")}`') return if text: edit(message, get_translation('sedResult', [text])) diff --git a/sedenbot/modules/tools/speedtest.py b/sedenbot/modules/tools/speedtest.py new file mode 100644 index 0000000..477c978 --- /dev/null +++ b/sedenbot/modules/tools/speedtest.py @@ -0,0 +1,95 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + + +from datetime import datetime + +from sedenbot import HELP +from sedenecem.core import edit, extract_args, get_translation, reply_img, sedenify + +from speedtest import Speedtest + + +def convert_bytes_to_human_readable(size_in_bytes): + power = 1024 + size = size_in_bytes + units = ['bytes', 'kilobytes', 'megabytes', 'gigabytes', 'terabytes'] + + for unit in units: + if size < power: + return f'{size:.2f} {unit}' + size /= power + + return f'{size:.2f} {units[-1]}' + + +@sedenify(pattern='^.speedtest') +def speed_test(message): + as_text = extract_args(message) == 'text' + edit(message, f'`{get_translation("speedtest")}`') + start_time = datetime.now() + spdtst = Speedtest() + spdtst.get_best_server() + spdtst.download() + spdtst.upload() + end_time = datetime.now() + elapsed_seconds = int((end_time - start_time).total_seconds()) + + results = spdtst.results.dict() + download_speed = results['download'] + upload_speed = results['upload'] + ping_time = int(results['ping']) + client_info = results['client'] + isp_name = client_info['isp'] + isp_rating = client_info['isprating'] + + try: + response = spdtst.results.share() + speedtest_image = response + if as_text: + result_text = get_translation( + 'speedtestResultText', + [ + '**', + elapsed_seconds, + convert_bytes_to_human_readable(download_speed), + convert_bytes_to_human_readable(upload_speed), + ping_time, + isp_name, + isp_rating, + '', + ], + ) + edit(message, result_text) + else: + reply_img( + message, + speedtest_image, + caption=get_translation('speedtestResultDoc', ['**', elapsed_seconds]), + delete_file=True, + delete_orig=True, + ) + except Exception as exc: + error_result_text = get_translation( + 'speedtestResultText', + [ + '**', + elapsed_seconds, + convert_bytes_to_human_readable(download_speed), + convert_bytes_to_human_readable(upload_speed), + ping_time, + isp_name, + isp_rating, + f'ERROR: {str(exc)}', + ], + ) + edit(message, error_result_text) + + +HELP.update({'speedtest': get_translation('speedtestInfo')}) diff --git a/sedenbot/modules/tools/translate.py b/sedenbot/modules/tools/translate.py new file mode 100644 index 0000000..0593be3 --- /dev/null +++ b/sedenbot/modules/tools/translate.py @@ -0,0 +1,131 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from emoji import demojize +from googletrans import Translator, LANGUAGES +from gtts import gTTS +from gtts.lang import tts_langs +from sedenbot import HELP, SEDEN_LANG +from sedenecem.core import ( + sedenify, + extract_args_split, + edit, + send_log, + reply_voice, + get_translation, +) + + +@sedenify(pattern='^.tts') +def text_to_speech(message): + reply = message.reply_to_message + args = extract_args_split(message) + + if reply and reply.text: + if args: + lang = args[0].lower() + text = reply.text + else: + lang = SEDEN_LANG + text = reply.text + elif args: + if len(args) >= 2: + lang = args[0].lower() + text = ' '.join(args[1:]) + else: + lang = args[0].lower() + text = '' + else: + edit(message, f'`{get_translation("ttsUsage")}`') + return + + if lang not in tts_langs(): + lang = SEDEN_LANG + text = ' '.join(args) + + if not text: + edit(message, f'`{get_translation("ttsUsage")}`') + return + + try: + tts = gTTS(text, lang=lang) + tts.save('h.mp3') + except AssertionError: + edit(message, f'`{get_translation("ttsBlank")}`') + return + except ValueError: + edit(message, f'`{get_translation("ttsNoSupport")}`') + return + except RuntimeError: + edit(message, f'`{get_translation("ttsError")}`') + return + + with open('h.mp3', 'rb') as audio: + linelist = list(audio) + linecount = len(linelist) + if linecount == 1: + tts = gTTS(text, lang=lang) + tts.save('h.mp3') + with open('h.mp3', 'r'): + reply_voice(reply if reply else message, 'h.mp3', delete_file=True) + + message.delete() + send_log(get_translation('ttsLog')) + + +@sedenify(pattern='^.trt') +def translate(message): + translator = Translator() + reply = message.reply_to_message + args = extract_args_split(message) + + if reply and reply.text: + if args: + dest_lang = args[0].lower() + text = reply.text + else: + dest_lang = SEDEN_LANG + text = reply.text + elif args: + if len(args) == 2: + lang, text = args + if lang.lower() in LANGUAGES: + dest_lang = lang.lower() + text = text + else: + dest_lang = SEDEN_LANG + text = ' '.join(args) + else: + dest_lang = SEDEN_LANG + text = ' '.join(args) + else: + edit(message, f'`{get_translation("trtUsage")}`') + return + + try: + translated_text = translator.translate(demojize(text), dest=dest_lang) + except ValueError: + edit(message, f'`{get_translation("trtError")}`') + return + + source_lang = LANGUAGES[translated_text.src.lower()] + transl_lang = LANGUAGES[translated_text.dest.lower()] + translated_text = '{}\n{}'.format( + get_translation( + 'transHeader', ['**', '`', source_lang.title(), transl_lang.title()] + ), + translated_text.text, + ) + + edit(message, translated_text) + + send_log(get_translation('trtLog', [source_lang.title(), transl_lang.title()])) + + +HELP.update({'translator': get_translation('translatorInfo')}) diff --git a/sedenbot/modules/tools/urban_dictionary.py b/sedenbot/modules/tools/urban_dictionary.py new file mode 100644 index 0000000..ac58f28 --- /dev/null +++ b/sedenbot/modules/tools/urban_dictionary.py @@ -0,0 +1,62 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from json import loads +from os import path, remove +from random import randrange + +from requests import get + +from sedenbot import HELP +from sedenecem.core import edit, extract_args, get_translation, reply_doc, sedenify + + +@sedenify(pattern='^.ud') +def urbandictionary(message): + query = extract_args(message) + if len(query) < 1: + edit(message, f'`{get_translation("wrongCommand")}`') + return + edit(message, f'`{get_translation("processing")}`') + response = get(f'https://api.urbandictionary.com/v0/define?term={query}') + data = loads(response.text) + if len(data["list"]): + list_size = len(data['list']) + item = data['list'][randrange(list_size)] + meanlen = item['definition'] + item['example'] + if len(meanlen) >= 4096: + edit(message, f'`{get_translation("outputTooLarge")}`') + file = open('urban_dictionary.txt', 'w+') + file.write( + f"Query: {query}\n\n" + f"Meaning: {item['definition']}\n\n" + f"Example: \n{item['example']}\n" + ) + file.close() + reply_doc( + message, + 'urbandictionary.txt', + caption=f'`{get_translation("outputTooLarge")}`', + ) + if path.exists('urbandictionary.txt'): + remove('urbandictionary.txt') + message.delete() + return + edit( + message, + get_translation( + 'sedenQueryUd', + ['**', '`', query, item['definition'], item['example']], + ), + ) + else: + edit(message, get_translation('udNoResult', ['**', query])) + + +HELP.update({'ud': get_translation('udInfo')}) diff --git a/sedenbot/modules/tools/weather.py b/sedenbot/modules/tools/weather.py new file mode 100644 index 0000000..c4a2ae2 --- /dev/null +++ b/sedenbot/modules/tools/weather.py @@ -0,0 +1,39 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from requests import get +from sedenbot import HELP, SEDEN_LANG, WEATHER +from sedenecem.core import edit, extract_args, get_translation, sedenify + +# ===== CONSTANT ===== +DEFAULT_CITY = WEATHER.capitalize() if WEATHER else None +# ==================== + + +@sedenify(pattern='^.(hava(|durumu)|w(eathe|tt)r)') +def weather(message): + args = extract_args(message).capitalize() + + CITY = DEFAULT_CITY if len(args) < 1 else args.split(',')[0].strip() + + try: + response = get( + f'http://wttr.in/{CITY}?mQT0', + headers={'User-Agent': 'curl/7.66.0', 'Accept-Language': SEDEN_LANG}, + ) + data = response.text + if '===' in data or '404' in data: + raise Exception + data = data.replace('`', '‛') + edit(message, f'**{CITY}**\n\n`{data}`') + except Exception: + edit(message, f'`{get_translation("weatherErrorServer")}`') + + +HELP.update({'weather': get_translation('infoWeather')}) \ No newline at end of file diff --git a/sedenbot/modules/tools/wiki_search.py b/sedenbot/modules/tools/wiki_search.py new file mode 100644 index 0000000..daeedc3 --- /dev/null +++ b/sedenbot/modules/tools/wiki_search.py @@ -0,0 +1,83 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from json import JSONDecodeError, loads + +from requests import RequestException, get + +from sedenbot import HELP, SEDEN_LANG +from sedenecem.core import ( + edit, + extract_args, + get_translation, + reply_doc, + sedenify, + send_log, +) + + +@sedenify(pattern='^.wiki') +def wiki(message): + args = extract_args(message) + if len(args) < 1: + edit(message, f'`{get_translation("wrongCommand")}`') + return + + try: + result = search_wiki(args) + except BaseException as e: + raise e + + if len(result) > 4096: + with open(f'{args}.txt', 'w', encoding='utf-8') as file: + file.write(result) + return reply_doc( + message, + f'{args}.txt', + caption=f'`{get_translation("outputTooLarge")}`', + delete_after_send=True, + ) + + edit(message, get_translation('sedenQuery', ['**', '`', args, result])) + send_log(get_translation('wikiLog', ['`', args])) + + +def search_wiki(query): + url = f'https://{SEDEN_LANG or "en"}.wikipedia.org/w/api.php' + params = { + 'action': 'query', + 'format': 'json', + 'prop': 'extracts', + 'titles': query, + 'exsectionformat': 'wiki', + 'explaintext': 1, + } + + try: + response = get(url, params=params) + response.raise_for_status() + data = loads(response.text) + pages = data.get('query', {}).get('pages', {}) + result = '' + + for page in pages.values(): + extract = page.get('extract', '') + result += extract + + if not result: + result = get_translation('wikiError') + + return result + + except (RequestException, JSONDecodeError) as e: + print(f'API Error: {e}') + return '' + + +HELP.update({'wiki': get_translation('wikiInfo')}) diff --git a/sedenbot/modules/tools/youtubedl.py b/sedenbot/modules/tools/youtubedl.py new file mode 100644 index 0000000..380b1a4 --- /dev/null +++ b/sedenbot/modules/tools/youtubedl.py @@ -0,0 +1,141 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2021 kisekinopureya <https://github.com/kisekinopureya> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from io import BytesIO +from os import remove + +from PIL import Image +from requests import get +from yt_dlp import YoutubeDL + +from sedenbot import HELP +from sedenecem.core import ( + edit, + extract_args_split, + get_download_dir, + get_translation, + reply_audio, + reply_video, + sedenify, +) + + +@sedenify(pattern='^.y(outube|t)dl') +def youtubedl(message): + args = extract_args_split(message) + if len(args) != 2: + edit(message, f'`{get_translation("wrongCommand")}`') + return + + util, url = args[0].lower(), args[1] + + if util == 'mp4': + ydl_opts = { + 'outtmpl': '%(id)s.%(ext)s', + 'format': 'bestvideo[height<=?1080]+bestaudio[ext=m4a]/best', + 'addmetadata': True, + 'prefer_ffmpeg': True, + 'geo_bypass': True, + 'nocheckcertificate': True, + 'quiet': True, + 'logtostderr': False, + } + thumb_path = None + with YoutubeDL(ydl_opts) as ydl: + try: + video_info = ydl.extract_info(url, False) + title = video_info['title'] + uploader = video_info['uploader'] + duration = video_info['duration'] + except KeyError: + uploader = get_translation('notFound') + duration = None + except BaseException as e: + return edit(message, get_translation('banError', ['`', '**', e])) + + edit(message, get_translation('downloadYTVideo', ['**', title, '`'])) + + try: + temp = BytesIO() + with get(video_info['thumbnail']) as req: + temp.name = 'file.webp' + temp.write(req.content) + temp.seek(0) + im = Image.open(temp) + imc = im.convert('RGB') + imc.save(thumb_path := f'{get_download_dir()}/{video_info["id"]}.jpg') + except BaseException: + thumb_path = None + + ydl.download([url]) + + edit(message, f'`{get_translation("uploadMedia")}`') + reply_video( + message, + f'{video_info["id"]}.{video_info["ext"]}', + thumb=thumb_path, + caption=f"**{get_translation('videoTitle')}** `{title}`\n**{get_translation('videoUploader')}** `{uploader}`", + duration=duration, + delete_orig=True, + delete_file=True, + ) + try: + remove(thumb_path) + except BaseException as e: + raise e + + elif util == 'mp3': + ydl_opts = { + 'outtmpl': '%(id)s.%(ext)s', + 'format': 'bestaudio/best', + 'addmetadata': True, + 'writethumbnail': True, + 'prefer_ffmpeg': True, + "extractaudio": True, + 'geo_bypass': True, + 'nocheckcertificate': True, + 'postprocessors': [ + { + 'key': 'FFmpegExtractAudio', + 'preferredcodec': 'mp3', + 'preferredquality': '320', + }, + {'key': 'EmbedThumbnail'}, + {'key': 'FFmpegMetadata'}, + ], + 'quiet': True, + 'logtostderr': False, + } + with YoutubeDL(ydl_opts) as ydl: + try: + video_info = ydl.extract_info(url, False) + title = video_info['title'] + uploader = video_info['uploader'] + duration = int(video_info['duration']) + except KeyError: + uploader = get_translation('notFound') + duration = None + except BaseException as e: + return edit(message, get_translation('banError', ['`', '**', e])) + + edit(message, get_translation('downloadYTAudio', ['**', title, '`'])) + + ydl.download([url]) + edit(message, f'`{get_translation("uploadMedia")}`') + reply_audio( + message, + f'{video_info["id"]}.mp3', + caption=f"**{get_translation('videoUploader')}** `{uploader}`", + duration=duration, + delete_orig=True, + delete_file=True, + ) + +HELP.update({'youtubedl': get_translation('youtubedlInfo')}) diff --git a/sedenbot/modules/weather.py b/sedenbot/modules/weather.py deleted file mode 100644 index c5a1e7b..0000000 --- a/sedenbot/modules/weather.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from requests import get - -from sedenbot import HELP, WEATHER, SEDEN_LANG -from sedenecem.core import edit, extract_args, sedenify, get_translation - -# ===== CONSTANT ===== -if WEATHER: - DEFCITY = WEATHER -else: - DEFCITY = None -# ==================== - - -@sedenify(pattern='^.(havadurumu|w(eathe|tt)r)') -def havadurumu(message): - args = extract_args(message) - - if len(args) < 1: - CITY = DEFCITY - if not CITY: - edit(message, f'`{get_translation("weatherErrorCity")}`') - return - else: - CITY = args - - if ',' in CITY: - CITY = CITY[:CITY.find(',')].strip() - - try: - req = get( - f'http://wttr.in/{CITY}?mqT0', - headers={ - 'User-Agent': 'curl/7.66.0', - 'Accept-Language': SEDEN_LANG}) - data = req.text - if '===' in data: - raise Exception - data = data.replace('`', '‛') - edit(message, f'`{data}`') - except Exception as e: - edit(message, f'`{get_translation("weatherErrorServer")}`') - raise e - - -HELP.update({'weather': get_translation('infoWeather')}) diff --git a/sedenbot/modules/whois.py b/sedenbot/modules/whois.py deleted file mode 100644 index 3c89420..0000000 --- a/sedenbot/modules/whois.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from sedenbot import HELP -from sedenecem.core import (extract_args, sedenify, edit, reply_img, - get_translation, download_media_wc) - - -@sedenify(pattern='^.whois', compat=False) -def who_is(client, message): - user_info = extract_args(message) - reply = message.reply_to_message - edit(message, f'`{get_translation("whoisProcess")}`') - media_perm = True - if 'group' in message.chat.type: - perm = message.chat.permissions - media_perm = perm.can_send_media_messages - - if user_info: - try: - reply_user = client.get_users(user_info) - reply_chat = client.get_chat(user_info) - except Exception: - edit(message, f'`{get_translation("whoisError")}`') - return - elif reply: - reply_user = client.get_users(reply.from_user.id) - reply_chat = client.get_chat(reply.from_user.id) - else: - edit(message, f'`{get_translation("whoisError")}`') - return - if reply_user or reply_chat is not None: - try: - user_photo = reply_user.photo.big_file_id - photo = download_media_wc(user_photo, 'photo.png') - except BaseException: - photo = None - pass - - first_name = reply_user.first_name or get_translation('notSet') - last_name = reply_user.last_name or get_translation('notSet') - username = f'@{reply_user.username}' if reply_user.username else get_translation( - 'notSet') - user_id = reply_user.id - photos = client.get_profile_photos_count(user_id) - dc_id = reply_user.dc_id - bot = reply_user.is_bot - scam = reply_user.is_scam - verified = reply_user.is_verified - chats = len(client.get_common_chats(user_id)) - bio = reply_chat.bio or get_translation('notSet') - status = reply_user.status - last_seen = LastSeen(bot, status) - - caption = get_translation( - 'whoisResult', - ['**', '`', first_name, last_name, username, user_id, photos, - dc_id, bot, scam, verified, chats, bio, last_seen]) - - if photo and media_perm: - reply_img( - message, - photo, - caption=caption, - delete_file=True, - delete_orig=True) - else: - return edit(message, caption) - - -def LastSeen(bot, status): - if bot: - return 'BOT' - elif status == 'online': - return get_translation('statusOnline') - elif status == 'recently': - return get_translation('statusRecently') - elif status == 'within_week': - return get_translation('statusWeek') - elif status == 'within_month': - return get_translation('statusMonth') - elif status == 'long_time_ago': - return get_translation('statusLong') - - -HELP.update({'whois': get_translation('whoisInfo')}) diff --git a/sedenbot/modules/youtubedl.py b/sedenbot/modules/youtubedl.py deleted file mode 100644 index 71be6de..0000000 --- a/sedenbot/modules/youtubedl.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (C) 2020 TeamDerUntergang <https://github.com/TeamDerUntergang> -# Copyright (C) 2021 kisekinopureya <https://github.com/kisekinopureya> -# -# This file is part of TeamDerUntergang project, -# and licensed under GNU Affero General Public License v3. -# See the GNU Affero General Public License for more details. -# -# All rights reserved. See COPYING, AUTHORS. -# - -from os import remove - -from youtube_dl import YoutubeDL -from youtube_dl.utils import DownloadError - -from sedenbot import HELP -from sedenecem.core import ( - edit, - extract_args, - sedenify, - get_translation, - reply_doc, - get_translation, - reply_audio) - - -@sedenify(pattern='^.(youtube|yt)dl') -def youtubedl(message): - args = extract_args(message).split(' ', 2) - - if len(args) != 2: - edit(message, f'`{get_translation("wrongCommand")}`') - return - - util = args[0].lower() - url = args[1] - - try: - video_info = YoutubeDL().extract_info(url, False) - except DownloadError as e: - return edit(message, get_translation('banError', ['`', '**', e])) - - title = video_info.get('title') - uploader = video_info.get('uploader') - duration = video_info.get('duration') - - if util == 'mp4': - edit(message, get_translation('downloadYTVideo', ['**', title, '`'])) - ydl_opts = { - 'outtmpl': f'{title}.%(ext)s', - 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best'} - with YoutubeDL(ydl_opts) as ydl: - ydl.download([url]) - edit(message, f'{get_translation("uploadMedia")}') - reply_doc( - message, - f'{title}.mp4', - caption=f"{get_translation('title', ['**' , ':'])} {title}`\n`{get_translation('uploader',['**',':'])} {uploader}", - delete_after_send=True, - delete_orig=True) - - elif util == 'mp3': - edit(message, get_translation('downloadYTAudio', ['**', title, '`'])) - ydl_opts = { - 'outtmpl': f'{title}.%(ext)s', - 'format': 'bestaudio/best', - 'postprocessors': [ - { - 'key': 'FFmpegExtractAudio', - 'preferredcodec': 'mp3', - 'preferredquality': '320', - }]} - with YoutubeDL(ydl_opts) as ydl: - ydl.download([url]) - edit(message, f'`{get_translation("uploadMedia")}`') - reply_audio( - message, - f'{title}.mp3', - caption=f"{get_translation('uploader',['**',':'])} {uploader}", - delete_orig=True) - remove(f'{title}.mp3') - - -HELP.update({'youtubedl': get_translation('youtubedlInfo')}) diff --git a/sedenecem/core/__init__.py b/sedenecem/core/__init__.py index 343f64f..cf0bd35 100644 --- a/sedenecem/core/__init__.py +++ b/sedenecem/core/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,11 +7,13 @@ # All rights reserved. See COPYING, AUTHORS. # +from .conv import * +from .filters import * +from .image import * from .misc import * +from .proxy import * +from .replier import * from .sedenify import * from .sedenlog import * from .send import * -from .replier import * from .webdriver import * -from .image import * -from .conv import * diff --git a/sedenecem/core/conv.py b/sedenecem/core/conv.py index 11da7ce..b763a53 100644 --- a/sedenecem/core/conv.py +++ b/sedenecem/core/conv.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,15 +7,47 @@ # All rights reserved. See COPYING, AUTHORS. # -from time import sleep from os import remove +from time import sleep + from sedenbot import CONVERSATION, app class PyroConversation: + """ + A context manager for a conversation with a specific chat ID. + + Args: + message (pyrogram.types.Message): The original message that initiated the conversation. + chat_id (int): The ID of the chat with which the conversation is taking place. + + Methods: + send_msg(text: str) -> None: + Sends a message to the chat. + + send_doc(doc: str, delete: bool = False) -> None: + Sends a document to the chat. + + recv_msg(read: bool = True) -> pyrogram.types.Message: + Receives a message from the chat. + + forward_msg(msg: pyrogram.types.Message) -> pyrogram.types.Message: + Forwards a message to the chat. + + init() -> None: + Initializes the conversation. + + stop() -> None: + Ends the conversation. + + Usage: + with PyroConversation(message, chat_id) as conv: + conv.send_msg("Hello!") + reply = conv.recv_msg() + """ - def __init__(self, client, chat_id): - self.client = client or app + def __init__(self, message, chat_id): + self.client = message._client or app self.chat_id = chat_id self.count = 0 @@ -40,7 +72,7 @@ def recv_msg(self, read=True): msg = conv[self.count] self.count = len(conv) if read: - self.client.read_history(chat_id=self.chat_id) + self.client.read_chat_history(chat_id=self.chat_id) return msg def forward_msg(self, msg): diff --git a/sedenecem/core/filters.py b/sedenecem/core/filters.py new file mode 100644 index 0000000..6181214 --- /dev/null +++ b/sedenecem/core/filters.py @@ -0,0 +1,176 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from asyncio import run +from re import search +from traceback import format_exc +from typing import Callable, List, Union + +from pyrogram import ContinuePropagation, StopPropagation +from pyrogram.handlers import RawUpdateHandler +from pyrogram.raw.types import ( + MessageActionContactSignUp, + MessageService, + UpdateNewMessage, +) +from pyrogram.types import Message, User + +from sedenbot import BLACKLIST, LOGS + + +class BaseFilter: + def __init__(self, invert: bool = False): + self.invert = invert + + def __verify__(self, _: Message): + raise NotImplementedError + + def verify(self, message: Message): + if ( + not isinstance(message, Message) + or not message + or message.empty + or not message.from_user + ): + return False + + try: + ret = self.__verify__(message) + ret = not ret if self.invert else ret + except (ContinuePropagation, StopPropagation, RetardsException) as e: + raise e + except BaseException: + ret = False + LOGS.error(format_exc()) + + if not ret: + return message.continue_propagation() + + return True + + +class AndFilter(BaseFilter): + def __init__(self, invert: bool = False, *filters: BaseFilter): + super().__init__(invert) + self.filters: List[BaseFilter, OrFilter] = [] + self.add_filter(*filters) + + def add_filter(self, *filters: BaseFilter): + self.filters.extend(filters) + + def __verify__(self, message: Message): + for item in self.filters: + if not item.verify(message): + return False + return True + + +class OrFilter(AndFilter): + def verify(self, message: Message): + for item in self.filters: + if item.verify(message): + return True + return False + + +class RegexFilter(BaseFilter): + def __init__(self, regex: str, invert: bool = False): + super().__init__(invert) + self.regex = regex + + def __verify__(self, message: Message): + return search(self.regex, message.text) if message.text else False + + +class IncomingFilter(BaseFilter): + def __init__(self): + super().__init__(invert=True) + + def __verify__(self, message: Message): + return message.outgoing + + +class UserFilter(BaseFilter): + def __init__( + self, users: Union[int, List[int], User, List[User]], invert: bool = False + ): + super().__init__(invert) + self.users = users + + def extract_uid(self, user): + return user.id if type(user) is User else user + + def __verify__(self, message: Message): + uid = message.from_user.id if message.from_user else 0 + + if type(self.users) is List: + self.users = [self.extract_uid(x) for x in self.users] + else: + self.users = [self.extract_uid(self.users)] + + for user in self.users: + if uid != user: + return False + return True + + +class BotFilter(BaseFilter): + def __init__(self, invert: bool = False): + super().__init__(invert) + + def __verify__(self, message: Message): + return message.from_user.is_bot if message.from_user else False + + +class MeFilter(BaseFilter): + def __init__(self, invert: bool = False): + super().__init__(invert) + + def __verify__(self, message: Message): + return ( + message.from_user.is_self or message.chat.id == message._client.me.id + if message.from_user and message.chat + else False + ) + + +class SedenUpdateHandler(RawUpdateHandler): + def __init__(self, callback: Callable, filter: AndFilter, handlers: List): + super().__init__(self.__callback__) + self.filter = filter + self.handlers = handlers + self.seden_callback = callback + + def __callback__(self, client, update, users, chats): + if client.me.id in BLACKLIST: + raise RetardsException('RETARDS CANNOT USE THIS BOT') + + if ( + isinstance(update, UpdateNewMessage) + and isinstance(update.message, MessageService) + and isinstance(update.message.action, MessageActionContactSignUp) + ): + LOGS.warning('User created an account') + raise StopPropagation + else: + parser = client.dispatcher.update_parsers.get(type(update), None) + parsed_update, handler_type = ( + run(parser(update, users, chats)) + if parser is not None + else (None, type(None)) + ) + + verified = [i.verify(parsed_update) for i in self.filter.filters] + if handler_type in self.handlers and all(verified): + self.seden_callback(parsed_update) + raise ContinuePropagation + + +class RetardsException(Exception): + pass diff --git a/sedenecem/core/image.py b/sedenecem/core/image.py index 7d9bd42..2f9f52b 100644 --- a/sedenecem/core/image.py +++ b/sedenecem/core/image.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,10 +8,23 @@ # from math import floor + from PIL import Image +from .misc import get_download_dir, get_status_out + def sticker_resize(photo): + """ + Resizes a given sticker image file to have a maximum dimension of 512 pixels while maintaining aspect ratio. + If the image is already smaller than 512x512 pixels, it will be resized to its original size. + + Args: + photo (str): The file path to the sticker image file to be resized. + + Returns: + str: The file path to the resized image file in PNG format, stored in a temporary directory. + """ image = Image.open(photo) if (image.width and image.height) < 512: size1 = image.width @@ -32,6 +45,31 @@ def sticker_resize(photo): maxsize = (512, 512) image.thumbnail(maxsize) - temp = 'temp.png' + temp = f'{get_download_dir()}/temp.png' image.save(temp, 'PNG') return temp + + +def video_convert(video): + """ + Converts a video file to a webm format with dimensions of 512x512 and duration of 3.0 seconds. + + Args: + video (str): Path of the video file to be converted. + + Returns: + str: Path of the converted webm file. + """ + get_status_out( + f'ffmpeg -i {video} \ + -vf scale=512:512:force_original_aspect_ratio=decrease \ + -c:v libvpx-vp9 \ + -crf 30 \ + -b:v 500k \ + -pix_fmt yuv420p \ + -t 2.9 \ + -an \ + -y {get_download_dir()}/temp.webm' + ) + output = f'{get_download_dir()}/temp.webm' + return output diff --git a/sedenecem/core/misc.py b/sedenecem/core/misc.py index 35df798..ea4f4b3 100644 --- a/sedenecem/core/misc.py +++ b/sedenecem/core/misc.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,30 +7,247 @@ # All rights reserved. See COPYING, AUTHORS. # +from os import makedirs +from random import choice from re import escape, sub +from subprocess import STDOUT, DEVNULL, CalledProcessError, check_output +from typing import List -from pyrogram.types import Message -from sedenbot import app, me, BRAIN, BOT_PREFIX +from bs4 import BeautifulSoup +from pyrogram import enums +from pyrogram.types import Message, User +from requests import get + +from sedenbot import BOT_PREFIX, BRAIN, LOG_VERBOSE, app MARKDOWN_FIX_CHAR = '\u2064' SPAM_COUNT = [0] _parsed_prefix = escape(BOT_PREFIX) if BOT_PREFIX else r'\.' +_admin_status_list = [ + enums.ChatMemberStatus.OWNER, + enums.ChatMemberStatus.ADMINISTRATOR, +] +google_domains = [ + 'www.google.com', + 'www.google.ad', + 'www.google.ae', + 'www.google.com.af', + 'www.google.com.ag', + 'www.google.com.ai', + 'www.google.am', + 'www.google.co.ao', + 'www.google.com.ar', + 'www.google.as', + 'www.google.at', + 'www.google.com.au', + 'www.google.az', + 'www.google.ba', + 'www.google.com.bd', + 'www.google.be', + 'www.google.bf', + 'www.google.bg', + 'www.google.com.bh', + 'www.google.bi', + 'www.google.bj', + 'www.google.com.bn', + 'www.google.com.bo', + 'www.google.com.br', + 'www.google.bs', + 'www.google.co.bw', + 'www.google.by', + 'www.google.com.bz', + 'www.google.ca', + 'www.google.cd', + 'www.google.cf', + 'www.google.cg', + 'www.google.ch', + 'www.google.ci', + 'www.google.co.ck', + 'www.google.cl', + 'www.google.cm', + 'www.google.cn', + 'www.google.com.co', + 'www.google.co.cr', + 'www.google.com.cu', + 'www.google.cv', + 'www.google.com.cy', + 'www.google.cz', + 'www.google.de', + 'www.google.dj', + 'www.google.dk', + 'www.google.dm', + 'www.google.com.do', + 'www.google.dz', + 'www.google.com.ec', + 'www.google.ee', + 'www.google.com.eg', + 'www.google.es', + 'www.google.com.et', + 'www.google.fi', + 'www.google.com.fj', + 'www.google.fm', + 'www.google.fr', + 'www.google.ga', + 'www.google.ge', + 'www.google.gg', + 'www.google.com.gh', + 'www.google.com.gi', + 'www.google.gl', + 'www.google.gm', + 'www.google.gp', + 'www.google.gr', + 'www.google.com.gt', + 'www.google.gy', + 'www.google.com.hk', + 'www.google.hn', + 'www.google.hr', + 'www.google.ht', + 'www.google.hu', + 'www.google.co.id', + 'www.google.ie', + 'www.google.co.il', + 'www.google.im', + 'www.google.co.in', + 'www.google.iq', + 'www.google.is', + 'www.google.it', + 'www.google.je', + 'www.google.com.jm', + 'www.google.jo', + 'www.google.co.jp', + 'www.google.co.ke', + 'www.google.com.kh', + 'www.google.ki', + 'www.google.kg', + 'www.google.co.kr', + 'www.google.com.kw', + 'www.google.kz', + 'www.google.la', + 'www.google.com.lb', + 'www.google.li', + 'www.google.lk', + 'www.google.co.ls', + 'www.google.lt', + 'www.google.lu', + 'www.google.lv', + 'www.google.com.ly', + 'www.google.co.ma', + 'www.google.md', + 'www.google.me', + 'www.google.mg', + 'www.google.mk', + 'www.google.ml', + 'www.google.mn', + 'www.google.ms', + 'www.google.com.mt', + 'www.google.mu', + 'www.google.mv', + 'www.google.mw', + 'www.google.com.mx', + 'www.google.com.my', + 'www.google.co.mz', + 'www.google.com.na', + 'www.google.com.nf', + 'www.google.com.ng', + 'www.google.com.ni', + 'www.google.ne', + 'www.google.nl', + 'www.google.no', + 'www.google.com.np', + 'www.google.nr', + 'www.google.nu', + 'www.google.co.nz', + 'www.google.com.om', + 'www.google.com.pa', + 'www.google.com.pe', + 'www.google.com.ph', + 'www.google.com.pk', + 'www.google.pl', + 'www.google.pn', + 'www.google.com.pr', + 'www.google.ps', + 'www.google.pt', + 'www.google.com.py', + 'www.google.com.qa', + 'www.google.ro', + 'www.google.ru', + 'www.google.rw', + 'www.google.com.sa', + 'www.google.com.sb', + 'www.google.sc', + 'www.google.se', + 'www.google.com.sg', + 'www.google.sh', + 'www.google.si', + 'www.google.sk', + 'www.google.com.sl', + 'www.google.sn', + 'www.google.so', + 'www.google.sm', + 'www.google.st', + 'www.google.com.sv', + 'www.google.td', + 'www.google.tg', + 'www.google.co.th', + 'www.google.com.tj', + 'www.google.tk', + 'www.google.tl', + 'www.google.tm', + 'www.google.tn', + 'www.google.to', + 'www.google.com.tr', + 'www.google.tt', + 'www.google.com.tw', + 'www.google.co.tz', + 'www.google.com.ua', + 'www.google.co.ug', + 'www.google.co.uk', + 'www.google.com.uy', + 'www.google.co.uz', + 'www.google.com.vc', + 'www.google.co.ve', + 'www.google.vg', + 'www.google.co.vi', + 'www.google.com.vn', + 'www.google.vu', + 'www.google.ws', + 'www.google.rs', + 'www.google.co.za', + 'www.google.co.zm', + 'www.google.co.zw', + 'www.google.cat', + 'www.google.xxx', +] def reply( - message, - text, - preview=True, - fix_markdown=False, - delete_orig=False, - parse='md'): + message, + text, + preview=True, + fix_markdown=False, + delete_orig=False, + parse=enums.ParseMode.MARKDOWN, +): + """ + Reply to a message with the given text. + + Args: + message (pyrogram.types.Message): The message to reply to. + text (str): The text to send in the reply. + preview (bool): Whether to enable link previews for URLs in the text. Defaults to True. + fix_markdown (bool): Whether to add a special character to fix issues with markdown formatting. Defaults to False. + delete_orig (bool): Whether to delete the original message after sending the reply. Defaults to False. + parse (pyrogram.enums.ParseMode): The parse mode to use for the text. Defaults to `pyrogram.enums.ParseMode.MARKDOWN`. + + Returns: + pyrogram.types.Message: The message object of the sent reply. + """ try: if fix_markdown: text += MARKDOWN_FIX_CHAR ret = message.reply_text( - text.strip(), - disable_web_page_preview=not preview, - parse_mode=parse) + text.strip(), disable_web_page_preview=not preview, parse_mode=parse + ) if delete_orig: message.delete() return ret @@ -38,9 +255,20 @@ def reply( pass -def extract_args(message, markdown=True): +def extract_args(message, markdown=True, line=True): + """ + Extracts arguments from a given text. + + Args: + message (pyrogram.types.Message): The message to extract arguments from. + markdown (bool): Whether to treat the message text as Markdown. Defaults to True. + line (bool): Whether to remove line breaks from the message text. Defaults to True. + + Returns: + str: The extracted arguments. + """ if not (message.text or message.caption): - return + return '' text = message.text or message.caption @@ -48,78 +276,139 @@ def extract_args(message, markdown=True): if ' ' not in text: return '' - text = sub(r'\s+', ' ', text) - text = text[text.find(' '):].strip() + text = sub(r'\s+', ' ', text) if line else text + text = text[text.find(' ') :].strip() return text -def extract_args_arr(message, markdown=True): - return extract_args(message, markdown).split() +def extract_args_split(message, markdown=True, line=True): + """ + Extracts arguments from a given text and splits them into a list. + + Args: + message (pyrogram.types.Message): The message to extract arguments from. + markdown (bool): Whether to treat the message text as Markdown. Defaults to True. + line (bool): Whether to remove line breaks from the message text. Defaults to True. + + Returns: + List[str]: The extracted arguments, split into a list. + """ + return extract_args(message, markdown, line).split() + +def edit( + message, text, preview=True, fix_markdown=False, parse=enums.ParseMode.MARKDOWN +): + """ + Edits the text of a message. -def edit(message, text, preview=True, fix_markdown=False, parse='md'): + Args: + message (pyrogram.types.Message): The message to edit. + text (str): The new text to replace the message with. + preview (bool): Whether to enable link previews for URLs in the text. Defaults to True. + fix_markdown (bool): Whether to add a special character to fix issues with markdown formatting. Defaults to False. + parse (pyrogram.enums.ParseMode): The parse mode to use for the text. Defaults to `pyrogram.enums.ParseMode.MARKDOWN`. + + Returns: + None + """ try: if fix_markdown: text += MARKDOWN_FIX_CHAR - if message.from_user.id != me[0].id: + if message.from_user.id != message._client.me.id: reply(message, text, preview=preview, parse=parse) return message.edit_text( - text.strip(), - disable_web_page_preview=not preview, - parse_mode=parse) + text.strip(), disable_web_page_preview=not preview, parse_mode=parse + ) except BaseException: pass -def download_media( - client, - data, - file_name=None, - progress=None, - sticker_orig=True): +def download_media(client, data, file_name=None, progress=None, sticker_orig=True): + """ + Downloads media from a given message and saves it to a file. + + Args: + client (pyrogram.Client): The client to use for downloading the media. + data (pyrogram.types.Message): The message containing the media to download. + file_name (str): The name of the file to save the media to. Defaults to None. + progress (callable, optional): A callback function to report the progress of the download. Defaults to None. + sticker_orig (bool): Whether to download the original sticker file. Defaults to True. + + Returns: + Union[None, str]: None if media download fails, else the absolute path of downloaded file. + """ if not file_name: if data.document: - file_name = (data.document.file_name - if data.document.file_name - else f'{data.document.file_id}.bin') + file_name = ( + data.document.file_name + if data.document.file_name + else f'{data.document.file_id}.bin' + ) elif data.audio: - file_name = (data.audio.file_name - if data.audio.file_name - else f'{data.audio.file_id}.mp3') + file_name = ( + data.audio.file_name + if data.audio.file_name + else f'{data.audio.file_id}.mp3' + ) elif data.photo: file_name = f'{data.photo.file_id}.png' elif data.voice: file_name = f'{data.voice.file_id}.ogg' elif data.video: - file_name = (data.video.file_name - if data.video.file_name - else f'{data.video.file_id}.mp4') + file_name = ( + data.video.file_name + if data.video.file_name + else f'{data.video.file_id}.mp4' + ) elif data.animation: file_name = f'{data.animation.file_id}.mp4' elif data.video_note: file_name = f'{data.video_note.file_id}.mp4' elif data.sticker: - file_name = f'sticker.{("tgs" if sticker_orig else "TGS") if data.sticker.is_animated else ("webp" if sticker_orig else "png")}' + file_name = f'sticker.{("tgs" if sticker_orig else "json.gz") if data.sticker.is_animated else "webm" if data.sticker.is_video else "webp" if sticker_orig else "png"}' else: return None if progress: - return client.download_media( - data, file_name=file_name, progress=progress) + return client.download_media(data, file_name=file_name, progress=progress) return client.download_media(data, file_name=file_name) def download_media_wc(data, file_name=None, progress=None, sticker_orig=False): + """ + Downloads media from a given message and saves it to a file. + + Args: + data (pyrogram.types.Message): The message containing the media to download. + file_name (str): The name to save the downloaded file as. If not specified, the file will be saved with a default name based on the media type. + progress (callable, optional): A function to call with the download progress percentage as an argument. Useful for displaying a progress bar. The function should take a single `float` argument between 0 and 100. + sticker_orig (bool): If `True`, downloads the original TGS or JSON file for stickers, instead of converting to a static image format. Has no effect for non-sticker media. + + Returns: + None: If the media type is not supported or the download fails for any reason. + str: The name of the file the media was saved as, if the download was successful. + """ return download_media(app, data, file_name, progress, sticker_orig) -def get_me(): - return app.get_me() +def forward(message, chat_id): + """ + Forwards a given message to a specified chat. + Args: + message (pyrogram.types.Message): The message to forward. + chat_id (int or str): The ID of the chat to forward the message to. + If not specified, forwards the message to the saved messages. -def forward(message, chat_id): + Raises: + Exception: If the message forwarding fails. + + Returns: + pyrogram.types.Message: The forwarded message object. + """ try: return message.forward(chat_id or 'me') except Exception as e: @@ -127,28 +416,65 @@ def forward(message, chat_id): def get_messages(chat_id, msg_ids=None, client=app): + """ + Retrieves one or more messages from a specified chat. + + Args: + chat_id (int or str): The ID of the chat to retrieve messages from. If not specified, retrieves messages from the saved messages. + msg_ids (int or List[int]): The ID or list of IDs of the messages to retrieve. If not specified, retrieves the latest message in the chat. + client (pyrogram.Client): The client instance used to retrieve the messages. + + Returns: + List[pyrogram.types.Message]: A list of message objects that were retrieved, sorted in ascending order based on their IDs (oldest message first). If no messages are found or an error occurs, an empty list is returned. + """ try: - ret = client.get_messages( - chat_id=(chat_id or 'me'), message_ids=msg_ids) + ret = client.get_messages(chat_id=(chat_id or 'me'), message_ids=msg_ids) return [ret] if ret and isinstance(ret, Message) else ret except BaseException: return [] def amisudo(): - return me[0].id in BRAIN + """ + Checks if the user is authorized to execute sudo commands. + + Returns: + bool: True if the user is authorized to execute sudo commands, False otherwise. + """ + return app.me.id in BRAIN def increment_spam_count(): + """ + Increments the spam count and checks if the current spam count is within the allowed limit. + + Returns: + bool: True if the current spam count is within the allowed limit, False otherwise. + """ SPAM_COUNT[0] += 1 return spam_allowed() def spam_allowed(): + """ + Checks if spamming is allowed based on the current spam count or user permissions. + + Returns: + bool: True if spamming is allowed, False otherwise. + """ return amisudo() or SPAM_COUNT[0] < 50 def get_cmd(message): + """ + Extracts the command from a given message. + + Args: + message (pyrogram.types.Message): The message from which to extract the command. + + Returns: + str: The command string without the leading slash, or an empty string if no command was found. + """ text = message.text or message.caption if text: text = text.strip() @@ -157,7 +483,172 @@ def get_cmd(message): def parse_cmd(text): + """ + Parses a command string from the given text. + + Args: + text (str): The text from which to parse the command. + + Returns: + str: The parsed command string without the leading slash. + """ cmd = sub(r'\s+', ' ', text) cmd = cmd.split()[0] cmd = cmd.split(_parsed_prefix)[-1] if BOT_PREFIX else cmd[1:] return cmd + + +def is_admin(message): + """ + Checks if the sender of the given message is an admin in the chat. + + Args: + message (pyrogram.types.Message): The message to check. + + Returns: + bool: True if the sender is an admin, False otherwise. + """ + if not message.chat.type in [enums.ChatType.SUPERGROUP, enums.ChatType.GROUP]: + return True + + user = app.get_chat_member(chat_id=message.chat.id, user_id=message.from_user.id) + return user.status in _admin_status_list + + +def is_admin_myself(chat): + """ + Checks if the user is an admin in the given chat. + + Args: + chat (pyrogram.types.Chat): The chat to check. + + Returns: + bool: True if the bot is an admin, False otherwise. + """ + if not chat.type in [enums.ChatType.SUPERGROUP, enums.ChatType.GROUP]: + return True + + user = app.get_chat_member(chat_id=chat.id, user_id='me') + return user.status in _admin_status_list + + +def get_download_dir() -> str: + """ + Gets the directory path for downloaded files. + + Returns: + str: The directory path. + """ + dir = './downloads' + makedirs(dir, exist_ok=True) + return dir + + +def get_duration(media): + """ + Returns the duration of a media file. + + Args: + media (str): The file path. + + Returns: + int: The duration in seconds or None if the duration cannot be determined. + """ + out = get_status_out( + f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "{media}"' + ) + if LOG_VERBOSE: + print(out) + if out[0] == 0: + return int(float(out[1])) + return None + + +def get_status_out(cmd, encoding='utf-8'): + """ + Runs a shell command and captures its output. + + Args: + cmd (str): The shell command to be run. + encoding (str): The encoding to use for the command's output. Defaults to 'utf-8'. + + Returns: + tuple: A tuple containing the return code of the command and its output. + """ + try: + output = check_output( + cmd, + shell=True, + text=True, + stderr=STDOUT if LOG_VERBOSE else DEVNULL, + encoding=encoding, + ) + return (0, output) + except CalledProcessError as ex: + return (ex.returncode, ex.output) + except BaseException as e: + if encoding != 'latin-1': + return get_status_out(cmd, 'latin-1') + raise e + + +def extract_user(message: Message) -> List[User]: + """ + Extracts user information from a given message. + + Args: + message (pyrogram.types.Message): Message object. + + Returns: + List[User]: A list of User objects representing the users mentioned or replied to in the message. + """ + users: List[User] = [] + mentions = None + + if message.text and not mentions: + try: + users.append(message._client.get_users(message.text.split()[1])) + except BaseException: + pass + + if message.reply_to_message: + users.append(message.reply_to_message.from_user) + + if message.entities: + mentions = [ + entity + for entity in message.entities + if entity.type == enums.MessageEntityType.TEXT_MENTION + ] + no_username = [ + i.user for i in mentions if i.type == enums.MessageEntityType.TEXT_MENTION + ] + users += no_username + + for i in mentions: + try: + users.append( + message._client.get_users( + message.text[i.offset : i.offset + i.length] + ) + ) + except BaseException: + pass + + return users + + +def useragent(): + """ + Generate a random user agent string. + + Returns: + str: A random user agent string. + """ + try: + req = get('https://gist.githubusercontent.com/naytseyd/b4f924774f68cf57e54d646ba600abbc/raw/b8fc2569bb600fa338b26b148736007c133ef026') + user_agents = req.text.split('\n') + return choice(user_agents) + except Exception as e: + print("Error fetching user agent:", e) + return 'Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0' diff --git a/sedenecem/core/proxy.py b/sedenecem/core/proxy.py new file mode 100644 index 0000000..09a21e0 --- /dev/null +++ b/sedenecem/core/proxy.py @@ -0,0 +1,120 @@ +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> +# +# This file is part of TeamDerUntergang project, +# and licensed under GNU Affero General Public License v3. +# See the GNU Affero General Public License for more details. +# +# All rights reserved. See COPYING, AUTHORS. +# + +from bs4 import BeautifulSoup +from requests import get + +from sedenbot import TEMP_SETTINGS, get_translation + +from .misc import edit, useragent + + +class ProxyHandler: + """ + A class for handling the retrieval and use of HTTP proxies. + + Attributes: + proxy (Union[None, dict[str, str]]): The current proxy to be used for HTTP requests. + + Methods: + use_proxy(message: Message) -> Union[None, dict[str, str]]: + Retrieves a valid proxy and returns it as a dictionary containing 'http' and 'https' keys. + + get_stored_proxy() -> str: + Retrieves the stored proxy URL from temporary settings. + + put_stored_proxy(proxy: str) -> None: + Sets the stored proxy URL in temporary settings. + + _xget_random_proxy() -> Union[None, tuple[str, str]]: + Attempts to retrieve a random proxy URL from the SSL Proxies website. + + _try_proxy(proxy: tuple[str, str]) -> tuple[int, Union[str, None]]: + Tests the provided proxy to ensure that it can be used for HTTP requests. + + get_random_proxy() -> Union[None, dict[str, str]]: + Retrieves a random, valid proxy URL and sets it as the current proxy for HTTP requests. + + Usage: + handler = ProxyHandler() + proxy = handler.use_proxy("Fetching proxy...") + req = requests.get("https://www.example.com", proxies=proxy) + """ + + def __init__(self): + self.proxy = None + + def use_proxy(self, message) -> None: + edit(message, f'`{get_translation("fetchProxy")}`') + proxy = self.get_random_proxy() + edit(message, f'`{get_translation("providedProxy")}`') + return proxy + + def get_stored_proxy(self): + return TEMP_SETTINGS.get('VALID_PROXY_URL', '') + + def put_stored_proxy(self, proxy): + TEMP_SETTINGS['VALID_PROXY_URL'] = proxy + + def _xget_random_proxy(self): + proxy = self.get_stored_proxy() + try_valid = tuple(proxy.split(":")) if len(proxy) else None + if try_valid: + valid = self._try_proxy(try_valid) + if valid[0] == 200 and "<title>Too" not in valid[1]: + return try_valid + + head = { + 'Accept-Encoding': 'gzip, deflate, sdch', + 'Accept-Language': 'en-US,en;q=0.8', + 'User-Agent': useragent(), + 'Referer': 'https://www.google.com/search?q=sslproxies', + } + + req = get('https://sslproxies.org/', headers=head) + soup = BeautifulSoup(req.text, 'html.parser') + res = soup.find('div', {'class': 'fpl-list'}).find('tbody') + res = res.findAll('tr') + for item in res: + infos = item.findAll('td') + ip = infos[0].text + port = infos[1].text + proxy = (ip, port) + if self._try_proxy(proxy)[0] == 200: + return proxy + + return None + + def _try_proxy(self, proxy): + try: + prxy = f'http://{proxy[0]}:{proxy[1]}' + req = get( + 'https://www.gsmarena.com/', + proxies={'http': prxy, 'https': prxy}, + timeout=1, + ) + if req.status_code == 200: + return (200, req.text) + raise Exception + except BaseException: + return (404, None) + + def get_random_proxy(self): + proxy = self._xget_random_proxy() + if not proxy: + return None + proxy = f'http://{proxy[0]}:{proxy[1]}' + self.put_stored_proxy(proxy) + + proxy_dict = { + 'https': proxy, + 'http': proxy, + } + + return proxy_dict diff --git a/sedenecem/core/replier.py b/sedenecem/core/replier.py index 61047d9..02f64a0 100644 --- a/sedenecem/core/replier.py +++ b/sedenecem/core/replier.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,24 +7,43 @@ # All rights reserved. See COPYING, AUTHORS. # -from os import remove -from .misc import MARKDOWN_FIX_CHAR, download_media_wc +from os import path, remove + +from pyrogram import enums +from pyrogram.types import Message + +from sedenbot import LOG_VERBOSE + +from .misc import MARKDOWN_FIX_CHAR, get_download_dir, get_duration, get_status_out def reply_img( - message, - photo, - caption='', - fix_markdown=False, - delete_orig=False, - delete_file=False): + message, + photo, + caption='', + fix_markdown=False, + delete_orig=False, + delete_file=False, + parse=enums.ParseMode.MARKDOWN, +): + """ + Replies to a given message with a photo and caption. + + Args: + message (pyrogram.types.Message): The message to reply to. + photo (str): The path to the photo file to send. + caption (str): The caption for the photo. + fix_markdown (bool): Whether to fix markdown issues in the caption. + delete_orig (bool): Whether to delete the original message after replying. + delete_file (bool): Whether to delete the photo file after sending. + parse (pyrogram.enums.ParseMode): The parse mode to use for the caption. Defaults to `pyrogram.enums.ParseMode.MARKDOWN`. + """ try: if len(caption) > 0 and fix_markdown: caption += MARKDOWN_FIX_CHAR - message.reply_photo(photo, caption=caption.strip()) + message.reply_photo(photo, caption=caption.strip(), parse_mode=parse) if delete_orig: message.delete() - if delete_file: remove(photo) except BaseException: @@ -32,51 +51,176 @@ def reply_img( def reply_audio( - message, - audio, - caption='', - fix_markdown=False, - delete_orig=False): + message, + audio, + caption='', + duration=None, + fix_markdown=False, + delete_orig=False, + delete_file=False, +): + """ + Reply to the given message with the given audio file and caption. + + Args: + message (pyrogram.types.Message): The message to reply to. + audio (str): The path to the audio file to send. + caption (str): The caption to send with the audio file. + duration (int): The duration of the audio file, in seconds. If not provided, the function will attempt to determine the duration automatically. + fix_markdown (bool): Whether to fix any issues with the Markdown in the caption. Defaults to False. + delete_orig (bool): Whether to delete the original message after sending the reply. Defaults to False. + delete_file (bool): Whether to delete the audio file after sending the reply. Defaults to False. + """ + try: + if len(caption) > 0 and fix_markdown: + caption += MARKDOWN_FIX_CHAR + + if not duration: + duration = get_duration(audio) + + message.reply_audio(audio, caption=caption.strip(), duration=int(duration)) + if delete_orig: + message.delete() + if delete_file: + remove(audio) + except BaseException as e: + raise e + pass + + +def reply_video( + message, + video, + caption='', + duration='', + thumb=None, + fix_markdown=False, + progress=None, + delete_orig=False, + delete_file=False, + parse=enums.ParseMode.MARKDOWN, +): + """ + Reply to message with a video file. + + Args: + message (pyrogram.types.Message): The message object to reply to. + video (str): The path to the video file to send. + caption (str): The caption to attach to the video. Defaults to ''. + duration (str): The duration of the video in seconds. If not specified, it will be calculated automatically. + thumb (str): The path to a thumbnail image to attach to the video. If not specified, a thumbnail will be automatically generated from the video. Defaults to None. + fix_markdown (bool): If True, fix any markdown formatting issues in the caption. Defaults to False. + progress (callable, optional): A callback function to report the progress of the upload. Defaults to None. + delete_orig (bool): If True, delete the original message that the reply is being sent to. Defaults to False. + delete_file (bool): If True, delete the video file after sending it. Defaults to False. + parse (pyrogram.enums.ParseMode): The parse mode to use for the caption. Defaults to `pyrogram.enums.ParseMode.MARKDOWN`. + """ try: + if not thumb: + thumb = f'{get_download_dir()}/thumb.png' + if path.exists(thumb): + remove(thumb) + out = get_status_out( + f'ffmpeg -i {video} -ss 00:00:01.000 -vframes 1 {thumb}' + ) + if LOG_VERBOSE: + print(out) + if out[0] != 0: + thumb = None + + if not duration: + duration = get_duration(video) + if len(caption) > 0 and fix_markdown: caption += MARKDOWN_FIX_CHAR - message.reply_audio(audio, caption=caption.strip()) + if not duration: + message.reply_video( + video, + caption=caption.strip(), + parse_mode=parse, + thumb=thumb, + progress=progress, + ) + else: + message.reply_video( + video, + caption=caption.strip(), + duration=int(duration), + parse_mode=parse, + thumb=thumb, + progress=progress, + ) if delete_orig: message.delete() + if delete_file: + remove(video) except BaseException: pass def reply_voice( - message, - voice, - caption='', - fix_markdown=False, - delete_orig=False): + message, + voice, + caption='', + duration=None, + fix_markdown=False, + delete_orig=False, + delete_file=False, +): + """ + Reply to message with a voice message. + + Args: + message (pyrogram.types.Message): The message object to reply to. + voice (str): The path to the voice message file to send. + caption (str): The caption to attach to the voice message. + duration (int): The duration of the voice message in seconds. If not specified, it will be calculated automatically. Defaults to None. + fix_markdown (bool): If True, fix any markdown formatting issues in the caption. Defaults to False. + delete_orig (bool): If True, delete the original message that the reply is being sent to. Defaults to False. + delete_file (bool): If True, delete the voice message file after sending it. Defaults to False. + """ try: if len(caption) > 0 and fix_markdown: caption += MARKDOWN_FIX_CHAR - message.reply_voice(voice, caption=caption.strip()) + + if not duration: + duration = get_duration(voice) + + message.reply_voice(voice, caption=caption.strip(), duration=duration) if delete_orig: message.delete() + if delete_file: + remove(voice) except BaseException: pass def reply_doc( - message, - doc, - caption='', - fix_markdown=False, - delete_orig=False, - progress=None, - delete_after_send=False): + message, + doc, + caption='', + fix_markdown=False, + delete_orig=False, + progress=None, + delete_after_send=False, +): + """ + Reply to message with a document or a media group of documents. + + Args: + message (pyrogram.types.Message): The message object to reply to. + doc (Union[str, List[pyrogram.types.Document]]): The document or media group of documents to send. + caption (str): The caption to attach to the document. + fix_markdown (bool): If True, fix any markdown formatting issues in the caption. Defaults to False. + delete_orig (bool): If True, delete the original message that the reply is being sent to. Defaults to False. + progress (callable, optional): A callback function that will be called with the number of bytes uploaded as an argument. Defaults to None. + delete_after_send (bool): If True, delete the document file(s) after sending. Defaults to False. + """ try: if len(caption) > 0 and fix_markdown: caption += MARKDOWN_FIX_CHAR if isinstance(doc, str): - message.reply_document( - doc, caption=caption.strip(), progress=progress) + message.reply_document(doc, caption=caption.strip(), progress=progress) if delete_after_send: remove(doc) else: @@ -90,44 +234,37 @@ def reply_doc( raise e -def reply_sticker(message, sticker, delete_orig=False): +def reply_sticker(message, sticker, delete_orig=False, delete_file=False): + """ + Reply to message with a sticker. + + Args: + message (pyrogram.types.Message): The message object to reply to. + sticker (str): The file path of the sticker to send. + delete_orig (bool): If True, delete the original message that the reply is being sent to. Defaults to False. + delete_file (bool): If True, delete the sticker file after sending. Defaults to False. + """ try: message.reply_sticker(sticker) if delete_orig: message.delete() + if delete_file: + remove(sticker) except BaseException: pass -def reply_msg(message, message2, delete_orig=False): +def reply_msg(message: Message, message2: Message, delete_orig=False): + """ + Reply to message with another message. + + Args: + message (pyrogram.types.Message): The original message object to reply to. + message2 (pyrogram.types.Message): The message object to send as the reply. + delete_orig (bool): If True, delete the original message that the reply is being sent to. Defaults to False. + """ try: - filename = None - if message2.media: - filename = download_media_wc(message2, sticker_orig=True) - if message2.audio: - message.reply_audio(filename) - elif message2.animation: - message.reply_animation(filename) - elif message2.sticker: - message.reply_sticker(filename) - elif message2.photo: - message.reply_photo(filename) - elif message2.video: - message.reply_video(filename) - elif message2.voice: - message.reply_voice(filename) - elif message2.video_note: - message.reply_video_note(filename) - elif message2.document: - message.reply_document(filename) - else: - filename = None - message2.forward(message.chat.id) - - if filename: - remove(filename) - else: - message.reply_text(message2.text) + message2.copy(chat_id=message.chat.id, reply_to_message_id=message.id) if delete_orig: message.delete() diff --git a/sedenecem/core/sedenify.py b/sedenecem/core/sedenify.py index f93edb9..ba67f61 100644 --- a/sedenecem/core/sedenify.py +++ b/sedenecem/core/sedenify.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,20 +7,31 @@ # All rights reserved. See COPYING, AUTHORS. # -from subprocess import Popen, PIPE +from os import getpid, kill +from signal import SIGTERM +from subprocess import PIPE, Popen from sys import exc_info from time import gmtime, strftime from traceback import format_exc -from pyrogram import ContinuePropagation, StopPropagation -from pyrogram.handlers import MessageHandler -from pyrogram import filters - -from sedenbot import (SUPPORT_GROUP, BLACKLIST, BOT_VERSION, - BRAIN, me, app, get_translation) +from pyrogram import ContinuePropagation, StopPropagation, enums +from pyrogram.handlers import EditedMessageHandler, MessageHandler + +from sedenbot import BOT_VERSION, BRAIN, app, get_translation + +from .filters import ( + AndFilter, + BotFilter, + IncomingFilter, + MeFilter, + OrFilter, + RegexFilter, + RetardsException, + SedenUpdateHandler, + UserFilter, +) +from .misc import _parsed_prefix, edit, get_cmd, is_admin from .sedenlog import send_log_doc -from .misc import edit, _parsed_prefix, get_cmd -from sedenbot.modules.admin import is_admin def sedenify(**args): @@ -29,7 +40,6 @@ def sedenify(**args): incoming = args.get('incoming', False) disable_edited = args.get('disable_edited', False) disable_notify = args.get('disable_notify', False) - compat = args.get('compat', True) brain = args.get('brain', False) private = args.get('private', True) group = args.get('group', True) @@ -40,33 +50,36 @@ def sedenify(**args): if pattern and '.' in pattern[:2]: args['pattern'] = pattern = pattern.replace('.', _parsed_prefix, 1) + if pattern and pattern[-1:] != '$': + args['pattern'] = pattern = f'{pattern}(?: |$)' + def msg_decorator(func): - def wrap(client, message): + def wrap(message): if message.empty or not message.from_user: return try: - if len(me) < 1: - me.append(app.get_me()) - - if me[0].id in BLACKLIST: - raise RetardsException('RETARDS CANNOT USE THIS BOT') - if message.service and not service: return - if message.chat.type == 'channel': + if message.chat.type == enums.ChatType.CHANNEL: return - if not bot and message.chat.type == 'bot': + if not bot and message.chat.type == enums.ChatType.BOT: message.continue_propagation() - if not private and message.chat.type in ['private', 'bot']: + if not private and message.chat.type in [ + enums.ChatType.PRIVATE, + enums.ChatType.BOT, + ]: if not disable_notify: edit(message, f'`{get_translation("groupUsage")}`') message.continue_propagation() - if not group and message.chat.type in ['group', 'supergroup']: + if not group and message.chat.type in [ + enums.ChatType.SUPERGROUP, + enums.ChatType.GROUP, + ]: if not disable_notify: edit(message, f'`{get_translation("privateUsage")}`') message.continue_propagation() @@ -75,14 +88,10 @@ def wrap(client, message): if not disable_notify: edit(message, f'`{get_translation("adminUsage")}`') message.continue_propagation() - - if not compat: - func(client, message) - else: - func(message) + func(message) except RetardsException: try: - app.stop() + kill(getpid(), SIGTERM) except BaseException: pass except (ContinuePropagation, StopPropagation) as c: @@ -95,22 +104,29 @@ def wrap(client, message): text = get_translation('logidTest') else: if not disable_notify: - edit( - message, - f'`{get_translation("errorLogSend")}`') - link = get_translation('supportGroup', [SUPPORT_GROUP]) - text = get_translation('sedenErrorText', ['**', link]) + edit(message, f'`{get_translation("errorLogSend")}`') + text = get_translation( + 'sedenErrorText', ['**', '`', exc_info()[1]] + ) ftext = get_translation( 'sedenErrorText2', - [date, message.chat.id, message.from_user.id - if message.from_user else 'Unknown', BOT_VERSION, - message.text, format_exc(), - exc_info()[1]]) + [ + date, + message.chat.id, + message.from_user.id if message.from_user else 'Unknown', + BOT_VERSION, + message.text, + format_exc(), + exc_info()[1], + ], + ) process = Popen( ['git', 'log', '--pretty=format:"%an: %s"', '-10'], - stdout=PIPE, stderr=PIPE) + stdout=PIPE, + stderr=PIPE, + ) out, err = process.communicate() out = f'{out.decode()}\n{err.decode()}'.strip() @@ -120,36 +136,36 @@ def wrap(client, message): file.write(ftext) file.close() - send_log_doc(get_translation('rbgLog'), - caption=text, remove_file=True) + send_log_doc( + get_translation('rbgLog'), caption=text, remove_file=True + ) raise e except Exception as x: raise x - filter = None + filter = AndFilter() if pattern: - filter = filters.regex(pattern) + filter.add_filter(RegexFilter(pattern)) if brain: - filter &= filters.user(BRAIN) + filter.add_filter(UserFilter(BRAIN)) if outgoing and not incoming: - filter &= filters.me + filter.add_filter(MeFilter()) elif incoming and not outgoing: - filter &= (filters.incoming & ~filters.bot & ~filters.me) + filter.add_filter(IncomingFilter(), BotFilter(True), MeFilter(True)) else: if outgoing and not incoming: - filter = filters.me + filter.add_filter(MeFilter()) elif incoming and not outgoing: - filter = (filters.incoming & ~filters.bot & ~filters.me) + filter.add_filter(IncomingFilter(), BotFilter(True), MeFilter(True)) else: - filter = (filters.me | filters.incoming) & ~filters.bot + filter.add_filter( + OrFilter(MeFilter(), IncomingFilter()), BotFilter(True) + ) - if disable_edited: - filter &= ~filters.edited + handlers = [MessageHandler] + if not disable_edited: + handlers.append(EditedMessageHandler) - app.add_handler(MessageHandler(wrap, filter)) + app.add_handler(SedenUpdateHandler(wrap, filter, handlers)) return msg_decorator - - -class RetardsException(Exception): - pass diff --git a/sedenecem/core/sedenlog.py b/sedenecem/core/sedenlog.py index fe9f7f9..615e825 100644 --- a/sedenecem/core/sedenlog.py +++ b/sedenecem/core/sedenlog.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,17 +8,33 @@ # from os import remove -from sedenbot import app, LOG_ID + +from sedenbot import LOG_ID, app + from .send import send, send_doc def send_log(text, fix_markdown=False): - send(app, LOG_ID or 'me', - text, fix_markdown=fix_markdown) + """ + Sends a log message to the specified Telegram chat or user(self). + + Args: + text (str): The log message to send. + fix_markdown (bool): Whether to fix the markdown of the log message. Defaults to False. + """ + send(app, LOG_ID or 'me', text, fix_markdown=fix_markdown) def send_log_doc(doc, caption='', fix_markdown=False, remove_file=False): - send_doc(app, LOG_ID or 'me', doc, - caption=caption, fix_markdown=fix_markdown) + """ + Sends a document file to the specified Telegram chat or user(self), along with an caption. + + Args: + doc (str): The path to the document file to send. + caption (str): The caption to include with the document file. Defaults to ''. + fix_markdown (bool): Whether to fix the markdown of the caption. Defaults to False. + remove_file (bool): Whether to remove the document file after sending. Defaults to False. + """ + send_doc(app, LOG_ID or 'me', doc, caption=caption, fix_markdown=fix_markdown) if remove_file: remove(doc) diff --git a/sedenecem/core/send.py b/sedenecem/core/send.py index fe5932b..406532d 100644 --- a/sedenecem/core/send.py +++ b/sedenecem/core/send.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -8,20 +8,33 @@ # from pyrogram.types import Chat + from .misc import MARKDOWN_FIX_CHAR def send(client, chat, text, fix_markdown=False, reply_id=None): + """ + Sends a message to the specified chat. If the message is too long, it is sent as a document. + + Args: + client (pyrogram.Client): The client used to send the message. + chat (Union[pyrogram.types.Chat, int, str]): The ID, username, or `Chat` object of the chat to send the message to. + text (str): The text of the message to send. + fix_markdown (bool): Whether to fix the markdown of the message. Defaults to False. + reply_id (int): The ID of the message to reply to. Defaults to None. + """ if fix_markdown: text += MARKDOWN_FIX_CHAR if len(text) < 4096: if not reply_id: - client.send_message(chat.id if isinstance( - chat, Chat) else chat, text) + client.send_message(chat.id if isinstance(chat, Chat) else chat, text) else: - client.send_message(chat.id if isinstance( - chat, Chat) else chat, text, reply_to_message_id=reply_id) + client.send_message( + chat.id if isinstance(chat, Chat) else chat, + text, + reply_to_message_id=reply_id, + ) return file = open('temp.txt', 'w+') @@ -30,19 +43,39 @@ def send(client, chat, text, fix_markdown=False, reply_id=None): send_doc(client, chat, 'temp.txt') -def send_sticker(client, chat, sticker): +def send_sticker(message, chat, sticker): + """ + Sends a sticker to the specified chat. + + Args: + message (pyrogram.types.Message): The original message object that the sticker is being sent in reply to. + chat (Union[pyrogram.types.Chat, int, str]): The ID, username, or `Chat` object of the chat to send the sticker to. + sticker (Union[pyrogram.types.Sticker, str]): The `Sticker` object or file ID of the sticker to send. + """ try: - client.send_sticker(chat.id if isinstance( - chat, Chat) else chat, sticker) + message._client.send_sticker( + chat.id if isinstance(chat, Chat) else chat, sticker + ) except BaseException: pass def send_doc(client, chat, doc, caption='', fix_markdown=False): + """ + Sends a document to the specified chat. + + Args: + client (pyrogram.Client): The client used to send the message. + chat (Union[pyrogram.types.Chat, int, str]): The ID, username, or `Chat` object of the chat to send the document to. + doc (str): The file path of the document to send. + caption (str): The caption of the document. Defaults to an empty string. + fix_markdown (bool): If True, the caption will be appended with a markdown fix character. Defaults to False. + """ try: if len(caption) > 0 and fix_markdown: caption += MARKDOWN_FIX_CHAR - client.send_document(chat.id if isinstance( - chat, Chat) else chat, doc, caption=caption) + client.send_document( + chat.id if isinstance(chat, Chat) else chat, doc, caption=caption + ) except BaseException: pass diff --git a/sedenecem/core/webdriver.py b/sedenecem/core/webdriver.py index f90bb80..edd343e 100644 --- a/sedenecem/core/webdriver.py +++ b/sedenecem/core/webdriver.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,11 +7,20 @@ # All rights reserved. See COPYING, AUTHORS. # -from selenium.webdriver import Chrome, ChromeOptions from sedenbot import CHROME_DRIVER +from selenium.webdriver import Chrome, ChromeOptions def get_webdriver(): + """ + Returns a headless Chrome webdriver object with specified options. + + Returns: + selenium.webdriver.chrome.webdriver.WebDriver: The Chrome webdriver object. + + Raises: + Exception: If the CHROME_DRIVER executable path is not found. + """ try: options = ChromeOptions() options.add_argument('--headless') @@ -24,4 +33,3 @@ def get_webdriver(): return Chrome(executable_path=CHROME_DRIVER, options=options) except BaseException: raise Exception('CHROME_DRIVER not found!') - return None diff --git a/sedenecem/fonts/GoogleSans.ttf b/sedenecem/fonts/GoogleSans.ttf deleted file mode 100644 index ab605f9..0000000 Binary files a/sedenecem/fonts/GoogleSans.ttf and /dev/null differ diff --git a/sedenecem/fonts/OpenSans.ttf b/sedenecem/fonts/OpenSans.ttf new file mode 100644 index 0000000..57d86a6 Binary files /dev/null and b/sedenecem/fonts/OpenSans.ttf differ diff --git a/sedenecem/fonts/impact.ttf b/sedenecem/fonts/impact.ttf new file mode 100644 index 0000000..114e6c1 Binary files /dev/null and b/sedenecem/fonts/impact.ttf differ diff --git a/sedenecem/sql/__init__.py b/sedenecem/sql/__init__.py index 517ee24..9f4b21c 100644 --- a/sedenecem/sql/__init__.py +++ b/sedenecem/sql/__init__.py @@ -1,7 +1,7 @@ +from sedenbot import DATABASE_URL from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker, scoped_session -from sedenbot import DATABASE_URL +from sqlalchemy.orm import scoped_session, sessionmaker BASE = declarative_base() diff --git a/sedenecem/sql/blacklist_sql.py b/sedenecem/sql/blacklist_sql.py index 93b1113..337bcd5 100644 --- a/sedenecem/sql/blacklist_sql.py +++ b/sedenecem/sql/blacklist_sql.py @@ -1,6 +1,7 @@ from threading import RLock -from sqlalchemy import func, distinct, Column, String, UnicodeText -from sedenecem.sql import SESSION, BASE + +from sedenecem.sql import BASE, SESSION +from sqlalchemy import Column, String, UnicodeText, distinct, func class BlackListFilters(BASE): @@ -16,12 +17,14 @@ def __repr__(self): return "<Blacklist filter '%s' for %s>" % (self.trigger, self.chat_id) def __eq__(self, other): - return bool(isinstance(other, BlackListFilters) - and self.chat_id == other.chat_id - and self.trigger == other.trigger) + return bool( + isinstance(other, BlackListFilters) + and self.chat_id == other.chat_id + and self.trigger == other.trigger + ) -BlackListFilters.__table__.create(checkfirst=True) +BlackListFilters.__table__.create(bind=SESSION.get_bind(), checkfirst=True) BLACKLIST_FILTER_INSERTION_LOCK = RLock() @@ -39,8 +42,7 @@ def add_to_blacklist(chat_id, trigger): def rm_from_blacklist(chat_id, trigger): with BLACKLIST_FILTER_INSERTION_LOCK: - blacklist_filt = SESSION.query( - BlackListFilters).get((str(chat_id), trigger)) + blacklist_filt = SESSION.query(BlackListFilters).get((str(chat_id), trigger)) if blacklist_filt: # sanity check if trigger in CHAT_BLACKLISTS.get(str(chat_id), set()): @@ -67,16 +69,18 @@ def num_blacklist_filters(): def num_blacklist_chat_filters(chat_id): try: - return SESSION.query(BlackListFilters.chat_id).filter( - BlackListFilters.chat_id == str(chat_id)).count() + return ( + SESSION.query(BlackListFilters.chat_id) + .filter(BlackListFilters.chat_id == str(chat_id)) + .count() + ) finally: SESSION.close() def num_blacklist_filter_chats(): try: - return SESSION.query(func.count( - distinct(BlackListFilters.chat_id))).scalar() + return SESSION.query(func.count(distinct(BlackListFilters.chat_id))).scalar() finally: SESSION.close() diff --git a/sedenecem/sql/filters_sql.py b/sedenecem/sql/filters_sql.py index 8679c0c..412c7ff 100644 --- a/sedenecem/sql/filters_sql.py +++ b/sedenecem/sql/filters_sql.py @@ -1,8 +1,8 @@ try: - from sedenecem.sql import SESSION, BASE + from sedenecem.sql import BASE, SESSION except ImportError: raise AttributeError -from sqlalchemy import Column, UnicodeText, Numeric, String +from sqlalchemy import Column, Numeric, String, UnicodeText class Filters(BASE): @@ -20,11 +20,13 @@ def __init__(self, chat_id, keyword, reply, f_mesg_id): def __eq__(self, other): return bool( - isinstance(other, Filters) and self.chat_id == other.chat_id - and self.keyword == other.keyword) + isinstance(other, Filters) + and self.chat_id == other.chat_id + and self.keyword == other.keyword + ) -Filters.__table__.create(checkfirst=True) +Filters.__table__.create(bind=SESSION.get_bind(), checkfirst=True) def get_filter(chat_id, keyword): @@ -36,8 +38,7 @@ def get_filter(chat_id, keyword): def get_filters(chat_id): try: - return SESSION.query(Filters).filter( - Filters.chat_id == str(chat_id)).all() + return SESSION.query(Filters).filter(Filters.chat_id == str(chat_id)).all() finally: SESSION.close() diff --git a/sedenecem/sql/gban_sql.py b/sedenecem/sql/gban_sql.py index 3194d13..b3d2250 100644 --- a/sedenecem/sql/gban_sql.py +++ b/sedenecem/sql/gban_sql.py @@ -1,5 +1,5 @@ try: - from sedenecem.sql import SESSION, BASE + from sedenecem.sql import BASE, SESSION except ImportError: raise AttributeError @@ -14,7 +14,7 @@ def __init__(self, sender): self.sender = str(sender) -GBan.__table__.create(checkfirst=True) +GBan.__table__.create(bind=SESSION.get_bind(), checkfirst=True) def is_gbanned(sender): @@ -27,6 +27,15 @@ def is_gbanned(sender): SESSION.close() +def gbanned_users(): + try: + return SESSION.query(GBan).all() + except BaseException: + return None + finally: + SESSION.close() + + def gban(sender): adder = GBan(str(sender)) SESSION.add(adder) diff --git a/sedenecem/sql/gdrive_sql.py b/sedenecem/sql/gdrive_sql.py new file mode 100644 index 0000000..f952b23 --- /dev/null +++ b/sedenecem/sql/gdrive_sql.py @@ -0,0 +1,42 @@ +from pickle import dumps, loads + +from sedenecem.sql import BASE, SESSION +from sqlalchemy import Column, Integer, LargeBinary + + +class GDriveCreds(BASE): + __tablename__ = 'GDrive' + + user_id = Column(Integer, primary_key=True) + credentials_string = Column(LargeBinary) + + def __init__(self, user_id): + self.user_id = user_id + + +GDriveCreds.__table__.create(bind=SESSION.get_bind(), checkfirst=True) + + +def set(user_id, credentials): + saved_creds = SESSION.query(GDriveCreds).get(user_id) + if not saved_creds: + saved_creds = GDriveCreds(user_id) + saved_creds.credentials_string = dumps(credentials) + + SESSION.add(saved_creds) + SESSION.commit() + + +def get(user_id): + saved_creds = SESSION.query(GDriveCreds).get(user_id) + creds = None + if saved_creds is not None: + creds = loads(saved_creds.credentials_string) + return creds + + +def remove_(user_id): + saved_cred = SESSION.query(GDriveCreds).get(user_id) + if saved_cred: + SESSION.delete(saved_cred) + SESSION.commit() diff --git a/sedenecem/sql/gmute_sql.py b/sedenecem/sql/gmute_sql.py index 4f766f6..44b546d 100644 --- a/sedenecem/sql/gmute_sql.py +++ b/sedenecem/sql/gmute_sql.py @@ -1,5 +1,5 @@ try: - from sedenecem.sql import SESSION, BASE + from sedenecem.sql import BASE, SESSION except ImportError: raise AttributeError @@ -14,7 +14,7 @@ def __init__(self, sender): self.sender = str(sender) -GMute.__table__.create(checkfirst=True) +GMute.__table__.create(bind=SESSION.get_bind(), checkfirst=True) def is_gmuted(sender): @@ -27,6 +27,15 @@ def is_gmuted(sender): SESSION.close() +def gmuted_users(): + try: + return SESSION.query(GMute).all() + except BaseException: + return None + finally: + SESSION.close() + + def gmute(sender): adder = GMute(str(sender)) SESSION.add(adder) diff --git a/sedenecem/sql/keep_read_sql.py b/sedenecem/sql/keep_read_sql.py index 1d6607a..08ac3d5 100644 --- a/sedenecem/sql/keep_read_sql.py +++ b/sedenecem/sql/keep_read_sql.py @@ -1,5 +1,5 @@ try: - from sedenecem.sql import SESSION, BASE + from sedenecem.sql import BASE, SESSION except ImportError: raise AttributeError @@ -14,7 +14,7 @@ def __init__(self, sender): self.groupid = str(sender) -KRead.__table__.create(checkfirst=True) +KRead.__table__.create(bind=SESSION.get_bind(), checkfirst=True) def is_kread(): diff --git a/sedenecem/sql/lydia_sql.py b/sedenecem/sql/lydia_sql.py deleted file mode 100644 index c805339..0000000 --- a/sedenecem/sql/lydia_sql.py +++ /dev/null @@ -1,74 +0,0 @@ -from sqlalchemy import Column, UnicodeText, LargeBinary, Numeric -from sedenecem.sql import SESSION, BASE - - -class LydiaAI(BASE): - __tablename__ = "lydia_ai" - user_id = Column(Numeric, primary_key=True) - chat_id = Column(Numeric, primary_key=True) - session_id = Column(UnicodeText) - session_expires = Column(Numeric) - - def __init__( - self, - user_id, - chat_id, - session_id, - session_expires - ): - self.user_id = user_id - self.chat_id = chat_id - self.session_id = session_id - self.session_expires = session_expires - - -LydiaAI.__table__.create(checkfirst=True) - - -def get_s(user_id, chat_id): - try: - return SESSION.query(LydiaAI).get((user_id, chat_id)) - except: - return None - finally: - SESSION.close() - - -def get_all_s(): - try: - return SESSION.query(LydiaAI).all() - except: - return None - finally: - SESSION.close() - - -def add_s( - user_id, - chat_id, - session_id, - session_expires -): - adder = SESSION.query(LydiaAI).get((user_id, chat_id)) - if adder: - adder.session_id = session_id - adder.session_expires = session_expires - else: - adder = LydiaAI( - user_id, - chat_id, - session_id, - session_expires - ) - SESSION.add(adder) - SESSION.commit() - - -def remove_s( - user_id, - chat_id -): - note = SESSION.query(LydiaAI).get((user_id, chat_id)) - if note: - SESSION.delete(note) - SESSION.commit() diff --git a/sedenecem/sql/mute_sql.py b/sedenecem/sql/mute_sql.py index e566382..56c9517 100644 --- a/sedenecem/sql/mute_sql.py +++ b/sedenecem/sql/mute_sql.py @@ -1,5 +1,5 @@ try: - from sedenecem.sql import SESSION, BASE + from sedenecem.sql import BASE, SESSION except ImportError: raise AttributeError @@ -16,13 +16,16 @@ def __init__(self, chat_id, sender): self.sender = str(sender) -Mute.__table__.create(checkfirst=True) +Mute.__table__.create(bind=SESSION.get_bind(), checkfirst=True) def is_muted(chat_id, sender): try: - ret = SESSION.query(Mute).filter(Mute.chat_id == str( - chat_id), Mute.sender == str(sender)).all() + ret = ( + SESSION.query(Mute) + .filter(Mute.chat_id == str(chat_id), Mute.sender == str(sender)) + .all() + ) return len(ret) > 0 except BaseException: return None @@ -37,8 +40,11 @@ def mute(chat_id, sender): def unmute(chat_id, sender): - rem = SESSION.query(Mute).filter(Mute.chat_id == str( - chat_id), Mute.sender == str(sender)).all() + rem = ( + SESSION.query(Mute) + .filter(Mute.chat_id == str(chat_id), Mute.sender == str(sender)) + .all() + ) if len(rem): for item in rem: SESSION.delete(item) diff --git a/sedenecem/sql/notes_sql.py b/sedenecem/sql/notes_sql.py index 3bc684e..a833505 100644 --- a/sedenecem/sql/notes_sql.py +++ b/sedenecem/sql/notes_sql.py @@ -1,8 +1,8 @@ try: - from sedenecem.sql import SESSION, BASE + from sedenecem.sql import BASE, SESSION except ImportError: raise AttributeError -from sqlalchemy import Column, UnicodeText, Numeric, String +from sqlalchemy import Column, Numeric, String, UnicodeText class Notes(BASE): @@ -19,7 +19,7 @@ def __init__(self, chat_id, keyword, reply, f_mesg_id): self.f_mesg_id = f_mesg_id -Notes.__table__.create(checkfirst=True) +Notes.__table__.create(bind=SESSION.get_bind(), checkfirst=True) def get_note(chat_id, keyword): diff --git a/sedenecem/sql/pm_permit_sql.py b/sedenecem/sql/pm_permit_sql.py index b8388c6..c34174c 100644 --- a/sedenecem/sql/pm_permit_sql.py +++ b/sedenecem/sql/pm_permit_sql.py @@ -1,5 +1,5 @@ try: - from sedenecem.sql import SESSION, BASE + from sedenecem.sql import BASE, SESSION except ImportError: raise AttributeError @@ -14,14 +14,13 @@ def __init__(self, chat_id): self.chat_id = str(chat_id) # ensure string -PMPermit.__table__.create(checkfirst=True) +PMPermit.__table__.create(bind=SESSION.get_bind(), checkfirst=True) def is_approved(chat_id): try: - return SESSION.query(PMPermit).filter( - PMPermit.chat_id == str(chat_id)).one() - except BaseException: + return SESSION.query(PMPermit).filter(PMPermit.chat_id == str(chat_id)).one() + except BaseException as e: return None finally: SESSION.close() diff --git a/sedenecem/sql/snips_sql.py b/sedenecem/sql/snips_sql.py index 56e7dce..18a4ef3 100644 --- a/sedenecem/sql/snips_sql.py +++ b/sedenecem/sql/snips_sql.py @@ -1,9 +1,9 @@ try: - from sedenecem.sql import SESSION, BASE + from sedenecem.sql import BASE, SESSION except ImportError: raise AttributeError -from sqlalchemy import Column, UnicodeText, Numeric +from sqlalchemy import Column, Numeric, UnicodeText class Snips(BASE): @@ -18,7 +18,7 @@ def __init__(self, snip, reply, f_mesg_id): self.f_mesg_id = f_mesg_id -Snips.__table__.create(checkfirst=True) +Snips.__table__.create(bind=SESSION.get_bind(), checkfirst=True) def get_snip(keyword): diff --git a/sedenecem/translator/__init__.py b/sedenecem/translator/__init__.py index fad3a63..8ef1003 100644 --- a/sedenecem/translator/__init__.py +++ b/sedenecem/translator/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,8 +7,8 @@ # All rights reserved. See COPYING, AUTHORS. # -from os import getcwd, listdir, path from json import loads +from os import getcwd, listdir, path pwd = f'{getcwd()}/sedenecem/translator' diff --git a/sedenecem/translator/de.json b/sedenecem/translator/de.json index 3c15646..6741e0e 100644 --- a/sedenecem/translator/de.json +++ b/sedenecem/translator/de.json @@ -107,6 +107,10 @@ "evalInfo": ".eval 2 + 3\nVerwendung: Mini ausdr\u00fccke auswerten.", "evalLog": "Eval abfrage %1 erfolgreich verarbeitet", "evalUsage": "Geben sie eine aussage zu bewerten.", + "exifError": "EXIF-Daten nicht gefunden.", + "exifInfo": ".exif\nVerwendung: Extrahieren Sie EXIF-Informationen aus der angegebenen Fotodatei.", + "exifLog": "EXIF-Daten f\u00fcr Bild:\n", + "exifProcess": "Extrahieren von EXIF-Informationen.", "ezanvaktiErrorInfo": "F\u00fcr %1 wurden keine informationen gefunden.", "ezanvaktiKonum": "Bitte geben sie eine stadt neben dem befehl an.", "ezanvaktiShowInfo": "%1Prayer Times%1\n\n\ud83d\udccd %1Location:%1 %2%3%2\n\n\ud83c\udfd9 %1Fajr:%1 %2%4%2\n\ud83c\udf05 %1Tulu:%1 %2%5%2\n\ud83c\udf07 %1Zuhr:%1 %2%6%2\n\ud83c\udf06 %1Asr:%1 %2%7%2\n\ud83c\udf03 %1Maghrib:%1 %2%8%2\n\ud83c\udf0c %1Isha:%1 %2%9%2", diff --git a/sedenecem/translator/en.json b/sedenecem/translator/en.json index a6ca8b5..21c1c19 100644 --- a/sedenecem/translator/en.json +++ b/sedenecem/translator/en.json @@ -1,32 +1,32 @@ { "added": "added", - "adminInfo": ".promote <username/reply> <custom rank (optional)>\nUsage: Provides admin rights to the person in the chat.\n\n.demote <username/reply>\nUsage: Revokes the person's admin permissions in the chat.\n\n.ban <username/reply>\nUsage: Bans the person off your chat.\n\n.unban <username/reply>\nUsage: Removes the ban from the person in the chat.\n\n.gban <username/reply>\nUsage: Bans the person in all groups you have in common with them.\n\n.ungban <username/reply>\nUsage: Removes the person from the gbanned list\n\n.mute <username/reply>\nUsage: Mutes the person in the chat.\n\n.unmute <username/reply>\nRemoves the person from the muted list.\n\n.gmute <username/reply>\nUsage: Mutes the person in all groups you have in common with them.\n\n.ungmute <username/reply>\nUsage: Removes the person from the gmuted list\n\n.kick <username/reply>\nUsage: Kicks user.\n\n.pin <reply>\nUsage: Pins the replied message.\n\n.unpin\nUsage: Unpin the pinned message.\n\n.admins\nUsage: Retrieves a list of admins in the chat.\n\n.users\nUsage: Retrieves all users in the chat.\n\n.usersdel\nUsage: Retrieves all deleted users in the chat.\n\n.bots\nUsage: Retrieves a list of bots in the chat.", + "adminInfo": ".promote <username/reply> <custom rank (optional)>\nUsage: Provides admin rights to the person in the chat.\n\n.demote <username/reply>\nUsage: Revokes the person's admin permissions in the chat.\n\n.ban <username/reply>\nUsage: Bans the person off your chat.\n\n.unban <username/reply>\nUsage: Removes the ban from the person in the chat.\n\n.mute <username/reply>\nUsage: Mutes the person in the chat.\n\n.unmute <username/reply>\nRemoves the person from the muted list.\n\n.kick <username/reply>\nUsage: Kicks user.\n\n.pin <reply> & .pin loud <reply>\nUsage: Pins the replied message.\n\n.unpin <reply> & .unpin all\nUsage: Unpin the pinned message(s).\n\n.admins\nUsage: Retrieves a list of admins in the chat.\n\n.users\nUsage: Retrieves all users in the chat.\n\n.usersdel\nUsage: Retrieves all deleted users in the chat.\n\n.bots\nUsage: Retrieves a list of bots in the chat.\n\n.zombies\nUsage: Finds deleted account(s) that are in a group. Use '.zombies clean' command to remove deleted account(s) from the group.\n\n.setgpic\nUsage: Changes group picture.", "adminUsage": "I'm not admin!", "adminlist": "%1Admins in%1 %2%3%2%1:%1", "afkEnd": "I'm no longer AFK.", "afkInfo": ".afk [Optional Reason]\nUsage: Sets you as afk.\nReplies to anyone who tags/PM's you telling them that you are AFK(reason).\n\nSwitches off AFK when you type back anything, anywhere.", "afkLog": "#AFK\nYou went AFK!", "afkMentionUsers": "%1[%2](tg://user?id=%3) sent you%1 %4%5%4 %1messages%1", - "afkMessage2": "%1[%2](tg://user?id=%3) still AFK.\nReason:%1 %4%5%4", + "afkMessage2": "%1[%2](tg://user?id=%3) still AFK.\nReason: %4%5%4\nLast Seen: %4%6%4 %1", "afkMessages": "%1%3%1 %2person sent%2 %1%4%1 %2messages to you while you were AFK.%2", "afkStart": "AFK AF!", - "afkStartReason": "%1AFK AF!\nReason%1: %2%3%2", + "afkStartReason": "%1AFK AF!\nReason: %2%3%2%1", "afkstr1": "I'm busy right now. Please talk in a bag and when I come back you can just give me the bag!", - "afkstr10": "I'm away over 7 seas and 7 countries,\n7 waters and 7 continents,\n7 mountains and 7 hills,\n7 plains and 7 mounds,\n7 pools and 7 lakes,\n7 springs and 7 meadows,\n7 cities and 7 neighborhoods,\n7 blocks and 7 houses...\n\nWhere not even your messages can reach me!", + "afkstr10": "I'm away over 7 seas and 7 countries,\n7 waters and 7 continents,\n7 mountains and 7 hills,\n7 plains and 7 mounds,\n7 pools and 7 lakes,\n7 springs and 7 meadows,\n7 cities and 7 neighborhoods,\n7 blocks and 7 houses\u2026\n\nWhere not even your messages can reach me!", "afkstr11": "I'm away from the keyboard at the moment, but if you'll scream loud enough at your screen, I might just hear you.", "afkstr12": "I went that way\n---->", "afkstr13": "I went this way\n<----", "afkstr14": "Please leave a message and make me feel even more important than I already am.", "afkstr15": "My F\u00fchrer not here so stop writing to me,\nor else you will find yourself with a screen full of your own messages.", - "afkstr16": "If I were here,\nI'd tell you where I am.\n\nBut I'm not,\nso ask me when I return...", + "afkstr16": "If I were here,\nI'd tell you where I am.\n\nBut I'm not,\nso ask me when I return\u2026", "afkstr17": "I am away!\nI don't know when I'll be back!\nHopefully a few minutes from now!", "afkstr18": "I'm not available right now so please leave your name, number, and address and I will stalk you later.", "afkstr19": "Sorry, I'm not here right now.\nFeel free to talk to my userbot as long as you like.\nI'll get back to you later.", "afkstr2": "I'm away right now. If you need anything, leave a message after the beep:\nbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep!", "afkstr20": "I bet you were expecting an away message!", - "afkstr21": "Life is so short, there are so many things to do...\nI'm away doing one of them..", - "afkstr22": "I am not here right now...\nbut if I was...\n\nwouldn't that be awesome?", - "afkstr3": "I'll be back in a few minutes and if I'm not...,\nwait longer.", + "afkstr21": "Life is so short, there are so many things to do\u2026\nI'm away doing one of them..", + "afkstr22": "I am not here right now\u2026\nbut if I was\u2026\n\nwouldn't that be awesome?", + "afkstr3": "I'll be back in a few minutes and if I'm not\u2026,\nwait longer.", "afkstr4": "I'm not here right now, so I'm probably somewhere else.", "afkstr5": "Roses are red,\nViolets are blue,\nLeave me a message,\nAnd I'll get back to you.", "afkstr6": "Sometimes the best things in life are worth waiting for\u2026\nI'll be right back.", @@ -37,35 +37,39 @@ "alreadyMuted": "Error!\nUser already muted!", "alreadyUnbanned": "Hata!\nUser already unbanned", "alreadyUnmuted": "Hata!\nUser already unmuted!", - "androidInfo": ".magisk\nGet latest Magisk releases\n\n.device <codename>\nUsage: Get info about android device codename or model.\n\n.codename <brand> <device>\nUsage: Search for android device codename.\n\n.specs <brand> <device>\nUsage: Get device specifications info.\n\n.twrp <codename>\nUsage: Get latest twrp download for android device.\n\n.orangefox <codename>\nUsage: Get latest OrangeFox download for android device.\n\n.phh <ABI>\nGet latest Phh releases", + "alwaysOnline": "Always online mode enabled!", + "alwaysOnline2": "Always online mode is already enabled!", + "alwaysOnlineOff": "Always online mode disabled!", + "alwaysOnlineOff2": "Always online mode is already disabled!", + "androidInfo": ".ksu\nGet latest KernelSU APK's\n\n.magisk\nGet latest Magisk releases\n\n.device <codename>\nUsage: Get info about android device codename or model.\n\n.codename <brand> <device>\nUsage: Search for android device codename.\n\n.specs <brand> <device>\nUsage: Get device specifications info.\n\n.twrp <codename>\nUsage: Get latest twrp download for android device.\n\n.orangefox <codename>\nUsage: Get latest OrangeFox download for android device.\n\n.phh <ABI>\nGet latest Phh releases", "androidPhhHeader": "%1Latest Phh %2AOSP Releases%1", "answerFromBot": "I didn't get an answer from bot!", "apiHashError": "API HASH Not Set. Please check your config.env file.", "apiIdError": "API ID Not Set. Please check your config.env file.", - "applyEarrape": "Applying earrape...", - "applyNightcore": "Applying nightcore...", - "applySlowedtoperfection": "Applying slowed to perfection...", + "applyEarrape": "Applying earrape\u2026", + "applyNightcore": "Applying nightcore\u2026", + "applySlowedtoperfection": "Applying slowed to perfection\u2026", "autoppConfig": "Please set AUTO_PP variable or put a profile photo.", "autoppDisabled": "Profile photo will no longer change automatically.", "autoppDisabledAlready": "It seems that profile photo doesn't change automatically anyway.", "autoppEnabledAlready": "It seems that profile photo is already changing automatically.", "autoppInfo": ".autopp <disable>\nUsage: This command makes the photo you specify as a profile picture and adds a time. This change it every minute.\nType .autopp to turn it on, .autopp disable to turn it off.\n\nNOTE: There is even a small possibility of getting banned. So use it carefully.", - "autoppLog": "[AUTOPP] File already exists, skipping download ...", - "autoppProcess": "Setting up profile photo ...", + "autoppLog": "[AUTOPP] File already exists, skipping download\u2026", + "autoppProcess": "Setting up profile photo\u2026", "autoppResult": "Profile photo has been updated!", + "banAdminError": "I guess i haven't enough perm for this.", "banError": "%1Something went wrong!%1\n\n%2%3%2", "banFailUser": "Please specify a valid user!", - "banLog": "%1#BAN\nUSER: [%2](tg://user?id=%3)\nGROUP: %4 (%1%5%6%5%1)%1", + "banLog": "#BAN\nUSER: [%1](tg://user?id=%2) (%4%2%4)\nGROUP: %3 (%4%5%4)\n%6", "banProcess": "Whacking the pest!", - "banResult": "%1[%2](tg://user?id=%3)%1 (%4%3%4) %4banned!%4", + "banReason": "%1Reason:%1 %2%3%2", + "banResult": "%1[%2](tg://user?id=%3)%1 %4banned!%4\n%5", "barcodeInfo": ".barcode <text>\nUsage: Make a barcode from provided content.\nEg: .barcode https://devotag.com\n\nNote: Use .decode command to get decoded content.", "barcodeUsage": "%1Usage:%1 %2.barcode <text>%2", - "base64Info": ".base64\nUsage: Find the base64 encoding of the given string. Eg: .base64 en hello", "bioSuccess": "Successfully changed Bio.", - "birakmamseniInfo": ".birakmemseni\nUsage: It shows number of support provided to Be\u015fikta\u015f's Birakmem Seni campaign", - "birakmamseniResult": "%1\u26ab\u26aa Birakmam Seni Data \u26ab\u26aa%1\n\nAs of now, as part of %1BIRAKMAM SENI%1 campaign %2%3%2 \ud83d\udda4\ud83e\udd0d pieces of support were provided.\nCome to, support our %1BE\u015eIKTA\u015e%1 \ud83e\udd85 !\n\n[https://birakmamseni.org](https://birakmamseni.org/)\n\n%2=============================\nSupports coming through SMS, Money Order / Eft and Postal Check channels are added to meter periodically.\n=============================%2", "blacklistAddSuccess": "%2%3%2 %1has been blacklisted for this chat.%1", "blacklistChats": "Blacklists in the Current Chat:", + "blacklistCheck": "This person is blacklisted in Seden UserBot!", "blacklistInfo": ".showblacklist\nUsage: Lists all active userbot blacklist in a chat.\n\n.addblacklist <keyword>\nUsage: Saves the message to the 'blacklist keyword'.\nThe bot will delete to the message whenever 'blacklist keyword' is mentioned.\n\n.rmblacklist <keyword>\nUsage: Stops the specified blacklist.", "blacklistLog": "#BLACKLIST\nChat ID: %1%2%1\nWord: %1%3%1", "blacklistPermission": "I don't have permission to delete messages in this group !", @@ -73,6 +77,7 @@ "blacklistSqlLog": "Unable to run blacklist module, no SQL connection found", "blacklistText": "Give me a text", "blankBlacklist": "There are no blacklist in current chat.", + "botError": "%1Something went wrong! Check%1 %2@%3%2 %1bot for detailed information.%1", "botlist": "%1Bots in%1 %2%3%2%1:%1", "brainError": "%1Error!%1\n%2[%3](tg://user?id=%4)%2 %1is Seden admin..\nSo I can't do this.%1", "callInfo": ".call #note or .call $snip\nUsage: Used to get notes or global notes", @@ -88,7 +93,7 @@ "carbonInfo": ".carbon <text>\nUsage: Beautify your code using carbon.now.sh\nUse .crblang <text> to set language for your code.", "carbonLang": "Language for carbon.now.sh set to %1%2%1", "carbonResult": "Made using [Carbon](https://carbon.now.sh/about/)\na project by [Dawn Labs](https://dawnlabs.io/)", - "carbonUpload": "Uploading...", + "carbonUpload": "Uploading.\u2026", "chatAlreadyMuted": "Chat already muted!", "chatAlreadyUnmuted": "Chat already unmuted!", "chatInfo": ".mutechat\nUsage: Allows you to mute any chat.\n\n.unmutechat\nUsage: Unmutes a muted chat.", @@ -112,44 +117,35 @@ "ddgoInfo": ".ddgo <query>\nUsage: Does a search on DuckDuckGo.", "ddgoLog": "DuckDuckGo Search query %1 was executed successfully", "decodeFail": "Decode failed.", - "deepfryApply": "%1Applying %2fry effect to media...%1", - "deepfryDownload": "Downloading media...", + "deepfryApply": "%1Applying %2fry effect to media\u2026%1", + "deepfryDownload": "Downloading media\u2026", "deepfryError": "I can't deepfry this!", "deepfryInfo": ".deepfry or .fry [1-5]\nUsage: Applies deepfry effect to specified image.", "deepfryNoPic": "%1reply to a picture or sticker for me to %2!%1", - "deezerArlMissing": "Deezer ARL Token missing! Add it to environment vars.", + "deezerArlMissing": "Deezer ARL Token missing! Add it to config vars.", "deezerInfo": ".deezer <deezer track url>\nUsage: Downloads songs or albums from Deezer.", "delErrorLog": "Well, I can't delete a message", "delInfo": ".del\nUsage: Deletes the message you replied to.", "delResultLog": "Deletion of message was successful.", + "delWelcome": "Welcome message deleted for this chat", "delayspamLog": "#DELAYSPAM\nDelaySpam was executed successfully", "deleted": "Deleted", "deletedAcc": "Deleted Account", - "demoteProcess": "Demoting...", + "demoteProcess": "Demoting\u2026", "demoteResult": "%1[%2](tg://user?id=%3)%1 %4demoted!%4", "deviceError": "%1Couldn't find info about %2!%1", "deviceSearch": "%1Search results for %2:%1", "deviceSearchResultChild": "%1Brand:%1 %2\n%1Name:%1 %3\n%1Model:%1 %4\n\n", "deviceUsage": "Usage: .device <codename> eg: .device raphael", "directError": "Error occurred while processing %1", - "directInfo": ".direct <url>\nUsage: Reply to a link or paste a URL to\ngenerate a direct download link\n\nList of supported URLs:\nYandex.Disk - AFH - ZippyShare -\nMediaFire - SourceForge - OSDN - GitHub", + "directGdriveCookie": "This file may require login Google account to access & download.\nIf you face any problem use cookies.txt to download file.\n\n", + "directGdriveCookieUsage": "Usage: `wget --load-cookies cookies.txt` directLink", + "directInfo": ".direct <url>\nUsage: Reply to a link or paste a URL to\ngenerate a direct download link\n\nList of supported URLs:\nYandex.Disk - AFH - ZippyShare - GDrive -\nGDocs - MediaFire - SourceForge - OSDN - GitHub", "directNoSupport": "%1 not supported", "directUrlNotFound": "URL not found", "directUsage": "Usage: .direct <url>", "directZippyLink": "%1 (%2)\n[Download](%3)", - "dogbinContent": "Getting dogbin content...", - "dogbinError": "Request returned an unsuccessful status code.\n\n %1", - "dogbinInfo": ".paste <text>\nUsage: Create a paste or a shortened url using dogbin (https://del.dog/)\n\n.getpaste\nUsage: Gets the content of a paste or shortened url from dogbin (https://del.dog/)", - "dogbinPasteResult": "%1Pasted successfully!\n\nDogbin URL:%1 %2", - "dogbinPasteResult2": "%1Pasted successfully!%1\n\n%1Shortened URL:%1 %2\n\n%1Original(non-shortened) URLs%1\n%1Dogbin URL:%1 %3\n", - "dogbinPasting": "Pasting text...", - "dogbinReach": "Failed to reach Dogbin", - "dogbinResult": "%1Fetched dogbin URL content successfully!\n\nContent:%1 %2", - "dogbinTimeOut": "Request timed out. %1", - "dogbinTooManyRedirects": "Request exceeded the configured number of maximum redirections. %1", - "dogbinUrlError": "Is that even a dogbin url?", - "dogbinUsage": "Elon Musk said i can't paste void.", - "downloadMedia": "Downloading ...", + "downloadMedia": "Downloading\u2026", "downloadMediaError": "My F\u00fchrer havent set me up to download this type of media yet.", "downloadReply": "Please reply a document.", "downloadYTAudio": "%3Downloading%3 %1%2%1 %3as audio%3", @@ -159,23 +155,25 @@ "effectsInfo": ".earrape mp3 or mp4\nUsage: Applies earrape effect to your video or audio.\n\n.nightcore\nUsage: Applies nightcore effect to your audio\n\n.slowedtoperfection\nUsage: Applies slowed to perfection to your audio", "envCopySuccess": "%2Variable%2 %1%3%1 %2successfully copied to%2 %1%4%1", "envGetValue": "%2Variable:%2 %1%3%1\n%2Value:%2 %1%4%1", - "envInfo": ".env (set, get, rem, copy, move) <Key> <Value>\nUsage: It allows you to easily change variables of bot.", + "envInfo": ".env get <Variable>\nUsage: Shows the value given to specified variable.\n\n.env set <Variable> <Value>\nUsage: Sets the value given to specified variable.\n\n.env rem <Variable>\nUsage: Deletes the specified variable.\n\n.env copy <Variable 1> <Variable 2>\nUsage: Copies the variable value to be copied into the given variable.\n\n.env move <Variable 1> <Variable 2>\nUsage: Moves the variable value to be moved to target variable.\n\n.env list\nUsage: Gives the list of available variables.", "envListKeys": "%1Available Variables:%1\n%3", "envMoveSuccess": "%2Variable%2 %1%3%1 %2successfully moved to%2 %1%4%1", "envNotFound": "%2Variable%2 %1%3%1 %2not found%2", "envRemSuccess": "%2Variable%2 %1%3%1 %2successfully deleted%2", "envSetSuccess": "%2Variable%2 %1%3%1 %2successfully changed%2", - "errorLogSend": "Something went wrong. Sending logs to log group...", + "errorLogSend": "Something went wrong. Sending logs to log group\u2026", "evalLog": "Eval query %1 processed successfully", "evalUsage": "Give a statement to evaluate.", - "ezanvaktiErrorInfo": "No information was found for %1.", - "ezanvaktiKonum": "Please specify a city next to the command.", - "ezanvaktiShowInfo": "%1Prayer Times%1\n\n\ud83d\udccd %1Location:%1 %2%3%2\n\n\ud83c\udfd9 %1Fajr:%1 %2%4%2\n\ud83c\udf05 %1Tulu:%1 %2%5%2\n\ud83c\udf07 %1Zuhr:%1 %2%6%2\n\ud83c\udf06 %1Asr:%1 %2%7%2\n\ud83c\udf03 %1Maghrib:%1 %2%8%2\n\ud83c\udf0c %1Isha:%1 %2%9%2", - "fetchProxy": "Fetching proxy ...", + "exifError": "EXIF data not found.", + "exifInfo": ".exif\nUsage: Extract EXIF information from given photo file.", + "exifLog": "EXIF data for image:\n", + "exifMaps": "Google Maps Link: ", + "exifProcess": "Extracting EXIF information.", + "fetchProxy": "Fetching proxy\u2026", "filterAdded": "%2Filter%2 %1%3%1 %2added%2", "filterChats": "Filters in chat:", "filterError": "Message couldn't be forwarded and filter couldn't be added.", - "filterInfo": ".filters\nUsage: Lists all active seden userbot filters in a chat.\n\n.addfilter <keyword> <reply text> or reply to a message with .addfilter <keyword>\nUsage: Saves the replied message as a reply to the 'keyword'.\nThe bot will reply to the message whenever 'keyword' is mentioned.\nWorks with everything from files to stickers.\n\n.stop <filter>\nUsage: Stops the specified filter.", + "filterInfo": ".filters\nUsage: Lists all active filters in a chat.\n\n.addfilter <keyword> <reply text> or reply to a message with .addfilter <keyword>\nUsage: Saves the replied message as a reply to the 'keyword'.\nThe bot will reply to the message whenever 'keyword' is mentioned.\nWorks with everything from files to stickers.\n\n.stop <filter>\nUsage: Stops the specified filter.", "filterLog": "#FILTER\nChat ID: %1%2%1\nFilter: %1%3%1\n\nAbove message saved for reply filter, please don't delete!", "filterNotFound": "%2Filter%2 %1%3%1 %2not found%2", "filterRemoved": "%2Filter%2 %1%3%1 %2removed%2", @@ -183,9 +181,29 @@ "filterUpdated": "%2Filter%2 %1%3%1 %2updated%2", "filtersSqlLog": "Unable to run filters module, no SQL connection found", "founderResult": "%1=======================================\n\nThis bot;\nDeveloped by%1 %2[NaytSeyd](https://t.me/NightShade)%2 %1and%1 %2[frknkrc44](https://t.me/KaldirimMuhendisi)%2\n%1In addition\nLovingly edited by%1 %2[Sedenogen](https://t.me/CiyanogenOneTeams)%2\n\n%1=======================================%1", - "gbanLog": "%1#GBAN\nUSER: [%2](tg://user?id=%3)", - "gbanResult": "%1[%2](tg://user?id=%3)%1 (%4%3%4) %4globally banned!%4", + "gauthFirstRun": "First try .gauth command for this operation.", + "gauthTokenErr": "There is a error in link you sending.Try .gauth command", + "gauthTokenInvalid": "Invalid code.Reuse .gauth command for generating new code.", + "gauthTokenRevoke": "Saved token removed succesfully.", + "gauthTokenSuccess": "Token saved successfully.", + "gauthURL": "%1Log in via the link below and use the received link with example command%1\n%1Example:%1 %2.gauth token <URL>%2\n\n", + "gayString": "\ud83c\udff3\ufe0f\u200d\ud83c\udf08 %1 is %2\u00bd gay!", + "gayString2": "\ud83c\udff3\ufe0f\u200d\ud83c\udf08 This person is %1\u00bd gay!", + "gayString3": "\ud83c\udff3\ufe0f\u200d\ud83c\udf08 I am %1\u00bd gay!", + "gbanLog": "#GBAN\nUSER: [%1](tg://user?id=%2) (%3%2%3)\n%4", + "gbanResult": "%1[%2](tg://user?id=%3)%1 %4globally banned!%4\n%5", + "gbannedUsers": "GBanned Users:", + "gdriveAuth": "Gdrive token is already in database.Token is refreshing...", + "gdriveCopyErr": "Gdrive folder not supported,only support files.", + "gdriveCopyFile": "File copied successfully.", + "gdriveDown": "%1Filename:%1 %2%3%2\n%1Downloading:%1 %2%4%2", + "gdriveDownComplete": "%1Filename:%1 %2%3%2\n%1File Downloaded.%1", + "gdriveTokenErr": "%1Reply a token file%1", + "gdriveUp": "%1Filename:%1 %2%3%2\n%1Uploading:%1 %2%4%2", + "gdriveUpComplete": "%1Filename:%1 %2%3%2\n%1File Uploaded.%1", + "gdriveUsage": ".gauth\nIt needed for authentication.It give you auth url.\n\n.gauth token <URL>\nGive the url you received from .gauth command for saving token\n\n.gauth revoke\nDelete token from db\n\n.gupload <reply_message> or <link>\nReply files or paste link for uploading to google drive.\n\n.gdownload <link>\nPaste link for uploading files to telegram.Limit 2GB", "geniusToken": "Please set the Genius token. Thank you!", + "getPasteOut": "%1Fetched Pastebin URL content successfully!\n\nContent:%1 %2", "gitAccount": "Account Type", "gitBio": "Bio", "gitCompany": "Company", @@ -209,55 +227,64 @@ "gitUserInfo": "%1 GitHub information", "gitUserNotFound": "User not found.", "gitWebsite": "Website", - "gmuteLog": "%1#GMUTE\nUSER: [%2](tg://user?id=%3)", - "gmuteResult": "%1[%2](tg://user?id=%3)%1 %4globally muted!%4", + "globalsInfo": ".gban <username/reply>\nUsage: Bans the person in all groups you have in common with them.\n\n.ungban <username/reply>\nUsage: Removes the person from the gbanned list\n\n.listgban\nUsage: List GBanned users.\n\n.gmute <username/reply>\nUsage: Mutes the person in all groups you have in common with them.\n\n.ungmute <username/reply>\nUsage: Removes the person from the gmuted list\n\n.listgmute\nUsage: List GMuted users.", + "globalsSqlLog": "Unable to run GBan and GMute command, no SQL connection found", + "gmuteLog": "#GMUTE\nUSER: [%1](tg://user?id=%2) (%3%2%3)\n%4", + "gmuteResult": "%1[%2](tg://user?id=%3)%1 %4globally muted!%4\n%5", + "gmutedUsers": "GMuted Users:", + "goodbyeMsg": "Goodbye\u2026", "googleDesc": "No description found.", "googleInfo": ".google <query>\nUsage: Does a search on Google.", "googleLog": "Google Search query %1 was executed successfully", "googleResult": "%1Search Query:%1\n%2%3%2\n\n%1Results:%1\n%4", + "groupInfo": ".whois <username/user id>\nUsage: Retrieves user's information.\n\n.ginfo [optional: <group id/group link (with @)>]\nUsage: Gives information about the group.", + "groupNotFound": "Chat not found!", + "groupPicChanged": "Group picture changed successfully..", "groupUsage": "This command can only be used on groups.", - "hashInfo": ".hash\nUsage: Find the md5, sha1, sha256, sha512 of the string when written into a txt file.", + "groupinfoResult": "%1GROUP INFO\n\nGroup Name:%1 %2%3%2\n%1Group ID:%1 %2%4%2\n%1DC ID:%1 %2%5%2\n%1Group Type:%1 %2%6%2\n%1Members:%1 %2%7%2\n%1Online Members:%1 %2%8%2\n%1Group Sticker Pack:%1 %9\n\n%1Group Link:%1 %10\n%1Group Description:%1 %2%11%2", "herokuInfo": ".quota\nUsage: It shows your Heroku resource & dyno usage.\n\n.drestart\nUsage: Restarts your Heroku dyno.\n\n.logs\nUsage: Sends Heroku app log.", "herokuQuotaInHM": "%1h %2m", "herokuQuotaInfo": "%2Heroku Remaining Quota%2\n\n%2Total:%2 %1%3%1\n%2Used:%2 %1%4 (%5\u00bd)%1\n%2Remaining:%2 %1%6 (%7\u00bd)%1\n%2App usage:%2 %1%8 (%9\u00bd)%1", + "imeiInfo": ".imeicheck <imeinumber>\nUsage: Check if IMEI is registered and get device information about model, vendor, etc.", "imgInfo": ".img <query>\nUsage: Does an image search on Google and shows 5 images.", "imgUsage": "You must enter a search term.", "infoWeather": ".weather <city> or .weather\nUsage: Gets the weather of a city.", - "kangstr1": "Using Witchery to kang this sticker...", - "kangstr10": "Mr.Steal Your Sticker is stealing this sticker... ", - "kangstr11": "Finally I'm kanging a sticker that Ecem will love...", - "kangstr12": "Ecem, this kang is for you...", - "kangstr2": "Plagiarising hehe...", - "kangstr3": "Inviting this sticker over to my pack...", - "kangstr4": "Kanging this sticker...", + "invalidProcess": "Invalid arg!", + "invalidUsername": "Enter a valid username", + "kangstr1": "Using Witchery to kang this sticker\u2026", + "kangstr10": "Mr.Steal Your Sticker is stealing this sticker\u2026", + "kangstr11": "Finally I'm kanging a sticker that Ecem will love\u2026", + "kangstr12": "Ecem, this kang is for you\u2026", + "kangstr2": "Plagiarising hehe\u2026", + "kangstr3": "Inviting this sticker over to my pack\u2026", + "kangstr4": "Kanging this sticker\u2026", "kangstr5": "Hey that's a nice sticker!\nMind if I kang?!..", "kangstr6": "hehe me stel ur stik\u00e9r\nhehe.", - "kangstr7": "Ay look over there (\u2609\uff61\u2609)!\u2192\nWhile I kang this...", + "kangstr7": "Ay look over there (\u2609\uff61\u2609)!\u2192\nWhile I kang this\u2026", "kangstr8": "Roses are red violets are blue, kanging this sticker so my pacc looks cool", - "kangstr9": "Imprisoning this sticker...", - "kickLog": "%1#KICK\nUSER: [%2](tg://user?id=%3)\nGROUP: %4 (%1%5%6%5%1)%1", - "kickProcess": "Kicking...", - "kickResult": "%1[%2](tg://user?id=%3)%1 %4kicked!%4", + "kangstr9": "Imprisoning this sticker\u2026", + "kickLog": "#KICK\nUSER: [%1](tg://user?id=%2) (%4%2%4)\nGROUP: %3 (%4%5%4)\n%6", + "kickProcess": "Kicking\u2026", + "kickResult": "%1[%2](tg://user?id=%3)%1 %4kicked!%4\n%5", "kickmeResult": "Goodbye.. i go away \ud83e\udd20", + "ksuReleases": "Latest KernelSU APKs:", "langName": "English", - "lastfmApiMissing": "%1[Last.fm](https://www.last.fm/api/account/create)%1 %2API key missing! Add it to environment vars.%2", + "lastfmApiMissing": "%1[Last.fm](https://www.last.fm/api/account/create)%1 %2API key missing! Add it to config vars.%2", "lastfmInfo": ".lastfm\nUsage: Shows currently scrobbling track or most recent scrobbles if nothing is playing.", "lastfmProcess": "[%1](%2) %3is now listening to:%3\n\n\u2022 [%4](%5)\n%6%7%6", "lastfmProcess2": "[%1](%2) %3was last listening to:%3\n\n", "lfyResult": "Here you are, help yourself.", - "loadedModules": "Modules to be loaded: %1", - "loadedModules2": "Loaded module: %1", + "listEmpty": "The list is empty!", + "loadedModules": "Modules to be loaded:\n%1", "loadedModulesError": "An error occurred while loading %1", "lockAll": "all", "lockError": "%1Invalid media type:%1 %2", - "lockGame": "game", - "lockGif": "gif", - "lockInfo": ".lock <all (or) type(s)> or .unlock <all (or) type(s)>\nUsage: Allows you to lock/unlock some common message types in the chat.\n[NOTE: Requires proper admin rights in the chat!]\n\nAvailable message types to lock/unlock are:\nall, msg, media, sticker, gif, game, inline, web, poll, invite, pin, info", + "lockInfo": ".lock <all (or) type(s)> or .unlock <all (or) type(s)>\nUsage: Allows you to lock/unlock some common message types in the chat.\n[NOTE: Requires proper admin rights in the chat!]\n\nAvailable message types to lock/unlock are:\nall, msg, media, other, web, poll, invite, pin, info", "lockInformation": "info", - "lockInline": "inline", "lockInvite": "invite", "lockMedia": "media", "lockMsg": "message", + "lockOther": "GIF, games, inline bots and sticker", "lockPerm": "%1Are you sure you have the rights to do this?%1\n%2Error:%2 %3", "lockPin": "pin", "lockPoll": "poll", @@ -267,42 +294,36 @@ "locksUnlockNoArgs": "I can't unlock void bruh", "locksUnlockSuccess": "%1unlocked %2 for this chat!%1", "logidTest": "This is a test report, it is for LOG_ID check.", - "lydiaError": "Invalid user type.", - "lydiaError2": "Reply to a user to activate Lydia AI on them", - "lydiaError3": "This person doesn't have Lydia activated on him/her.", - "lydiaInfo": ".addcf <username/reply>\nUsage: Add's lydia auto chat request in the chat.\n\n.remcf <username/reply>\nUsage: Remove's lydia auto chat request in the chat.\n\n.repcf <username/reply>\nUsage: Starts lydia repling to perticular person in the chat.", - "lydiaMissingApi": "%1[Lydia](https://coffeehouse.intellivoid.info/dashboard)%1 %2API key missing! Add it to environment vars.%2", - "lydiaResult": "%1Hey dude:%1 %2", - "lydiaResult2": "%1Lydia successfully enabled for user:%1 %2%3%2 %1in chat:%1 %2%4%2", - "lydiaResult3": "%1Lydia successfully disabled for user:%1 %2%3%2 %1in chat:%1 %2%3%2", - "lydiaSqlLog": "Unable to run lydia module, no SQL connection found", + "losBuild": "%1Latest LineageOS for%1 %2%3%2%1:\nFilename: [%4](%5)\nSize:%1 %2%6%2\n%1Version:%1 %2%7%2\n%1Date:%1 %2%8%2\n\n%2For other builds:%2\n%1https://download.lineageos.org/%3%1", + "losNoBuild": "%1No suitable version found for%1 %2%3%2%1!%1", "lyricsError": "Error: please use '-' as divider for <artist> and <song>\neg: Adele - Hello", "lyricsError2": "Please give the artist and song name", "lyricsInfo": ".lyrics\nUsage: .lyrics <artist> - <song>\nNOTE: '-' separator is important!", "lyricsNotFound": "Song %1%2 - %3%1 not found!", "lyricsOutput": "Lyrics is too big, view the file to see it.", "lyricsQuery": "%1Search query:%1 \n%2%3 - %4%2\n\n%1Lyrics:%1\n%5", - "lyricsSearch": "%1Searching lyrics for %2 - %3...%1", + "lyricsSearch": "%1Searching lyrics for %2 - %3\u2026%1", "magiskReleases": "Latest Magisk Releases:", - "makeQuote": "Making a Quote ...", + "makeQuote": "Making a Quote\u2026", "makeqrInfo": ".makeqr <text>\nUsage: Make a QR code from provided content.\nEg: .makeqr https://devotag.com\n\nNote: Use .decode command to get decoded content.", "makeqrUsage": "%1Usage:%1 %2.makeqr <text>%2", "mediaInvalid": "Invalid media.", - "memesInfo": ".cowsay\nUsage: cow which says things.\n\n:/\nUsage: Check yourself ;)\n\n.cp\nUsage: Copypasta the famous meme\n\n.vapor\nUsage: Vaporize everything!\n\n.str\nUsage: Stretch it.\n\n.10iq\nUsage: You retard !!\n\n.mizah\nUsage: Measure your stupidity !!\n\n.zal\nUsage: Invoke the feeling of chaos.\n\noof\nUsage: Ooooof\n\nskrrt\nUsage: skrrrrt\n\n.owo\nUsage: UwU\n\n.react\nUsage: Make your seden userbot react to everything.\n\n.cry\nUsage: y u du dis, i cri.\n\n.shg\nUsage: \ud83e\udd37\ud83c\udffb\u200d\u2642\ufe0f\n\n.run\nUsage: Let Me Run, run, RUNNN!\n\n.mock\nUsage: Do it and find the real fun.\n\n.clap\nUsage: Praise people!\n\n.f <emoji/character>\nUsage: Pay Respects.\n\n.type\nUsage: Just a small command to make your keyboard become a typewriter!\n\n.lfy <query>\nUsage: Let me Google that for you real quick !!\n\n.xda or reply to someone's text with .xda\nUsage: Famous words of XDA.", + "memesInfo": ".cowsay\nUsage: cow which says things.\n\n:/\nUsage: Check yourself ;)\n\n.cp\nUsage: Copypasta the famous meme\n\n.vapor\nUsage: Vaporize everything!\n\n.str\nUsage: Stretch it.\n\n.10iq\nUsage: You retard !!\n\n.mizah\nUsage: Measure your stupidity !!\n\n.zal\nUsage: Invoke the feeling of chaos.\n\noof\nUsage: Ooooof\n\nskrrt\nUsage: skrrrrt\n\n.owo\nUsage: UwU\n\n.react\nUsage: Make your bot react to everything.\n\n.cry\nUsage: y u du dis, i cri.\n\n.shg\nUsage: \ud83e\udd37\ud83c\udffb\u200d\u2642\ufe0f\n\n.run\nUsage: Let Me Run, run, RUNNN!\n\n.mock\nUsage: Do it and find the real fun.\n\n.clap\nUsage: Praise people!\n\n.f <emoji/character>\nUsage: Pay Respects.\n\n.type\nUsage: Just a small command to make your keyboard become a typewriter!\n\n.lfy <query>\nUsage: Let me Google that for you real quick !!\n\n.xda or reply to someone's text with .xda\nUsage: Famous words of XDA.\n\n.amogus <text>\nUsage: Converts typed text to amogus\n\n.mem <top text>,<bottom text>\nUsage: Make meme with top and bottom text", "mirrorError": "Error: Different mirror not found for the link", - "miscInfo": ".id\nUsage: Fetches the ID of the user in reply, if its a forwarded message, finds the ID for the source.\n\n.chatid\nUsage: Fetches the current chat/'s ID\n\n.kickme\nUsage: Leave from a targeted group.\n\n.repeat <count> <text>\nUsage: Repeats the text for a number of times. Don't confuse this with spam tho.\n\n.random <item1> <item2> ... <itemN>\nUsage: Get a random item from the list of items.\n\n.founder\nUsage: Know who created this awesome bot :-)\n\n.support\nUsage: Use this command if you need help.\n\n.repo\nUsage: Seden UserBot GitHub Repo\n\n.readme\nUsage: A link to the README file of the Seden bot on GitHub.\n\n.tagall\nUsage: When you use this command, it tags everyone in the chat.\n\n.admin\nUsage: When you use this command, it tags all admins in the chat.", + "miscInfo": ".id\nUsage: Fetches the ID of the user in reply, if its a forwarded message, finds the ID for the source.\n\n.chatid\nUsage: Fetches the current chat's ID\n\n.kickme\nUsage: Leave from a targeted group.\n\n.repeat <count> <text>\nUsage: Repeats the text for a number of times. Don't confuse this with spam tho.\n\n.random <item1> <item2> \u2026 <itemN>\nUsage: Get a random item from the list of items.\n\n.founder\nUsage: Know who created this awesome bot :-)\n\n.support\nUsage: Use this command if you need help.\n\n.repo\nUsage: Seden UserBot GitHub Repo\n\n.readme\nUsage: A link to the README file of the Seden bot on GitHub.\n\n.tagall\nUsage: When you use this command, it tags everyone in the chat.\n\n.admin\nUsage: When you use this command, it tags all admins in the chat.\n\n.hash\nUsage: Find the md5, sha1, sha256, sha512 of the string when written into a txt file.\n\n.base64\nUsage: Find the base64 encoding of the given string. Eg: .base64 en hello\n\n.ascii\nUsage: Sends the replied photo or sticker in ASCII.\n\n.invitelink\nUsage: Gives invite link on current chat.", "mockUsage": "gIvE sOMEtHInG tO MoCk!", - "muteLog": "%1#MUTE\nUSER: [%2](tg://user?id=%3)\nGROUP: %4 (%1%5%6%5%1)%1", + "muteLog": "#MUTE\nUSER: [%1](tg://user?id=%2) (%4%2%4)\nGROUP: %3 (%4%5%4)\n%6", "muteProcess": "Gets a tape!", - "muteResult": "%1[%2](tg://user?id=%3)%1 %4muted!%4", + "muteResult": "%1[%2](tg://user?id=%3)%1 %4muted!%4\n%5", "nameOk": "Your name was succesfully changed.", - "nasaResult": "Connecting..", - "nasaResult2": "Signal Lost....", "neofetchNotFound": "Please install neofetch.", "noFilter": "There are no filters in this chat.", "noNote": "No notes found in this chat", "noSnip": "No snip is currently available.", + "noWelcome": "No welcome message found in this chat", + "noWelcome2": "I didn't have any welcome messages here!", "nonSqlMode": "Running on Non-SQL mode!", + "notFound": "Not Found", "notHeroku": "Bot is not running on Heroku", "notSet": "Not set!", "noteError": "Message couldn't be forwarded and note couldn't be added.", @@ -317,17 +338,18 @@ "notesRemoved": "%2Note%2 #%1%3%1 %2removed%2", "notesSqlLog": "Unable to run notes module, no SQL connection found", "notesUpdated": "%1Note successfully updated. You can call the note with .call #%2%1", - "ocrApiMissing": "%1[%2](https://ocr.space/ocrapi)%1 %3API key missing! Add it to environment vars.%3", + "ocrApiMissing": "%1[%2](https://ocr.space/ocrapi)%1 %3API key missing! Add it to config vars.%3", "ocrError": "Couldn't read this.\nI guess i need new glasses.", "ocrInfo": ".ocr <language>\nUsage: Reply to an image or sticker to extract text from it.\n\nGet language codes from [here](https://ocr.space/ocrapi)", - "ocrReading": "Reading...", + "ocrReading": "Reading\u2026", "ocrResult": "%1Here's what I could read from it:%1\n\n%2", - "ofrpConnect": "Connecting to OrangeFox servers ...", + "ofrpConnect": "Connecting to OrangeFox servers\u2026", "ofrpError": "This list is probably empty.", "ofrpErrorDate": "An error occurred due to pull date", "ofrpNotFound": "%1%2 codename probably doesn't belong to an official device. You can check it at%1 %3", "ofrpUrl": "https://orangefox.download/en", "ofrpUsage": "Usage: .orangefox <codename> eg: .orangefox raphael", + "onlySupportGdrive": "Only support Google drive link.", "outputTooLarge": "Output is too large, sending as a file.", "owoUsage": "UwU no text given!", "packFull": "%1Switching to Pack%1 %2%3%2 %1due to insufficient space..%1", @@ -335,12 +357,13 @@ "packinfoError": "I can't fetch info from nothing, can I ?!", "packinfoProcess": "Fetching details of the sticker pack, please wait..", "packinfoResult": "%1Sticker Title:%1 %2%3%2\n%1Sticker Short Name:%1 %2%4%2\n%1Official:%1 %2%5%2\n%1Archived:%1 %2%6%2\n%1Animated:%1 %2%7%2\n%1Stickers In Pack:%1 %2%8%2\n%1Emojis In Pack:%1\n%9", + "pasteConnErr": "Could not connect to server.", + "pasteErr": "Enter text for paste.", + "pasteInfo": ".paste <text/reply>\nUsage: Pastes the given text on the Pastebin\n\n.getpaste <text/reply>\nUsage: Gets the content of a paste url from Pastebin\n\n(https://dpaste.org/)", "phhError": "%1Couldn't find anything for%1 %2%3%2", "picspamLog": "#PICSPAM\nPicSpam was executed successfully", - "pinLog": "%1#PIN\nGROUP: %2 (%1%3%4%3%1)%1", + "pinLog": "#PIN\nGROUP: %1 (%2%3%2)", "pinResult": "Pinned Successfully!", - "pipHelp": "Use .seden system command to see an example.", - "pipSearch": "Searching...", "pmApproveError": "You're not currently in a PM and you haven't reply someone's message.", "pmApproveError2": "User may already be approved.", "pmApproveLog": "#APPROVED\nUser: [%1](tg://user?id=%2)", @@ -354,62 +377,66 @@ "pmUnblocked": "You've been unblocked.", "pmUnblockedLog": "[%1](tg://user?id=%2) was unblocc'd!", "pmUnblockedUsage": "You should reply message of the person you are unblocking.", - "pmpermitBlock": "You were spamming my F\u00fchrer's PM, which I don't like.\nI'mma block you.", + "pmpermitBlock": "You were spamming my F\u00fchrer's PM, which I don't like.\nI'mma block you and reporting as SPAM.", "pmpermitInfo": ".approve\nUsage: Approves the replied person to PM.\n\n.disapprove\nUsage: Disapproves the replied person to PM.\n\n.block\nUsage: Blocks the person.\n\n.unblock\nUsage: Unblocks the person so they can PM you.\n\n.notifoff\nUsage: Clears/Disables any notifications of unapproved PMs.\n\n.notifon\nUsage: Allows notifications for unapproved PMs.", "pmpermitLog": "[%1](tg://user?id=%2) was just another retarded nibba", "pmpermitMessage": "%1Bleep blop! This is a bot. Don't fret.\n\nMy F\u00fchrer hasn't approved you to PM.\nPlease wait for my F\u00fchrer to look in, he mostly approves PMs.\n\nAs far as I know, he doesn't usually approve retards though.%1", "pmpermitSqlLog": "Unable to run pmpermit module, no SQL connection found", + "ppChanged": "Profile picture changed successfully..", + "ppDeleted": "%1 profile photos were deleted", "ppError": "Failure occured while processing image.", - "ppchanged": "Profile picture changed successfully..", + "ppSmall": "This image is too small!", "privacySettings": "Privacy settings prevented me from doing this.", "privateUsage": "This command can only be used private chats.", - "processing": "Processing...", - "profileInfo": ".username <new username>\nUsage: Changes your Telegram username.\n\n.name <firstname> or .name <firstname> <lastname>\nUsage: Changes your Telegram name.(First and last name will get split by the first space)\n\n.setbio <new bio>\nUsage: Changes your Telegram bio.\n\n.reserved\nUsage: Shows usernames reserved by you.\n\n.block\nUsage: Blocks the person.\n\n.unblock\nUsage: Unblocks the person so they can PM you.", - "promoteLog": "%1#PROMOTE\nUSER: [%2](tg://user?id=%3)\nGROUP: %4 (%1%5%6%5%1)%1", - "promoteProcess": "Promoting...", + "processing": "Processing\u2026", + "profileInfo": ".username <new username>\nUsage: Changes your Telegram username.\n\n.name <firstname> or .name <firstname> <lastname>\nUsage: Changes your Telegram name.(First and last name will get split by the first space)\n\n.setbio <new bio>\nUsage: Changes your Telegram bio.\n\n.reserved\nUsage: Shows usernames reserved by you.\n\n.block\nUsage: Blocks the person.\n\n.unblock\nUsage: Unblocks the person so they can PM you.\n\n.online <disable>\n\nUsage: It shows your account online even if you are not online on Telegram.\nType .online to turn it on, .online disable to turn it off.\n\nNOTE: Make your last seen settings public to be effective.", + "promoteLog": "#PROMOTE\nUSER: [%1](tg://user?id=%2)\nGROUP: %3 (%4%5%4)", + "promoteProcess": "Promoting\u2026", "promoteResult": "%1[%2](tg://user?id=%3)%1 %4promoted!%4", - "providedProxy": "Proxy connection provided ...", - "purgeError": "%1Something went wrong...%1\n\n%2%3%2", + "providedProxy": "Proxy connection provided\u2026", + "purgeError": "%1Something went wrong\u2026%1\n\n%2%3%2", "purgeInfo": ".purge\nUsage: Purges all messages starting from the reply.", "purgeLog": "%1Purge of%1 %2%3%2 %1messages done successfully.%1", "purgeResult": "%1Purge complete!\nPurged%1 %2%3%2 %1messages.%1", "purgeUsage": "I need a message to start purging from.", "purgemeInfo": ".purgeme <X>\nUsage: Deletes x amount of your latest messages.", "purgemeUsage": "Purgeme failed, please specify number.", + "pyrogramDown": "%1Filename:%1 %2%3%2\n%1Downloading:%1 %2%4%2", + "pyrogramUp": "%1Filename:%1 %2%3%2\n%1Uploaded%1 %2%4%2", "pythonVersionError": "You must have at least a Python version 3.8\nMultiple features depend on this. Bot quitting.", "quotlyInfo": ".q\nUsage: Enhance ur text to sticker.", "randomResult": "%1Query:%1\n%2%3%2\n%1Output:%1\n%2%4%2", "randomUsage": "2 or more argument required.", - "rbgApiMissing": "%1[%2](https://www.remove.bg/api)%1 %3API key missing! Add it to environment vars.%3", + "rbgApiMissing": "%1[%2](https://www.remove.bg/api)%1 %3API key missing! Add it to config vars.%3", "rbgInfo": ".rbg reply to any image (Warning: does not work on stickers.)\nUsage: Removes the background of images, using remove.bg API", - "rbgLog": "error.log", + "rbgLog": "error.txt", "rbgProcessing": "Removing background from this image..", "rbgResult": "Background removed using Remove.bg", - "rbgUsage": "Reply to an image...", - "removeFirstLine": "Please remove the first specified line from config.env file", + "rbgUsage": "Reply to an image\u2026", "repeatInfo": ".repeat <no.> <text>\nUsage: Repeats the text for a number of times. Don't confuse this with spam tho.", "replyMessage": "Reply to a message.", "replySticker": "Reply a sticker.", - "restart": "BRB... *PornHub intro*", + "reqDown": "%1Filename:%1 %2%3%2\n%1Downloading:%1 %2%%4%2", + "restart": "Restarting\u2026", "restartLog": "#RESTART\nBot restarted", "reverseError": "Unsupported type", "reverseError2": "I couldn't find anything for your ugly ass.", "reverseGoogle": "Google told me to fuck off", "reverseInfo": ".reverse\nUsage: Reply to a pic/sticker to revers-search it on Google Images !", "reverseProcess": "Image successfully uploaded to Google. Parsing source now. Maybe.", - "reverseResult": "[%1](%2)\n%3\n\nLooking for a picture...\n%3", + "reverseResult": "[%1](%2)\n%3\n\nLooking for a picture\u2026\n%3", "reverseResult2": "[%1](%2)\n\n[Similar images](%3)", "reverseUsage": "Please reply to a photo or sticker.", "rgbInfo": ".rgb\nUsage: Text and generate rgb sticker.", - "rgbProcessing": "Converting to rgb sticker...", + "rgbProcessing": "Converting to rgb sticker\u2026", "runningBot": "Your bot is running! You can test it by typing .alive in any chat. You can get the list of modules by typing .seden If you need help, you can check out our support group https://t.me/%1", "runstr1": "Where do you think you're going?", - "runstr10": "You're gonna regret that...", + "runstr10": "You're gonna regret that\u2026", "runstr11": "You could also try /kickme, I hear that's fun.", "runstr12": "Go bother someone else, no-one here cares.", "runstr13": "You can run, but you can't hide.", "runstr14": "Is that all you've got?", - "runstr15": "I'm behind you...", + "runstr15": "I'm behind you\u2026", "runstr16": "You've got company!", "runstr17": "We can do this the easy way, or the hard way.", "runstr18": "You just don't get it, do you?", @@ -424,8 +451,8 @@ "runstr26": "\"Oh, look at me! I'm so cool, I can run from a bot!\" - this person", "runstr27": "Yeah yeah, just tap /kickme already.", "runstr28": "Here, take this ring and head to Mordor while you're at it.", - "runstr29": "Legend has it, they're still running...", - "runstr3": "ZZzzZZzz... Huh? what? oh, just them again, nevermind.", + "runstr29": "Legend has it, they're still running\u2026", + "runstr3": "ZZzzZZzz\u2026 Huh? what? oh, just them again, nevermind.", "runstr30": "Unlike Harry Potter, your parents can't protect you from me.", "runstr31": "Fear leads to anger. Anger leads to hate. Hate leads to suffering. If you keep running in fear, you might be the next Vader.", "runstr32": "Multiple calculations later, I have decided my interest in your shenanigans is exactly 0.", @@ -439,26 +466,19 @@ "runstr4": "Get back here!", "runstr40": "Ah, what a waste. I liked that one.", "runstr41": "Frankly, my dear, I don't give a damn.", - "runstr42": "My milkshake brings all the boys to yard... So run faster!", + "runstr42": "My milkshake brings all the boys to yard\u2026 So run faster!", "runstr43": "You can't HANDLE the truth!", - "runstr44": "A long time ago, in a galaxy far far away... Someone would've cared about that. Not anymore though.", - "runstr45": "Hey, look at them! They're running from the inevitable banhammer... Cute.", + "runstr44": "A long time ago, in a galaxy far far away\u2026 Someone would've cared about that. Not anymore though.", + "runstr45": "Hey, look at them! They're running from the inevitable banhammer\u2026 Cute.", "runstr46": "Han shot first. So will I.", "runstr47": "What are you running after, a white rabbit?", - "runstr48": "As The Doctor would say... RUN!", - "runstr5": "Not so fast...", + "runstr48": "As The Doctor would say\u2026 RUN!", + "runstr5": "Not so fast\u2026", "runstr6": "Look out for the wall!", "runstr7": "Don't leave me alone with them!!", "runstr8": "You run, you die.", "runstr9": "Jokes on you, I'm everywhere", "safeEval": "This may not be a safe eval query.", - "sangmataInfo": ".sangmata\nUsage: View the name history of specified user.", - "scraper1": "Translator", - "scraper2": "Text to Speech", - "scraperLog": "%1Language for %2 changed to %3.%1", - "scraperResult": "%1Language for %2 changed to %3.%1", - "scraperTrt": "%1Invalid Language code !%1\n%1Available language codes for TRT%1:\n\n%1%2%1", - "scraperTts": "%1Invalid Language code !%1\n%1Available language codes for TTS%1:\n\n%1%2%1", "sedError": "I don't have brains. Well you too don't I guess.", "sedError2": "That's a reply. Don't use sed", "sedInfo": "sed<delimiter><old word(s)><delimiter><new word(s)>\nUsage: Replaces a word or words using sed.\nDelimiters: /, :, |, _", @@ -466,22 +486,25 @@ "sedResult": "Did you mean ? \n\n%1", "sedenAlive": "Hello Seden! I am alive \u2764\ufe0f", "sedenErrorResult": "Query could not be returned / wrong", - "sedenErrorText": "%1SEDENBOT ERROR REPORT%1\nIf you want to, you can report it\njust forward this message to %2.\nNothing is logged except the fact of error and date\n", - "sedenErrorText2": "========== DISCLAIMER ==========\nThis file uploaded only here,\nwe logged only fact of error and date,\nour respect your privacy,'\nyou may not report this error if you've\nany confidential data here, no one will see your data.\n================================\n\n--------BEGIN SEDENBOT TRACEBACK LOG--------\n\nDate: %1\nChat ID: %2\nSender ID: %3\nSeden version: %4\n\nError Trigger:\n%5\n\nTraceback info:\n%6\n\nError text:\n%7\n\n--------END SEDENBOT TRACEBACK LOG--------\n\n\nLast 10 commits:\n", + "sedenErrorText": "%1ERROR:%1\n\n%2%3%2\n\n%1See the Log File for details.%1", + "sedenErrorText2": "-------- DETAILS --------\n\nDate: %1\nChat ID: %2\nSender ID: %3\nSeden version: %4\n\nError Trigger:\n%5\n\nTraceback info:\n%6\n\nError text:\n%7\n\n----------------------\n\n\nLast 10 commits:\n", "sedenGitNotFound": "btw, Seden loves u \u2764\ufe0f", "sedenNearestDC": "%1Country:%1 %2%3%2\n%1Nearest Datacenter:%1 %2%4%2\n%1Current Datacenter:%1 %2%5%2", "sedenQuery": "%1Query:%1\n%2%3%2\n%1Result:%1\n%2%4%2\n", "sedenQueryUd": "%1Query:%1\n%2%3%2\n%1Meaning:%1\n%2%4%2\n\n%1Example:%1\n%2%5%2", "sedenSetAlive": "New alive message is %1", - "sedenShowBotVersion": "%1[%3](https://t.me/%4)%1\n%1Version:%1 %2v%5%2\n%1Commits:%1 %2%6%2", + "sedenShowBotVersion": "%1[Seden UserBot](https://t.me/%3)%1\n%1Version:%1 %2v%4%2\n%1Commits:%1 %2%5%2", "sedenShowLoadedModules": "%1Loaded modules:%1 %2%3%2\n%1Modules:%1", "sedenUsage": "Please specify a Seden module.", "sedenUsage2": "%1Please indicate which Seden module you want help with!\nUsage:%1 %2.seden <module name>%2", "sedenVersion": "Bot version; Seden v%1", - "sedenZeroResults": "Nothing found.", - "shutdown": "Goodbye *Windows XP shutdown sound*...", + "shippingMovements": "\n\n%1%3Last movement%4%2\n\n%5Unit: %7\nStatus: %8\nDate: %9\nTime: %10\nAction: %11%6", + "shippingNoResult": "Tracking information not found!", + "shippingResult": "%1Company:%2 %3%5%4\n%1Track ID:%2 %3%6%4\n%1Status:%2 %3%7%4\n%1Sender:%2 %3%8%4\n%1Receiver:%2 %3%9%4\n%1Sender Unit:%2 %3%10%4\n%1Receiver Unit:%2 %3%11%4\n%1Sent Date:%2 %3%12%4\n%1Delivered Date:%2 %3%13%4", + "shippingTrack": ".<company> <trackNo>\n\nUsage: Shows shipping information.\n\nExample: `.ups 1234567890`\n\nAllowed companies:\n`mng, ptt, ups, aras, surat, yurtici, hepsijet`", + "showWelcome": "I'm currently welcoming new users with this welcome message", + "shutdown": "Goodbye *Windows XP shutdown sound\u2026", "shutdownLog": "#SHUTDOWN\nBot shut down", - "smallingResult": "Getting smaller...", "snipChats": "Available snips:", "snipError": "Message couldn't be forwarded and snip couldn't be added.", "snipError2": "There was a problem fetching a snip!", @@ -494,9 +517,10 @@ "snipsRemoved": "%2Snip%2 $%1%3%1 %2removed%2", "snipsSqlLog": "Unable to run snips module, no SQL connection found", "snipsUpdated": "%1Snip successfully updated. You can call the snip with .call $%2%1", - "solarResult": "Moon and sun", "spamInfo": ".tspam <text>\nUsage: Spam the text letter by letter\n\n.spam <count> <text>\nUsage: Floods text in the chat\n\n.picspam <count> <url>\nUsage: As if text spam was not enough\n\n.delayspam <delay> <count> <text>\nUsage: .spam but with custom delay\n\n\nNOTE: Spam at your own risk ! !", "spamLog": "#SPAM\nSpam was executed successfully", + "spamWatchBan": "#SPAMWATCH\nReported user [%1](tg://user?id=%2) banned successfully", + "spamWatchInfo": "Works automatically when you entered a SpamWatch key", "spamWrong": "Something seems missing / wrong.", "specsError": "There is no information about this device or You made too many requests.", "specsError2": "Couldn't get information", @@ -506,41 +530,44 @@ "speedtestInfo": ".speedtest\nUsage: Does a speedtest and shows results.", "speedtestResultDoc": "%1SpeedTest%1 completed in %2 seconds!", "speedtestResultText": "%1SpeedTest%1 completed in %2 seconds!\nDownload: %3\nUpload: %4\nPing: %5\nISP: %6\nISP rating: %7\n%8", + "spotifyInfo": "Shows user information\n.spoti|spotify show <username>\n\nDownloads and seding playlist as zip\n.spoti|spotify dl zip <playlisturl>\n\nDownloads and send playlist as without zip.\n.spoti|spotify dl <playlisturl>", + "spotifyResult": "%1Username:%1 %2%3%2\n%1Profile URL: %4\nPlaylist(s):%1\n%2%5%2\n\n%2%6%2 %1playlists found!%1", "ssInfo": ".ss <url>\nUsage: Takes a screenshot of a website and sends the screenshot.\nExample of a valid URL: https://devotag.com", - "ssResult": "Generating screenshot of the page...\nHeight of page = %1px\nWidth of page = %2px\nWaiting %3 for the page to load.", - "ssUpload": "Uploading screenshot ...", + "ssResult": "Generating screenshot of the page\u2026\nHeight of page = %1px\nWidth of page = %2px\nWaiting %3 for the page to load.", + "ssUpload": "Uploading screenshot\u2026", "ssUsage": "You must provide a valid link before I can take a screenshot.", + "statsResult": "%1Total Chats:%1 %2%3%2\n%1Channels:%1 %2%4%2\n%1Groups:%1 %2%5%2\n%1Super Groups:%1 %2%6%2\n%1Bots:%1 %2%7%2\n%1PM's:%1 %2%8%2\n%1Unread Messages:%1 %2%9%2", "statusLong": "A long time ago", "statusMonth": "Within the last month", + "statusOffline": "Offline", "statusOnline": "Online", "statusRecently": "Recently", "statusWeek": "Within the last week", "stickerAdded": "%1Sticker kanged successfully!%1\nPack can be found [here](https://t.me/addstickers/%2)", "stickerError": "I can't kang that", - "stickerInfo": ".kang\nUsage: Reply .kang to a sticker or an image to kang it to your sticker pack.\n\n.kang [number]\nUsage: Kang's the sticker/image to the specified pack but uses \ud83e\udd24 as emoji.\n\n.getsticker\nUsage: Sends the replied sticker in file format.\n\n.packinfo\nUsage: Gets info about the sticker pack.", + "stickerInfo": ".kang [Optional: <Pack Number> <Emoji>]\nUsage: Reply .kang to a sticker or an image to kang it to your sticker pack. Default emoji is \ud83e\udd24\n\n.getsticker [-v (verbosity)]\nUsage: Sends the replied sticker in file format.\n\n.packinfo\nUsage: Gets info about the sticker pack.", "stickerPackFull": "Pack %1 is full.", "stickerUsage": "Give me something..", "strUsage": "GiiiiiiiB sooooooomeeeeeee teeeeeeext!", - "supportGroup": "[Seden Support Group](https://telegram.dog/%1)", + "sudoCheck": "This person is Seden admin!", "supportResult": "You can reach our support group [here](http://t.me/%1).", "syntaxError": "Syntax error.", - "systemInfo": ".alive\nUsage: Type .alive to see wether your Seden Bot is working or not.\n\n.ping\nUsage: Shows how long it takes to ping your bot.\n\n.echo\nUsage: Repeats text you type.\n\n.botver\nUsage: Shows Seden UserBot version.\n\n.dc\nUsage: Shows nearest data center to your server.\n\n.neofetch\nUsage: Displays system information using Neofetch command.\n\n.pip <module name>\nUsage: Searches in pip modules.\n\n.eval 2 + 3\nUsage: Evalute mini expressions.\n\n.term echo Hi Seden!\nUsage: Run bash commands and scripts on your server.\n\n.restart\nUsage: Restarts the bot.\n\n.shutdown\nUsage: Sometimes you need to shut down your bot. Sometimes you just hope to hear Windows XP shutdown sound... but you don/'t.", + "systemInfo": ".alive\nUsage: Type .alive to see wether your Seden Bot is working or not.\n\n.ping\nUsage: Shows how long it takes to ping your bot.\n\n.echo\nUsage: Repeats text you type.\n\n.botver\nUsage: Shows Seden UserBot version.\n\n.dc\nUsage: Shows nearest data center to your server.\n\n.neofetch\nUsage: Displays system information using Neofetch command.\n\n.eval 2 + 3\nUsage: Evalute mini expressions.\n\n.term echo Hi Seden!\nUsage: Run bash commands and scripts on your server.\n\n.restart\nUsage: Restarts the bot.\n\n.shutdown\nUsage: Sometimes you need to shut down your bot. Sometimes you just hope to hear Windows XP shutdown sound\u2026 but you don't.", "termHelp": "You can look at example by typing .seden system for help.", "termLog": "Terminal command %1 executed successfully", "termNoResult": "No result", "termUsage": "Please write a command.", "testException": "This is a test, don't submit a bug log.", - "testLogId": "Testing LOG_ID value ...", - "title": "%1Title%2%1", + "testLogId": "Testing LOG_ID value\u2026", + "tgUpLimit": "%1File size too big.I cant do that.%1", "transHeader": "%1From:%1 %2%3%2\n%1To:%1 %2%4%2\n", + "translatorInfo": ".trt <text> [or reply]\nUsage: Translates text to the language which is set.\n\n.tts <text> [or reply]\nUsage: Translates text to speech for the language which is set.", "trtError": "Invalid destination language.", - "trtInfo": ".trt <text> [or reply]\nUsage: Translates text to the language which is set.\nUse .lang trt <language code> to set language for trt. (Default is English)", "trtLog": "Translated some %1 stuff to %2 just now.", "trtUsage": "Give a text or reply to a message to translate!", "tspamLog": "#TSPAM\nTSpam was executed successfully", "ttsBlank": "Text is blank.\nAfter preprocessing, tokenization and cleaning, there is nothing left to talk about.", "ttsError": "An error has occurred in viewing the language's dictionary.", - "ttsInfo": ".tts <text> [or reply]\nUsage: Translates text to speech for the language which is set.\nUse .lang tts <language code> to set language for tts. (Default is English)", "ttsLog": "Text has been successfully converted to speech!", "ttsNoSupport": "This language is not yet supported.", "ttsUsage": "Enter some text to translate from text to speech.", @@ -549,63 +576,80 @@ "twrpUsage": "Usage: .twrp <codename> eg: .twrp raphael", "udInfo": ".ud <query>\nDoes a search on Urban Dictionary.", "udNoResult": "%1No result for%1 %2", + "udNotFound": "Expression not found.", "udResult": "Sorry, No results were found for %1%2%1", - "unbanProcess": "Unbanning...", + "unbanProcess": "Unbanning\u2026", "unbanResult": "%1[%2](tg://user?id=%3)%1 %4unbanned!%4", "unblockChat": "%2Please unblock%2 %1@%3%1 %2and try again%2", - "unmuteProcess": "Unmuting...", + "unmuteProcess": "Unmuting\u2026", "unmuteResult": "%1[%2](tg://user?id=%3)%1 %4unmuted!%4", - "updateBotUpdating": "SedenBot Updating...\nThis process may take 1-2 minutes, please wait patiently. Worth your wait :)", - "updateCheck": "Checking updates for SedenBot...", - "updateComplete": "Update successfully completed!\nSedenBot is rebooting, thank you for waiting patiently :)", - "updateCustomBranch": "%1[SedenBot Updater]:%1` Looks like you are using your own custom branch: %2. If this happens, I cannot update your bot.\nBecause the name of the branch does not match..\nPlease use your bot from the SedenBot official GitHub repo.", + "updateBotUpdating": "Bot Updating\u2026", + "updateCheck": "Checking updates\u2026", + "updateComplete": "Update completed!\nRebooting.", "updateFailed": "Update failed! We ran into some problems.", "updateFolderError": "%1\n%2%3 folder was not found.%2", - "updateForceSync": "Force syncing to latest SedenBot, please wait...", + "updateForceSync": "Force syncing to latest repo, please wait\u2026", "updateGitError": "%1\n%2Git error! %3%2", "updateGitNotFound": "%1 folder doesn't look like a git repo.\nHowever, you can solve this problem by force updating the bot with the .update now command.", + "updateHerokuApiKey": "I can't do this because Heroku API key is wrong. Please change the API key by editing HEROKU_KEY variable", "updateHerokuAppName": "Please set up the HEROKU_APPNAME variable to be able to update bot.", "updateHerokuVariables": "%1\nHeroku variables are incorrectly or under defined.", "updateInfo": ".update\nUsage: Checks if seden userbot repository has any updates and shows a changelog if so.\n\n.update now\nUsage: Updates your userbot, if there are any updates in seden userbot repository.", - "updateLocalComplate": "Update successfully completed!\nSedenBot is rebooting.", + "updateLocalComplate": "Update completed!\nRebooting.", "updateLog": "LOG:", "updateNow": "%1do%1 \"%2.update now%2\" %1to update.%1", "updateOutput": "Changelog is too big, view the file to see it.", - "updateSedenBot": "Updating bot, please wait...", + "updateSedenBot": "Updating bot, please wait\u2026", "updated": "updated", "updaterGitError": "%2\n%1Here is the error log:%1\n%3", "updaterHasUpdate": "%1New update available for %3!\n\nChangelog:%1\n%4", "updaterUsingLatest": "%2Your bot is%2 %1up-to-date%1 %2Branch:%2 %1%3%1", - "updownDownload": "%1Downloading ... %2%1", + "updownDownload": "%1Downloading\u2026 %2%1", "updownDownloadSuccess": "Successfully downloaded to %1%2%1", - "updownUpload": "%1Uploading ... %2%1\n%3", + "updownUpload": "%1Uploading\u2026 %2%1\n%3", "uploadError": "File couldn't be uploaded.", "uploadFileError": "File not found.", "uploadFinish": "Uploading completed!", "uploadInfo": ".download reply to media \nUsage: Downloads file to server.\n\n.upload <path in server>\nUsage: Uploads a locally stored file to chat.", - "uploadMedia": "Uploading media...", + "uploadMedia": "Uploading media\u2026", "uploadReply": "I cant upload nothing here.", - "uploader": "%1Uploader%2%1", + "uploadingZip": "Sending zip file\u2026", "urlError": "Error: Unable to extract link", + "userNotFound": "%1User%1 %2%3%2 %1not found!%1", "useridResult": "%1Username:%1 %2\n%1User ID:%1 %3%4%3", "userlist": "%1%2 Users in%1 %3%4%3%1:%1", "usernameSuccess": "Your username was succesfully changed.", "usernameTaken": "This username is already taken.", "vaporUsage": "\uff27\uff49\uff56\uff45 \uff53\uff4f\uff4d\uff45 \uff54\uff45\uff58\uff54 \uff46\uff4f\uff52 \uff56\uff41\uff50\uff4f\uff52\uff01", + "videoTitle": "Title:", + "videoUploader": "Uploader:", "weatherErrorCity": "Specify a city as default with the WEATHER variable, or specify which city you want the weather when typing the command!", "weatherErrorServer": "Weather information couldn't be retrieved.", + "welcomeAdded": "Welcome message succesfully added!", + "welcomeError": "Welcome message couldn't be added", + "welcomeInfo": ".setwelcome <text/reply>\nUsage: Saves message as a welcome message in the chat\n\nAvailable variables for welcome messages\n{mention}, {title}, {first}, {last}, {userid}, {username}, {my_first}, {my_last}, {my_mention}, {my_username}\n\n.checkwelcome\nUsage: Check whether you have a welcome message in the chat\n\n.delwelcome\nUsage: Deletes the welcome message for the current chat", + "welcomeLog": "#WELCOME\nChat ID: %1%2%1\n\nAbove message saved for reply note, please don't delete!", + "welcomeSqlLog": "Unable to run welcomes module, no SQL connection found", + "welcomeUpdated": "Welcome message succesfully updated!", "whoisError": "Failed to fetching user..", - "whoisInfo": ".whois\nUsage: Retrieves user's information.", "whoisProcess": "Fetching user information..", - "whoisResult": "%1User Info:\n\nFirst name:%1 %2%3%2\n%1Last name:%1 %2%4%2\n%1Username: %5\nUser ID:%1 %2%6%2\n%1Profile Photos:%1 %2%7%2\n%1DC ID:%1 %2%8%2\n%1Is Bot:%1 %2%9%2\n%1Is Restricted:%1 %2%10%2\n%1Is Verified by Telegram:%1 %2%11%2\n%1Common chats:%1 %2%12%2\n\n%1Bio:%1 %2%13%2\n%1Last Seen:%1 %2%14%2\n%1Profile link: [%3](tg://user?id=%6)%1", - "wikiError": "Disambiguated page found.\n\n%1", - "wikiError2": "Page not found.\n\n%1", + "whoisResult": "%1User Info:\n\nFirst name:%1 %2%3%2\n%1Last name:%1 %2%4%2\n%1Username: %5\nUser ID:%1 %2%6%2\n%1Profile Photos:%1 %2%7%2\n%1DC ID:%1 %2%8%2\n%1Common chats:%1 %2%9%2\n%1Is Premium:%1 %2%10%2\n\n%1Bio:%1 %2%11%2\n%1Last Seen:%1 %2%12%2\n%1Profile link: [%3](tg://user?id=%6)\n\n%13\n%14%1", + "wikiError": "Page not found.", "wikiInfo": ".wiki <query>\nUsage: Does a search on Wikipedia.", "wikiLog": "Wiki query %1%2%1 was executed successfully", "wrongCommand": "Command usage is wrong.", "wrongFilter": "Filter is wrong!", "wrongMedia": "Wrong media type!", + "wrongUrl": "Wrong URL!", "yadiskError": "Error: File not found / Download limit exceeded", "youtubedlInfo": ".youtubedl <mp3 or mp4> <url>\nUsage: Downloads video or audio from the link you provided.", - "zalUsage": "g\u036b \u0306 i\u031b \u033a v\u0347\u0306 e\u030f\u0345 a\u0322\u0366 s\u0334\u032a c\u0322\u0338 a\u0338\u0308 r\u0369\u0363 y\u0356\u035e t\u0328\u035a e\u0320\u0301 x\u0322\u0356 t\u035b\u0354" + "zalUsage": "g\u036b \u0306 i\u031b \u033a v\u0347\u0306 e\u030f\u0345 a\u0322\u0366 s\u0334\u032a c\u0322\u0338 a\u0338\u0308 r\u0369\u0363 y\u0356\u035e t\u0328\u035a e\u0320\u0301 x\u0322\u0356 t\u035b\u0354", + "zombiesError": "I don't have enough rights.", + "zombiesFind": "Finding deleted accounts\u2026", + "zombiesFound": "%1Found%1 %2%3%2 %1deleted account.\nClean them by using%1 %2.zombies clean%2", + "zombiesLog": "#CLEANUP\n%1Removed%1 %2%3%2 %1deleted account.\nGROUP: %4%1 | %2%5%2", + "zombiesNoAccount": "No deleted account(s) found!", + "zombiesRemove": "Removing deleted account(s)\u2026", + "zombiesResult": "%1Removed%1 %2%3%2 %1deleted account(s).%1", + "zombiesResult2": "%1Removed%1 %2%3%2 %1deleted account(s).%1\n%2%4%2 %1deleted admin account are not removed.%1" } \ No newline at end of file diff --git a/sedenecem/translator/tr.json b/sedenecem/translator/tr.json index c119c99..c806ba6 100644 --- a/sedenecem/translator/tr.json +++ b/sedenecem/translator/tr.json @@ -1,32 +1,32 @@ { "added": "eklendi", - "adminInfo": ".promote <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama> <\u00f6zel isim (iste\u011fe ba\u011fl\u0131)>\nKullan\u0131m: Sohbetteki ki\u015fiye y\u00f6netici haklar\u0131 sa\u011flar.\n\n.demote <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Sohbetteki ki\u015finin y\u00f6netici izinlerini iptal eder.\n\n.ban <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Sohbetteki ki\u015fiyi gruptan yasaklar.\n\n.unban <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Sohbetteki ki\u015finin yasa\u011f\u0131n\u0131 kald\u0131r\u0131r.\n\n.gban <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Ki\u015fiyi y\u00f6netici oldu\u011funuz t\u00fcm gruplardan yasaklar.\n\n.ungban <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Ki\u015fiyi k\u00fcresel olarak yasaklananlar listesinden kald\u0131r\u0131r.\n\n.mute <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Sohbetteki ki\u015fiyi susturur.\n\n.unmute <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Ki\u015fiyi sessize al\u0131nanlar listesinden kald\u0131r\u0131r.\n\n.gmute <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Ki\u015fiyi y\u00f6netici oldu\u011funuz t\u00fcm gruplardan susturur.\n\n.ungmute <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Ki\u015fiyi k\u00fcresel olarak susturulanlar listesinden kald\u0131r\u0131r.\n\n.kick <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Kullan\u0131c\u0131y\u0131 gruba geri kat\u0131labilecek \u015fekilde \u00e7\u0131kart\u0131r.\n\n.pin <yan\u0131tlama>\nKullan\u0131m: Yan\u0131tlanan mesaj\u0131 sabitler.\n\n.unpin\nKullan\u0131m: Sabitlenen mesaj\u0131 sabitden kald\u0131r\u0131r.\n\n.admins\nKullan\u0131m: Sohbetteki y\u00f6neticileri listeler.\n\n.users\nKullan\u0131m: Sohbetteki kullan\u0131c\u0131lar\u0131 listeler.\n\n.usersdel\nKullan\u0131m: Sohbetteki silinmi\u015f hesaplar\u0131 listeler.\n\n.bots\nKullan\u0131m: Sohbetteki botlar\u0131 listeler.", + "adminInfo": ".promote <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama> <\u00f6zel isim (iste\u011fe ba\u011fl\u0131)>\nKullan\u0131m: Sohbetteki ki\u015fiye y\u00f6netici haklar\u0131 sa\u011flar.\n\n.demote <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Sohbetteki ki\u015finin y\u00f6netici izinlerini iptal eder.\n\n.ban <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Sohbetteki ki\u015fiyi gruptan yasaklar.\n\n.unban <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Sohbetteki ki\u015finin yasa\u011f\u0131n\u0131 kald\u0131r\u0131r.\n\n.mute <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Sohbetteki ki\u015fiyi susturur.\n\n.unmute <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Ki\u015fiyi sessize al\u0131nanlar listesinden kald\u0131r\u0131r.\n\n.kick <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Kullan\u0131c\u0131y\u0131 gruba geri kat\u0131labilecek \u015fekilde \u00e7\u0131kart\u0131r.\n\n.pin <yan\u0131tlama> & .pin loud <yan\u0131tlama>\nKullan\u0131m: Yan\u0131tlanan mesaj\u0131 sabitler.\n\n.unpin <yan\u0131tlama> & .unpin all\nKullan\u0131m: Sabitlenen mesaj\u0131 sabitden kald\u0131r\u0131r.\n\n.admins\nKullan\u0131m: Sohbetteki y\u00f6neticileri listeler.\n\n.users\nKullan\u0131m: Sohbetteki kullan\u0131c\u0131lar\u0131 listeler.\n\n.usersdel\nKullan\u0131m: Sohbetteki silinmi\u015f hesaplar\u0131 listeler.\n\n.bots\nKullan\u0131m: Sohbetteki botlar\u0131 listeler.\n\n.zombies\nKullan\u0131m: Bir grupta olan silinmi\u015f hesaplar\u0131 bulur. Silinmi\u015f hesaplar\u0131 gruptan atmak i\u00e7in '.zombies clean' komutunu kullan.\n\n.setgpic\nKullan\u0131m: Grubun foto\u011fraf\u0131n\u0131 de\u011fi\u015ftirir.", "adminUsage": "Y\u00f6netici de\u011filim!", "adminlist": "%2%3%2 %1sohbetinde bulunan y\u00f6neticiler:%1", "afkEnd": "Art\u0131k AFK de\u011filim.", "afkInfo": ".afk [\u0130ste\u011fe ba\u011fl\u0131 sebep]\nKullan\u0131m: AFK oldu\u011funuzu belirtir.\nKim size pm atarsa ya da sizi etiketlerse sizin AFK oldu\u011funuzu ve belirledi\u011finiz sebebi g\u00f6sterir.\n\nHerhangi bir yere mesaj yazd\u0131\u011f\u0131n\u0131zda AFK modu kapan\u0131r.", "afkLog": "#AFK\nAFK oldunuz.", "afkMentionUsers": "%1[%2](tg://user?id=%3) size%1 %4%5%4 %1mesaj g\u00f6nderdi%1", - "afkMessage2": "%1[%2](tg://user?id=%3) h\u00e2l\u00e2 AFK.\nSebep:%1 %4%5%4", + "afkMessage2": "%1[%2](tg://user?id=%3) h\u00e2l\u00e2 AFK.\nSebep: %4%5%4\nSon G\u00f6r\u00fclme: %4%6%4%1", "afkMessages": "%2Siz AFK iken%2 %1%3%1 %2ki\u015fi size%2 %1%4%1 %2mesaj g\u00f6nderdi.%2", "afkStart": "Art\u0131k AFK'y\u0131m.", - "afkStartReason": "%1Art\u0131k AFK'y\u0131m.\nSebep%1: %2%3%2", + "afkStartReason": "%1Art\u0131k AFK'y\u0131m.\nSebep: %2%3%2%1", "afkstr1": "\u015eu an acele i\u015fim var, daha sonra mesaj atsan olmaz m\u0131? Zaten yine gelece\u011fim.", - "afkstr10": "7 deniz ve 7 \u00fclkeden uzaktay\u0131m,\n7 su ve 7 k\u0131ta,\n7 da\u011f ve 7 tepe,\n7 ovala ve 7 h\u00f6y\u00fck,\n7 havuz ve 7 g\u00f6l,\n7 bahar ve 7 \u00e7ay\u0131r,\n7 \u015fehir ve 7 mahalle,\n7 blok ve 7 ev...\n\nMesajlar\u0131n bile bana ula\u015famayaca\u011f\u0131 bir yer!", + "afkstr10": "7 deniz ve 7 \u00fclkeden uzaktay\u0131m,\n7 su ve 7 k\u0131ta,\n7 da\u011f ve 7 tepe,\n7 ovala ve 7 h\u00f6y\u00fck,\n7 havuz ve 7 g\u00f6l,\n7 bahar ve 7 \u00e7ay\u0131r,\n7 \u015fehir ve 7 mahalle,\n7 blok ve 7 ev\u2026\n\nMesajlar\u0131n bile bana ula\u015famayaca\u011f\u0131 bir yer!", "afkstr11": "\u015eu anda klavyeden uzaktay\u0131m, ama ekran\u0131n\u0131zda yeterince y\u00fcksek sesle \u00e7\u0131\u011fl\u0131k atarsan\u0131z, sizi duyabilirim.", "afkstr12": "\u015eu y\u00f6nde ilerliyorum\n---->", "afkstr13": "\u015eu y\u00f6nde ilerliyorum\n<----", "afkstr14": "L\u00fctfen mesaj b\u0131rak\u0131n ve beni zaten oldu\u011fumdan daha \u00f6nemli hissettirin.", "afkstr15": "Sahibim burada de\u011fil, bu y\u00fczden bana yazmay\u0131 b\u0131rak.", - "afkstr16": "Burada olsayd\u0131m,\nSana nerede oldu\u011fumu s\u00f6ylerdim.\n\nAma ben de\u011filim,\ngeri d\u00f6nd\u00fc\u011f\u00fcmde bana sor...", + "afkstr16": "Burada olsayd\u0131m,\nSana nerede oldu\u011fumu s\u00f6ylerdim.\n\nAma ben de\u011filim,\ngeri d\u00f6nd\u00fc\u011f\u00fcmde bana sor\u2026", "afkstr17": "Uzaklarday\u0131m!\nNe zaman d\u00f6nerim bilmiyorum !\nUmar\u0131m birka\u00e7 dakika sonra!", - "afkstr18": "Sahibim \u015fuan da m\u00fcsait de\u011fil. Ad\u0131n\u0131z\u0131, numar\u0131n\u0131z\u0131 ve adresinizi verirseniz ona iletibilirm ve b\u00f6ylelikle geri d\u00f6nd\u00fc\u011f\u00fc zaman.", + "afkstr18": "Sahibim \u015fu anda m\u00fcsait de\u011fil. Ad\u0131n\u0131z\u0131, numaran\u0131z\u0131 ve adresinizi verirseniz ona iletibilirim ve b\u00f6ylelikle geri d\u00f6nd\u00fc\u011f\u00fc zaman sizinle ilgilenebilir.", "afkstr19": "\u00dczg\u00fcn\u00fcm, sahibim burada de\u011fil.\nO gelene kadar benimle konu\u015fabilirsiniz.\nSahibim size sonra d\u00f6ner.", "afkstr2": "Arad\u0131\u011f\u0131n\u0131z ki\u015fi \u015fu anda telefona cevap veremiyor. Sinyal sesinden sonra kendi tarifeniz \u00fczerinden mesaj\u0131n\u0131z\u0131 b\u0131rakabilirsiniz. Mesaj \u00fccreti 49 kuru\u015ftur. \nbiiiiiiiiiiiiiiiiiiiiiiiiiiiiip!", "afkstr20": "Bahse girerim bir mesaj bekliyordun!", - "afkstr21": "Hayat \u00e7ok k\u0131sa, yapacak \u00e7ok \u015fey var...\nOnlardan birini yap\u0131yorum...", - "afkstr22": "\u015eu an burada de\u011filim....\nama \u00f6yleysem ...\n\nbu harika olmaz m\u0131yd\u0131?", - "afkstr3": "Birka\u00e7 dakika i\u00e7inde gelece\u011fim. Fakat gelmezsem...\ndaha fazla bekle.", + "afkstr21": "Hayat \u00e7ok k\u0131sa, yapacak \u00e7ok \u015fey var\u2026\nOnlardan birini yap\u0131yorum\u2026", + "afkstr22": "\u015eu an burada de\u011filim\u2026\nama \u00f6yleysem\u2026\n\nbu harika olmaz m\u0131yd\u0131?", + "afkstr3": "Birka\u00e7 dakika i\u00e7inde gelece\u011fim. Fakat gelmezsem\u2026\ndaha fazla bekle.", "afkstr4": "\u015eu an burada de\u011filim, muhtemelen ba\u015fka bir yerdeyim.", "afkstr5": "G\u00fcller k\u0131rm\u0131z\u0131\nMenek\u015feler mavi\nBana bir mesaj b\u0131rak\nVe sana d\u00f6nece\u011fim.", "afkstr6": "Bazen hayattaki en iyi \u015feyler beklemeye de\u011fer\u2026\nHemen d\u00f6nerim.", @@ -37,35 +37,40 @@ "alreadyMuted": "Hata!\nKullan\u0131c\u0131 zaten susturuldu!", "alreadyUnbanned": "Hata!\nKullan\u0131c\u0131 daha \u00f6nceden yasaklanmam\u0131\u015f!", "alreadyUnmuted": "Hata!\nKullan\u0131c\u0131 daha \u00f6nceden susturulmam\u0131\u015f!", - "androidInfo": ".magisk\nG\u00fcncel Magisk s\u00fcr\u00fcmleri\n\n.device <kod ad\u0131>\nKullan\u0131m: Android cihaz\u0131 hakk\u0131nda bilgi\n\n.codename <marka> <cihaz>\nKullan\u0131m: Android cihaz kod adlar\u0131n\u0131 aray\u0131n.\n\nspecs <marka> <cihaz>\nKullan\u0131m: Cihaz \u00f6zellikleri hakk\u0131nda bilgi al\u0131n.\n\n.twrp <kod ad\u0131>\nKullan\u0131m: Hedeflenen cihaz i\u00e7in resmi olan g\u00fcncel TWRP s\u00fcr\u00fcmlerini al\u0131n.\n\n.orangefox <kod ad\u0131>\nKullan\u0131m: Hedeflenen cihaz i\u00e7in resmi olan g\u00fcncel OrangeFox Recovery s\u00fcr\u00fcmlerini al\u0131n.\n\n.phh <mimari>\nG\u00fcncel Phh AOSP s\u00fcr\u00fcmlerini al\u0131n.", + "alwaysOnline": "S\u00fcrekli \u00e7evrimi\u00e7i modu etkinle\u015ftirildi!", + "alwaysOnline2": "S\u00fcrekli \u00e7evrimi\u00e7i modu zaten etkinle\u015ftirildi!", + "alwaysOnlineOff": "S\u00fcrekli \u00e7evrimi\u00e7i modu devre d\u0131\u015f\u0131 b\u0131rak\u0131ld\u0131!", + "alwaysOnlineOff2": "S\u00fcrekli \u00e7evrimi\u00e7i modu zaten devre d\u0131\u015f\u0131 b\u0131rak\u0131ld\u0131!", + "androidInfo": ".ksu\nG\u00fcncel KernelSU s\u00fcr\u00fcmleri\n\n.magisk\nG\u00fcncel Magisk s\u00fcr\u00fcmleri\n\n.device <kod ad\u0131>\nKullan\u0131m: Android cihaz\u0131 hakk\u0131nda bilgi\n\n.codename <marka> <cihaz>\nKullan\u0131m: Android cihaz kod adlar\u0131n\u0131 aray\u0131n.\n\nspecs <marka> <cihaz>\nKullan\u0131m: Cihaz \u00f6zellikleri hakk\u0131nda bilgi al\u0131n.\n\n.twrp <kod ad\u0131>\nKullan\u0131m: Hedeflenen cihaz i\u00e7in resmi olan g\u00fcncel TWRP s\u00fcr\u00fcmlerini al\u0131n.\n\n.orangefox <kod ad\u0131>\nKullan\u0131m: Hedeflenen cihaz i\u00e7in resmi olan g\u00fcncel OrangeFox Recovery s\u00fcr\u00fcmlerini al\u0131n.\n\n.phh <mimari>\nG\u00fcncel Phh AOSP s\u00fcr\u00fcmlerini al\u0131n.", "androidPhhHeader": "%1G\u00fcncel PHH %2AOSP S\u00fcr\u00fcmleri%1", "answerFromBot": "Botdan cevap alamad\u0131m!", "apiHashError": "API HASH Ayarlanmad\u0131. L\u00fctfen config.env dosyas\u0131n\u0131z kontrol edin.", "apiIdError": "API ID Ayarlanmad\u0131. L\u00fctfen config.env dosyas\u0131n\u0131z kontrol edin.", - "applyEarrape": "Earrape efekti uygulan\u0131yor...", - "applyNightcore": "Nightcore uygulan\u0131yor...", - "applySlowedtoperfection": "Slowed to perfection uygulan\u0131yor...", + "applyEarrape": "Earrape efekti uygulan\u0131yor\u2026", + "applyNightcore": "Nightcore uygulan\u0131yor\u2026", + "applySlowedtoperfection": "Slowed to perfection uygulan\u0131yor\u2026", "autoppConfig": "L\u00fctfen AUTO_PP de\u011fi\u015fkeninizi ayarlay\u0131n veya bir profil foto\u011fraf\u0131 koyun.", "autoppDisabled": "Profil foto\u011fraf\u0131n\u0131z art\u0131k otomatik olarak de\u011fi\u015fmeyecek.", "autoppDisabledAlready": "G\u00f6r\u00fcn\u00fc\u015fe g\u00f6re profil foto\u011fraf\u0131n\u0131z zaten otomatik olarak de\u011fi\u015fmiyor.", "autoppEnabledAlready": "G\u00f6r\u00fcn\u00fc\u015fe g\u00f6re profil foto\u011fraf\u0131n\u0131z zaten otomatik olarak de\u011fi\u015fiyor.", "autoppInfo": ".autopp <disable>\nKullan\u0131m: Bu komut belirledi\u011finiz foto\u011fraf\u0131 profil resmi yapar\nve bir saat ekler. Bu saat her dakika de\u011fi\u015fir.\nA\u00e7mak i\u00e7in .autopp, kapatmak i\u00e7in .autopp disable yaz\u0131n.\n\nNOT: K\u00fc\u00e7\u00fck bir ihtimal bile olsa ban yeme riskiniz var. Bu y\u00fczden dikkatli kullan\u0131n.", - "autoppLog": "[AUTOPP] Dosya zaten mevcut, indirme atlan\u0131yor ...", - "autoppProcess": "Profil foto\u011fraf\u0131n\u0131z ayarlan\u0131yor ...", + "autoppLog": "[AUTOPP] Dosya zaten mevcut, indirme atlan\u0131yor\u2026", + "autoppProcess": "Profil foto\u011fraf\u0131n\u0131z ayarlan\u0131yor\u2026", "autoppResult": "Profil foto\u011fraf\u0131n\u0131z ayarland\u0131!", + "banAdminError": "San\u0131r\u0131m bunun i\u00e7in yeterli yetkim olmayabilir.", "banError": "%1Bir hata olu\u015ftu!%1\n\n%2%3%2", "banFailUser": "Ge\u00e7erli bir kullan\u0131c\u0131 belirtiniz!", - "banLog": "%1#BAN\nKULLANICI: [%2](tg://user?id=%3)\nGRUP: %4 (%1%5%6%5%1)%1", + "banLog": "#BAN\nKULLANICI: [%1](tg://user?id=%2) (%4%2%4)\nGRUP: %3 (%4%5%4)\n%6", "banProcess": "D\u00fc\u015fman vuruldu!", - "banResult": "%1[%2](tg://user?id=%3)%1 (%4%3%4) %1yasakland\u0131!%1", + "banReason": "%1Sebep:%1 %2%3%2", + "banResult": "%1[%2](tg://user?id=%3)%1 %1yasakland\u0131!%1\n%5", "barcodeInfo": ".barcode <metin>\nKullan\u0131m: Verilen i\u00e7erikten bir barkod yap\u0131n.\n\u00d6rnek: .barcode https://devotag.com\n\nNot: \u00e7\u00f6z\u00fclm\u00fc\u015f i\u00e7erik almak i\u00e7in .decode komutunu kullan\u0131n.", "barcodeUsage": "%1Kullan\u0131m:%1 %2.barcode <metin>%2", "base64Info": ".base64\nKullan\u0131m: Verilen dizenin base64 kodlamas\u0131n\u0131 bulun. \u00d6rnek .base64 en merhaba", "bioSuccess": "Biyografi ba\u015far\u0131yla de\u011fi\u015ftirildi.", - "birakmamseniInfo": ".birakmemseni\nKullan\u0131m: Be\u015fikta\u015f'\u0131n B\u0131rakmam Seni kampanyas\u0131na yap\u0131lan destek say\u0131s\u0131n\u0131 g\u00f6stermektedir.", - "birakmamseniResult": "%1\u26ab\u26aa B\u0131rakmam Seni Kampanyas\u0131 Verileri \u26ab\u26aa%1\n\n\u015eu an itibar\u0131yla %1BIRAKMAM SEN\u0130%1 kampanyas\u0131 kapsam\u0131nda %2%3%2 \ud83d\udda4\ud83e\udd0d adet destekte bulunuldu.\nHaydi sen de hemen %1B\u00dcY\u00dcK BE\u015e\u0130KTA\u015e\u2019IMIZA%1 \ud83e\udd85 destek ol !\n\n[https://birakmamseni.org](https://birakmamseni.org/)\n\n%2=============================\nSMS, Havale/Eft ve Posta \u00c7eki kanallar\u0131 ile gelen destekler periyodik olarak sayaca eklenmektedir.\n=============================%2", "blacklistAddSuccess": "%2%3%2 %1bu sohbet i\u00e7in kara listeye al\u0131nd\u0131.%1", "blacklistChats": "Bu grup i\u00e7in ayarlanan karaliste:", + "blacklistCheck": "Bu ki\u015finin Seden UserBot kullan\u0131m\u0131 yasaklanm\u0131\u015ft\u0131r!", "blacklistInfo": ".showblacklist\nKullan\u0131m: Bir sohbetteki etkin kara listeyi listeler.\n\n.addblacklist <kelime>\nKullan\u0131m: \u0130letiyi 'kara liste anahtar kelimesine' kaydeder. 'Kara liste anahtar kelimesinden' bahsedildi\u011finde bot iletiyi siler.\n\n.rmblacklist <kelime>\nKullan\u0131m: Belirtilen kara listeyi durdurur.", "blacklistLog": "#BLACKLIST\nSohbet ID: %1%2%1\nKelime: %1%3%1", "blacklistPermission": "Bu grupta mesaj silme iznim yok !", @@ -73,6 +78,7 @@ "blacklistSqlLog": "Karaliste mod\u00fcl\u00fc \u00e7al\u0131\u015ft\u0131r\u0131lam\u0131yor, SQL ba\u011flant\u0131s\u0131 bulunamad\u0131", "blacklistText": "Bana bir metin ver", "blankBlacklist": "Karalisteye eklenmi\u015f kelime bulunamad\u0131.", + "botError": "%1Hata olu\u015ftu! Detayl\u0131 bilgi i\u00e7in%1 %2@%3%2 %1botunu kontrol edin.%1", "botlist": "%2%3%2 %1sohbetinde bulunan botlar:%1", "brainError": "%1Hata!%1\n%2[%3](tg://user?id=%4)%2 %1bir Seden yetkilisi..\nYani bunu yapamam.%1", "callInfo": ".call #note veya .call $snip\nKullan\u0131m: Notlar\u0131 veya genel notlar\u0131 \u00e7a\u011f\u0131rmak i\u00e7in kullan\u0131l\u0131r", @@ -88,7 +94,7 @@ "carbonInfo": ".carbon <metin>\nKullan\u0131m: carbon.now.sh sitesini kullanarak yazd\u0131klar\u0131n\u0131n a\u015f\u015f\u015f\u015f\u015f\u015f\u0131r\u0131 \u015fekil g\u00f6r\u00fcnmesini sa\u011flar.\n.crblang <dil> komutuyla varsay\u0131lan dilini ayarlayabilirsin.", "carbonLang": "Karbon mod\u00fcl\u00fc i\u00e7in varsay\u0131lan dil %1%2%1 olarak ayarland\u0131.", "carbonResult": "Bu resim [Carbon](https://carbon.now.sh/about/) kullan\u0131larak yap\u0131ld\u0131,\nbir [Dawn Labs](https://dawnlabs.io/) projesi.", - "carbonUpload": "Resim kar\u015f\u0131ya y\u00fckleniyor...", + "carbonUpload": "Resim kar\u015f\u0131ya y\u00fckleniyor\u2026", "chatAlreadyMuted": "Sohbet zaten susturulmu\u015f!", "chatAlreadyUnmuted": "Sohbetin zaten sesi a\u00e7\u0131lm\u0131\u015f!", "chatInfo": ".mutechat\nKullan\u0131m: Belirlenen grubu susturur.\n\n.unmutechat\nKullan\u0131m: Susturulmu\u015f bir sohbetin sesini a\u00e7ar.", @@ -107,13 +113,13 @@ "covidError": "Bir hata olu\u015ftu.", "cpUsage": "\ud83d\ude02Bana\ud83d\udcafBIR\u270c\ufe0fmE\ud83c\udd71\ufe0fIn\ud83d\udc50Ver\ud83d\udc4f", "currencyError": "Yazd\u0131\u011f\u0131n \u015fey uzayl\u0131lar\u0131n kulland\u0131\u011f\u0131 bir para birimine benziyor, bu y\u00fczden d\u00f6n\u00fc\u015ft\u00fcremiyorum.", - "currencyInfo": ".currency <miktar> <d\u00f6n\u00fc\u015ft\u00fcr\u00fclecek birim> <d\u00f6n\u00fc\u015fecek birim>\nKullan\u0131m: Belirtilen para miktarlar\u0131n\u0131 d\u00f6n\u00fc\u015ft\u00fcr\u00fcr.", + "currencyInfo": ".currency <miktar> <d\u00f6n\u00fc\u015ft\u00fcr\u00fclecek birim> <d\u00f6n\u00fc\u015fecek birim>\nKullan\u0131m: Belirtilen para miktarlar\u0131n\u0131 d\u00f6n\u00fc\u015ft\u00fcr\u00fcr.\n\n.d\u00f6viz\nKullan\u0131m: G\u00fcncel d\u00f6viz kurlar\u0131n\u0131 getirir.", "ddgoDesc": "A\u00e7\u0131klama sa\u011flanmam\u0131\u015f", "ddgoInfo": ".ddgo <kelime>\nKullan\u0131m: H\u0131zl\u0131 bir DuckDuckGo aramas\u0131 yapar.", "ddgoLog": "%1 s\u00f6zc\u00fc\u011f\u00fc ba\u015far\u0131yla DuckDuckGo'da arat\u0131ld\u0131!", "decodeFail": "Decode ba\u015far\u0131s\u0131z oldu.", - "deepfryApply": "%1Medyaya %2fry efekti uygulan\u0131yor...%1", - "deepfryDownload": "Medya indiriliyor...", + "deepfryApply": "%1Medyaya %2fry efekti uygulan\u0131yor\u2026%1", + "deepfryDownload": "Medya indiriliyor\u2026", "deepfryError": "Bunu deepfry yapamam!", "deepfryInfo": ".deepfry veya .fry [numara 1-5]\nKullan\u0131m: Belirlenen g\u00f6r\u00fcnt\u00fcye deepfry efekti uygular.", "deepfryNoPic": "%1%2 yapmam i\u00e7in bir resim veya \u00e7\u0131kartma al\u0131nt\u0131lamal\u0131s\u0131n!%1", @@ -122,34 +128,25 @@ "delErrorLog": "Bu mesaj\u0131 silemiyorum.", "delInfo": ".del\nKullan\u0131m: Yan\u0131tlad\u0131\u011f\u0131n\u0131z mesaj\u0131 siler.", "delResultLog": "Hedeflenen mesaj\u0131n silinmesi ba\u015far\u0131l\u0131yla tamamland\u0131", + "delWelcome": "Welcome message deleted for this chat", "delayspamLog": "#DELAYSPAM\nDelaySpam ba\u015far\u0131yla ger\u00e7ekle\u015ftirildi", "deleted": "Silinmi\u015f", "deletedAcc": "Silinen Hesap", - "demoteProcess": "Yetki d\u00fc\u015f\u00fcr\u00fcl\u00fcyor...", + "demoteProcess": "Yetki d\u00fc\u015f\u00fcr\u00fcl\u00fcyor\u2026", "demoteResult": "%1[%2](tg://user?id=%3)%1 %4art\u0131k y\u00f6netici de\u011fil!%4", - "deviceError": "%1%2cihaz\u0131 i\u00e7in bilgi bulunamad\u0131!%1", + "deviceError": "%1%2 cihaz\u0131 i\u00e7in bilgi bulunamad\u0131!%1", "deviceSearch": "%1%2 i\u00e7in arama sonu\u00e7lar\u0131:%1", - "deviceSearchResultChild": "%1Marka:%1 %2\n%1Ad:%1 %3\n%1Model:%1 %4", + "deviceSearchResultChild": "%1Marka:%1 %2\n%1Ad:%1 %3\n%1Model:%1 %4\n\n", "deviceUsage": "Kullan\u0131m: .device <kod ad\u0131> \u00d6rnek: .device raphael", "directError": "%1 i\u015flenirken hata olu\u015ftu", - "directInfo": ".direct <link>\nKullan\u0131m: Kullan\u0131m: Bir ba\u011flant\u0131y\u0131 yan\u0131tlay\u0131n veya do\u011frudan indirme ba\u011flant\u0131s\u0131\nolu\u015fturmak i\u00e7in bir URL yap\u0131\u015ft\u0131r\u0131n\n\nDesteklenen URL'lerin listesi:\nYandex.Disk - AFH - ZippyShare -\nMediaFire - SourceForge - OSDN - GitHub", + "directGdriveCookie": "Bu dosyay\u0131 indirebilmek i\u00e7in Google hesab\u0131n\u0131za giri\u015f yapman\u0131z gerekebilir.\n\u0130ndirme i\u015flemi esnas\u0131nda herhangi bir hata ile kar\u015f\u0131la\u015f\u0131rsan\u0131z cookies.txt dosyas\u0131n\u0131 kullan\u0131n.\n\n", + "directGdriveCookieUsage": "Kullan\u0131m: `wget --load-cookies cookies.txt` directLink", + "directInfo": ".direct <link>\nKullan\u0131m: Kullan\u0131m: Bir ba\u011flant\u0131y\u0131 yan\u0131tlay\u0131n veya do\u011frudan indirme ba\u011flant\u0131s\u0131\nolu\u015fturmak i\u00e7in bir URL yap\u0131\u015ft\u0131r\u0131n\n\nDesteklenen URL'lerin listesi:\nYandex.Disk - AFH - ZippyShare - GDrive -\nGDocs - MediaFire - SourceForge - OSDN - GitHub", "directNoSupport": "%1 desteklenmiyor", "directUrlNotFound": "Link bulunamad\u0131", "directUsage": "Kullan\u0131m: .direct <link>", "directZippyLink": "%1 (%2)\n[\u0130ndir](%3)", - "dogbinContent": "Dogbin i\u00e7eri\u011fi al\u0131n\u0131yor...", - "dogbinError": "\u0130stek ba\u015far\u0131s\u0131z bir durum kodu d\u00f6nd\u00fcrd\u00fc.\n\n %1", - "dogbinInfo": ".paste <metin>\nKullan\u0131m: Dogbin kullanarak yap\u0131\u015ft\u0131r\u0131lm\u0131\u015f veya k\u0131salt\u0131lm\u0131\u015f url olu\u015fturma (https://del.dog/)\n\n.getpaste\nKullan\u0131m: Dogbin url i\u00e7eri\u011fini metne aktar\u0131r (https://del.dog/)", - "dogbinPasteResult": "%1Ba\u015far\u0131yla yap\u0131\u015ft\u0131r\u0131ld\u0131!\n\nDogbin URL:%1 %2", - "dogbinPasteResult2": "%1Ba\u015far\u0131yla yap\u0131\u015ft\u0131r\u0131ld\u0131!%1\n\n%1K\u0131salt\u0131lm\u0131\u015f link:%1 %2\n\n%1K\u0131salt\u0131lmam\u0131\u015f linkler%1\n%1Dogbin linki:%1 %3\n", - "dogbinPasting": "Metin yap\u0131\u015ft\u0131r\u0131l\u0131yor...", - "dogbinReach": "Dogbine ula\u015f\u0131lamad\u0131", - "dogbinResult": "%1Dogbin URL i\u00e7eri\u011fi ba\u015far\u0131yla getirildi!\n\n\u0130\u00e7erik:%1 %2", - "dogbinTimeOut": "\u0130stek zaman a\u015f\u0131m\u0131na u\u011frad\u0131. %1", - "dogbinTooManyRedirects": "\u0130stek, yap\u0131land\u0131r\u0131lm\u0131\u015f en fazla y\u00f6nlendirme say\u0131s\u0131n\u0131 a\u015ft\u0131. %1", - "dogbinUrlError": "Bu bir Dogbin URL'si mi?", - "dogbinUsage": "Elon Musk bo\u015flu\u011fu yap\u0131\u015ft\u0131ramayaca\u011f\u0131m\u0131 s\u00f6yledi.", - "downloadMedia": "\u0130ndiriliyor ...", + "downloadMedia": "\u0130ndiriliyor\u2026", "downloadMediaError": "Hen\u00fcz yap\u0131mc\u0131lar\u0131m bu t\u00fcrde bir medyay\u0131 indirmem i\u00e7in beni ayarlamam\u0131\u015f.", "downloadReply": "Please reply a document.", "downloadYTAudio": "%1%2%1 %3Ses olarak indiriliyor%3", @@ -159,19 +156,21 @@ "effectsInfo": ".earrape mp3 veya mp4\nKullan\u0131m: Videonuza veya ses dosyan\u0131za earrape efekti uygular.\n\n.nightcore\nKullan\u0131m: Yollad\u0131\u011f\u0131n\u0131z ses dosyas\u0131n\u0131 nightcore'a \u00c7evirir\n\n.slowedtoperfection \nKullan\u0131m: Yollad\u0131\u011f\u0131n\u0131z ses dosyas\u0131n\u0131 slowed to perfection'a \u00c7evirir.", "envCopySuccess": "%1%3%1 %2de\u011fi\u015fkeni ba\u015far\u0131yla%2 %1%4%1 %2de\u011fi\u015fkenine kopyaland\u0131%2", "envGetValue": "%2De\u011fi\u015fken:%2 %1%3%1\n%2De\u011fer:%2 %1%4%1", - "envInfo": ".env (set, get, rem, copy, move) <De\u011fi\u015fken> <De\u011fer>\nKullan\u0131m: Botun de\u011fi\u015fkenlerini kolayca de\u011fi\u015fmenize yarar.", + "envInfo": ".env get <De\u011fi\u015fken>\nKullan\u0131m: Belirtilen de\u011fi\u015fkene verilen de\u011feri g\u00f6sterir.\n\n.env set <De\u011fi\u015fken> <De\u011fer>\nKullan\u0131m: Belirtilen de\u011fi\u015fkene verilen de\u011feri ayarlar.\n\n.env rem <De\u011fi\u015fken>\nKullan\u0131m: Belirtilen de\u011fi\u015fkeni siler.\n\n.env copy <De\u011fi\u015fken 1> <De\u011fi\u015fken 2>\nKullan\u0131m: Kopyalanacak olan de\u011fi\u015fken de\u011ferini verilen de\u011fi\u015fkene kopyalar.\n\n.env move <De\u011fi\u015fken 1> <De\u011fi\u015fken 2>\nKullan\u0131m: Ta\u015f\u0131nacak de\u011fi\u015fken de\u011ferini hedef de\u011fi\u015fkene ta\u015f\u0131r.\n\n.env list\nKullan\u0131m: Mevcut de\u011fi\u015fkenlerin listesini verir.", "envListKeys": "%1Mevcut De\u011fi\u015fkenler:%1\n%3", "envMoveSuccess": "%1%3%1 %2de\u011fi\u015fkeni ba\u015far\u0131yla%2 %1%4%1 %2de\u011fi\u015fkenine ta\u015f\u0131nd\u0131%2", "envNotFound": "%1%3%1 %2de\u011fi\u015fkeni bulunamad\u0131%2", "envRemSuccess": "%1%3%1 %2de\u011fi\u015fkeni ba\u015far\u0131yla silindi%2", "envSetSuccess": "%1%3%1 %2de\u011fi\u015fkeni ba\u015far\u0131yla de\u011fi\u015ftirildi%2", - "errorLogSend": "Bir sorun olu\u015ftu, kay\u0131tlar\u0131 log grubuna g\u00f6nderiyorum...", + "errorLogSend": "Bir sorun olu\u015ftu, kay\u0131tlar\u0131 log grubuna g\u00f6nderiyorum\u2026", "evalLog": "Eval sorgusu %1 ba\u015far\u0131yla y\u00fcr\u00fct\u00fcld\u00fc", "evalUsage": "De\u011ferlendirmek i\u00e7in bir ifade verin.", - "ezanvaktiErrorInfo": "%1 i\u00e7in bir bilgi bulunamad\u0131.", - "ezanvaktiKonum": "L\u00fctfen komutun yan\u0131na bir \u015fehir belirtin", - "ezanvaktiShowInfo": "%1Diyanet Namaz Vakitleri%1\n\n\ud83d\udccd %1Yer:%1 %2%3%2\n\n\ud83c\udfd9 %1\u0130msak:%1 %2%4%2\n\ud83c\udf05 %1G\u00fcne\u015f:%1 %2%5%2\n\ud83c\udf07 %1\u00d6\u011fle:%1 %2%6%2\n\ud83c\udf06 %1\u0130kindi:%1 %2%7%2\n\ud83c\udf03 %1Ak\u015fam:%1 %2%8%2\n\ud83c\udf0c %1Yats\u0131:%1 %2%9%2", - "fetchProxy": "Proxy getiriliyor ...", + "exifError": "EXIF verisi bulunamad\u0131.", + "exifInfo": ".exif\nKullan\u0131m: Verilen foto\u011fraf dosyas\u0131ndan EXIF verisini \u00e7\u0131kart\u0131r.", + "exifLog": "EXIF verisi:\n", + "exifMaps": "Google Maps Linki: ", + "exifProcess": "EXIF verisi \u00e7\u0131kart\u0131l\u0131yor.", + "fetchProxy": "Proxy getiriliyor\u2026", "filterAdded": "%2Filtre%2 %1%3%1 %2eklendi%2", "filterChats": "Sohbetteki filtreler:", "filterError": "Mesaj y\u00f6nlendirilemedi ve filtre eklenemedi.", @@ -183,9 +182,29 @@ "filterUpdated": "%2Filtre%2 %1%3%1 %2g\u00fcncellendi%2", "filtersSqlLog": "Filters mod\u00fcl\u00fc \u00e7al\u0131\u015ft\u0131r\u0131lam\u0131yor, SQL ba\u011flant\u0131s\u0131 bulunamad\u0131", "founderResult": "%1=======================================\n\nBu bot;%1\n%2[NaytSeyd](https://t.me/NightShade)%2 %1ve%1 %2[frknkrc44](https://t.me/KaldirimMuhendisi)%2 %1taraf\u0131ndan geli\u015ftirilmektedir.\nEk olarak%1\n%2[Sedenogen](https://t.me/CiyanogenOneTeams)%2 %1taraf\u0131ndan sevgi ile d\u00fczenlenmi\u015ftir.\n\n=======================================%1", - "gbanLog": "%1#GBAN\nKullan\u0131c\u0131: [%2](tg://user?id=%3)", - "gbanResult": "%1[%2](tg://user?id=%3)%1 (%4%3%4) %4k\u00fcresel yasakland\u0131!%4", + "gauthFirstRun": "\u0130lk \u00f6nce .gauth komutunu \u00e7al\u0131\u015ft\u0131r\u0131n.", + "gauthTokenErr": "G\u00f6nderdi\u011finiz linkte hata mevcut.Yeniden .gauth komutu \u00e7al\u0131\u015ft\u0131r\u0131n.", + "gauthTokenInvalid": "G\u00f6nderilen Kod Ge\u00e7ersiz.Tekrar .gauth komutunu deneyin.", + "gauthTokenRevoke": "Kay\u0131tl\u0131 token ba\u015far\u0131yla silindi.", + "gauthTokenSuccess": "Token ba\u015far\u0131yla kaydedildi.", + "gauthURL": "%1A\u015fa\u011f\u0131daki ba\u011flant\u0131 \u00fczerinden giri\u015f yap ve ald\u0131\u011f\u0131n yeni ba\u011flant\u0131 ile komutlar\u0131 kullan%1\n%1\u00d6rnek:%1 %2.gauth token <URL>%2\n\n", + "gayString": "\ud83c\udff3\ufe0f\u200d\ud83c\udf08 %1 \u00bd%2 gay!", + "gayString2": "\ud83c\udff3\ufe0f\u200d\ud83c\udf08 Sen \u00bd%1 gaysin!", + "gayString3": "\ud83c\udff3\ufe0f\u200d\ud83c\udf08 \u00bd%1 gayim!", + "gbanLog": "#GBAN\nKullan\u0131c\u0131: [%1](tg://user?id=%2) (%3%2%3)\n%4", + "gbanResult": "%1[%2](tg://user?id=%3)%1 %4k\u00fcresel yasakland\u0131!%4\n%5", + "gbannedUsers": "K\u00fcresel yasaklanan kullan\u0131c\u0131lar:", + "gdriveAuth": "Gdrive token kay\u0131tl\u0131.Token yenileniyor...", + "gdriveCopyErr": "Gdrive klas\u00f6rleri desteklenmiyor,yanl\u0131zca dosyalar destekleniyor.", + "gdriveCopyFile": "Dosya ba\u015far\u0131yla kopyaland\u0131.", + "gdriveDown": "%1Dosya Ad\u0131:%1 %2%3%2\n%1\u0130ndirildi:%1 %2%4%2", + "gdriveDownComplete": "%1Dosya Ad\u0131:%1 %2%3%2\n%1Dosya \u0130ndirildi.%1", + "gdriveTokenErr": "%1Sadece token Dosyas\u0131n\u0131 yan\u0131tlay\u0131n.%1", + "gdriveUp": "%1Dosya Ad\u0131:%1 %2%3%2\n%1Y\u00fcklendi:%1 %2%4%2", + "gdriveUpComplete": "%1Dosya Ad\u0131:%1 %2%3%2\n%1Dosya Y\u00fcklendi.%1", + "gdriveUsage": ".gauth \nYetkilendirme i\u00e7in gereklidir.\n\n.gauth token <URL>\n.gauth kullanarak ald\u0131\u011f\u0131n\u0131z url yi girin.Tokeni kaydeder.\n\n.gauth revoke\nGoogle drive tokenini siler.\n\n.gupload <reply_message> yada <link>\nTelegrama at\u0131lm\u0131\u015f bir dosyay\u0131 .gupload ile yan\u0131tlay\u0131n veya link belirtin.Yant\u0131lanan dosyay\u0131 veya linki google drive'a y\u00fckler.\n\n.gdownload <link>\nKi\u015fisel drive'\u0131n\u0131zdan veya google drive linkinden dosyay\u0131 indirir ve telegrama y\u00fckler.S\u0131n\u0131r 2GB", "geniusToken": "L\u00fctfen Genius tokeni ayarlay\u0131n\u0131z. Te\u015fekk\u00fcrler!", + "getPasteOut": "%1Pastebin i\u00e7eri\u011fi ba\u015far\u0131yla getirildi!\n\n\u0130\u00e7erik:%1 %2", "gitAccount": "Kullan\u0131c\u0131 tipi", "gitBio": "Biyografi", "gitCompany": "\u015eirket", @@ -202,62 +221,72 @@ "gitRepo": "Depo bulunamad\u0131.", "gitRepoList": "Depolar:", "gitTotalGist": "Toplam gist", - "gitTotalRepo": "Toplam Repo", + "gitTotalRepo": "Toplam Depo", "gitTwitter": "Twitter", "gitUsage": "Kullan\u0131m: .github <kullan\u0131c\u0131-ad\u0131>", "gitUser": "Kullan\u0131c\u0131 ID", "gitUserInfo": "%1 GitHub bilgileri", "gitUserNotFound": "Kullan\u0131c\u0131 bulunamad\u0131.", "gitWebsite": "Website", - "gmuteLog": "%1#GMUTE\nKullan\u0131c\u0131: [%2](tg://user?id=%3)", - "gmuteResult": "%1[%2](tg://user?id=%3)%1 %4k\u00fcresel susturuldu!%4", + "globalsInfo": ".gban <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Ki\u015fiyi y\u00f6netici oldu\u011funuz t\u00fcm gruplardan yasaklar.\n\n.ungban <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Ki\u015fiyi k\u00fcresel olarak yasaklananlar listesinden kald\u0131r\u0131r.\n\n.listgban\nKullan\u0131m: K\u00fcresel yasaklanan kullan\u0131c\u0131lar\u0131 listeler.\n\n.gmute <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Ki\u015fiyi y\u00f6netici oldu\u011funuz t\u00fcm gruplardan susturur.\n\n.ungmute <kullan\u0131c\u0131 ad\u0131/yan\u0131tlama>\nKullan\u0131m: Ki\u015fiyi k\u00fcresel olarak susturulanlar listesinden kald\u0131r\u0131r.\n\n.listgmute\nKullan\u0131m: K\u00fcresel susturulan kullan\u0131c\u0131lar\u0131 listeler.", + "globalsSqlLog": "GBan ve GMute komutu \u00e7al\u0131\u015ft\u0131r\u0131lam\u0131yor, SQL ba\u011flant\u0131s\u0131 bulunamad\u0131", + "gmuteLog": "#GMUTE\nKullan\u0131c\u0131: [%1](tg://user?id=%2) (%3%2%3)\n%4", + "gmuteResult": "%1[%2](tg://user?id=%3)%1 %4k\u00fcresel susturuldu!%4\n%5", + "gmutedUsers": "K\u00fcresel susturulan kullan\u0131c\u0131lar:", + "goodbyeMsg": "G\u00f6r\u00fc\u015f\u00fcr\u00fcz\u2026", "googleDesc": "A\u00e7\u0131klama bulunamad\u0131.", "googleInfo": ".google <kelime>\nKullan\u0131m: H\u0131zl\u0131 bir Google aramas\u0131 yapar.", "googleLog": "%1 s\u00f6zc\u00fc\u011f\u00fc ba\u015far\u0131yla Google'da arat\u0131ld\u0131!", "googleResult": "%1Arama Sorgusu:%1\n%2%3%2\n\n%1Sonu\u00e7lar:%1\n%4", + "groupInfo": ".whois <kullan\u0131c\u0131 ad\u0131/kullan\u0131c\u0131 id>\nKullan\u0131m: Kullan\u0131c\u0131 hakk\u0131nda bilgi verir.\n\n.ginfo [iste\u011fe ba\u011fl\u0131: <grup id/grup linki (@ ile)>]\nKullan\u0131m: Verilen grup hakk\u0131nda bilgi verir.", + "groupNotFound": "Grup bulunamad\u0131!", + "groupPicChanged": "Grup resmi ba\u015far\u0131yla de\u011fi\u015ftirildi.", "groupUsage": "Bu komut sadece gruplarda kullan\u0131labilir.", + "groupinfoResult": "%1GRUP BILGISI\n\nGrup \u0130smi:%1 %2%3%2\n%1Grup ID:%1 %2%4%2\n%1DC ID:%1 %2%5%2\n%1Grup T\u00fcr\u00fc:%1 %2%6%2\n%1\u00dcye Say\u0131s\u0131:%1 %2%7%2\n%1\u00c7evrimi\u00e7i \u00dcye Say\u0131s\u0131:%1 %2%8%2\n%1Grup \u00c7\u0131kartma Paketi:%1 %9\n\n%1Grup Ba\u011flant\u0131s\u0131:%1 %10\n%1Grup A\u00e7\u0131klamas\u0131:%1 %2%11%2", "hashInfo": ".hash\nKullan\u0131m: Bir txt dosyas\u0131 yaz\u0131ld\u0131\u011f\u0131nda md5, sha1, sha256, sha512 dizelerini bulun.", "herokuInfo": ".kota\nKullan\u0131m: Heroku kaynak kullan\u0131m\u0131n\u0131z\u0131 g\u00f6sterir.\n\n.drestart\nKullan\u0131m: Heroku dynosunu yeniden ba\u015flat\u0131r.\n\n.logs\nKullan\u0131m: Heroku uygulama g\u00fcnl\u00fc\u011f\u00fc g\u00f6nderir.", "herokuQuotaInHM": "%1s %2d", "herokuQuotaInfo": "%2Heroku Kalan Kota%2\n\n%2Toplam:%2 %1%3%1\n%2Kullan\u0131lan:%2 %1%4 (\u00bd%5)%1\n%2Kalan:%2 %1%6 (\u00bd%7)%1\n%2Uygulama kullan\u0131m\u0131:%2 %1%8 (\u00bd%9)%1", + "imeiInfo": ".imeicheck <imeinumber>\nKullan\u0131m: IMEI numaras\u0131n\u0131n kay\u0131tl\u0131 olup olmad\u0131\u011f\u0131n\u0131 kontrol eder ve cihaz hakk\u0131nda bilgi verir", "imgInfo": ".img <kelime>\nKullan\u0131m: Google \u00fczerinde h\u0131zl\u0131 bir resim aramas\u0131 yapar ve ilk 5 resmi g\u00f6sterir.", "imgUsage": "Bir arama terimi girmelisiniz.", "infoWeather": "Kullan\u0131m: .havadurumu <\u015fehir-ad\u0131> veya .havadurumu\nBir b\u00f6lgenin hava durumunu verir.", - "kangstr1": "\u00c7\u0131kartmay\u0131 d\u0131zl\u0131yorum...", - "kangstr10": "Bay d\u0131zc\u0131 bu \u00e7\u0131kartmay\u0131 d\u0131zl\u0131yor...", - "kangstr11": "Sonunda Ecem'in sevece\u011fi bir \u00e7\u0131kartma d\u0131zl\u0131yorum...", - "kangstr12": "Ecem, bu d\u0131z senin i\u00e7in...", - "kangstr2": "Ya\u015fas\u0131n d\u0131zc\u0131l\u0131k...", - "kangstr3": "Bu \u00e7\u0131kartmay\u0131 kendi paketime davet ediyorum...", - "kangstr4": "Bunu d\u0131zlamam laz\u0131m...", - "kangstr5": "Hey bu g\u00fczel bir \u00e7\u0131kartma!\nHemen d\u0131zl\u0131yorum...", + "invalidProcess": "Ge\u00e7ersiz \u0130\u015flem!", + "invalidUsername": "Ge\u00e7erli bir kullan\u0131c\u0131 girin!", + "kangstr1": "\u00c7\u0131kartmay\u0131 d\u0131zl\u0131yorum\u2026", + "kangstr10": "Bay d\u0131zc\u0131 bu \u00e7\u0131kartmay\u0131 d\u0131zl\u0131yor\u2026", + "kangstr11": "Sonunda Ecem'in sevece\u011fi bir \u00e7\u0131kartma d\u0131zl\u0131yorum\u2026", + "kangstr12": "Ecem, bu d\u0131z senin i\u00e7in\u2026", + "kangstr2": "Ya\u015fas\u0131n d\u0131zc\u0131l\u0131k\u2026", + "kangstr3": "Bu \u00e7\u0131kartmay\u0131 kendi paketime davet ediyorum\u2026", + "kangstr4": "Bunu d\u0131zlamam laz\u0131m\u2026", + "kangstr5": "Hey bu g\u00fczel bir \u00e7\u0131kartma!\nHemen d\u0131zl\u0131yorum\u2026", "kangstr6": "\u00c7\u0131kartman\u0131 d\u0131zl\u0131yorum\nhahaha.", - "kangstr7": "Hey \u015furaya bak. (\u2609\uff61\u2609)!\u2192\nBen bunu d\u0131zlarken...", - "kangstr8": "G\u00fcller k\u0131rm\u0131z\u0131 menek\u015feler mavi, bu \u00e7\u0131kartmay\u0131 paketime d\u0131zlayarak haval\u0131 olaca\u011f\u0131m...", - "kangstr9": "\u00c7\u0131kartma hapsediliyor...", - "kickLog": "%1#KICK\nKULLANICI: [%2](tg://user?id=%3)\nGRUP: %4 (%1%5%6%5%1)%1", - "kickProcess": "\u00c7\u0131kart\u0131l\u0131yor...", - "kickResult": "%1[%2](tg://user?id=%3)%1 %4gruptan at\u0131ld\u0131!%4", + "kangstr7": "Hey \u015furaya bak. (\u2609\uff61\u2609)!\u2192\nBen bunu d\u0131zlarken\u2026", + "kangstr8": "G\u00fcller k\u0131rm\u0131z\u0131 menek\u015feler mavi, bu \u00e7\u0131kartmay\u0131 paketime d\u0131zlayarak haval\u0131 olaca\u011f\u0131m\u2026", + "kangstr9": "\u00c7\u0131kartma hapsediliyor\u2026", + "kickLog": "#KICK\nKULLANICI: [%1](tg://user?id=%2) (%4%2%4)\nGRUP: %3 (%4%5%4)\n%6", + "kickProcess": "\u00c7\u0131kart\u0131l\u0131yor\u2026", + "kickResult": "%1[%2](tg://user?id=%3)%1 %4gruptan at\u0131ld\u0131!%4\n%5", "kickmeResult": "G\u00fcle G\u00fcle ben gidiyorum \ud83e\udd20", + "ksuReleases": "G\u00fcncel KernelSU APK:", "langName": "T\u00fcrk\u00e7e", "lastfmApiMissing": "%1[Last.fm](https://www.last.fm/api/account/create)%1 %2API key eksik! L\u00fctfen ekleyin.%2", "lastfmInfo": ".lastfm\nKullan\u0131m: Anl\u0131k oynat\u0131lan par\u00e7a ya da en son oynat\u0131lan par\u00e7a g\u00f6sterilir.", "lastfmProcess": "[%1](%2) %3\u015fuan \u015funu dinliyor:%3\n\n\u2022 [%4](%5)\n%6%7%6", "lastfmProcess2": "[%1](%2) %3en son \u015funu dinledi:%3\n\n", "lfyResult": "\u0130\u015fte, keyfine bak.", - "loadedModules": "Y\u00fcklenecek mod\u00fcller: %1", - "loadedModules2": "Y\u00fcklenen mod\u00fcl: %1", + "listEmpty": "Liste bo\u015f!", + "loadedModules": "Y\u00fcklenecek mod\u00fcller:\n%1", "loadedModulesError": "%1 mod\u00fcl\u00fc y\u00fcklenirken bir hata olu\u015ftu.", "lockAll": "her \u015fey", "lockError": "%1Ge\u00e7ersiz medya tipi:%1 %2", - "lockGame": "oyun", - "lockGif": "GIF ve \u00e7\u0131kartma yollama", - "lockInfo": ".lock <kilitlenecek medya tipi> veya .unlock <kilitlenecek medya tipi>\nKullan\u0131m: Sohbetteki birtak\u0131m \u015feyleri engelleyebilmeni sa\u011flar. (sticker atmak, oyun oynamak vs.)\n[Not: Y\u00f6netici haklar\u0131 gerektirir!]\n\nKilitleyebilece\u011fin ve kilidini a\u00e7abileceklerin \u015funlard\u0131r:\nall, msg, media, sticker, gif, game, inline, web, poll, invite, pin, info", + "lockInfo": ".lock <kilitlenecek medya tipi> veya .unlock <kilitlenecek medya tipi>\nKullan\u0131m: Sohbetteki birtak\u0131m \u015feyleri engelleyebilmeni sa\u011flar. (sticker atmak, oyun oynamak vs.)\n[Not: Y\u00f6netici haklar\u0131 gerektirir!]\n\nKilitleyebilece\u011fin ve kilidini a\u00e7abileceklerin \u015funlard\u0131r:\nall, msg, media, other, web, poll, invite, pin, info", "lockInformation": "sohbet bilgisi de\u011fi\u015ftirme", - "lockInline": "sohbet i\u00e7i botlar", "lockInvite": "davet etme", "lockMedia": "medya yollama", "lockMsg": "mesaj atma", + "lockOther": "GIF, oyunlar, sohbet i\u00e7i botlar ve \u00e7\u0131kartma yollama", "lockPerm": "%1Bunun i\u00e7in gerekli haklara sahip oldu\u011funa emin misin?%1\n%2Hata:%2 %3", "lockPin": "sabitleme", "lockPoll": "anket yollama", @@ -267,42 +296,36 @@ "locksUnlockNoArgs": "Hi\u00e7li\u011fin kilidini a\u00e7amam dostum", "locksUnlockSuccess": "%1Bu sohbet i\u00e7in %2 kilidi a\u00e7\u0131ld\u0131!%1", "logidTest": "Bu bir test raporudur, LOG_ID kontrol\u00fc i\u00e7indir.", - "lydiaError": "Ge\u00e7ersiz kullan\u0131c\u0131.", - "lydiaError2": "Lydia AI'y\u0131 etkinle\u015ftirmek i\u00e7in bir kullan\u0131c\u0131y\u0131 yan\u0131tlay\u0131n.", - "lydiaError3": "Bu kullan\u0131c\u0131da Lydia aktif de\u011fil.", - "lydiaInfo": ".addcf <kullan\u0131c\u0131 ad\u0131/yan\u0131tlayarak>\nKullan\u0131m: Lydia'n\u0131n otomatik sohbetini etkinle\u015ftirir.\n\n.remcf <kullan\u0131c\u0131 ad\u0131/yan\u0131tlayarak>\nKullan\u0131m: Lydia'n\u0131n otomatik sohbetini devre d\u0131\u015f\u0131 b\u0131rak\u0131r.\n\n.repcf <kullan\u0131c\u0131 ad\u0131/yan\u0131tlayarak>\nKullan\u0131m: Lydia'n\u0131n otomatik sohbetiini belli bir ki\u015fi i\u00e7in etkinle\u015ftirir.", - "lydiaMissingApi": "%1[Lydia](https://coffeehouse.intellivoid.info/dashboard)%1 %2API key eksik! L\u00fctfen ekleyin.%2", - "lydiaResult": "%1Hey dostum:%1 %2", - "lydiaResult2": "%1Lydia etkinle\u015ftirildi!\nKullan\u0131c\u0131 ID:%1 %2%3%2\n%1Grup ID:%1 %2%4%2", - "lydiaResult3": "%1Lydia devre d\u0131\u015f\u0131 b\u0131rak\u0131ld\u0131!\nKullan\u0131c\u0131 ID:%1 %2%3%2 %1Grup ID:%1 %2%3%2", - "lydiaSqlLog": "Lydia mod\u00fcl\u00fc \u00e7al\u0131\u015ft\u0131r\u0131lam\u0131yor, SQL ba\u011flant\u0131s\u0131 bulunamad\u0131.", + "losBuild": "%2%3%2 %1i\u00e7in G\u00fcncel LineageOS:\nDosya: [%4](%5)\nBoyut:%1 %2%6%2\n%1S\u00fcr\u00fcm:%1 %2%7%2\n%1Tarih:%1 %2%8%2\n\n%2Di\u011fer s\u00fcr\u00fcmler i\u00e7in:%2\n%1https://download.lineageos.org/%3%1", + "losNoBuild": "%2%3%2 %1i\u00e7in uygun s\u00fcr\u00fcm bulunamad\u0131!%1", "lyricsError": "Hata: l\u00fctfen <sanat\u00e7\u0131> ve <\u015fark\u0131> i\u00e7in b\u00f6l\u00fcc\u00fc olarak '-' kullan\u0131n\n\u00d6rnek: Rota - Belki Ba\u015fka Zaman", "lyricsError2": "L\u00fctfen sanat\u00e7\u0131 ve \u015fark\u0131 ismini veriniz", "lyricsInfo": ".lyrics\nKullan\u0131m: .`lyrics <sanat\u00e7\u0131 ad\u0131> - <\u015fark\u0131 ismi>\nNOT: '-' ayrac\u0131 \u00f6nemli!", "lyricsNotFound": "\u015eark\u0131 %1%2 - %3%1 bulunamad\u0131!", "lyricsOutput": "\u015eark\u0131 s\u00f6zleri \u00e7ok uzun, g\u00f6rmek i\u00e7in dosyay\u0131 g\u00f6r\u00fcnt\u00fcleyin.", "lyricsQuery": "%1Arama sorgusu:%1 \n%2%3 - %4%2\n\n%1S\u00f6zler:%1\n%5", - "lyricsSearch": "%1%2 - %3 i\u00e7in \u015fark\u0131 s\u00f6zleri aran\u0131yor...%1", + "lyricsSearch": "%1%2 - %3 i\u00e7in \u015fark\u0131 s\u00f6zleri aran\u0131yor\u20261", "magiskReleases": "G\u00fcncel Magisk s\u00fcr\u00fcmleri:", - "makeQuote": "Al\u0131nt\u0131 yap\u0131l\u0131yor ...", + "makeQuote": "Al\u0131nt\u0131 yap\u0131l\u0131yor\u2026", "makeqrInfo": ".makeqr <metin>\nKullan\u0131m: Verilen i\u00e7erikten bir QR kodu yap\u0131n.\n\u00d6rnek: .makeqr https://devotag.com\n\nNot: \u00c7\u00f6z\u00fclm\u00fc\u015f i\u00e7erik almak i\u00e7in .decode komutunu kullan\u0131n.", "makeqrUsage": "%1Kullan\u0131m:%1 %2.makeqr <metin>%2", "mediaInvalid": "Medya ge\u00e7erli de\u011fil.", - "memesInfo": ".cowsay\nKullan\u0131m: bir \u015feyler s\u00f6yleyen inek.\n\n:/\nKullan\u0131m: Kendinizi kontrol edin ;)\n\n.cp\nKullan\u0131m: Me\u015fhur copypasta mod\u00fcl\u00fc\n\n.vapor\nKullan\u0131m: Her \u015feyi vaporla\u015ft\u0131r\u0131n!\n\n.str\nKullan\u0131m: Mesaj\u0131 iyice uzat\u0131n.\n\n.10iq\nKullan\u0131m: Aptall\u0131k seviyenizi \u00f6l\u00e7\u00fcn !!\n\n.mizah\nKullan\u0131m: Aptall\u0131k seviyenizi \u00f6l\u00e7\u00fcn !!\n\n.zal\nKullan\u0131m: Kaos duygusunu \u00e7a\u011f\u0131r\u0131n.\n\noof\nKullan\u0131m: Ooooof\n\nskrrt\nKullan\u0131m: skrrrrt\n\n.owo\nKullan\u0131m: UwU\n\n.react\nKullan\u0131m: UserBot'un her \u015feye tepki vermesini sa\u011flay\u0131n.\n\n.cry\nKullan\u0131m: bunu yaparsan, her zaman a\u011flar\u0131m.\n\n.shg\nKullan\u0131m: \ud83e\udd37\ud83c\udffb\u200d\u2642\ufe0f\n\n.run\nKullan\u0131m: Seden UserBot'un ko\u015fmas\u0131n\u0131 sa\u011flar!\n\n.mock\nKullan\u0131m: Yap ve ger\u00e7ek e\u011flenceyi bul.\n\n.clap\nKullan\u0131m: \u0130nsanlar\u0131 \u00f6v\u00fcn!\n\n.f <emoji/karakter>\nKullan\u0131m: Sayg\u0131lar..\n\n.type\nKullan\u0131m: Klavyenizi daktilo haline getirmek i\u00e7in k\u00fc\u00e7\u00fck bir komut!\n\n.lfy <sorgu>\nKullan\u0131m: B\u0131rak\u0131n Google bunu sizin i\u00e7in ara\u015ft\u0131rs\u0131n.\n\n.xda veya .xda ile birinin metnine cevap verin.\nKullan\u0131m: XDA'n\u0131n me\u015fhur s\u00f6zleri.", + "memesInfo": ".cowsay\nKullan\u0131m: bir \u015feyler s\u00f6yleyen inek.\n\n:/\nKullan\u0131m: Kendinizi kontrol edin ;)\n\n.cp\nKullan\u0131m: Me\u015fhur copypasta mod\u00fcl\u00fc\n\n.vapor\nKullan\u0131m: Her \u015feyi vaporla\u015ft\u0131r\u0131n!\n\n.str\nKullan\u0131m: Mesaj\u0131 iyice uzat\u0131n.\n\n.10iq\nKullan\u0131m: Aptall\u0131k seviyenizi \u00f6l\u00e7\u00fcn !!\n\n.mizah\nKullan\u0131m: Aptall\u0131k seviyenizi \u00f6l\u00e7\u00fcn !!\n\n.zal\nKullan\u0131m: Kaos duygusunu \u00e7a\u011f\u0131r\u0131n.\n\noof\nKullan\u0131m: Ooooof\n\nskrrt\nKullan\u0131m: skrrrrt\n\n.owo\nKullan\u0131m: UwU\n\n.react\nKullan\u0131m: UserBot'un her \u015feye tepki vermesini sa\u011flay\u0131n.\n\n.cry\nKullan\u0131m: bunu yaparsan, her zaman a\u011flar\u0131m.\n\n.shg\nKullan\u0131m: \ud83e\udd37\ud83c\udffb\u200d\u2642\ufe0f\n\n.run\nKullan\u0131m: Seden UserBot'un ko\u015fmas\u0131n\u0131 sa\u011flar!\n\n.mock\nKullan\u0131m: Yap ve ger\u00e7ek e\u011flenceyi bul.\n\n.clap\nKullan\u0131m: \u0130nsanlar\u0131 \u00f6v\u00fcn!\n\n.f <emoji/karakter>\nKullan\u0131m: Sayg\u0131lar..\n\n.type\nKullan\u0131m: Klavyenizi daktilo haline getirmek i\u00e7in k\u00fc\u00e7\u00fck bir komut!\n\n.lfy <sorgu>\nKullan\u0131m: B\u0131rak\u0131n Google bunu sizin i\u00e7in ara\u015ft\u0131rs\u0131n.\n\n.xda veya .xda ile birinin metnine cevap verin.\nKullan\u0131m: XDA'n\u0131n me\u015fhur s\u00f6zleri.\n\n.amogus <metin>\nKullan\u0131m: Yaz\u0131lan metni amogus yapar\n\n.mem <\u00fcst metin>,<alt metin>\nKullan\u0131m: Alt ve \u00fcst metin ile caps yapar", "mirrorError": "Hata: link i\u00e7in farkl\u0131 mirror bulunamad\u0131", - "miscInfo": ".id\nKullan\u0131m: Belirlenen kullan\u0131c\u0131n\u0131n ID numaras\u0131n\u0131 verir.\n\n.chatid\nKullan\u0131m: Belirlenen grubun ID numaras\u0131n\u0131 verir\n\n.kickme\nKullan\u0131m: Belirlenen gruptan ayr\u0131lman\u0131z\u0131 sa\u011flar.\n\n.repeat <say\u0131> <metin>\nKullan\u0131m: Bir metni belli bir say\u0131da tekrar eder. Spam komutu ile kar\u0131\u015ft\u0131rma!\n\n.random <e\u015fya1> <e\u015fya2> ... <e\u015fyaN>\nKullan\u0131m: E\u015fya listesinden rastgele bir e\u015fya se\u00e7er\n\n.founder\nKullan\u0131m: Bu g\u00fczel botu kimlerin olu\u015fturdu\u011funu \u00f6\u011fren :-)\n\n.support\nKullan\u0131m: Yard\u0131ma ihtiyac\u0131n olursa bu komutu kullan.\n\n.repo\nKullan\u0131m: Seden UserBot GitHub Repo\n\n.readme\nKullan\u0131m: Seden botunun GitHub'daki README dosyas\u0131na giden bir ba\u011flant\u0131.\n\n.tagall\nKullan\u0131m: Bu komutu kulland\u0131\u011f\u0131n\u0131zda sohbet i\u00e7erisinde ki herkesi etiketler.\n\n.admin\nKullan\u0131m: Bu komutu kulland\u0131\u011f\u0131n\u0131zda sohbet i\u00e7erisinde ki y\u00f6neticileri etiketler.", + "miscInfo": ".id\nKullan\u0131m: Belirlenen kullan\u0131c\u0131n\u0131n ID numaras\u0131n\u0131 verir.\n\n.chatid\nKullan\u0131m: Belirlenen grubun ID numaras\u0131n\u0131 verir\n\n.kickme\nKullan\u0131m: Belirlenen gruptan ayr\u0131lman\u0131z\u0131 sa\u011flar.\n\n.repeat <say\u0131> <metin>\nKullan\u0131m: Bir metni belli bir say\u0131da tekrar eder. Spam komutu ile kar\u0131\u015ft\u0131rma!\n\n.random <e\u015fya1> <e\u015fya2> \u2026 <e\u015fyaN>\nKullan\u0131m: E\u015fya listesinden rastgele bir e\u015fya se\u00e7er\n\n.founder\nKullan\u0131m: Bu g\u00fczel botu kimlerin olu\u015fturdu\u011funu \u00f6\u011fren :-)\n\n.support\nKullan\u0131m: Yard\u0131ma ihtiyac\u0131n olursa bu komutu kullan.\n\n.repo\nKullan\u0131m: Seden UserBot GitHub Depo\n\n.readme\nKullan\u0131m: Seden botunun GitHub'daki README dosyas\u0131na giden bir ba\u011flant\u0131.\n\n.tagall\nKullan\u0131m: Bu komutu kulland\u0131\u011f\u0131n\u0131zda sohbet i\u00e7erisinde ki herkesi etiketler.\n\n.admin\nKullan\u0131m: Bu komutu kulland\u0131\u011f\u0131n\u0131zda sohbet i\u00e7erisinde ki y\u00f6neticileri etiketler.\n\n.ascii\nKullan\u0131m: Yan\u0131tlanan foto\u011fraf veya \u00e7\u0131kartmay\u0131 ASCII olarak g\u00f6nderir.\n\n.invitelink\nKullan\u0131m: Mevcut grubun davet ba\u011flant\u0131s\u0131 verir.", "mockUsage": "bANa bIr mETin vEr!", - "muteLog": "%1#MUTE\nKULLANICI: [%2](tg://user?id=%3)\nGRUP: %4 (%1%5%6%5%1)%1", - "muteProcess": "Sessize al\u0131n\u0131yor...", - "muteResult": "%1[%2](tg://user?id=%3)%1 %4susturuldu!%4", + "muteLog": "#MUTE\nKULLANICI: [%1](tg://user?id=%2) (%4%2%4)\nGRUP: %3 (%4%5%4)\n%6", + "muteProcess": "Sessize al\u0131n\u0131yor\u2026", + "muteResult": "%1[%2](tg://user?id=%3)%1 %4susturuldu!%4\n%5", "nameOk": "Ad\u0131n ba\u015far\u0131yla de\u011fi\u015ftirildi.", - "nasaResult": "Ba\u011flan\u0131yor..", - "nasaResult2": "Sinyal Kaybedildi....", "neofetchNotFound": "L\u00fctfen neofetch y\u00fckleyin.", "noFilter": "Bu sohbette hi\u00e7 filtre yok.", "noNote": "Bu sohbette kaydedilmi\u015f not bulunamad\u0131", "noSnip": "No snip is currently available.", + "noWelcome": "No welcome message found in this chat", + "noWelcome2": "I didn't have any welcome messages here!", "nonSqlMode": "SQL d\u0131\u015f\u0131 modda \u00e7al\u0131\u015f\u0131yorum, bunu ger\u00e7ekle\u015ftiremem", + "notFound": "Bulunamad\u0131", "notHeroku": "Bot \u015fu an Heroku'da \u00e7al\u0131\u015fm\u0131yor", "notSet": "Ayarlanmam\u0131\u015f!", "noteError": "Mesaj y\u00f6nlendirilemedi ve not eklenemedi.", @@ -320,26 +343,28 @@ "ocrApiMissing": "%1[%2](https://ocr.space/ocrapi)%1 %3API key eksik! L\u00fctfen ekleyin.%3", "ocrError": "Bunu okuyamad\u0131m.\nSan\u0131r\u0131m yeni g\u00f6zl\u00fcklere ihtiyac\u0131m var.", "ocrInfo": ".ocr <dil>\nKullan\u0131m: Metin ay\u0131klamak i\u00e7in bir resme veya \u00e7\u0131kartmaya cevap verin.\n\nDil kodlar\u0131n\u0131 [buradan](https://ocr.space/ocrapi) al\u0131n.", - "ocrReading": "Okunuyor...", + "ocrReading": "Okunuyor\u2026", "ocrResult": "%1\u0130\u015fte okuyabildi\u011fim \u015fey:%1\n\n%2", - "ofrpConnect": "OrangeFox sunucular\u0131na ba\u011flan\u0131l\u0131yor ...", + "ofrpConnect": "OrangeFox sunucular\u0131na ba\u011flan\u0131l\u0131yor\u2026", "ofrpError": "Muhtemelen bu liste bo\u015f.", "ofrpErrorDate": "Tarih bilgisi \u00e7ekilirken bir hata olu\u015ftu", - "ofrpNotFound": "%1%2 kod ad\u0131 muhtemelen resmi bir cihaza ait de\u011fil.%1 %3 %1adresinden kontrol edebilirsiniz.%1", + "ofrpNotFound": "%1%2 kod ad\u0131 muhtemelen resmi bir cihaza ait de\u011fil.%1\n%3%1adresinden kontrol edebilirsiniz.%1", "ofrpUrl": "https://orangefox.download/tr-TR", "ofrpUsage": "Kullan\u0131m: .orangefox <kod ad\u0131> \u00d6rnek: .orangefox raphael", + "onlySupportGdrive": "Yanl\u0131zca Google Drive linkleri destekleniyor.", "outputTooLarge": "\u00c7\u0131kt\u0131 \u00e7ok b\u00fcy\u00fck, dosya olarak g\u00f6nderiliyor.", "owoUsage": "UwU bana bir metin ver!", "packFull": "%1Yetersiz alandan dolay\u0131%1 %2%3%2 %1numaral\u0131 pakete ge\u00e7iliyor..%1", "packinfoErrer2": "Paket detaylar\u0131n\u0131 g\u00f6rmek i\u00e7in bir \u00e7\u0131kartmay\u0131 yan\u0131tlay\u0131n", "packinfoError": "Hi\u00e7likten bir bilgi \u00e7ekemem, sence yapabilir miyim?!", "packinfoResult": "%1\u00c7\u0131kartma paketi ba\u015fl\u0131\u011f\u0131:%1 %2%3%2\n%1\u00c7\u0131kartma paketi k\u0131sa ad\u0131:%1 %2%4%2\n%1Resmi paket mi:%1 %2%5%2\n%1Ar\u015fivlenmi\u015f mi:%1 %2%6%2\n%1Animasyonlu mu:%1 %2%7%2\n%1Pakettki \u00e7\u0131kartma say\u0131s\u0131:%1 %2%8%2\n%1Paketteki emoji say\u0131s\u0131:%1\n%9", + "pasteConnErr": "Sunucuya ba\u011flan\u0131lamad\u0131", + "pasteErr": "Yap\u0131\u015ft\u0131rmak i\u00e7in bir \u015fey yaz\u0131n.", + "pasteInfo": ".paste <metin/yan\u0131tlama>\nKullan\u0131m: Verilen metni Pastebine'e yap\u0131\u015ft\u0131r\u0131r\n\n.getpaste <metin/yan\u0131tlama>\nKullan\u0131m: Pastebin link i\u00e7eri\u011fini metne aktar\u0131r\n\n(https://dpaste.org/)", "phhError": "%2%3%2 %1aramas\u0131 i\u00e7in bir ROM bulunamad\u0131%1", "picspamLog": "#PICSPAM\nPicSpam ba\u015far\u0131yla ger\u00e7ekle\u015ftirildi", - "pinLog": "%1#PIN\nGRUP: %2 (%1%3%4%3%1)%1", + "pinLog": "#PIN\nGRUP: %1 (%2%3%2)", "pinResult": "Ba\u015far\u0131yla sabitlendi!", - "pipHelp": "Bir \u00f6rnek g\u00f6rmek i\u00e7in .seden system komutunu kullan\u0131n.", - "pipSearch": "Aran\u0131yor...", "pmApproveError": "\u015eu an bir PM'de de\u011filsin ve birinin mesaj\u0131n\u0131 al\u0131nt\u0131lamad\u0131n.", "pmApproveError2": "Kullan\u0131c\u0131 halihaz\u0131rda PM g\u00f6nderebiliyor olmal\u0131d\u0131r.", "pmApproveLog": "#ONAYLANDI\nKullan\u0131c\u0131: [%1](tg://user?id=%2)", @@ -353,61 +378,65 @@ "pmUnblocked": "Engelin kald\u0131r\u0131ld\u0131.", "pmUnblockedLog": "[%1](tg://user?id=%2) ki\u015fisinin engeli kald\u0131r\u0131ld\u0131.", "pmUnblockedUsage": "Engelini kald\u0131raca\u011f\u0131n ki\u015finin mesaj\u0131n\u0131 al\u0131nt\u0131lamal\u0131s\u0131n.", - "pmpermitBlock": "Sen benim sahibimin PM'ini spaml\u0131yorsun, bu benim ho\u015fuma gitmiyor.\n\u015eu an ENGELLENDIN, ileride de\u011fi\u015fiklik olmad\u0131\u011f\u0131 s\u00fcrece...", + "pmpermitBlock": "Sen benim sahibimin PM'ini spaml\u0131yorsun, bu benim ho\u015fuma gitmiyor.\n\u015eu an ENGELLENDIN ve SPAM olarak bildirildin, ileride de\u011fi\u015fiklik olmad\u0131\u011f\u0131 s\u00fcrece\u2026", "pmpermitInfo": ".approve\nKullan\u0131m: Yan\u0131t verilen kullan\u0131c\u0131ya PM atma izni verilir.\n\n.disapprove\nKullan\u0131m: Yan\u0131t verilen kullan\u0131c\u0131n\u0131n PM onay\u0131n\u0131 kald\u0131r\u0131r.\n\n.notifoff\nKullan\u0131m: Onaylanmam\u0131\u015f \u00f6zel mesajlar\u0131n bildirimlerini temizler ya da devre d\u0131\u015f\u0131 b\u0131rak\u0131r.\n\n.notifon\nKullan\u0131m: Onaylanmam\u0131\u015f \u00f6zel mesajlar\u0131n bildirim g\u00f6ndermesine izin verir.", "pmpermitLog": "[%1](tg://user?id=%2) ki\u015fisi sadece bir hayal k\u0131r\u0131kl\u0131\u011f\u0131yd\u0131.\nPM'ni me\u015fgul etti\u011fi i\u00e7in engellendi.", "pmpermitMessage": "%1Hey! Bu bir bot. Endi\u015felenme.\n\nSahibim sana PM atma izni vermedi.\nL\u00fctfen sahibimin aktif olmas\u0131n\u0131 bekleyin, o genellikle PM'leri onaylar.\n\nBildi\u011fim kadar\u0131yla o kafay\u0131 yemi\u015f insanlara PM izni vermiyor.%1", "pmpermitSqlLog": "Pmpermit mod\u00fcl\u00fc \u00e7al\u0131\u015ft\u0131r\u0131lam\u0131yor, SQL ba\u011flant\u0131s\u0131 bulunamad\u0131", + "ppChanged": "Profil resmi ba\u015far\u0131yla de\u011fi\u015ftirildi.", + "ppDeleted": "%1 adet profil foto\u011fraf\u0131 silindi.", "ppError": "Resim i\u015flenirken bir hata olu\u015ftu.", - "ppchanged": "Profil resmi ba\u015far\u0131yla de\u011fi\u015ftirildi.", + "ppSmall": "G\u00f6r\u00fcnt\u00fc \u00e7ok k\u00fc\u00e7\u00fck!", "privacySettings": "Gizlilik ayarlar\u0131 bunu yapmama engel oldu.", "privateUsage": "Bu komut sadece \u00f6zelde kullan\u0131labilir.", - "processing": "\u0130\u015fleniyor...", - "profileInfo": ".username <yeni kullan\u0131c\u0131 ad\u0131>\nKullan\u0131m: Telegram'daki kullan\u0131c\u0131 ad\u0131n\u0131z\u0131 de\u011fi\u015fir.\n\n.name <isim> veya .name <isim> <soyisim>\nKullan\u0131m: Telegram'daki isminizi de\u011fi\u015fir. (Ad ve soyad ilk bo\u015flu\u011fa dayanarak birle\u015ftirilir.)\n\n.setbio <yeni biyografi>\nKullan\u0131m: Telegram'daki biyografinizi bu komutu kullanarak de\u011fi\u015ftirir.\n\n.reserved\nKullan\u0131m: Rezerve etti\u011finiz kullan\u0131c\u0131 adlar\u0131n\u0131 g\u00f6sterir.\n\n.block\nKullan\u0131m: Bir kullan\u0131c\u0131y\u0131 engeller.\n\n.unblock\nKullan\u0131m: Engellenmi\u015f kullan\u0131c\u0131n\u0131n engelini kald\u0131r\u0131r.", - "promoteLog": "%1#PROMOTE\nKULLANICI: [%2](tg://user?id=%3)\nGRUP: %4 (%1%5%6%5%1)%1", - "promoteProcess": "Yetkilendiriliyor...", + "processing": "\u0130\u015fleniyor\u2026", + "profileInfo": ".username <yeni kullan\u0131c\u0131 ad\u0131>\nKullan\u0131m: Telegram'daki kullan\u0131c\u0131 ad\u0131n\u0131z\u0131 de\u011fi\u015fir.\n\n.name <isim> veya .name <isim> <soyisim>\nKullan\u0131m: Telegram'daki isminizi de\u011fi\u015fir. (Ad ve soyad ilk bo\u015flu\u011fa dayanarak birle\u015ftirilir.)\n\n.setbio <yeni biyografi>\nKullan\u0131m: Telegram'daki biyografinizi bu komutu kullanarak de\u011fi\u015ftirir.\n\n.reserved\nKullan\u0131m: Rezerve etti\u011finiz kullan\u0131c\u0131 adlar\u0131n\u0131 g\u00f6sterir.\n\n.block\nKullan\u0131m: Bir kullan\u0131c\u0131y\u0131 engeller.\n\n.unblock\nKullan\u0131m: Engellenmi\u015f kullan\u0131c\u0131n\u0131n engelini kald\u0131r\u0131r.\n\n.online <disable>\nKullan\u0131m: Hesab\u0131n\u0131z\u0131, siz Telegram'da \u00e7evrimi\u00e7i olmasan\u0131z bile \u00e7evrimi\u00e7i g\u00f6sterir.\nA\u00e7mak i\u00e7in .online, kapatmak i\u00e7in .online disable yaz\u0131n.\n\nNOT: Etkili olmas\u0131 i\u00e7in son g\u00f6r\u00fclmenizi herkese a\u00e7\u0131k yap\u0131n.", + "promoteLog": "#PROMOTE\nKULLANICI: [%1](tg://user?id=%2)\nGRUP: %3 (%4%5%4)", + "promoteProcess": "Yetkilendiriliyor\u2026", "promoteResult": "%1[%2](tg://user?id=%3)%1 %4art\u0131k y\u00f6netici!%4", - "providedProxy": "Proxy ba\u011flant\u0131s\u0131 sa\u011flan\u0131yor ...", - "purgeError": "%1Bir sorun olu\u015ftu...%1\n\n%2%3%2", + "providedProxy": "Proxy ba\u011flant\u0131s\u0131 sa\u011flan\u0131yor\u2026", + "purgeError": "%1Bir sorun olu\u015ftu\u2026%1\n\n%2%3%2", "purgeInfo": ".purge\nKullan\u0131m: Hedeflenen yan\u0131ttan ba\u015flayarak t\u00fcm mesajlar\u0131 temizler.", "purgeLog": "%1Hedeflenen%1 %2%3%2 %1mesaj ba\u015far\u0131yla silindi.%1", "purgeResult": "%1Temizlik tamamland\u0131!%1\n%2%3%2 %1tane mesaj silindi.%1", "purgeUsage": "Temizlemeye ba\u015flamak i\u00e7in bir mesaja ihtiyac\u0131m var.", "purgemeInfo": ".purgeme <X>\nKullan\u0131m: Hedeflenen yan\u0131ttan ba\u015flayarak t\u00fcm mesajlar\u0131 temizler..", "purgemeUsage": "Temizlik yap\u0131lamad\u0131, say\u0131 ge\u00e7ersiz.", + "pyrogramDown": "%1Dosya Ad\u0131:%1 %2%3%2\n%1\u0130ndirildi:%1 %2%4%2", + "pyrogramUp": "%1Dosya Ad\u0131:%1 %2%3%2\n%1Y\u00fcklendi%1 %2%4%2", "pythonVersionError": "En az Python 3.8 s\u00fcr\u00fcm\u00fcne sahip olman\u0131z gerekir.\nBirden fazla \u00f6zellik buna ba\u011fl\u0131d\u0131r. Bot kapat\u0131l\u0131yor.", "quotlyInfo": ".q\nKullan\u0131m: Metninizi \u00e7\u0131kartmaya d\u00f6n\u00fc\u015ft\u00fcr\u00fcn.", "randomResult": "%1Sorgu:%1\n%2%3%2\n%1\u00c7\u0131kt\u0131:%1\n%2%4%2", "randomUsage": "2 veya daha fazla se\u00e7enek gerekli.", "rbgApiMissing": "%1[%2](https://www.remove.bg/api)%1 %3API key eksik! L\u00fctfen ekleyin.%3", "rbgInfo": ".rbg herhangi bir foto\u011fraf\u0131 yan\u0131tlay\u0131n (Uyar\u0131: \u00e7\u0131kartmalar \u00fczerinde \u00e7al\u0131\u015fmaz.)\nKullan\u0131m: Remove.bg API kullanarak g\u00f6r\u00fcnt\u00fclerin arka plan\u0131n\u0131 kald\u0131r\u0131r.", - "rbgLog": "hata.log", + "rbgLog": "hata.txt", "rbgProcessing": "Bu g\u00f6r\u00fcnt\u00fcden arka plan kald\u0131r\u0131l\u0131yor..", "rbgResult": "Remove.bg kullan\u0131larak arka plan kald\u0131r\u0131ld\u0131", - "rbgUsage": "Bir g\u00f6r\u00fcnt\u00fcye yan\u0131t verin...", - "removeFirstLine": "L\u00fctfen ilk belirtilen sat\u0131r\u0131 config.env dosyas\u0131ndan kald\u0131r\u0131n", + "rbgUsage": "Bir g\u00f6r\u00fcnt\u00fcye yan\u0131t verin\u2026", "replyMessage": "Bir mesaja yan\u0131t verin.", "replySticker": "L\u00fctfen bir \u00e7\u0131kartmay\u0131 al\u0131nt\u0131lay\u0131n.", - "restart": "Yeniden ba\u015flat\u0131l\u0131yor ...", + "reqDown": "%1Dosya Ad\u0131:%1 %2%3%2\n%1\u0130ndirildi:%1 %2%4%2", + "restart": "Yeniden ba\u015flat\u0131l\u0131yor\u2026", "restartLog": "#RESTART\nBot yeniden ba\u015flat\u0131ld\u0131.", "reverseError": "Desteklenmeyen t\u00fcr", "reverseError2": "\u00c7irkin k\u0131\u00e7\u0131n i\u00e7in bir \u015fey bulamad\u0131m.", "reverseGoogle": "Google siktirip gitmemi s\u00f6yledi.", "reverseInfo": ".reverse\nKullan\u0131m: Foto\u011fraf veya \u00e7\u0131kartmaya yan\u0131t vererek g\u00f6r\u00fcnt\u00fcy\u00fc Google \u00fczerinden arayabilirsiniz", "reverseProcess": "G\u00f6r\u00fcnt\u00fc ba\u015far\u0131yla Google'a y\u00fcklendi. \u015eimdi kaynak ayr\u0131\u015ft\u0131r\u0131l\u0131yor.", - "reverseResult": "[%1](%2)\n%3\n\nResim ar\u0131yorum...\n%3", + "reverseResult": "[%1](%2)\n%3\n\nResim ar\u0131yorum\u2026\n%3", "reverseResult2": "[%1](%2)\n\n[Benzer g\u00f6r\u00fcnt\u00fcler](%3)", "reverseUsage": "L\u00fctfen bir foto\u011frafa veya \u00e7\u0131kartmaya yan\u0131t verin.", "rgbInfo": ".rgb\nKullan\u0131m: Metninizi RGB \u00e7\u0131kartmaya d\u00f6n\u00fc\u015ft\u00fcr\u00fcn.", - "rgbProcessing": "Resme d\u00f6n\u00fc\u015ft\u00fcr\u00fcl\u00fcyor...", + "rgbProcessing": "Resme d\u00f6n\u00fc\u015ft\u00fcr\u00fcl\u00fcyor\u2026", "runningBot": "Botun \u00e7al\u0131\u015f\u0131yor! Herhangi bir sohbete .alive yazarak test edebilirsin, .seden yazarak mod\u00fcllerin listesini alabilirsin. Yard\u0131ma ihtiyac\u0131n varsa, destek grubumuza bakabilirsin https://t.me/%1", "runstr1": "Hey! Nereye gidiyorsun?", - "runstr10": "Bunu yapt\u0131\u011f\u0131na pi\u015fman olacaks\u0131n...", + "runstr10": "Bunu yapt\u0131\u011f\u0131na pi\u015fman olacaks\u0131n\u2026", "runstr11": "/kickme tu\u015funuda deneyebilirsin, E\u011flenceli oldu\u011funu s\u00f6yl\u00fcyorlar.", "runstr12": "Git ba\u015fka birini rahats\u0131z et, burda kimse takm\u0131yor.", "runstr13": "Ka\u00e7abilirsin ama saklanamazs\u0131n.", "runstr14": "Yapabildiklerin bunlar m\u0131?", - "runstr15": "Arkanday\u0131m...", + "runstr15": "Arkanday\u0131m\u2026\u2026\u2026", "runstr16": "Misafirlerin var!", "runstr17": "Bunu kolay yoldan yapabiliriz, yada zor yoldan.", "runstr18": "Anlam\u0131yorsun, de\u011fil mi?", @@ -422,8 +451,8 @@ "runstr26": "\"Hey, bana bak\u0131n! Bottan ka\u00e7abiliyorum \u00e7ok haval\u0131y\u0131m!\" - bu ki\u015fi", "runstr27": "Evet evet, /kickme tu\u015funa \u015fimdiden bas.", "runstr28": "\u0130\u015fte, bu y\u00fcz\u00fc\u011f\u00fc al\u0131n ve Mordor'a gidin.", - "runstr29": "Efsaneye g\u00f6re onlar hala \u00e7al\u0131\u015f\u0131yor...", - "runstr3": "ZZzzZZzz... Noldu? oh, yine onlarm\u0131\u015f, bo\u015fver.", + "runstr29": "Efsaneye g\u00f6re onlar hala \u00e7al\u0131\u015f\u0131yor\u2026", + "runstr3": "ZZzzZZzz\u2026 Noldu? oh, yine onlarm\u0131\u015f, bo\u015fver.", "runstr30": "Harry Potter'\u0131n aksine, ebeveynlerin seni benden koruyamaz.", "runstr31": "Korku \u00f6fkeye, \u00f6fke nefrete, nefret ac\u0131ya yol a\u00e7ar. Korku i\u00e7inde ka\u00e7maya devam edersen, bir sonraki Vader sen olabilirsin.", "runstr32": "Birden fazla hesaplama yap\u0131ld\u0131ktan sonra, dalaverelerine olan ilgimin tam olarak 0 oldu\u011funa karar verdim.", @@ -437,26 +466,19 @@ "runstr4": "Geri gel!", "runstr40": "Ah, ne b\u00fcy\u00fck kay\u0131p. Bu seferkini sevmi\u015ftim.", "runstr41": "A\u00e7\u0131kcas\u0131 can\u0131m, umrumda de\u011fil.", - "runstr42": "S\u00fct\u00fcm t\u00fcm erkekleri avluya \u00e7ekiyor... Daha h\u0131zl\u0131 ko\u015f!", + "runstr42": "S\u00fct\u00fcm t\u00fcm erkekleri avluya \u00e7ekiyor\u2026 Daha h\u0131zl\u0131 ko\u015f!", "runstr43": "Ger\u00e7e\u011fi KALDIRAMAZSIN!", "runstr44": "Uzun zaman \u00f6nce, \u00e7ok \u00e7ok uzaktaki bir galakside birileri takabilirdi. Ama art\u0131k de\u011fil.", - "runstr45": "Hey, onlara bak! Ka\u00e7\u0131n\u0131lmaz banhammer'dan ka\u00e7\u0131yorlar... Ne kadarda tatl\u0131.", + "runstr45": "Hey, onlara bak! Ka\u00e7\u0131n\u0131lmaz banhammer'dan ka\u00e7\u0131yorlar\u2026 Ne kadarda tatl\u0131.", "runstr46": "Han \u00f6nce vuruldu. Ben de \u00f6yle yapaca\u011f\u0131m", "runstr47": "Beyaz tav\u015fan\u0131n, arkas\u0131nda ne yap\u0131yorsun?", - "runstr48": "Doktorunda s\u00f6yleyece\u011fi gibi... KA\u00c7!", + "runstr48": "Doktorunda s\u00f6yleyece\u011fi gibi\u2026 KA\u00c7!", "runstr5": "Ka\u00e7\u0131n OneBot geliyor !!", "runstr6": "Duvara dikkat et!", "runstr7": "Beni onlarla sak\u0131n yaln\u0131z b\u0131rakma!!", "runstr8": "Ka\u00e7arsan, \u00f6l\u00fcrs\u00fcn.", "runstr9": "\u015eakac\u0131 seni, Ben heryerdeyim.", "safeEval": "Bu g\u00fcvenli bir eval sorgusu olmayabilir.", - "sangmataInfo": ".sangmata\nKullan\u0131m: Belirtilen kullan\u0131c\u0131n\u0131n isim ge\u00e7mi\u015fini g\u00f6r\u00fcnt\u00fcleyin.", - "scraper1": "\u00c7eviri", - "scraper2": "Yaz\u0131dan sese", - "scraperLog": "%1%2 mod\u00fcl\u00fc i\u00e7in varsay\u0131lan dil %3 diline \u00e7evirildi.%1", - "scraperResult": "%1%2 mod\u00fcl\u00fc i\u00e7in varsay\u0131lan dil %3 diline \u00e7evirildi.%1", - "scraperTrt": "%1Ge\u00e7ersiz dil kodu!%1\n%1Ge\u00e7erli dil kodlar\u0131:%1\n\n%1%2%1", - "scraperTts": "%1Ge\u00e7ersiz dil kodu!%1\n%1Ge\u00e7erli dil kodlar\u0131:%1\n\n%1%2%1", "sedError": "Bunun i\u00e7in yeterli zek\u00e2ya sahip de\u011filim.", "sedError2": "Bu bir yan\u0131tlama. Sed kullanma", "sedInfo": "sed<s\u0131n\u0131rlay\u0131c\u0131><eski kelime(ler)><s\u0131n\u0131rlay\u0131c\u0131><yeni kelime(ler)>\nKullan\u0131m: Sed kullanarak bir kelimeyi veya kelimeleri de\u011fi\u015ftirir.\nS\u0131n\u0131rlay\u0131c\u0131lar: /, :, |, _", @@ -464,22 +486,25 @@ "sedResult": "Bunu mu demek istedin ? \n\n%1", "sedenAlive": "Merhaba Seden! Seni Seviyorum \u2764\ufe0f", "sedenErrorResult": "Sorgu eksik veya hatal\u0131", - "sedenErrorText": "%1SEDENBOT HATA RAPORU%1\n\u0130sterseniz, bunu rapor edebilirsiniz\nsadece bu mesaj\u0131 buraya iletin %2.\nHata ve Tarih d\u0131\u015f\u0131nda hi\u00e7bir \u015fey kaydedilmez\n", - "sedenErrorText2": "========== UYARI ==========\nBu dosya sadece burada y\u00fcklendi,\nsadece hata ve tarih k\u0131sm\u0131n\u0131 kaydettik,\ngizlili\u011finize sayg\u0131 duyuyoruz,\nburada herhangi bir gizli veri varsa\nbu hata raporu olmayabilir, kimse verilerinize ula\u015famaz.\n================================\n\n--------SEDENBOT HATA GUNLUGU--------\n\nTarih: %1\nGrup ID: %2\nG\u00f6nderen ki\u015finin ID: %3\nSeden s\u00fcr\u00fcm\u00fc: %4\n\nOlay Tetikleyici:\n%5\n\nGeri izleme bilgisi::\n%6\n\nHata metni:\n%7\n\n--------SEDENBOT HATA GUNLUGU BITIS--------\n\n\nSon 10 commit:\n", + "sedenErrorText": "%1HATA:%1\n\n%2%3%2\n\n%1Detaylar i\u00e7in dosyaya bak\u0131n\u0131z.%1", + "sedenErrorText2": "-------- DETAYLAR --------\n\nTarih: %1\nGrup ID: %2\nG\u00f6nderen ki\u015finin ID: %3\nSeden s\u00fcr\u00fcm\u00fc: %4\n\nHata Tetikleyici:\n%5\n\nGeri izleme bilgisi:\n%6\n\nHata metni:\n%7\n\n----------------------\n\n\nSon 10 commit:\n", "sedenGitNotFound": "Bu arada Seden seni \u00e7ok seviyor \u2764\ufe0f", "sedenNearestDC": "%1\u00dclke:%1 %2%3%2\n%1En Yak\u0131n Veri Merkezi:%1 %2%4%2\n%1\u015eu Anki Veri Merkezi:%1 %2%5%2", "sedenQuery": "%1Sorgu:%1\n%2%3%2\n%1Sonu\u00e7:%1\n%2%4%2\n", "sedenQueryUd": "%1Sorgu:%1\n%2%3%2\n%1S\u00f6yleyi\u015f:%1\n%2%4%2\n\n%1\u00d6rnek:%1\n%2%5%2", "sedenSetAlive": "Alive mesaj\u0131, %1 olarak ayarland\u0131", - "sedenShowBotVersion": "%1[%3](https://t.me/%4)%1\n%1S\u00fcr\u00fcm:%1 %2v%5%2\n%1De\u011fi\u015fiklikler:%1 %2%6%2", + "sedenShowBotVersion": "%1[Seden UserBot](https://t.me/%3)%1\n%1S\u00fcr\u00fcm:%1 %2v%4%2\n%1De\u011fi\u015fiklikler:%1 %2%5%2", "sedenShowLoadedModules": "%1Y\u00fcklenen mod\u00fcl say\u0131s\u0131:%1 %2%3%2\n%1Mod\u00fcller:%1", "sedenUsage": "L\u00fctfen bir Seden mod\u00fcl\u00fc ad\u0131 belirtin.", "sedenUsage2": "%1L\u00fctfen hangi Seden mod\u00fcl\u00fc i\u00e7in yard\u0131m istedi\u011finizi belirtin!\nKullan\u0131m:%1 %2.seden <mod\u00fcl ad\u0131>%2", "sedenVersion": "Bot s\u00fcr\u00fcm\u00fc; Seden v%1", - "sedenZeroResults": "Hi\u00e7bir \u015fey bulunamad\u0131.", - "shutdown": "Ben kapan\u0131yorum, g\u00f6r\u00fc\u015f\u00fcr\u00fcz ...", + "shippingMovements": "\n\n%1%3Son hareket%4%2\n\n%5Yer: %7\nDurum: %8\nTarih: %9\nZaman: %10\n\u0130\u015flem: %11%6", + "shippingNoResult": "Takip bilgisi bulunamad\u0131!", + "shippingResult": "%1Firma:%2 %3%5%4\n%1Takip No:%2 %3%6%4\n%1Durum:%2 %3%7%4\n%1G\u00f6nderici:%2 %3%8%4\n%1Al\u0131c\u0131:%2 %3%9%4\n%1G\u00f6nderim yeri:%2 %3%10%4\n%1Al\u0131m yeri:%2 %3%11%4\n%1G\u00f6nderi tarihi:%2 %3%12%4\n%1Teslim tarihi:%2 %3%13%4", + "shippingTrack": ".<firma> <takipNo>\n\nKullan\u0131m: Kargo bilgilerini g\u00f6sterir.\n\n\u00d6rnek: `.ups 1234567890`\n\nDesteklenen firmalar:\n`mng, ptt, ups, aras, surat, yurtici, hepsijet`", + "showWelcome": "\u015eu anda a\u015fa\u011f\u0131daki kar\u015f\u0131lama mesaj\u0131 ile yeni kullan\u0131c\u0131lar\u0131 a\u011f\u0131rl\u0131yorum", + "shutdown": "Ben kapan\u0131yorum, g\u00f6r\u00fc\u015f\u00fcr\u00fcz\u2026", "shutdownLog": "#SHUTDOWN\nBot kapat\u0131ld\u0131.", - "smallingResult": "K\u00fc\u00e7\u00fcl\u00fcyor...", "snipChats": "Mevcut snipler:", "snipError": "Mesaj y\u00f6nlendirilemedi ve k\u00fcresel not eklenemedi.", "snipError2": "K\u00fcresel not getirilirken bir sorun olu\u015ftu!", @@ -492,9 +517,10 @@ "snipsRemoved": "%2Genel not%2 $%1%3%1 %2kald\u0131r\u0131ld\u0131%2", "snipsSqlLog": "Snips mod\u00fcl\u00fc \u00e7al\u0131\u015ft\u0131r\u0131lam\u0131yor, SQL ba\u011flant\u0131s\u0131 bulunamad\u0131", "snipsUpdated": "%1Genel not ba\u015far\u0131yla g\u00fcncellendi. Notu .call $%2 yazarak \u00e7a\u011f\u0131rabilirsiniz%1", - "solarResult": "Ay ve G\u00fcne\u015f", "spamInfo": ".tspam <metin>\nKullan\u0131m: Verilen mesaj\u0131 tek tek g\u00f6ndererek spam yapar\n\n.spam <miktar> <metin>\nKullan\u0131m: Verilen miktarda spam g\u00f6nderir\n\n.picspam <miktar> <link>\nKullan\u0131m: Verilen miktarda resimli spam g\u00f6nderir\n\n.delayspam <gecikme> <miktar> <metin>\nKullan\u0131m: Verilen miktar ve verilen gecikme ile gecikmeli spam yapar\n\n\nNOT: Sorumluluk size aittir !", "spamLog": "#SPAM\nSpam ba\u015far\u0131yla ger\u00e7ekle\u015ftirildi", + "spamWatchBan": "#SPAMWATCH\nRaporlanm\u0131\u015f kullan\u0131c\u0131 [%1](tg://user?id=%2) ba\u015far\u0131yla engellendi", + "spamWatchInfo": "Bir SpamWatch anahtar\u0131 ekledi\u011finizde kendili\u011finden \u00e7al\u0131\u015fmaya ba\u015flar", "spamWrong": "Bir \u015feyler eksik/yanl\u0131\u015f gibi g\u00f6r\u00fcn\u00fcyor.", "specsError": "Bu cihaza dair bir bilgi bulunamad\u0131 veya \u00e7ok fazla istek att\u0131n\u0131z.", "specsError2": "Bilgi al\u0131namad\u0131", @@ -504,41 +530,44 @@ "speedtestInfo": ".speedtest\nKullan\u0131m: Bir speedtest uygular ve sonucu g\u00f6sterir.", "speedtestResultDoc": "%1SpeedTest%1 %2 saniyede tamamland\u0131!", "speedtestResultText": "%1SpeedTest%1 %2 saniyede tamamland\u0131!\n\u0130ndirme: %3\nY\u00fckleme: %4\nGecikme: %5\nISS: %6\nISS puan\u0131: %7\n%8", + "spotifyInfo": "Kullan\u0131c\u0131 bilgilerini g\u00f6sterir\n.spoti|spotify show <username>\n\nPlaylist indirir ve zipliyip g\u00f6nderir.\n.spoti|spotify dl zip <playlisturl>\n\nPlaylist indirir ve ziplemeden g\u00f6nderir.\n.spoti|spotify dl <playlisturl>", + "spotifyResult": "%1Kullan\u0131c\u0131 Ad\u0131:%1 %2%3%2\n%1Profil Ba\u011flant\u0131s\u0131: %4\n\u00c7alma Listeleri:%1\n%2%5%2\n\n%1Toplamda%1 %2%6%2 %1adet \u00e7alma listesi bulundu!%1", "ssInfo": ".ss <link>\nKullan\u0131m: Belirtilen web sitesinden bir ekran g\u00f6r\u00fcnt\u00fcs\u00fc al\u0131r ve g\u00f6nderir.\nGe\u00e7erli bir site ba\u011flant\u0131s\u0131 \u00f6rne\u011fi: https://devotag.com", - "ssResult": "Sayfan\u0131n ekran g\u00f6r\u00fcnt\u00fcs\u00fc olu\u015fturuluyor...\nSayfan\u0131n y\u00fcksekli\u011fi: %1 piksel\nSayfan\u0131n geni\u015fli\u011fi: %2 piksel\nSayfan\u0131n y\u00fcklenmesi i\u00e7in %3 saniye beklendi.", - "ssUpload": "Ekran g\u00f6r\u00fcnt\u00fcs\u00fc kar\u015f\u0131ya y\u00fckleniyor ...", + "ssResult": "Sayfan\u0131n ekran g\u00f6r\u00fcnt\u00fcs\u00fc olu\u015fturuluyor\u2026\nSayfan\u0131n y\u00fcksekli\u011fi: %1 piksel\nSayfan\u0131n geni\u015fli\u011fi: %2 piksel\nSayfan\u0131n y\u00fcklenmesi i\u00e7in %3 saniye beklendi.", + "ssUpload": "Ekran g\u00f6r\u00fcnt\u00fcs\u00fc kar\u015f\u0131ya y\u00fckleniyor\u2026", "ssUsage": "Ekran g\u00f6r\u00fcnt\u00fcs\u00fc alabilmem i\u00e7in ge\u00e7erli bir ba\u011flant\u0131 vermelisin.", + "statsResult": "%1Toplam Sohbet:%1 %2%3%2\n%1Kanallar:%1 %2%4%2\n%1Gruplar:%1 %2%5%2\n%1S\u00fcper Gruplar:%1 %2%6%2\n%1Botlar:%1 %2%7%2\n%1\u00d6zel Mesajlar:%1 %2%8%2\n%1Okunmam\u0131\u015f Mesajlar:%1 %2%9%2", "statusLong": "Uzun zaman \u00f6nce", "statusMonth": "Bir ay i\u00e7inde", + "statusOffline": "\u00c7evrimd\u0131\u015f\u0131", "statusOnline": "\u00c7evrimi\u00e7i", "statusRecently": "Yak\u0131nlarda", "statusWeek": "Bir hafta i\u00e7inde", "stickerAdded": "%1Ba\u015far\u0131yla d\u0131zland\u0131.%1\nEri\u015fmek i\u00e7in [buraya](https://t.me/addstickers/%2) dokunun.", "stickerError": "Bunu d\u0131zlayamam.", - "stickerInfo": ".d\u0131zla\nKullan\u0131m: .d\u0131zla ile bir \u00e7\u0131kartmaya ya da resme yan\u0131tlayarak kendi \u00e7\u0131kartma paketinize \u00e7\u0131kartma olarak ekleyebilirsiniz.\n\n.d\u0131zla [numara]\nKullan\u0131m: \u00c7\u0131kartmay\u0131 ya da resmi belirtilen pakete ekler fakat emoji olarak \u015fu kullan\u0131l\u0131r: \ud83e\udd24\n\n.getsticker\nKullan\u0131m: Yan\u0131tlanan \u00e7\u0131kartmay\u0131 dosya bi\u00e7iminde g\u00f6nderir.\n\n.packinfo\nKullan\u0131m: \u00c7\u0131kartma paketi hakk\u0131nda bilgi verir.", + "stickerInfo": ".d\u0131zla [\u0130ste\u011fe ba\u011fl\u0131: <Paket Numaras\u0131> <Emoji>]\nKullan\u0131m: .d\u0131zla ile bir \u00e7\u0131kartmaya ya da resme yan\u0131tlayarak kendi \u00e7\u0131kartma paketinize \u00e7\u0131kartma olarak ekleyebilirsiniz. Varsay\u0131lan emoji \ud83e\udd24 kullan\u0131l\u0131r.\n\n.getsticker [-v (ayr\u0131nt\u0131lar)]\nKullan\u0131m: Yan\u0131tlanan \u00e7\u0131kartmay\u0131 dosya bi\u00e7iminde g\u00f6nderir.\n\n.packinfo\nKullan\u0131m: \u00c7\u0131kartma paketi hakk\u0131nda bilgi verir.", "stickerPackFull": "%1 numaral\u0131 paket dolu.", "stickerUsage": "Bana d\u0131zlayabilece\u011fim bir \u015fey ver.", "strUsage": "Baaaaanaaaaa biiiiir meeeeetiiiiin veeeeer!", - "supportGroup": "[Seden Destek Grubu](https://telegram.dog/%1)", + "sudoCheck": "Bu ki\u015fi Seden yetkilisi!", "supportResult": "[Buradan](http://t.me/%1) destek grubumuza ula\u015fabilirsiniz.", "syntaxError": "S\u00f6zdizimi hatas\u0131.", - "systemInfo": ".alive\nKullan\u0131m: Seden botunun \u00e7al\u0131\u015f\u0131p \u00e7al\u0131\u015fmad\u0131\u011f\u0131n\u0131 kontrol etmek i\u00e7in kullan\u0131l\u0131r.\n\n.ping\nKullan\u0131m: Botun ping de\u011ferini g\u00f6sterir.\n\n.echo\nKullan\u0131m: Yazd\u0131\u011f\u0131n\u0131z metni tekrar eder.\n\n.botver\nKullan\u0131m: Seden UserBot s\u00fcr\u00fcm\u00fcn\u00fc g\u00f6sterir.\n\n.dc\nKullan\u0131m: Sunucunuza en yak\u0131n veri merkezini g\u00f6sterir.\n\n.neofetch\nKullan\u0131m: Neofetch komutunu kullanarak sistem bilgisi g\u00f6sterir.\n\n.pip <mod\u00fcl ismi>\nKullan\u0131m: Pip mod\u00fcllerinde arama yapar.\n\n.eval 2 + 3\nKullan\u0131m: Mini ifadeleri de\u011ferlendirin.\n\n.term echo Merhaba Seden!\nKullan\u0131m: Sunucunuzda bash komutlar\u0131n\u0131 ve komut dosyalar\u0131n\u0131 \u00e7al\u0131\u015ft\u0131r\u0131n.\n\n.restart\nKullan\u0131m: Botu yeniden ba\u015flat\u0131r.\n\n.kapat\nKullan\u0131m: Bazen can\u0131n botunu kapatmak ister. Ger\u00e7ekten o nostaljik Windows XP kapan\u0131\u015f sesini duyabilece\u011fini zannedersin...", + "systemInfo": ".alive\nKullan\u0131m: Botunun \u00e7al\u0131\u015f\u0131p \u00e7al\u0131\u015fmad\u0131\u011f\u0131n\u0131 kontrol etmek i\u00e7in kullan\u0131l\u0131r.\n\n.ping\nKullan\u0131m: Botun ping de\u011ferini g\u00f6sterir.\n\n.echo\nKullan\u0131m: Yazd\u0131\u011f\u0131n\u0131z metni tekrar eder.\n\n.botver\nKullan\u0131m: Seden UserBot s\u00fcr\u00fcm\u00fcn\u00fc g\u00f6sterir.\n\n.dc\nKullan\u0131m: Sunucunuza en yak\u0131n veri merkezini g\u00f6sterir.\n\n.neofetch\nKullan\u0131m: Neofetch komutunu kullanarak sistem bilgisi g\u00f6sterir.\n\n.eval 2 + 3\nKullan\u0131m: Mini ifadeleri de\u011ferlendirin.\n\n.term echo Merhaba Seden!\nKullan\u0131m: Sunucunuzda bash komutlar\u0131n\u0131 ve komut dosyalar\u0131n\u0131 \u00e7al\u0131\u015ft\u0131r\u0131n.\n\n.restart\nKullan\u0131m: Botu yeniden ba\u015flat\u0131r.\n\n.kapat\nKullan\u0131m: Bazen can\u0131n botunu kapatmak ister. Ger\u00e7ekten o nostaljik Windows XP kapan\u0131\u015f sesini duyabilece\u011fini zannedersin\u2026", "termHelp": "Yard\u0131m almak i\u00e7in .seden system yazarak \u00f6rne\u011fe bakabilirsin.", "termLog": "Terminal komutu %1 ba\u015far\u0131yla \u00e7al\u0131\u015ft\u0131r\u0131ld\u0131", "termNoResult": "Komutun sonucu al\u0131namad\u0131.", "termUsage": "L\u00fctfen bir komut yaz\u0131n.", "testException": "Bu bir test, hata kayd\u0131 g\u00f6ndermeyin.", - "testLogId": "LOG_ID de\u011feri test ediliyor ...", - "title": "%1Ba\u015fl\u0131k%2%1", + "testLogId": "LOG_ID de\u011feri test ediliyor\u2026", + "tgUpLimit": "%1Dosya boyutu fazla oldu\u011fu i\u00e7in bunu yapamam.%1", "transHeader": "%1Kaynak:%1 %2%3%2\n%1Hedef:%1 %2%4%2\n", + "translatorInfo": ".trt <metin>\nKullan\u0131m: Basit bir \u00e7eviri mod\u00fcl\u00fc.\n\n.tts <metin>\nKullan\u0131m: Metni sese d\u00f6n\u00fc\u015ft\u00fcr\u00fcr.", "trtError": "Ayarlanan hedef dil ge\u00e7ersiz.", - "trtInfo": ".trt <metin>\nKullan\u0131m: Basit bir \u00e7eviri mod\u00fcl\u00fc.\n.lang trt komutuyla varsay\u0131lan dili ayarlayabilirsin. (T\u00fcrk\u00e7e ayarl\u0131 geliyor merak etme)", "trtLog": "Birka\u00e7 %1 kelime az \u00f6nce %2 diline \u00e7evirildi.", "trtUsage": "Bana \u00e7evirilecek bir metin ver!", "tspamLog": "#TSPAM\nTSpam ba\u015far\u0131yla ger\u00e7ekle\u015ftirildi", "ttsBlank": "Metin bo\u015f.\n\u00d6n i\u015fleme, tokenizasyon ve temizlikten sonra konu\u015facak hi\u00e7bir \u015fey kalmad\u0131.", "ttsError": "Dilin s\u00f6zl\u00fc\u011f\u00fcn\u00fc g\u00f6r\u00fcnt\u00fclemede bir hata ger\u00e7ekle\u015fti.", - "ttsInfo": ".tts <metin>\nKullan\u0131m: Metni sese d\u00f6n\u00fc\u015ft\u00fcr\u00fcr.\n.lang tts komutuyla varsay\u0131lan dili ayarlayabilirsin. (T\u00fcrk\u00e7e ayarl\u0131 geliyor merak etme)", "ttsLog": "Metin ba\u015far\u0131yla sese d\u00f6n\u00fc\u015ft\u00fcr\u00fcld\u00fc!", "ttsNoSupport": "Bu dil hen\u00fcz desteklenmiyor.", "ttsUsage": "Yaz\u0131dan sese \u00e7evirmek i\u00e7in bir metin gir.", @@ -547,63 +576,80 @@ "twrpUsage": "Kullan\u0131m: .twrp <kod ad\u0131> \u00d6rnek: .twrp raphael", "udInfo": ".ud <terim>\nKullan\u0131m: Urban Dictionary aramas\u0131 yapman\u0131n kolay yolu.", "udNoResult": "%2 %1i\u00e7in hi\u00e7bir sonu\u00e7 bulunamad\u0131%1", + "udNotFound": "\u0130fade bulunamad\u0131.", "udResult": "\u00dczg\u00fcn\u00fcm, %1%2%1 i\u00e7in hi\u00e7bir sonu\u00e7 bulunamad\u0131.", - "unbanProcess": "Yasak kald\u0131r\u0131l\u0131yor...", + "unbanProcess": "Yasak kald\u0131r\u0131l\u0131yor\u2026", "unbanResult": "%1[%2](tg://user?id=%3)%1 %4i\u00e7in yasaklama ba\u015far\u0131yla kald\u0131r\u0131ld\u0131!%4", "unblockChat": "%2L\u00fctfen%2 %1@%3%1 %2engelini kald\u0131r\u0131n ve tekrar deneyin%2", - "unmuteProcess": "Sessizden \u00e7\u0131kar\u0131l\u0131yor...", + "unmuteProcess": "Sessizden \u00e7\u0131kar\u0131l\u0131yor\u2026", "unmuteResult": "%1[%2](tg://user?id=%3)%1 %4tekrardan konu\u015fabilir!%4", - "updateBotUpdating": "SedenBot G\u00fcncelleniyor...\nBu i\u015flem 1-2 dakika s\u00fcrebilir, l\u00fctfen sab\u0131rla bekle. Beklemene de\u011fer :)", - "updateCheck": "SedenBot i\u00e7in g\u00fcncellemeler denetleniyor...", - "updateComplete": "G\u00fcncelleme ba\u015far\u0131yla tamamland\u0131!\nSedenBot yeniden ba\u015flat\u0131l\u0131yor, sab\u0131rla bekledi\u011fin i\u00e7in te\u015fekk\u00fcr ederiz :)", - "updateCustomBranch": "%1[SedenBot G\u00fcncelleyici]:%1` Galiba botunun branch ismini de\u011fi\u015ftirdin. Kulland\u0131\u011f\u0131n branch ismi: %2. B\u00f6yle olursa botunu g\u00fcncelleyemem. \u00c7\u00fcnk\u00fc branch ismi uyu\u015fmuyor..\nL\u00fctfen botunu SedenBot resmi repodan kullan.", + "updateBotUpdating": "Bot g\u00fcncelleniyor\u2026", + "updateCheck": "G\u00fcncellemeler denetleniyor\u2026", + "updateComplete": "G\u00fcncelleme tamamland\u0131!\nYeniden ba\u015flat\u0131l\u0131yor.", "updateFailed": "G\u00fcncelleme ba\u015far\u0131s\u0131z oldu! Baz\u0131 sorunlarla kar\u015f\u0131la\u015ft\u0131k.", "updateFolderError": "%1\n%2%3 klas\u00f6r\u00fc bulunamad\u0131.%2", - "updateForceSync": "G\u00fcncel SedenBot kodu zorla e\u015fitleniyor...", + "updateForceSync": "G\u00fcncel depo kodu zorla e\u015fitleniyor\u2026", "updateGitError": "%1\n%2Git hatas\u0131! %3%2", - "updateGitNotFound": "%1 klas\u00f6r\u00fc bir git reposu gibi g\u00f6r\u00fcnm\u00fcyor.\nFakat bu sorunu .update now komutuyla botu zorla g\u00fcncelleyerek \u00e7\u00f6zebilirsin.", - "updateHerokuAppName": "SedenBot G\u00fcncelleyiciyi kullanabilmek i\u00e7in HEROKU_APPNAME de\u011fi\u015fkenini tan\u0131mlamal\u0131s\u0131n. Aksi halde g\u00fcncelleyici \u00e7al\u0131\u015fmaz.", + "updateGitNotFound": "%1 klas\u00f6r\u00fc bir git deposu gibi g\u00f6r\u00fcnm\u00fcyor.\nFakat bu sorunu .update now komutuyla botu zorla g\u00fcncelleyerek \u00e7\u00f6zebilirsin.", + "updateHerokuApiKey": "Heroku API anahtar\u0131 hatal\u0131 oldu\u011fundan bu i\u015flemi ger\u00e7ekle\u015ftiremiyorum. L\u00fctfen HEROKU_KEY de\u011fi\u015fkenini d\u00fczenleyerek API anahtar\u0131n\u0131 de\u011fi\u015ftirin", + "updateHerokuAppName": "G\u00fcncelleme yapabilmek i\u00e7in HEROKU_APPNAME de\u011fi\u015fkenini tan\u0131mlamal\u0131s\u0131n.", "updateHerokuVariables": "%1\nHeroku de\u011fi\u015fkenleri yanl\u0131\u015f veya eksik tan\u0131mlanm\u0131\u015f.", "updateInfo": ".update\nKullan\u0131m: Botunuza siz kurduktan sonra herhangi bir g\u00fcncelleme gelip gelmedi\u011fini kontrol eder.\n\n.update now\nKullan\u0131m: Botunuzu g\u00fcnceller.", - "updateLocalComplate": "G\u00fcncelleme ba\u015far\u0131yla tamamland\u0131!\nSedenBot yeniden ba\u015flat\u0131l\u0131yor.", + "updateLocalComplate": "G\u00fcncelleme tamamland\u0131!\nYeniden ba\u015flat\u0131l\u0131yor.", "updateLog": "LOG:", "updateNow": "%1G\u00fcncellemeyi yapmak i\u00e7in%1 %2.update now%2 %1komutunu kullan.%1", "updateOutput": "De\u011fi\u015fiklik listesi \u00e7ok b\u00fcy\u00fck, dosya olarak g\u00f6r\u00fcnt\u00fclemelisin.", - "updateSedenBot": "Bot g\u00fcncelle\u015ftiriliyor, l\u00fctfen bekle....", + "updateSedenBot": "Bot g\u00fcncelle\u015ftiriliyor, l\u00fctfen bekle\u2026", "updated": "g\u00fcncellendi", "updaterGitError": "%2\n%1Hata kayd\u0131:%1\n%3", "updaterHasUpdate": "%1%3 i\u00e7in g\u00fcncelleme mevcut!\n\nDe\u011fi\u015fiklik listesi:%1\n%4", "updaterUsingLatest": "%2Botunuz%2 %1g\u00fcncel%1 %2Dal:%2 %1%3%1", - "updownDownload": "%1\u0130ndiriliyor ... %2%1", + "updownDownload": "%1\u0130ndiriliyor\u2026 %2%1", "updownDownloadSuccess": "Ba\u015far\u0131yla %1%2%1 konumuna indirildi", - "updownUpload": "%1Y\u00fckleniyor ... %2%1\n%3", + "updownUpload": "%1Y\u00fckleniyor\u2026 %2%1\n%3", "uploadError": "Dosya y\u00fcklenemedi.", "uploadFileError": "Dosya bulunamad\u0131.", "uploadFinish": "Y\u00fckleme tamamland\u0131!", "uploadInfo": ".download <bir \u015feye cevap vererek>\nKullan\u0131m: Sunucuya dosyay\u0131 indirir.\n\n.upload <sunucudaki dosya yolu>\nKullan\u0131m: Sunucunuzdaki bir dosyay\u0131 sohbete upload eder.", - "uploadMedia": "Medya y\u00fckleniyor...", + "uploadMedia": "Medya y\u00fckleniyor\u2026", "uploadReply": "Buraya hi\u00e7li\u011fi y\u00fckleyemem.", - "uploader": "%1Y\u00fckleyen%2%1", + "uploadingZip": "Zip dosyas\u0131 g\u00f6nderiliyor\u2026", "urlError": "Hata: link \u00e7\u0131kar\u0131lam\u0131yor", + "userNotFound": "%1Kullan\u0131c\u0131%1 %2%3%2 %1bulunamad\u0131%1", "useridResult": "%1Kullan\u0131c\u0131 Ad\u0131:%1 %2\n%1Kullan\u0131c\u0131 ID:%1 %3%4%3", "userlist": "%3%4%3 %1i\u00e7erisinde bulunan %2 kullan\u0131c\u0131lar:%1", "usernameSuccess": "Kullan\u0131c\u0131 ad\u0131n ba\u015far\u0131yla de\u011fi\u015ftirildi.", "usernameTaken": "Kullan\u0131c\u0131 ad\u0131 m\u00fcsait de\u011fil.", "vaporUsage": "\uff22\uff41\uff4e\uff41 \uff42\uff49\uff52 \uff4d\uff45\uff54\uff49\uff4e \uff56\uff45\uff52!", + "videoTitle": "Ba\u015fl\u0131k:", + "videoUploader": "Y\u00fckleyen:", "weatherErrorCity": "WEATHER de\u011fi\u015fkeniyle bir \u015fehri varsay\u0131lan olarak belirt, ya da komutu yazarken hangi \u015fehrin hava durumunu istedi\u011fini de belirt!", "weatherErrorServer": "Hava durumu bilgisi al\u0131namad\u0131.", + "welcomeAdded": "Kar\u015f\u0131lama mesaj\u0131 kaydedildi!", + "welcomeError": "Kar\u015f\u0131lama mesaj\u0131 eklenemedi!", + "welcomeInfo": ".setwelcome <metin/yan\u0131tlama>\nUsage: Kullan\u0131m: Mesaj\u0131 sohbete kar\u015f\u0131lama mesaj\u0131 olarak kaydeder\n\nKar\u015f\u0131lama mesaj\u0131 i\u00e7in kullan\u0131labilir de\u011fi\u015fkenler:\n{mention}, {title}, {first}, {last}, {userid}, {username}, {my_first}, {my_last}, {my_mention}, {my_username}\n\n.checkwelcome\nKullan\u0131m: Sohbette kar\u015f\u0131lama mesaj\u0131 olup olmad\u0131\u011f\u0131n\u0131 kontrol eder\n\n.delwelcome\nKullan\u0131m: Ge\u00e7erli sohbet i\u00e7in kar\u015f\u0131lama mesaj\u0131n\u0131 siler", + "welcomeLog": "#WELCOME\nGrup ID: %1%2%1\n\nA\u015fa\u011f\u0131daki mesaj sohbet i\u00e7in yeni Kar\u015f\u0131lama Mesaj\u0131 olarak kaydedildi, l\u00fctfen silmeyin!", + "welcomeSqlLog": "Welcomes mod\u00fcl\u00fc \u00e7al\u0131\u015ft\u0131r\u0131lam\u0131yor, SQL ba\u011flant\u0131s\u0131 bulunamad\u0131", + "welcomeUpdated": "Kar\u015f\u0131lama mesaj\u0131 g\u00fcncellendi!", "whoisError": "Kullan\u0131c\u0131 bulunamad\u0131..", - "whoisInfo": ".whois\nKullan\u0131m: Kullan\u0131c\u0131n\u0131n bilgilerini al\u0131r.", "whoisProcess": "Kullan\u0131c\u0131 bilgisi getiriliyor..", - "whoisResult": "%1Kullan\u0131c\u0131 Bilgisi:\n\n\u0130sim:%1 %2%3%2\n%1Soyisim:%1 %2%4%2\n%1Kullan\u0131c\u0131 Ad\u0131: %5\nKullan\u0131c\u0131 ID:%1 %2%6%2\n%1Profil Foto\u011fraf Say\u0131s\u0131:%1 %2%7%2\n%1DC ID:%1 %2%8%2\n%1Bot Mu:%1 %2%9%2\n%1K\u0131s\u0131tl\u0131 M\u0131:%1 %2%10%2\n%1Telegram Taraf\u0131ndan Onayl\u0131 M\u0131:%1 %2%11%2\n%1Ortak Sohbetler:%1 %2%12%2\n\n%1Biyografi:%1 %2%13%2\n%1Son G\u00f6r\u00fclme:%1 %2%14%2\n%1Profil Ba\u011flant\u0131s\u0131: [%3](tg://user?id=%6)%1", - "wikiError": "Belirsiz bir sayfa bulundu.\n\n%1", - "wikiError2": "Arad\u0131\u011f\u0131n\u0131z sayfa bulunamad\u0131.\n\n%1", + "whoisResult": "%1Kullan\u0131c\u0131 Bilgisi:\n\n\u0130sim:%1 %2%3%2\n%1Soyisim:%1 %2%4%2\n%1Kullan\u0131c\u0131 Ad\u0131: %5\nKullan\u0131c\u0131 ID:%1 %2%6%2\n%1Profil Foto\u011fraf Say\u0131s\u0131:%1 %2%7%2\n%1DC ID:%1 %2%8%2\n%1Ortak Sohbetler:%1 %2%9%2\n%1Premium Kullan\u0131c\u0131:%1 %2%10%2\n\n%1Biyografi:%1 %2%11%2\n%1Son G\u00f6r\u00fclme:%1 %2%12%2\n%1Profil Ba\u011flant\u0131s\u0131: [%3](tg://user?id=%6)\n\n%13\n%14%1", + "wikiError": "Arad\u0131\u011f\u0131n\u0131z sayfa bulunamad\u0131.", "wikiInfo": ".wiki <terim>\nKullan\u0131m: Bir Vikipedi aramas\u0131 ger\u00e7ekle\u015ftirir.", "wikiLog": "%1%2%1 teriminin Wikipedia sorgusu ba\u015far\u0131yla ger\u00e7ekle\u015ftirildi!", "wrongCommand": "Komut kullan\u0131m\u0131 hatal\u0131.", "wrongFilter": "Filtre hatal\u0131!", "wrongMedia": "Ge\u00e7ersiz medya t\u00fcr\u00fc!", + "wrongUrl": "Hatal\u0131 link!", "yadiskError": "Hata: Dosya bulunamad\u0131 / \u0130ndirme limiti a\u015f\u0131lm\u0131\u015ft\u0131r", "youtubedlInfo": ".youtubedl <mp3 ya da mp4> <link>\nKullan\u0131m: Verdi\u011finiz linki video ya da ses olarak indirir.", - "zalUsage": "\uff22\u036c\u033a\uff41\u0351\u0320\uff4e\u0335\u0309\uff41\u032c\u035c \uff42\u0354\u0336\uff49\u033c\u035a\uff52\u0348\u035e \uff4d\u033c\u0358\uff45\u0328\u031d\uff54\u0354\u0359\uff49\u036e\u0322\uff4e\u031c\u0357 \uff56\u0362\u035c\uff45\u0350\u0317\uff52\u036e\u0334" + "zalUsage": "\uff22\u036c\u033a\uff41\u0351\u0320\uff4e\u0335\u0309\uff41\u032c\u035c \uff42\u0354\u0336\uff49\u033c\u035a\uff52\u0348\u035e \uff4d\u033c\u0358\uff45\u0328\u031d\uff54\u0354\u0359\uff49\u036e\u0322\uff4e\u031c\u0357 \uff56\u0362\u035c\uff45\u0350\u0317\uff52\u036e\u0334", + "zombiesError": "Yeterli yetkim bulunmamakta.", + "zombiesFind": "Silinen hesaplar bulunuyor\u2026", + "zombiesFound": "%1Bu grupta%1 %2%3%2 %1adet silinen hesap bulundu.\nTemizlemek i\u00e7in%1 %2.zombies clean%2 %1komutunu kullan.%1", + "zombiesLog": "#TEMIZLIK\n%2%3%2 %1tane silinen hesap at\u0131ld\u0131.\nGRUP: %4%1 | %2%5%2", + "zombiesNoAccount": "Silinen hesap(lar) bulunamad\u0131!", + "zombiesRemove": "Silinen hesap(lar) \u00e7\u0131kar\u0131l\u0131yor\u2026", + "zombiesResult": "%2%3%2 %1adet silinen hesap gruptan at\u0131ld\u0131.%1", + "zombiesResult2": "%2%3%2 %1adet silinen hesap gruptan at\u0131ld\u0131.%1\n%2%4%2 %1adet silinen y\u00f6netici hesab\u0131 at\u0131lamad\u0131.%1" } \ No newline at end of file diff --git a/session.py b/session.py index b005d48..4da9b3e 100644 --- a/session.py +++ b/session.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2021 TeamDerUntergang <https://github.com/TeamDerUntergang> +# Copyright (C) 2020-2024 TeamDerUntergang <https://github.com/TeamDerUntergang> # # This file is part of TeamDerUntergang project, # and licensed under GNU Affero General Public License v3. @@ -7,89 +7,88 @@ # All rights reserved. See COPYING, AUTHORS. # +from os import environ +from os.path import isfile + +from dotenv import load_dotenv from pyrogram import Client -lang = input('Select lang (tr, en): ').lower() +if isfile('config.env'): + load_dotenv('config.env') -if lang == 'en': - print('''\nPlease go to my.telegram.org -Login using your Telegram account -Click on API Development Tools -Create a new application, by entering the required details\n''') - API_ID = '' - API_HASH = '' +def select_lang(): + lang = '' + while lang not in ('tr', 'en'): + lang = input('Select lang (tr, en): ').lower() + return lang + - while not API_ID.isdigit() or len(API_ID) < 5 or len(API_ID) > 7: +def get_api_hash(): + API_ID = environ.get('API_ID', '') + API_HASH = environ.get('API_HASH', '') + + while not API_ID.isdigit() or len(API_ID) < 5 or len(API_ID) > 8: API_ID = input('API ID: ') API_ID = int(API_ID) while len(API_HASH) != 32: API_HASH = input('API HASH: ') + return API_ID, API_HASH - app = Client( - 'sedenuserbot', - api_id=API_ID, - api_hash=API_HASH, - app_version='Seden UserBot', - device_model='DerUntergang', - system_version='Session', - lang_code='en', - ) +def get_session(): + if lang == 'en': + print( + '''\nPlease go to my.telegram.org +Login using your Telegram account +Click on API Development Tools +Create a new application, by entering the required details\n''' + ) + elif lang == 'tr': + print( + '''Lütfen my.telegram.org adresine gidin +Telegram hesabınızı kullanarak giriş yapın +API Development Tools kısmına tıklayın +Gerekli ayrıntıları girerek yeni bir uygulama oluşturun\n''' + ) + + API_ID, API_HASH = get_api_hash() + app = Client('sedenify', api_id=API_ID, api_hash=API_HASH) with app: self = app.get_me() session = app.export_session_string() - out = f'''**Hi [{self.first_name}](tg://user?id={self.id}) + if lang == 'en': + out = f'''**Hi [{self.first_name}](tg://user?id={self.id}) \nAPI_ID:** `{API_ID}` \n**API_HASH:** `{API_HASH}` \n**SESSION:** `{session}` \n**NOTE: Don't give your account information to others!**''' - app.send_message('me', out) - print('''Session successfully generated! -Please check your Telegram Saved Messages''') - - -elif lang == 'tr': - print('''Lütfen my.telegram.org adresine gidin -Telegram hesabınızı kullanarak giriş yapın -API Development Tools kısmına tıklayın -Gerekli ayrıntıları girerek yeni bir uygulama oluşturun\n''') - - API_ID = '' - API_HASH = '' - - while not API_ID.isdigit() or len(API_ID) < 5 or len(API_ID) > 7: - API_ID = input('API ID: ') - - API_ID = int(API_ID) - - while len(API_HASH) != 32: - API_HASH = input('API HASH: ') - - app = Client( - 'sedenuserbot', - api_id=API_ID, - api_hash=API_HASH, - app_version='Seden UserBot', - device_model='DerUntergang', - system_version='Session', - lang_code='tr', - ) - - with app: - self = app.get_me() - session = app.export_session_string() - out = f'''**Merhaba [{self.first_name}](tg://user?id={self.id}) + out2 = 'Session successfully generated!' + elif lang == 'tr': + out = f'''**Merhaba [{self.first_name}](tg://user?id={self.id}) \nAPI_ID:** `{API_ID}` \n**API_HASH:** `{API_HASH}` \n**SESSION:** `{session}` \n**NOT: Hesap bilgileriniz başkalarına vermeyin!**''' - app.send_message('me', out) - print('''Session başarıyla oluşturuldu! -Lütfen Telegram Kayıtlı Mesajlarınızı kontrol edin.''') - - -else: - print('\nWhat? Please select en or tr') + out2 = 'Session başarıyla oluşturuldu!' + + if self.is_bot: + print(f'{session}\n{out2}') + else: + app.send_message('me', out) + if lang == 'en': + print( + '''Session successfully generated! +Please check your Telegram Saved Messages''' + ) + elif lang == 'tr': + print( + '''Session başarıyla oluşturuldu! +Lütfen Telegram Kayıtlı Mesajlarınızı kontrol edin.''' + ) + + +lang = select_lang() +get_session() diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..7ea70b0 --- /dev/null +++ b/shell.nix @@ -0,0 +1,41 @@ +{ pkgs ? import <nixpkgs> {} }: + +pkgs.mkShell { + buildInputs = with pkgs; [ + git nano python3 chromedriver gnused + python3.pkgs.setuptools + python3.pkgs.pip + ]; + + shellHook = '' + export PIP_PREFIX="$(pwd)/_build/pip_packages" + export PYTHONPATH="$PIP_PREFIX/${pkgs.python3.sitePackages}:$PYTHONPATH" + export PATH="$PIP_PREFIX/bin:$PATH" + # use nix binaries because we don't want to invoke host configs + export GIT="${pkgs.git}/bin/git" + export PIP="${pkgs.python3.pkgs.pip}/bin/pip" + export PYTHON="${pkgs.python3}/bin/python" + unset SOURCE_DATE_EPOCH + + # update bot + $GIT pull && $GIT checkout seden + echo "Fetching dependencies..." + $PIP install -r requirements.txt + + if [ -f "config.env" -a -f "sedenuserbot.session" ]; then + # update chromedriver path + sed -i -E '/CHROME_DRIVER/d' config.env + echo "CHROME_DRIVER='${pkgs.chromedriver}/bin/chromedriver'" >> config.env + $PYTHON seden.py + exit + else + # first run + $PYTHON session.py + mv sample_config.env config.env + echo "Press enter to edit config.env file..." && read >> /dev/null + nano config.env + echo "Type 'nix-shell' to start SedenUserBot!" + exit + fi + ''; +} \ No newline at end of file