59 | )
60 | }
61 |
62 | export default function MyApp() {
63 | return
64 | }
65 |
--------------------------------------------------------------------------------
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "All-Url-Uploader",
3 | "projectOwner": "kalanakt",
4 | "repoType": "github",
5 | "repoHost": "https://github.com",
6 | "files": [
7 | "README.md"
8 | ],
9 | "imageSize": 100,
10 | "commit": false,
11 | "commitConvention": "angular",
12 | "contributors": [
13 | {
14 | "login": "kalanakt",
15 | "name": "Hash Minner",
16 | "avatar_url": "https://avatars.githubusercontent.com/u/86665964?v=4",
17 | "profile": "https://github.com/kalanakt",
18 | "contributions": [
19 | "code",
20 | "ideas",
21 | "infra",
22 | "maintenance",
23 | "test"
24 | ]
25 | },
26 | {
27 | "login": "TAMILVIP007",
28 | "name": "TAMILVIP007",
29 | "avatar_url": "https://avatars.githubusercontent.com/u/79161058?v=4",
30 | "profile": "http://www.tamilvip007.me",
31 | "contributions": [
32 | "code"
33 | ]
34 | },
35 | {
36 | "login": "ananth-28",
37 | "name": "ananth-28",
38 | "avatar_url": "https://avatars.githubusercontent.com/u/106482929?v=4",
39 | "profile": "https://github.com/ananth-28",
40 | "contributions": [
41 | "code"
42 | ]
43 | },
44 | {
45 | "login": "Divarion-D",
46 | "name": "Danil",
47 | "avatar_url": "https://avatars.githubusercontent.com/u/42798043?v=4",
48 | "profile": "https://t.me/Divarion_D",
49 | "contributions": [
50 | "code"
51 | ]
52 | },
53 | {
54 | "login": "millysboy",
55 | "name": "millysboy",
56 | "avatar_url": "https://avatars.githubusercontent.com/u/108298343?v=4",
57 | "profile": "https://github.com/millysboy",
58 | "contributions": [
59 | "code",
60 | "ideas"
61 | ]
62 | },
63 | {
64 | "login": "all-contributors",
65 | "name": "All Contributors",
66 | "avatar_url": "https://avatars.githubusercontent.com/u/46410174?v=4",
67 | "profile": "https://allcontributors.org",
68 | "contributions": [
69 | "design"
70 | ]
71 | },
72 | {
73 | "login": "hybridvamp",
74 | "name": "HYBRID",
75 | "avatar_url": "https://avatars.githubusercontent.com/u/48980248?v=4",
76 | "profile": "https://github.com/hybridvamp",
77 | "contributions": [
78 | "code"
79 | ]
80 | },
81 | {
82 | "login": "Drago991",
83 | "name": "Damin.du",
84 | "avatar_url": "https://avatars.githubusercontent.com/u/69932259?v=4",
85 | "profile": "https://github.com/Drago991",
86 | "contributions": [
87 | "code"
88 | ]
89 | },
90 | {
91 | "login": "AvikaTrivedi",
92 | "name": "Avika Trivedi",
93 | "avatar_url": "https://avatars.githubusercontent.com/u/84050503?v=4",
94 | "profile": "https://t.me/sudodotsh",
95 | "contributions": [
96 | "code"
97 | ]
98 | },
99 | {
100 | "login": "satyanandatripathi",
101 | "name": "Parthiv Katpara ",
102 | "avatar_url": "https://avatars.githubusercontent.com/u/83153745?v=4",
103 | "profile": "https://telegram.me/mrstrange_genuine",
104 | "contributions": [
105 | "code"
106 | ]
107 | }
108 | ],
109 | "contributorsPerLine": 7,
110 | "skipCi": true,
111 | "commitType": "docs"
112 | }
113 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on: [push]
15 |
16 | jobs:
17 | analyze:
18 | name: Analyze
19 | # Runner size impacts CodeQL analysis time. To learn more, please see:
20 | # - https://gh.io/recommended-hardware-resources-for-running-codeql
21 | # - https://gh.io/supported-runners-and-hardware-resources
22 | # - https://gh.io/using-larger-runners
23 | # Consider using larger runners for possible analysis time improvements.
24 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
25 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
26 | permissions:
27 | # required for all workflows
28 | security-events: write
29 |
30 | # only required for workflows in private repositories
31 | actions: read
32 | contents: read
33 |
34 | strategy:
35 | fail-fast: false
36 | matrix:
37 | language: [ 'python' ]
38 | # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ]
39 | # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both
40 | # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
41 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
42 |
43 | steps:
44 | - name: Checkout repository
45 | uses: actions/checkout@v4
46 |
47 | # Initializes the CodeQL tools for scanning.
48 | - name: Initialize CodeQL
49 | uses: github/codeql-action/init@v3
50 | with:
51 | languages: ${{ matrix.language }}
52 | # If you wish to specify custom queries, you can do so here or in a config file.
53 | # By default, queries listed here will override any specified in a config file.
54 | # Prefix the list here with "+" to use these queries and those in the config file.
55 |
56 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
57 | # queries: security-extended,security-and-quality
58 |
59 |
60 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
61 | # If this step fails, then you should remove it and run the build manually (see below)
62 | - name: Autobuild
63 | uses: github/codeql-action/autobuild@v3
64 |
65 | # ℹ️ Command-line programs to run using the OS shell.
66 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
67 |
68 | # If the Autobuild fails above, remove it and uncomment the following three lines.
69 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
70 |
71 | # - run: |
72 | # echo "Run, Build Application using script"
73 | # ./location_of_script_within_repo/buildscript.sh
74 |
75 | - name: Perform CodeQL Analysis
76 | uses: github/codeql-action/analyze@v3
77 | with:
78 | category: "/language:${{matrix.language}}"
79 |
--------------------------------------------------------------------------------
/plugins/script.py:
--------------------------------------------------------------------------------
1 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
2 |
3 |
4 | class Translation:
5 | START_TEXT = """
6 | Hi {}
7 |
8 | I am Powerful Url Uploader Bot
9 | """
10 |
11 | HELP_TEXT = """
12 |
13 | # Send me the Google Drive | ytdl | direct links.
14 |
15 | # Select the desired option.
16 |
17 | # Then be relaxed your file will be uploaded soon..
18 | """
19 |
20 | # give credit to developer
21 |
22 | ABOUT_TEXT = """
23 | ♻️ My Name : Url Uploader Bot
24 |
25 | 🌀 Channel : @TMWAD
26 |
27 | 🌺 Heroku : Heroku
28 |
29 | 📑 Language :Python 3.10.5
30 |
31 | 🇵🇲 Framework :Pyrogram 2.0.30
32 |
33 | 👲 Developer :@kinu6
34 |
35 | """
36 |
37 | PROGRESS = """
38 | 🔰 Speed : {3}/s\n\n
39 | 🌀 Done : {1}\n\n
40 | 🎥 Tᴏᴛᴀʟ sɪᴢᴇ : {2}\n\n
41 | ⏳ Tɪᴍᴇ ʟᴇғᴛ : {4}\n\n
42 | """
43 | ID_TEXT = """
44 | 🆔 Your Telegram ID 𝐢𝐬 :- {}
45 | """
46 |
47 | INFO_TEXT = """
48 |
49 | 🤹 First Name : {}
50 |
51 | 🚴♂️ Second Name : {}
52 |
53 | 🧑🏻🎓 Username : @{}
54 |
55 | 🆔 Telegram Id : {}
56 |
57 | 📇 Profile Link : {}
58 |
59 | 📡 Dc : {}
60 |
61 | 📑 Language : {}
62 |
63 | 👲 Status : {}
64 | """
65 |
66 | START_BUTTONS = InlineKeyboardMarkup(
67 | [
68 | [
69 | InlineKeyboardButton("❓ Help", callback_data="help"),
70 | InlineKeyboardButton("🦊 About", callback_data="about"),
71 | ],
72 | [InlineKeyboardButton("📛 Close", callback_data="close")],
73 | ]
74 | )
75 | HELP_BUTTONS = InlineKeyboardMarkup(
76 | [
77 | [
78 | InlineKeyboardButton("🏠 Home", callback_data="home"),
79 | InlineKeyboardButton("🦊 About", callback_data="about"),
80 | ],
81 | [InlineKeyboardButton("📛 Close", callback_data="close")],
82 | ]
83 | )
84 | ABOUT_BUTTONS = InlineKeyboardMarkup(
85 | [
86 | [
87 | InlineKeyboardButton("🏠 Home", callback_data="home"),
88 | InlineKeyboardButton("❓ Help", callback_data="help"),
89 | ],
90 | [InlineKeyboardButton("📛 Close", callback_data="close")],
91 | ]
92 | )
93 | BUTTONS = InlineKeyboardMarkup(
94 | [[InlineKeyboardButton("📛 Close", callback_data="close")]]
95 | )
96 | FORMAT_SELECTION = "Now Select the desired formats"
97 | SET_CUSTOM_USERNAME_PASSWORD = """"""
98 | DOWNLOAD_START = "Trying to Download ⌛\n\n {} "
99 | UPLOAD_START = "{} \n\n📤 Uploading Please Wait "
100 | RCHD_TG_API_LIMIT = "Downloaded in {} seconds.\nDetected File Size: {}\nSorry. But, I cannot upload files greater than 2GB due to Telegram API limitations."
101 | AFTER_SUCCESSFUL_UPLOAD_MSG_WITH_TS = (
102 | "Dᴏᴡɴʟᴏᴀᴅᴇᴅ ɪɴ {} sᴇᴄᴏɴᴅs.\n\nTʜᴀɴᴋs Fᴏʀ Usɪɴɢ Mᴇ\n\nUᴘʟᴏᴀᴅᴇᴅ ɪɴ {} sᴇᴄᴏɴᴅs"
103 | )
104 | FF_MPEG_DEL_ETED_CUSTOM_MEDIA = "✅ Media cleared succesfully."
105 | CUSTOM_CAPTION_UL_FILE = ""
106 | NO_VOID_FORMAT_FOUND = "ERROR... {}"
107 | FREE_USER_LIMIT_Q_SZE = "Cannot Process, Time OUT..."
108 | SLOW_URL_DECED = """
109 | Gosh that seems to be a very slow URL. Since you were screwing my home,
110 | I am in no mood to download this file. Meanwhile, why don't you try this:==> https://shrtz.me/PtsVnf6
111 | and get me a fast URL so that I can upload to Telegram, without me slowing down for other users."""
112 |
--------------------------------------------------------------------------------
/.github/workflows/docker-publish.yml:
--------------------------------------------------------------------------------
1 | name: Docker
2 |
3 | # This workflow uses actions that are not certified by GitHub.
4 | # They are provided by a third-party and are governed by
5 | # separate terms of service, privacy policy, and support
6 | # documentation.
7 |
8 | on:
9 | schedule:
10 | - cron: '22 2 * * *'
11 | push:
12 | branches: [ "main" ]
13 | # Publish semver tags as releases.
14 | tags: [ 'v*.*.*' ]
15 | pull_request:
16 | branches: [ "main" ]
17 |
18 | env:
19 | # Use docker.io for Docker Hub if empty
20 | REGISTRY: ghcr.io
21 | # github.repository as /
22 | IMAGE_NAME: ${{ github.repository }}
23 |
24 |
25 | jobs:
26 | build:
27 |
28 | runs-on: ubuntu-latest
29 | permissions:
30 | contents: read
31 | packages: write
32 | # This is used to complete the identity challenge
33 | # with sigstore/fulcio when running outside of PRs.
34 | id-token: write
35 |
36 | steps:
37 | - name: Checkout repository
38 | uses: actions/checkout@v3
39 |
40 | # Install the cosign tool except on PR
41 | # https://github.com/sigstore/cosign-installer
42 | - name: Install cosign
43 | if: github.event_name != 'pull_request'
44 | uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1
45 | with:
46 | cosign-release: 'v2.1.1'
47 |
48 | # Workaround: https://github.com/docker/build-push-action/issues/461
49 | - name: Setup Docker buildx
50 | uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf
51 |
52 | # Login against a Docker registry except on PR
53 | # https://github.com/docker/login-action
54 | - name: Log into registry ${{ env.REGISTRY }}
55 | if: github.event_name != 'pull_request'
56 | uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
57 | with:
58 | registry: ${{ env.REGISTRY }}
59 | username: ${{ github.actor }}
60 | password: ${{ secrets.GITHUB_TOKEN }}
61 |
62 | # Extract metadata (tags, labels) for Docker
63 | # https://github.com/docker/metadata-action
64 | - name: Extract Docker metadata
65 | id: meta
66 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
67 | with:
68 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
69 |
70 | # Build and push Docker image with Buildx (don't push on PR)
71 | # https://github.com/docker/build-push-action
72 | - name: Build and push Docker image
73 | id: build-and-push
74 | uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a
75 | with:
76 | context: .
77 | push: ${{ github.event_name != 'pull_request' }}
78 | tags: ${{ steps.meta.outputs.tags }}
79 | labels: ${{ steps.meta.outputs.labels }}
80 | cache-from: type=gha
81 | cache-to: type=gha,mode=max
82 |
83 |
84 | # Sign the resulting Docker image digest except on PRs.
85 | # This will only write to the public Rekor transparency log when the Docker
86 | # repository is public to avoid leaking data. If you would like to publish
87 | # transparency data even for private images, pass --force to cosign below.
88 | # https://github.com/sigstore/cosign
89 | - name: Sign the published Docker image
90 | if: ${{ github.event_name != 'pull_request' }}
91 | env:
92 | # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
93 | TAGS: ${{ steps.meta.outputs.tags }}
94 | DIGEST: ${{ steps.build-and-push.outputs.digest }}
95 | # This step uses the identity token to provision an ephemeral certificate
96 | # against the sigstore community Fulcio instance.
97 | run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}
98 |
--------------------------------------------------------------------------------
/plugins/functions/display_progress.py:
--------------------------------------------------------------------------------
1 | import math
2 | import time
3 | import logging
4 |
5 | logging.basicConfig(
6 | level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
7 | )
8 | logger = logging.getLogger(__name__)
9 | logging.getLogger("pyrogram").setLevel(logging.WARNING)
10 |
11 |
12 | async def progress_for_pyrogram(current, total, ud_type, message, start):
13 | """
14 | Display progress for a Pyrogram file upload or download.
15 |
16 | Parameters:
17 | - current (int): Current progress value.
18 | - total (int): Total value (completion point).
19 | - ud_type (str): Type of upload/download (e.g., "Uploading", "Downloading").
20 | - message: The Pyrogram message to edit.
21 | - start: The start time of the operation.
22 |
23 | Returns:
24 | None
25 | """
26 | now = time.time()
27 | diff = now - start
28 | if round(diff % 10.00) == 0 or current == total:
29 | percentage = current * 100 / total
30 | speed = current / diff
31 | elapsed_time = round(diff) * 1000
32 | time_to_completion = round((total - current) / speed) * 1000
33 | estimated_total_time = elapsed_time + time_to_completion
34 |
35 | elapsed_time = TimeFormatter(milliseconds=elapsed_time)
36 | estimated_total_time = TimeFormatter(milliseconds=estimated_total_time)
37 |
38 | progress = "[{0}{1}] \nP: {2}%\n".format(
39 | "".join(["◾" for _ in range(math.floor(percentage / 5))]),
40 | "".join(["◽" for _ in range(20 - math.floor(percentage / 5))]),
41 | round(percentage, 2),
42 | )
43 |
44 | tmp = progress + "{0} of {1}\n\nSpeed: {2}/s\n\nETA: {3}\n\n".format(
45 | humanbytes(current),
46 | humanbytes(total),
47 | humanbytes(speed),
48 | estimated_total_time if estimated_total_time != "" else "0 s",
49 | )
50 | try:
51 | await message.edit(text=f"{ud_type}\n {tmp}")
52 | except Exception as e:
53 | logger.info("Error %s", e)
54 | return
55 |
56 |
57 | SIZE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB"]
58 |
59 |
60 | def huanbytes(size_in_bytes) -> str:
61 | """
62 | Convert size in bytes to human-readable format.
63 |
64 | Parameters:
65 | - size_in_bytes (int): Size in bytes.
66 |
67 | Returns:
68 | str: Human-readable size.
69 | """
70 | if size_in_bytes is None:
71 | return "0B"
72 | index = 0
73 | while size_in_bytes >= 1024:
74 | size_in_bytes /= 1024
75 | index += 1
76 | try:
77 | return f"{round(size_in_bytes, 2)}{SIZE_UNITS[index]}"
78 | except IndexError:
79 | return "File too large"
80 |
81 |
82 | def humanbytes(size):
83 | """
84 | Convert size to human-readable format.
85 |
86 | Parameters:
87 | - size (int): Size in bytes.
88 |
89 | Returns:
90 | str: Human-readable size.
91 | """
92 | if not size:
93 | return ""
94 | power = 2**10
95 | n = 0
96 | Dic_powerN = {0: " ", 1: "K", 2: "M", 3: "G", 4: "T"}
97 | while size > power:
98 | size /= power
99 | n += 1
100 | return f"{str(round(size, 2))} {Dic_powerN[n]}B"
101 |
102 |
103 | def TimeFormatter(milliseconds: int) -> str:
104 | """
105 | Format time in milliseconds to a human-readable string.
106 |
107 | Parameters:
108 | - milliseconds (int): Time in milliseconds.
109 |
110 | Returns:
111 | str: Formatted time string.
112 | """
113 | seconds, milliseconds = divmod(milliseconds, 1000)
114 | minutes, seconds = divmod(seconds, 60)
115 | hours, minutes = divmod(minutes, 60)
116 | days, hours = divmod(hours, 24)
117 | tmp = (
118 | (f"{str(days)}d, " if days else "")
119 | + (f"{str(hours)}h, " if hours else "")
120 | + (f"{str(minutes)}m, " if minutes else "")
121 | + (f"{str(seconds)}s, " if seconds else "")
122 | + (f"{str(milliseconds)}ms, " if milliseconds else "")
123 | )
124 |
125 | return tmp[:-2]
126 |
--------------------------------------------------------------------------------
/plugins/youtube.py:
--------------------------------------------------------------------------------
1 | import os
2 | import asyncio
3 |
4 | from youtube_dl import YoutubeDL
5 | from pyrogram import enums
6 | from pyrogram.types import Message
7 | from pyrogram import Client, filters
8 |
9 | from config import Config
10 | from plugins.functions.help_ytdl import get_file_extension_from_url, get_resolution
11 | YTDL_REGEX = r"^((?:https?:)?\/\/)"
12 |
13 |
14 | @Client.on_callback_query(filters.regex("^ytdl_audio$"))
15 | async def callback_query_ytdl_audio(_, callback_query):
16 | try:
17 | url = callback_query.message.reply_to_message.text
18 | ydl_opts = {
19 | "format": "bestaudio",
20 | "outtmpl": "%(title)s - %(extractor)s-%(id)s.%(ext)s",
21 | "writethumbnail": True,
22 | }
23 | with YoutubeDL(ydl_opts) as ydl:
24 | message = callback_query.message
25 | await message.reply_chat_action(enums.ChatAction.TYPING)
26 | info_dict = ydl.extract_info(url, download=False)
27 | # download
28 | await callback_query.edit_message_text("**Downloading audio...**")
29 | ydl.process_info(info_dict)
30 | # upload
31 | audio_file = ydl.prepare_filename(info_dict)
32 | task = asyncio.create_task(send_audio(message, info_dict, audio_file))
33 | while not task.done():
34 | await asyncio.sleep(3)
35 | await message.reply_chat_action(enums.ChatAction.UPLOAD_DOCUMENT)
36 | await message.reply_chat_action(enums.ChatAction.CANCEL)
37 | await message.delete()
38 | except Exception as e:
39 | await message.reply_text(e)
40 | await callback_query.message.reply_to_message.delete()
41 | await callback_query.message.delete()
42 |
43 |
44 | async def send_audio(message: Message, info_dict, audio_file):
45 | basename = audio_file.rsplit(".", 1)[-2]
46 | if info_dict["ext"] == "webm":
47 | audio_file_weba = f"{basename}.weba"
48 | os.rename(audio_file, audio_file_weba)
49 | audio_file = audio_file_weba
50 | thumbnail_url = info_dict["thumbnail"]
51 | thumbnail_file = f"{basename}.{get_file_extension_from_url(thumbnail_url)}"
52 | download_location = f"{Config.DOWNLOAD_LOCATION}/{message.from_user.id}.jpg"
53 | thumb = download_location if os.path.isfile(download_location) else None
54 | webpage_url = info_dict["webpage_url"]
55 | title = info_dict["title"] or ""
56 | caption = f'{title}'
57 | duration = int(float(info_dict["duration"]))
58 | performer = info_dict["uploader"] or ""
59 | await message.reply_audio(
60 | audio_file,
61 | caption=caption,
62 | duration=duration,
63 | performer=performer,
64 | title=title,
65 | parse_mode=enums.ParseMode.HTML,
66 | thumb=thumb,
67 | )
68 |
69 | os.remove(audio_file)
70 | os.remove(thumbnail_file)
71 |
72 |
73 | async def send_video(message: Message, info_dict, video_file):
74 | basename = video_file.rsplit(".", 1)[-2]
75 | thumbnail_url = info_dict["thumbnail"]
76 | thumbnail_file = f"{basename}.{get_file_extension_from_url(thumbnail_url)}"
77 | download_location = f"{Config.DOWNLOAD_LOCATION}/{message.from_user.id}.jpg"
78 | thumb = download_location if os.path.isfile(download_location) else None
79 | webpage_url = info_dict["webpage_url"]
80 | title = info_dict["title"] or ""
81 | caption = f'{title}'
82 | duration = int(float(info_dict["duration"]))
83 | width, height = get_resolution(info_dict)
84 | await message.reply_video(
85 | video_file,
86 | caption=caption,
87 | duration=duration,
88 | width=width,
89 | height=height,
90 | parse_mode=enums.ParseMode.HTML,
91 | thumb=thumb,
92 | )
93 |
94 | os.remove(video_file)
95 | os.remove(thumbnail_file)
96 |
97 |
98 | @Client.on_callback_query(filters.regex("^ytdl_video$"))
99 | async def callback_query_ytdl_video(_, callback_query):
100 | try:
101 | # url = callback_query.message.text
102 | url = callback_query.message.reply_to_message.text
103 | ydl_opts = {
104 | "format": "best[ext=mp4]",
105 | "outtmpl": "%(title)s - %(extractor)s-%(id)s.%(ext)s",
106 | "writethumbnail": True,
107 | }
108 | with YoutubeDL(ydl_opts) as ydl:
109 | message = callback_query.message
110 | await message.reply_chat_action(enums.ChatAction.TYPING)
111 | info_dict = ydl.extract_info(url, download=False)
112 | # download
113 | await callback_query.edit_message_text("**Downloading video...**")
114 | ydl.process_info(info_dict)
115 | # upload
116 | video_file = ydl.prepare_filename(info_dict)
117 | task = asyncio.create_task(send_video(message, info_dict, video_file))
118 | while not task.done():
119 | await asyncio.sleep(3)
120 | await message.reply_chat_action(enums.ChatAction.UPLOAD_DOCUMENT)
121 | await message.reply_chat_action(enums.ChatAction.CANCEL)
122 | await message.delete()
123 | except Exception as e:
124 | await message.reply_text(e)
125 | await callback_query.message.reply_to_message.delete()
126 | await callback_query.message.delete()
127 |
--------------------------------------------------------------------------------
/plugins/functions/help_Nekmo_ffmpeg.py:
--------------------------------------------------------------------------------
1 | import os
2 | import asyncio
3 | from hachoir.parser import createParser
4 | from hachoir.metadata import extractMetadata
5 | import time
6 | import logging
7 |
8 | logging.basicConfig(
9 | level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
10 | )
11 | logger = logging.getLogger(__name__)
12 |
13 |
14 | async def place_water_mark(input_file, output_file, water_mark_file):
15 | """
16 | Place a watermark on the input file and save the result to the output file.
17 |
18 | Parameters:
19 | - input_file (str): Path to the input file.
20 | - output_file (str): Path to save the output file.
21 | - water_mark_file (str): Path to the watermark file.
22 |
23 | Returns:
24 | str: Path to the watermarked output file.
25 | """
26 | watermarked_file = f"{output_file}.watermark.png"
27 | metadata = extractMetadata(createParser(input_file))
28 | width = metadata.get("width")
29 | # Command to shrink the watermark file
30 | shrink_watermark_file_genertor_command = [
31 | "ffmpeg",
32 | "-i",
33 | water_mark_file,
34 | "-y -v quiet",
35 | "-vf",
36 | f"scale={width}*0.5:-1",
37 | watermarked_file,
38 | ]
39 |
40 | process = await asyncio.create_subprocess_exec(
41 | *shrink_watermark_file_genertor_command,
42 | stdout=asyncio.subprocess.PIPE,
43 | stderr=asyncio.subprocess.PIPE,
44 | )
45 | stdout, stderr = await process.communicate()
46 | stderr.decode().strip()
47 | stdout.decode().strip()
48 |
49 | # Command to overlay the watermark on the input file
50 | commands_to_execute = [
51 | "ffmpeg",
52 | "-i",
53 | input_file,
54 | "-i",
55 | watermarked_file,
56 | "-filter_complex",
57 | '"overlay=(main_w-overlay_w):(main_h-overlay_h)"',
58 | output_file,
59 | ]
60 | process = await asyncio.create_subprocess_exec(
61 | *commands_to_execute,
62 | stdout=asyncio.subprocess.PIPE,
63 | stderr=asyncio.subprocess.PIPE,
64 | )
65 | stdout, stderr = await process.communicate()
66 | stderr.decode().strip()
67 | stdout.decode().strip()
68 | return output_file
69 |
70 |
71 | async def take_screen_shot(video_file, output_directory, ttl):
72 | """
73 | Take a screenshot from a video file at a specified time and save it to the output directory.
74 |
75 | Parameters:
76 | - video_file (str): Path to the video file.
77 | - output_directory (str): Directory to save the screenshot.
78 | - ttl (int): Time to take the screenshot in seconds.
79 |
80 | Returns:
81 | str: Path to the saved screenshot file.
82 | """
83 | out_put_file_name = output_directory + "/" + str(time.time()) + ".jpg"
84 | file_genertor_command = [
85 | "ffmpeg",
86 | "-ss",
87 | str(ttl),
88 | "-i",
89 | video_file,
90 | "-vframes",
91 | "1",
92 | out_put_file_name,
93 | ]
94 | process = await asyncio.create_subprocess_exec(
95 | *file_genertor_command,
96 | stdout=asyncio.subprocess.PIPE,
97 | stderr=asyncio.subprocess.PIPE,
98 | )
99 | stdout, stderr = await process.communicate()
100 | stderr.decode().strip()
101 | stdout.decode().strip()
102 | return out_put_file_name if os.path.lexists(out_put_file_name) else None
103 |
104 |
105 | async def cult_small_video(video_file, output_directory, start_time, end_time):
106 | """
107 | Cut a small portion of a video file and save it to the output directory.
108 |
109 | Parameters:
110 | - video_file (str): Path to the video file.
111 | - output_directory (str): Directory to save the cut video.
112 | - start_time (int): Start time of the cut in seconds.
113 | - end_time (int): End time of the cut in seconds.
114 |
115 | Returns:
116 | str: Path to the saved cut video file.
117 | """
118 | out_put_file_name = output_directory + "/" + str(round(time.time())) + ".mp4"
119 | file_genertor_command = [
120 | "ffmpeg",
121 | "-i",
122 | video_file,
123 | "-ss",
124 | start_time,
125 | "-to",
126 | end_time,
127 | "-async",
128 | "1",
129 | "-strict",
130 | "-2",
131 | out_put_file_name,
132 | ]
133 | process = await asyncio.create_subprocess_exec(
134 | *file_genertor_command,
135 | stdout=asyncio.subprocess.PIPE,
136 | stderr=asyncio.subprocess.PIPE,
137 | )
138 | stdout, stderr = await process.communicate()
139 | stderr.decode().strip()
140 | stdout.decode().strip()
141 | return out_put_file_name if os.path.lexists(out_put_file_name) else None
142 |
143 |
144 | async def generate_screen_shots(
145 | video_file, output_directory, is_watermarkable, wf, min_duration, no_of_photos
146 | ):
147 | """
148 | Generate screen shots from a video file and optionally apply a watermark.
149 |
150 | Parameters:
151 | - video_file (str): Path to the video file.
152 | - output_directory (str): Directory to save the screen shots.
153 | - is_watermarkable (bool): Whether to apply a watermark.
154 | - wf (str): Path to the watermark file.
155 | - min_duration (int): Minimum duration of the video to generate screen shots.
156 | - no_of_photos (int): Number of screen shots to generate.
157 |
158 | Returns:
159 | List[str]: List of paths to the generated screen shots.
160 | """
161 | metadata = extractMetadata(createParser(video_file))
162 | duration = 0
163 | if metadata is not None and metadata.has("duration"):
164 | duration = metadata.get("duration").seconds
165 | if duration > min_duration:
166 | images = []
167 | ttl_step = duration // no_of_photos
168 | current_ttl = ttl_step
169 | for _ in range(no_of_photos):
170 | ss_img = await take_screen_shot(video_file, output_directory, current_ttl)
171 | current_ttl = current_ttl + ttl_step
172 | if is_watermarkable:
173 | ss_img = await place_water_mark(
174 | ss_img, f"{output_directory}/{str(time.time())}.jpg", wf
175 | )
176 | images.append(ss_img)
177 | return images
178 |
179 | return None
180 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Citizen Code of Conduct
2 |
3 | ## 1. Purpose
4 |
5 | A primary goal of All Url Uploader is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
6 |
7 | This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
8 |
9 | We invite all those who participate in All Url Uploader to help us create safe and positive experiences for everyone.
10 |
11 | ## 2. Open [Source/Culture/Tech] Citizenship
12 |
13 | A supplemental goal of this Code of Conduct is to increase open [source/culture/tech] citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.
14 |
15 | Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
16 |
17 | If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.
18 |
19 | ## 3. Expected Behavior
20 |
21 | The following behaviors are expected and requested of all community members:
22 |
23 | * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
24 | * Exercise consideration and respect in your speech and actions.
25 | * Attempt collaboration before conflict.
26 | * Refrain from demeaning, discriminatory, or harassing behavior and speech.
27 | * Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.
28 | * Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.
29 |
30 | ## 4. Unacceptable Behavior
31 |
32 | The following behaviors are considered harassment and are unacceptable within our community:
33 |
34 | * Violence, threats of violence or violent language directed against another person.
35 | * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
36 | * Posting or displaying sexually explicit or violent material.
37 | * Posting or threatening to post other people's personally identifying information ("doxing").
38 | * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
39 | * Inappropriate photography or recording.
40 | * Inappropriate physical contact. You should have someone's consent before touching them.
41 | * Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
42 | * Deliberate intimidation, stalking or following (online or in person).
43 | * Advocating for, or encouraging, any of the above behavior.
44 | * Sustained disruption of community events, including talks and presentations.
45 |
46 | ## 5. Weapons Policy
47 |
48 | No weapons will be allowed at All Url Uploader events, community spaces, or in other spaces covered by the scope of this Code of Conduct. Weapons include but are not limited to guns, explosives (including fireworks), and large knives such as those used for hunting or display, as well as any other item used for the purpose of causing injury or harm to others. Anyone seen in possession of one of these items will be asked to leave immediately, and will only be allowed to return without the weapon. Community members are further expected to comply with all state and local laws on this matter.
49 |
50 | ## 6. Consequences of Unacceptable Behavior
51 |
52 | Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.
53 |
54 | Anyone asked to stop unacceptable behavior is expected to comply immediately.
55 |
56 | If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event).
57 |
58 | ## 7. Reporting Guidelines
59 |
60 | If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. e19198@eng.pdn.ac.lk.
61 |
62 | e19198@eng.pdn.ac.lk
63 |
64 | Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.
65 |
66 | ## 8. Addressing Grievances
67 |
68 | If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify codecrush with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies. [Policy](https://urluploader.vercel.app/policy)
69 |
70 | https://urluploader.vercel.app/
71 |
72 | ## 9. Scope
73 |
74 | We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues--online and in-person--as well as in all one-on-one communications pertaining to community business.
75 |
76 | This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.
77 |
78 | ## 10. Contact info
79 |
80 | e19198@eng.pdn.ac.lk
81 |
82 | ## 11. License and attribution
83 |
84 | The Citizen Code of Conduct is distributed by [Stumptown Syndicate](http://stumptownsyndicate.org) under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
85 |
86 | Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
87 |
88 | _Revision 2.3. Posted 6 March 2017._
89 |
90 | _Revision 2.2. Posted 4 February 2016._
91 |
92 | _Revision 2.1. Posted 23 June 2014._
93 |
94 | _Revision 2.0, adopted by the [Stumptown Syndicate](http://stumptownsyndicate.org) board on 10 January 2013. Posted 17 March 2013._
95 |
--------------------------------------------------------------------------------
/plugins/button.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 | import json
4 | import time
5 | import shutil
6 | import asyncio
7 | from datetime import datetime
8 | from config import Config
9 | from plugins.functions.display_progress import humanbytes, progress_for_pyrogram
10 | from plugins.functions.ran_text import random_char
11 | from plugins.script import Translation
12 | from plugins.utitles import Mdata01, Mdata02, Mdata03
13 |
14 | # Set up logging configuration
15 | logging.basicConfig(
16 | level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
17 | )
18 | logger = logging.getLogger(__name__)
19 | logging.getLogger("pyrogram").setLevel(logging.WARNING)
20 |
21 |
22 | async def youtube_dl_call_back(_bot, update):
23 | # Constants
24 | AD_STRING_TO_REPLACE = "please report this issue on https://github.com/kalanakt/All-Url-Uploader/issues"
25 |
26 | cb_data = update.data
27 | tg_send_type, youtube_dl_format, youtube_dl_ext, ranom = cb_data.split("|")
28 | print(cb_data)
29 | random1 = random_char(5)
30 | save_ytdl_json_path = (
31 | Config.DOWNLOAD_LOCATION + "/" + str(update.from_user.id) + f"{ranom}" + ".json"
32 | )
33 |
34 | try:
35 | with open(save_ytdl_json_path, "r", encoding="utf8") as f:
36 | response_json = json.load(f)
37 | except FileNotFoundError as e:
38 | await update.message.edit(f"Error: {e}")
39 | await update.message.delete()
40 | return False
41 |
42 | youtube_dl_url = update.message.reply_to_message.text
43 | custom_file_name = (
44 | str(response_json.get("title")) + "_" + youtube_dl_format + "." + youtube_dl_ext
45 | )
46 | youtube_dl_username = None
47 | youtube_dl_password = None
48 |
49 | if "|" in youtube_dl_url:
50 | url_parts = youtube_dl_url.split("|")
51 | if len(url_parts) == 2:
52 | youtube_dl_url, custom_file_name = map(str.strip, url_parts)
53 | elif len(url_parts) == 4:
54 | (
55 | youtube_dl_url,
56 | custom_file_name,
57 | youtube_dl_username,
58 | youtube_dl_password,
59 | ) = map(str.strip, url_parts)
60 | else:
61 | for entity in update.message.reply_to_message.entities:
62 | if entity.type == "text_link":
63 | youtube_dl_url = entity.url
64 | elif entity.type == "url":
65 | o = entity.offset
66 | length = entity.length
67 | youtube_dl_url = youtube_dl_url[o: o + length]
68 |
69 | # Cleaning up inputs
70 | youtube_dl_url, custom_file_name, youtube_dl_username, youtube_dl_password = (
71 | map(
72 | str.strip,
73 | [
74 | youtube_dl_url,
75 | custom_file_name,
76 | youtube_dl_username,
77 | youtube_dl_password,
78 | ],
79 | )
80 | )
81 |
82 | else:
83 | for entity in update.message.reply_to_message.entities:
84 | if entity.type == "text_link":
85 | youtube_dl_url = entity.url
86 | elif entity.type == "url":
87 | o = entity.offset
88 | length = entity.length
89 | youtube_dl_url = youtube_dl_url[o: o + length]
90 |
91 | await update.message.edit_caption(
92 | caption=Translation.DOWNLOAD_START.format(custom_file_name)
93 | )
94 | description = Translation.CUSTOM_CAPTION_UL_FILE
95 |
96 | if "fulltitle" in response_json:
97 | description = response_json["fulltitle"][:1021]
98 |
99 | tmp_directory_for_each_user = (
100 | Config.DOWNLOAD_LOCATION + "/" + str(update.from_user.id) + f"{random1}"
101 | )
102 |
103 | if not os.path.isdir(tmp_directory_for_each_user):
104 | os.makedirs(tmp_directory_for_each_user)
105 |
106 | download_directory = f"{tmp_directory_for_each_user}/{custom_file_name}"
107 |
108 | command_to_exec = []
109 |
110 | if tg_send_type == "audio":
111 | command_to_exec = [
112 | "yt-dlp",
113 | "-c",
114 | "--max-filesize",
115 | str(Config.TG_MAX_FILE_SIZE),
116 | "--bidi-workaround",
117 | "--extract-audio",
118 | "--audio-format",
119 | youtube_dl_ext,
120 | "--audio-quality",
121 | youtube_dl_format,
122 | youtube_dl_url,
123 | "-o",
124 | download_directory,
125 | ]
126 | else:
127 | minus_f_format = youtube_dl_format
128 | if "youtu" in youtube_dl_url:
129 | minus_f_format = f"{youtube_dl_format}+bestaudio"
130 | command_to_exec = [
131 | "yt-dlp",
132 | "-c",
133 | "--max-filesize",
134 | str(Config.TG_MAX_FILE_SIZE),
135 | "--embed-subs",
136 | "-f",
137 | minus_f_format,
138 | "--bidi-workaround",
139 | youtube_dl_url,
140 | "-o",
141 | download_directory,
142 | ]
143 |
144 | if Config.HTTP_PROXY != "":
145 | command_to_exec.extend(["--proxy", Config.HTTP_PROXY])
146 |
147 | if youtube_dl_username is not None:
148 | command_to_exec.extend(["--username", youtube_dl_username])
149 |
150 | if youtube_dl_password is not None:
151 | command_to_exec.extend(["--password", youtube_dl_password])
152 |
153 | command_to_exec.extend(["--no-warnings"])
154 |
155 | logger.info(command_to_exec)
156 | start = datetime.now()
157 |
158 | process = await asyncio.create_subprocess_exec(
159 | *command_to_exec,
160 | stdout=asyncio.subprocess.PIPE,
161 | stderr=asyncio.subprocess.PIPE,
162 | )
163 |
164 | stdout, stderr = await process.communicate()
165 | e_response = stderr.decode().strip()
166 | t_response = stdout.decode().strip()
167 |
168 | logger.info(e_response)
169 | logger.info(t_response)
170 |
171 | if e_response and AD_STRING_TO_REPLACE in e_response:
172 | error_message = e_response.replace(AD_STRING_TO_REPLACE, "")
173 | await update.message.edit_caption(text=error_message)
174 | return False
175 |
176 | if t_response:
177 | logger.info(t_response)
178 | try:
179 | os.remove(save_ytdl_json_path)
180 | except FileNotFoundError:
181 | pass
182 |
183 | end_one = datetime.now()
184 | time_taken_for_download = (end_one - start).seconds
185 | file_size = Config.TG_MAX_FILE_SIZE + 1
186 |
187 | try:
188 | file_size = os.stat(download_directory).st_size
189 | except FileNotFoundError:
190 | download_directory = os.path.splitext(download_directory)[0] + "." + "mkv"
191 | file_size = os.stat(download_directory).st_size
192 |
193 | download_location = f"{Config.DOWNLOAD_LOCATION}/{update.from_user.id}.jpg"
194 | thumb = download_location if os.path.isfile(download_location) else None
195 |
196 | if file_size > Config.TG_MAX_FILE_SIZE:
197 | await update.message.edit_caption(
198 | caption=Translation.RCHD_TG_API_LIMIT.format(
199 | time_taken_for_download, humanbytes(file_size)
200 | )
201 | )
202 | else:
203 | await update.message.edit_caption(
204 | caption=Translation.UPLOAD_START.format(custom_file_name)
205 | )
206 |
207 | start_time = time.time()
208 |
209 | if tg_send_type == "video":
210 | width, height, duration = await Mdata01(download_directory)
211 | await update.message.reply_video(
212 | video=download_directory,
213 | caption=description,
214 | duration=duration,
215 | width=width,
216 | height=height,
217 | supports_streaming=True,
218 | thumb=thumb,
219 | progress=progress_for_pyrogram,
220 | progress_args=(
221 | Translation.UPLOAD_START,
222 | update.message,
223 | start_time,
224 | ),
225 | )
226 | elif tg_send_type == "audio":
227 | duration = await Mdata03(download_directory)
228 | await update.message.reply_audio(
229 | audio=download_directory,
230 | caption=description,
231 | duration=duration,
232 | thumb=thumb,
233 | progress=progress_for_pyrogram,
234 | progress_args=(
235 | Translation.UPLOAD_START,
236 | update.message,
237 | start_time,
238 | ),
239 | )
240 | elif tg_send_type == "vm":
241 | width, duration = await Mdata02(download_directory)
242 | await update.message.reply_video_note(
243 | video_note=download_directory,
244 | duration=duration,
245 | length=width,
246 | thumb=thumb,
247 | progress=progress_for_pyrogram,
248 | progress_args=(
249 | Translation.UPLOAD_START,
250 | update.message,
251 | start_time,
252 | ),
253 | )
254 | else:
255 | await update.message.reply_document(
256 | document=download_directory,
257 | caption=description,
258 | thumb=thumb,
259 | progress=progress_for_pyrogram,
260 | progress_args=(
261 | Translation.UPLOAD_START,
262 | update.message,
263 | start_time,
264 | ),
265 | )
266 |
267 | end_two = datetime.now()
268 | time_taken_for_upload = (end_two - end_one).seconds
269 |
270 | shutil.rmtree(tmp_directory_for_each_user)
271 |
272 | await update.message.edit_caption(
273 | caption=Translation.AFTER_SUCCESSFUL_UPLOAD_MSG_WITH_TS.format(
274 | time_taken_for_download, time_taken_for_upload
275 | )
276 | )
277 |
278 | logger.info("Downloaded in: %s", str(time_taken_for_download))
279 | logger.info("Uploaded in: %s", str(time_taken_for_upload))
280 |
--------------------------------------------------------------------------------
/plugins/dl_button.py:
--------------------------------------------------------------------------------
1 | """Module for handling download callback and related functions"""
2 |
3 | import asyncio
4 | from datetime import datetime
5 | import logging
6 | import os
7 | import time
8 | import aiohttp
9 | from plugins.functions.display_progress import (
10 | progress_for_pyrogram,
11 | humanbytes,
12 | TimeFormatter,
13 | )
14 | from plugins.script import Translation
15 | from plugins.utitles import Mdata01, Mdata02, Mdata03
16 | from config import Config
17 |
18 | logging.basicConfig(
19 | level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
20 | )
21 | logger = logging.getLogger(__name__)
22 | logging.getLogger("pyrogram").setLevel(logging.WARNING)
23 |
24 |
25 | async def ddl_call_back(bot, update):
26 | cb_data = update.data
27 | tg_send_type, youtube_dl_format, youtube_dl_ext = cb_data.split("=")
28 | youtube_dl_url = update.message.reply_to_message.text
29 | custom_file_name = os.path.basename(youtube_dl_url)
30 |
31 | if " " in youtube_dl_url:
32 | url_parts = youtube_dl_url.split(" * ")
33 | if len(url_parts) == 2:
34 | youtube_dl_url = url_parts[0]
35 | custom_file_name = url_parts[1]
36 | else:
37 | for entity in update.message.reply_to_message.entities:
38 | if entity.type == "text_link":
39 | youtube_dl_url = entity.url
40 | elif entity.type == "url":
41 | o = entity.offset
42 | length = entity.length
43 | youtube_dl_url = youtube_dl_url[o: o + length]
44 | if youtube_dl_url is not None:
45 | youtube_dl_url = youtube_dl_url.strip()
46 | if custom_file_name is not None:
47 | custom_file_name = custom_file_name.strip()
48 | else:
49 | for entity in update.message.reply_to_message.entities:
50 | if entity.type == "text_link":
51 | youtube_dl_url = entity.url
52 | elif entity.type == "url":
53 | o = entity.offset
54 | length = entity.length
55 | youtube_dl_url = youtube_dl_url[o: o + length]
56 |
57 | description = custom_file_name
58 |
59 | if f".{youtube_dl_ext}" not in custom_file_name:
60 | custom_file_name += f".{youtube_dl_ext}"
61 |
62 | logger.info(youtube_dl_url)
63 | logger.info(custom_file_name)
64 |
65 | start = datetime.now()
66 |
67 | await bot.edit_message_text(
68 | text=Translation.DOWNLOAD_START.format(custom_file_name),
69 | chat_id=update.message.chat.id,
70 | message_id=update.message.id,
71 | )
72 |
73 | tmp_directory_for_each_user = (
74 | f"{Config.DOWNLOAD_LOCATION}/{str(update.from_user.id)}"
75 | )
76 |
77 | if not os.path.isdir(tmp_directory_for_each_user):
78 | os.makedirs(tmp_directory_for_each_user)
79 |
80 | download_directory = f"{tmp_directory_for_each_user}/{custom_file_name}"
81 |
82 | async with aiohttp.ClientSession() as session:
83 | c_time = time.time()
84 | try:
85 | await download_coroutine(
86 | bot,
87 | session,
88 | youtube_dl_url,
89 | download_directory,
90 | update.message.chat.id,
91 | update.message.id,
92 | c_time,
93 | )
94 |
95 | except asyncio.TimeoutError:
96 | await bot.edit_message_text(
97 | text=Translation.SLOW_URL_DECED,
98 | chat_id=update.message.chat.id,
99 | message_id=update.message.id,
100 | )
101 | return False
102 |
103 | if os.path.exists(download_directory):
104 | save_ytdl_json_path = (
105 | f"{Config.DOWNLOAD_LOCATION}/{str(update.message.chat.id)}.json"
106 | )
107 | download_location = f"{Config.DOWNLOAD_LOCATION}/{update.from_user.id}.jpg"
108 | thumb = download_location if os.path.isfile(download_location) else None
109 |
110 | if os.path.exists(save_ytdl_json_path):
111 | os.remove(save_ytdl_json_path)
112 |
113 | end_one = datetime.now()
114 |
115 | await bot.edit_message_text(
116 | text=Translation.UPLOAD_START,
117 | chat_id=update.message.chat.id,
118 | message_id=update.message.id,
119 | )
120 |
121 | file_size = Config.TG_MAX_FILE_SIZE + 1
122 |
123 | try:
124 | file_size = os.stat(download_directory).st_size
125 | except FileNotFoundError:
126 | download_directory = f"{os.path.splitext(download_directory)[0]}.mkv"
127 | file_size = os.stat(download_directory).st_size
128 |
129 | if file_size > Config.TG_MAX_FILE_SIZE:
130 | await bot.edit_message_text(
131 | chat_id=update.message.chat.id,
132 | text=Translation.RCHD_TG_API_LIMIT,
133 | message_id=update.message.id,
134 | )
135 |
136 | else:
137 | start_time = time.time()
138 |
139 | if tg_send_type == "video":
140 | width, height, duration = await Mdata01(download_directory)
141 | await bot.send_video(
142 | chat_id=update.message.chat.id,
143 | video=download_directory,
144 | thumb=thumb,
145 | caption=description,
146 | duration=duration,
147 | width=width,
148 | height=height,
149 | supports_streaming=True,
150 | reply_to_message_id=update.message.reply_to_message.id,
151 | progress=progress_for_pyrogram,
152 | progress_args=(
153 | Translation.UPLOAD_START,
154 | update.message,
155 | start_time,
156 | ),
157 | )
158 |
159 | elif tg_send_type == "audio":
160 | duration = await Mdata03(download_directory)
161 | await bot.send_audio(
162 | chat_id=update.message.chat.id,
163 | audio=download_directory,
164 | thumb=thumb,
165 | caption=description,
166 | duration=duration,
167 | reply_to_message_id=update.message.reply_to_message.id,
168 | progress=progress_for_pyrogram,
169 | progress_args=(
170 | Translation.UPLOAD_START,
171 | update.message,
172 | start_time,
173 | ),
174 | )
175 |
176 | elif tg_send_type == "vm":
177 | width, duration = await Mdata02(download_directory)
178 | await bot.send_video_note(
179 | chat_id=update.message.chat.id,
180 | video_note=download_directory,
181 | thumb=thumb,
182 | duration=duration,
183 | length=width,
184 | reply_to_message_id=update.message.reply_to_message.id,
185 | progress=progress_for_pyrogram,
186 | progress_args=(
187 | Translation.UPLOAD_START,
188 | update.message,
189 | start_time,
190 | ),
191 | )
192 |
193 | else:
194 | await bot.send_document(
195 | chat_id=update.message.chat.id,
196 | document=download_directory,
197 | thumb=thumb,
198 | caption=description,
199 | reply_to_message_id=update.message.reply_to_message.id,
200 | progress=progress_for_pyrogram,
201 | progress_args=(
202 | Translation.UPLOAD_START,
203 | update.message,
204 | start_time,
205 | ),
206 | )
207 |
208 | end_two = datetime.now()
209 |
210 | try:
211 | os.remove(download_directory)
212 | except Exception:
213 | pass
214 |
215 | time_taken_for_download = (end_one - start).seconds
216 | time_taken_for_upload = (end_two - end_one).seconds
217 |
218 | await bot.edit_message_text(
219 | text=Translation.AFTER_SUCCESSFUL_UPLOAD_MSG_WITH_TS.format(
220 | time_taken_for_download, time_taken_for_upload
221 | ),
222 | chat_id=update.message.chat.id,
223 | message_id=update.message.id,
224 | disable_web_page_preview=True,
225 | )
226 |
227 | logger.info("Downloaded in: %s", str(time_taken_for_download))
228 | logger.info("Uploaded in: %s", str(time_taken_for_upload))
229 | else:
230 | await bot.edit_message_text(
231 | text=Translation.NO_VOID_FORMAT_FOUND.format("Incorrect Link"),
232 | chat_id=update.message.chat.id,
233 | message_id=update.message.id,
234 | disable_web_page_preview=True,
235 | )
236 |
237 |
238 | async def download_coroutine(bot, session, url, file_name, chat_id, message_id, start):
239 | downloaded = 0
240 | display_message = ""
241 |
242 | async with session.get(url, timeout=Config.PROCESS_MAX_TIMEOUT) as response:
243 | total_length = int(response.headers["Content-Length"])
244 | content_type = response.headers["Content-Type"]
245 |
246 | if "text" in content_type and total_length < 500:
247 | return await response.release()
248 |
249 | with open(file_name, "wb") as f_handle:
250 | while True:
251 | chunk = await response.content.read(Config.CHUNK_SIZE)
252 |
253 | if not chunk:
254 | break
255 |
256 | f_handle.write(chunk)
257 | downloaded += Config.CHUNK_SIZE
258 | now = time.time()
259 | diff = now - start
260 |
261 | if round(diff % 5.0) == 0 or downloaded == total_length:
262 | percentage = downloaded * 100 / total_length
263 | speed = downloaded / diff
264 | elapsed_time = round(diff) * 1000
265 | time_to_completion = (
266 | round((total_length - downloaded) / speed) * 1000
267 | )
268 | estimated_total_time = elapsed_time + time_to_completion
269 |
270 | try:
271 | current_message = """**Download Status**
272 | Percentage : {}
273 | URL: {}
274 | File Size: {}
275 | Downloaded: {}
276 | ETA: {}""".format(
277 | percentage,
278 | url,
279 | humanbytes(total_length),
280 | humanbytes(downloaded),
281 | TimeFormatter(estimated_total_time),
282 | )
283 |
284 | if current_message != display_message:
285 | await bot.edit_message_text(
286 | chat_id, message_id, text=current_message
287 | )
288 |
289 | display_message = current_message
290 |
291 | except Exception as e:
292 | logger.info(str(e))
293 |
294 | return await response.release()
295 |
--------------------------------------------------------------------------------
/plugins/echo.py:
--------------------------------------------------------------------------------
1 | import time
2 | import json
3 | import asyncio
4 | import logging
5 |
6 | from pyrogram.types import Thumbnail
7 | from pyrogram import Client, filters
8 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
9 |
10 | from config import Config
11 | from plugins.script import Translation
12 | from plugins.functions.ran_text import random_char
13 | from plugins.functions.display_progress import humanbytes
14 |
15 | logging.basicConfig(
16 | level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
17 | )
18 | logger = logging.getLogger(__name__)
19 | logging.getLogger("pyrogram").setLevel(logging.WARNING)
20 |
21 |
22 | @Client.on_message(filters.private & filters.regex(pattern=".*http.*"))
23 | async def echo(bot, update):
24 | logger.info(update.from_user)
25 | url = update.text
26 | youtube_dl_username = None
27 | youtube_dl_password = None
28 | file_name = None
29 |
30 | if "youtu.be" in url:
31 | return await update.reply_text(
32 | "**Choose Download type**",
33 | reply_markup=InlineKeyboardMarkup(
34 | [
35 | [
36 | InlineKeyboardButton("Audio 🎵", callback_data="ytdl_audio"),
37 | InlineKeyboardButton("Video 🎬", callback_data="ytdl_video"),
38 | ]
39 | ]
40 | ),
41 | quote=True,
42 | )
43 |
44 | if "|" in url:
45 | url_parts = url.split("|")
46 | if len(url_parts) == 2:
47 | url = url_parts[0]
48 | file_name = url_parts[1]
49 | elif len(url_parts) == 4:
50 | url = url_parts[0]
51 | file_name = url_parts[1]
52 | youtube_dl_username = url_parts[2]
53 | youtube_dl_password = url_parts[3]
54 | else:
55 | for entity in update.entities:
56 | if entity.type == "text_link":
57 | url = entity.url
58 | elif entity.type == "url":
59 | o = entity.offset
60 | length = entity.length
61 | url = url[o: o + length]
62 | if url is not None:
63 | url = url.strip()
64 | if file_name is not None:
65 | file_name = file_name.strip()
66 | # https://stackoverflow.com/a/761825/4723940
67 | if youtube_dl_username is not None:
68 | youtube_dl_username = youtube_dl_username.strip()
69 | if youtube_dl_password is not None:
70 | youtube_dl_password = youtube_dl_password.strip()
71 | logger.info(url)
72 | logger.info(file_name)
73 | else:
74 | for entity in update.entities:
75 | if entity.type == "text_link":
76 | url = entity.url
77 | elif entity.type == "url":
78 | o = entity.offset
79 | length = entity.length
80 | url = url[o: o + length]
81 | if Config.HTTP_PROXY != "":
82 | command_to_exec = [
83 | "yt-dlp",
84 | "--no-warnings",
85 | "--allow-dynamic-mpd",
86 | "-j",
87 | url,
88 | "--proxy",
89 | Config.HTTP_PROXY,
90 | ]
91 | else:
92 | command_to_exec = ["yt-dlp", "--no-warnings", "--allow-dynamic-mpd", "-j", url]
93 | if youtube_dl_username is not None:
94 | command_to_exec.append("--username")
95 | command_to_exec.append(youtube_dl_username)
96 | if youtube_dl_password is not None:
97 | command_to_exec.append("--password")
98 | command_to_exec.append(youtube_dl_password)
99 | logger.info(command_to_exec)
100 | chk = await bot.send_message(
101 | chat_id=update.chat.id,
102 | text="Proccesing your ⌛",
103 | disable_web_page_preview=True,
104 | reply_to_message_id=update.id,
105 | )
106 | if update.from_user.id not in Config.AUTH_USERS:
107 |
108 | if str(update.from_user.id) in Config.ADL_BOT_RQ:
109 | current_time = time.time()
110 | previous_time = Config.ADL_BOT_RQ[str(update.from_user.id)]
111 | process_max_timeout = round(Config.PROCESS_MAX_TIMEOUT / 60)
112 | present_time = round(
113 | Config.PROCESS_MAX_TIMEOUT - (current_time - previous_time)
114 | )
115 | Config.ADL_BOT_RQ[str(update.from_user.id)] = time.time()
116 | if round(current_time - previous_time) < Config.PROCESS_MAX_TIMEOUT:
117 | await bot.edit_message_text(
118 | chat_id=update.chat.id,
119 | text=Translation.FREE_USER_LIMIT_Q_SZE.format(
120 | process_max_timeout, present_time
121 | ),
122 | disable_web_page_preview=True,
123 | message_id=chk.id,
124 | )
125 | return
126 | else:
127 | Config.ADL_BOT_RQ[str(update.from_user.id)] = time.time()
128 |
129 | process = await asyncio.create_subprocess_exec(
130 | *command_to_exec,
131 | # stdout must a pipe to be accessible as process.stdout
132 | stdout=asyncio.subprocess.PIPE,
133 | stderr=asyncio.subprocess.PIPE,
134 | )
135 | # Wait for the subprocess to finish
136 | stdout, stderr = await process.communicate()
137 | e_response = stderr.decode().strip()
138 | logger.info(e_response)
139 | t_response = stdout.decode().strip()
140 | # logger.info(t_response)
141 | # https://github.com/rg3/youtube-dl/issues/2630#issuecomment-38635239
142 | if e_response and "nonnumeric port" not in e_response:
143 | # logger.warn("Status : FAIL", exc.returncode, exc.output)
144 | error_message = e_response.replace(
145 | """
146 | please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version;
147 | see https://yt-dl.org/update on how to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.
148 | """,
149 | "",
150 | )
151 | if "This video is only available for registered users." in error_message:
152 | error_message += Translation.SET_CUSTOM_USERNAME_PASSWORD
153 | await chk.delete()
154 |
155 | time.sleep(40.5)
156 | await bot.send_message(
157 | chat_id=update.chat.id,
158 | text=Translation.NO_VOID_FORMAT_FOUND.format(str(error_message)),
159 | reply_to_message_id=update.id,
160 | disable_web_page_preview=True,
161 | )
162 | return False
163 | if t_response:
164 | # logger.info(t_response)
165 | x_reponse = t_response
166 | if "\n" in x_reponse:
167 | x_reponse, _ = x_reponse.split("\n")
168 | response_json = json.loads(x_reponse)
169 | randem = random_char(5)
170 | save_ytdl_json_path = (
171 | Config.DOWNLOAD_LOCATION
172 | + "/"
173 | + str(update.from_user.id)
174 | + f"{randem}"
175 | + ".json"
176 | )
177 | with open(save_ytdl_json_path, "w", encoding="utf8") as outfile:
178 | json.dump(response_json, outfile, ensure_ascii=False)
179 | # logger.info(response_json)
180 | inline_keyboard = []
181 | duration = None
182 | if "duration" in response_json:
183 | duration = response_json["duration"]
184 | if "formats" in response_json:
185 | for formats in response_json["formats"]:
186 | format_id = formats.get("format_id")
187 | format_string = formats.get("format_note")
188 | if format_string is None:
189 | format_string = formats.get("format")
190 | if "DASH" in format_string.upper():
191 | continue
192 |
193 | format_ext = formats.get("ext")
194 |
195 | if formats.get("filesize"):
196 | size = formats["filesize"]
197 | elif formats.get("filesize_approx"):
198 | size = formats["filesize_approx"]
199 | else:
200 | size = 0
201 |
202 | cb_string_video = f"video |{format_id}|{format_ext}|{randem}"
203 | cb_string_file = f"fille |{format_id}|{format_ext}|{randem}"
204 |
205 | if format_string is not None and not ("audio only" in format_string):
206 | ikeyboard = [
207 | InlineKeyboardButton(
208 | "🎬 "
209 | + format_string
210 | + " "
211 | + format_ext
212 | + " "
213 | + humanbytes(size)
214 | + " ",
215 | callback_data=(cb_string_video).encode("UTF-8"),
216 | )
217 | ]
218 | else:
219 | # special weird case :\
220 | ikeyboard = [
221 | InlineKeyboardButton(
222 | "🎬 [" + "] ( " + humanbytes(size) + " )",
223 | callback_data=(cb_string_video).encode("UTF-8"),
224 | )
225 | ]
226 | inline_keyboard.append(ikeyboard)
227 | if duration is not None:
228 | cb_string_64 = "{}|{}|{}|{}".format("audio", "64k", "mp3", randem)
229 | cb_string_128 = "{}|{}|{}|{}".format("audio", "128k", "mp3", randem)
230 | cb_string = "{}|{}|{}|{}".format("audio", "320k", "mp3", randem)
231 | inline_keyboard.append(
232 | [
233 | InlineKeyboardButton(
234 | "🎼 ᴍᴘ𝟹 " + "(" + "64 ᴋʙᴘs" + ")",
235 | callback_data=cb_string_64.encode("UTF-8"),
236 | ),
237 | InlineKeyboardButton(
238 | "🎼 ᴍᴘ𝟹 " + "(" + "128 ᴋʙᴘs" + ")",
239 | callback_data=cb_string_128.encode("UTF-8"),
240 | ),
241 | ]
242 | )
243 | inline_keyboard.append(
244 | [
245 | InlineKeyboardButton(
246 | "🎼 ᴍᴘ𝟹 " + "(" + "320 ᴋʙᴘs" + ")",
247 | callback_data=cb_string.encode("UTF-8"),
248 | )
249 | ]
250 | )
251 | inline_keyboard.append(
252 | [InlineKeyboardButton("⛔ ᴄʟᴏsᴇ", callback_data="close")]
253 | )
254 | else:
255 | format_id = response_json["format_id"]
256 | format_ext = response_json["ext"]
257 | cb_string_file = "{}|{}|{}|{}".format("file", format_id, format_ext, randem)
258 | cb_string_video = "{}|{}|{}|{}".format(
259 | "video", format_id, format_ext, randem
260 | )
261 | inline_keyboard.append(
262 | [
263 | InlineKeyboardButton(
264 | "🎬 Video", callback_data=(cb_string_video).encode("UTF-8")
265 | )
266 | ]
267 | )
268 | cb_string_file = "{}={}={}".format("file", format_id, format_ext)
269 | cb_string_video = "{}={}={}".format("video", format_id, format_ext)
270 | inline_keyboard.append(
271 | [
272 | InlineKeyboardButton(
273 | "📁 Document", callback_data=(cb_string_file).encode("UTF-8")
274 | )
275 | ]
276 | )
277 | reply_markup = InlineKeyboardMarkup(inline_keyboard)
278 | await chk.delete()
279 |
280 | await bot.send_message(
281 | chat_id=update.chat.id,
282 | text=Translation.FORMAT_SELECTION.format(Thumbnail)
283 | + "\n"
284 | + Translation.SET_CUSTOM_USERNAME_PASSWORD,
285 | reply_markup=reply_markup,
286 | reply_to_message_id=update.id,
287 | )
288 | else:
289 | # fallback for nonnumeric port a.k.a seedbox.io
290 | inline_keyboard = []
291 | cb_string_file = "{}={}={}".format("file", "LFO", "NONE")
292 | cb_string_video = "{}={}={}".format("video", "OFL", "ENON")
293 | inline_keyboard.append(
294 | [
295 | InlineKeyboardButton(
296 | "🎬 ᴍᴇᴅɪᴀ", callback_data=(cb_string_video).encode("UTF-8")
297 | )
298 | ]
299 | )
300 | reply_markup = InlineKeyboardMarkup(inline_keyboard)
301 | await chk.delete(True)
302 |
303 | await bot.send_message(
304 | chat_id=update.chat.id,
305 | text=Translation.FORMAT_SELECTION,
306 | reply_markup=reply_markup,
307 | reply_to_message_id=update.id,
308 | )
309 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Get Your Bot Developed and Hosted for Free! 🔥
7 |
Are you tired of paying for bot hosting? Look no further! I offer affordable bot development services starting at just $5, and all bots are hosted for free on vercel.com Whether you need a chatbot, a trading bot, or anything in between, I've got you covered. Contact me on fiverr today to get started.
8 |
9 |
10 | ## Check Out Youtube Video & Playlist Downloder Bot
11 |
12 |
13 |
14 |
15 |
16 | Download YouTube videos and playlists. It supports both video and audio downloads, and provides a variety of resolution and format options for video downloads.
17 |
18 | Try Now...
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
⛩ Introduction
39 |
40 |
A simple Telegram bot, upload media file | video to Telegram using the direct download link. (YouTube, Mediafire, Google Drive, Mega drive, etc)
41 |
42 |
YouTube, Google Drive, Apple Music, Spotify, Resso, & direct Links support.
43 |
Bot can upload documents and files of video & audio types.
55 | A simple Telegram Bot, upload media file | video To Telegram using the direct download link. (YouTube, Mediafire, Google Drive, Mega drive, etc)
56 |
57 | Complete Documentation for All-Url-Uploader Bot
58 |
59 |
60 |
⚡️ Ecosystem
61 |
62 | | Version | Status | Description |
63 | | -------------------- | --------------------- | ------------------------------------------------------------------------------ |
64 | | v1.0.0 | ✘ | ☑ Upload all ytdl links, direct links |
65 | | v2.0.0 | ✔ | ☑ Upload all ytdl links, direct links, google drive etc. (Speed is much higher than version 1) |
66 | | v2.0.1 | ✘ | ☑ custom thumbnail support, fixed youtube download |
67 | | v2.0.2 | ✔ | ☑ fixed Bugs in v2.0.1, Modify custom thumbnail support |
68 |
69 |
🎯 To-Dos
70 |
71 | * [x] deploy to vps setup
72 | * [x] custom thumbnail support
73 | * [ ] custom caption support
74 | * [ ] mega.nz support
75 | * [ ] pdisk support
76 |
77 |
💥 Deploy
78 |
79 | Deploy To Heroku
80 |
81 |
82 | * Fork the repo
83 | * Copy forked repo link
84 | * Click Here To Continue.
85 |
86 |
87 |
88 | Deploy Locally
89 |
This bot is developed by using many Github open source projects.
175 |
Special thanks to people who starred to this repo and contributed to this project
176 |
177 |
💷 License
178 |
179 | [MIT License](https://opensource.org/licenses/MIT)
180 |
181 | ## Star History
182 |
183 | [](https://star-history.com/#kalanakt/All-Url-Uploader&Date)
184 |
185 |
186 |
187 | Copyright (c) 2022-present Hash Minner
188 |
189 |
212 |
--------------------------------------------------------------------------------
/.pylintrc:
--------------------------------------------------------------------------------
1 | [MAIN]
2 |
3 | # Specify a configuration file.
4 | #rcfile=
5 |
6 | # Python code to execute, usually for sys.path manipulation such as
7 | # pygtk.require().
8 | #init-hook=
9 |
10 | # Files or directories to be skipped. They should be base names, not
11 | # paths.
12 | ignore=CVS
13 |
14 | # Add files or directories matching the regex patterns to the ignore-list. The
15 | # regex matches against paths and can be in Posix or Windows format.
16 | ignore-paths=
17 |
18 | # Files or directories matching the regex patterns are skipped. The regex
19 | # matches against base names, not paths.
20 | ignore-patterns=^\.#
21 |
22 | # Pickle collected data for later comparisons.
23 | persistent=yes
24 |
25 | # List of plugins (as comma separated values of python modules names) to load,
26 | # usually to register additional checkers.
27 | load-plugins=
28 | pylint.extensions.check_elif,
29 | pylint.extensions.bad_builtin,
30 | pylint.extensions.docparams,
31 | pylint.extensions.for_any_all,
32 | pylint.extensions.set_membership,
33 | pylint.extensions.code_style,
34 | pylint.extensions.overlapping_exceptions,
35 | pylint.extensions.typing,
36 | pylint.extensions.redefined_variable_type,
37 | pylint.extensions.comparison_placement,
38 | pylint.extensions.mccabe,
39 |
40 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
41 | # number of processors available to use.
42 | jobs=0
43 |
44 | # When enabled, pylint would attempt to guess common misconfiguration and emit
45 | # user-friendly hints instead of false-positive error messages.
46 | suggestion-mode=yes
47 |
48 | # Allow loading of arbitrary C extensions. Extensions are imported into the
49 | # active Python interpreter and may run arbitrary code.
50 | unsafe-load-any-extension=no
51 |
52 | # A comma-separated list of package or module names from where C extensions may
53 | # be loaded. Extensions are loading into the active Python interpreter and may
54 | # run arbitrary code
55 | extension-pkg-allow-list=
56 |
57 | # Minimum supported python version
58 | py-version = 3.7.2
59 |
60 | # Control the amount of potential inferred values when inferring a single
61 | # object. This can help the performance when dealing with large functions or
62 | # complex, nested conditions.
63 | limit-inference-results=100
64 |
65 | # Specify a score threshold to be exceeded before program exits with error.
66 | fail-under=10.0
67 |
68 | # Return non-zero exit code if any of these messages/categories are detected,
69 | # even if score is above --fail-under value. Syntax same as enable. Messages
70 | # specified are enabled, while categories only check already-enabled messages.
71 | fail-on=
72 |
73 |
74 | [MESSAGES CONTROL]
75 |
76 | # Only show warnings with the listed confidence levels. Leave empty to show
77 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
78 | # confidence=
79 |
80 | # Enable the message, report, category or checker with the given id(s). You can
81 | # either give multiple identifier separated by comma (,) or put this option
82 | # multiple time (only on the command line, not in the configuration file where
83 | # it should appear only once). See also the "--disable" option for examples.
84 | enable=
85 | use-symbolic-message-instead,
86 | useless-suppression,
87 |
88 | # Disable the message, report, category or checker with the given id(s). You
89 | # can either give multiple identifiers separated by comma (,) or put this
90 | # option multiple times (only on the command line, not in the configuration
91 | # file where it should appear only once).You can also use "--disable=all" to
92 | # disable everything first and then re-enable specific checks. For example, if
93 | # you want to run only the similarities checker, you can use "--disable=all
94 | # --enable=similarities". If you want to run only the classes checker, but have
95 | # no Warning level messages displayed, use"--disable=all --enable=classes
96 | # --disable=W"
97 |
98 | disable=
99 | attribute-defined-outside-init,
100 | invalid-name,
101 | missing-docstring,
102 | protected-access,
103 | too-few-public-methods,
104 | # handled by black
105 | format,
106 | # We anticipate #3512 where it will become optional
107 | fixme,
108 | cyclic-import,
109 | import-error,
110 | C0116,
111 | C0114
112 |
113 |
114 | [REPORTS]
115 |
116 | # Set the output format. Available formats are text, parseable, colorized, msvs
117 | # (visual studio) and html. You can also give a reporter class, eg
118 | # mypackage.mymodule.MyReporterClass.
119 | output-format=text
120 |
121 | # Tells whether to display a full report or only the messages
122 | reports=no
123 |
124 | # Python expression which should return a note less than 10 (10 is the highest
125 | # note). You have access to the variables 'fatal', 'error', 'warning', 'refactor', 'convention'
126 | # and 'info', which contain the number of messages in each category, as
127 | # well as 'statement', which is the total number of statements analyzed. This
128 | # score is used by the global evaluation report (RP0004).
129 | evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
130 |
131 | # Template used to display messages. This is a python new-style format string
132 | # used to format the message information. See doc for all details
133 | #msg-template=
134 |
135 | # Activate the evaluation score.
136 | score=yes
137 |
138 |
139 | [LOGGING]
140 |
141 | # Logging modules to check that the string format arguments are in logging
142 | # function parameter format
143 | logging-modules=logging
144 |
145 | # The type of string formatting that logging methods do. `old` means using %
146 | # formatting, `new` is for `{}` formatting.
147 | logging-format-style=old
148 |
149 |
150 | [MISCELLANEOUS]
151 |
152 | # List of note tags to take in consideration, separated by a comma.
153 | notes=FIXME,XXX,TODO
154 |
155 | # Regular expression of note tags to take in consideration.
156 | #notes-rgx=
157 |
158 |
159 | [SIMILARITIES]
160 |
161 | # Minimum lines number of a similarity.
162 | min-similarity-lines=6
163 |
164 | # Ignore comments when computing similarities.
165 | ignore-comments=yes
166 |
167 | # Ignore docstrings when computing similarities.
168 | ignore-docstrings=yes
169 |
170 | # Ignore imports when computing similarities.
171 | ignore-imports=yes
172 |
173 | # Signatures are removed from the similarity computation
174 | ignore-signatures=yes
175 |
176 |
177 | [VARIABLES]
178 |
179 | # Tells whether we should check for unused import in __init__ files.
180 | init-import=no
181 |
182 | # A regular expression matching the name of dummy variables (i.e. expectedly
183 | # not used).
184 | dummy-variables-rgx=_$|dummy
185 |
186 | # List of additional names supposed to be defined in builtins. Remember that
187 | # you should avoid defining new builtins when possible.
188 | additional-builtins=
189 |
190 | # List of strings which can identify a callback function by name. A callback
191 | # name must start or end with one of those strings.
192 | callbacks=cb_,_cb
193 |
194 | # Tells whether unused global variables should be treated as a violation.
195 | allow-global-unused-variables=yes
196 |
197 | # List of names allowed to shadow builtins
198 | allowed-redefined-builtins=
199 |
200 | # Argument names that match this expression will be ignored. Default to name
201 | # with leading underscore.
202 | ignored-argument-names=_.*
203 |
204 | # List of qualified module names which can have objects that can redefine
205 | # builtins.
206 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
207 |
208 |
209 | [FORMAT]
210 |
211 | # Maximum number of characters on a single line.
212 | max-line-length=120
213 |
214 | # Regexp for a line that is allowed to be longer than the limit.
215 | ignore-long-lines=^\s*(# )??$
216 |
217 | # Allow the body of an if to be on the same line as the test if there is no
218 | # else.
219 | single-line-if-stmt=no
220 |
221 | # Allow the body of a class to be on the same line as the declaration if body
222 | # contains single statement.
223 | single-line-class-stmt=no
224 |
225 | # Maximum number of lines in a module
226 | max-module-lines=1000
227 |
228 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
229 | # tab).
230 | indent-string=' '
231 |
232 | # Number of spaces of indent required inside a hanging or continued line.
233 | indent-after-paren=4
234 |
235 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
236 | expected-line-ending-format=
237 |
238 |
239 | [BASIC]
240 |
241 | # Good variable names which should always be accepted, separated by a comma
242 | good-names=i,j,k,ex,Run,_
243 |
244 | # Good variable names regexes, separated by a comma. If names match any regex,
245 | # they will always be accepted
246 | good-names-rgxs=
247 |
248 | # Bad variable names which should always be refused, separated by a comma
249 | bad-names=foo,bar,baz,toto,tutu,tata
250 |
251 | # Bad variable names regexes, separated by a comma. If names match any regex,
252 | # they will always be refused
253 | bad-names-rgxs=
254 |
255 | # Colon-delimited sets of names that determine each other's naming style when
256 | # the name regexes allow several styles.
257 | name-group=
258 |
259 | # Include a hint for the correct naming format with invalid-name
260 | include-naming-hint=no
261 |
262 | # Naming style matching correct function names.
263 | function-naming-style=snake_case
264 |
265 | # Regular expression matching correct function names
266 | function-rgx=[a-z_][a-z0-9_]{2,30}$
267 |
268 | # Naming style matching correct variable names.
269 | variable-naming-style=snake_case
270 |
271 | # Regular expression matching correct variable names
272 | variable-rgx=[a-z_][a-z0-9_]{2,30}$
273 |
274 | # Naming style matching correct constant names.
275 | const-naming-style=UPPER_CASE
276 |
277 | # Regular expression matching correct constant names
278 | const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
279 |
280 | # Naming style matching correct attribute names.
281 | attr-naming-style=snake_case
282 |
283 | # Regular expression matching correct attribute names
284 | attr-rgx=[a-z_][a-z0-9_]{2,}$
285 |
286 | # Naming style matching correct argument names.
287 | argument-naming-style=snake_case
288 |
289 | # Regular expression matching correct argument names
290 | argument-rgx=[a-z_][a-z0-9_]{2,30}$
291 |
292 | # Naming style matching correct class attribute names.
293 | class-attribute-naming-style=any
294 |
295 | # Regular expression matching correct class attribute names
296 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
297 |
298 | # Naming style matching correct class constant names.
299 | class-const-naming-style=UPPER_CASE
300 |
301 | # Regular expression matching correct class constant names. Overrides class-
302 | # const-naming-style.
303 | #class-const-rgx=
304 |
305 | # Naming style matching correct inline iteration names.
306 | inlinevar-naming-style=any
307 |
308 | # Regular expression matching correct inline iteration names
309 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
310 |
311 | # Naming style matching correct class names.
312 | class-naming-style=PascalCase
313 |
314 | # Regular expression matching correct class names
315 | class-rgx=[A-Z_][a-zA-Z0-9]+$
316 |
317 |
318 | # Naming style matching correct module names.
319 | module-naming-style=snake_case
320 |
321 | # Regular expression matching correct module names
322 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
323 |
324 |
325 | # Naming style matching correct method names.
326 | method-naming-style=snake_case
327 |
328 | # Regular expression matching correct method names
329 | method-rgx=[a-z_][a-z0-9_]{2,}$
330 |
331 | # Regular expression which can overwrite the naming style set by typevar-naming-style.
332 | #typevar-rgx=
333 |
334 | # Regular expression which should only match function or class names that do
335 | # not require a docstring. Use ^(?!__init__$)_ to also check __init__.
336 | no-docstring-rgx=__.*__
337 |
338 | # Minimum line length for functions/classes that require docstrings, shorter
339 | # ones are exempt.
340 | docstring-min-length=-1
341 |
342 | # List of decorators that define properties, such as abc.abstractproperty.
343 | property-classes=abc.abstractproperty
344 |
345 |
346 | [TYPECHECK]
347 |
348 | # Regex pattern to define which classes are considered mixins if ignore-mixin-
349 | # members is set to 'yes'
350 | mixin-class-rgx=.*MixIn
351 |
352 | # List of module names for which member attributes should not be checked
353 | # (useful for modules/projects where namespaces are manipulated during runtime
354 | # and thus existing member attributes cannot be deduced by static analysis). It
355 | # supports qualified module names, as well as Unix pattern matching.
356 | ignored-modules=
357 |
358 | # List of class names for which member attributes should not be checked (useful
359 | # for classes with dynamically set attributes). This supports the use of
360 | # qualified names.
361 | ignored-classes=SQLObject, optparse.Values, thread._local, _thread._local
362 |
363 | # List of members which are set dynamically and missed by pylint inference
364 | # system, and so shouldn't trigger E1101 when accessed. Python regular
365 | # expressions are accepted.
366 | generated-members=REQUEST,acl_users,aq_parent,argparse.Namespace
367 |
368 | # List of decorators that create context managers from functions, such as
369 | # contextlib.contextmanager.
370 | contextmanager-decorators=contextlib.contextmanager
371 |
372 | # Tells whether to warn about missing members when the owner of the attribute
373 | # is inferred to be None.
374 | ignore-none=yes
375 |
376 | # This flag controls whether pylint should warn about no-member and similar
377 | # checks whenever an opaque object is returned when inferring. The inference
378 | # can return multiple potential results while evaluating a Python object, but
379 | # some branches might not be evaluated, which results in partial inference. In
380 | # that case, it might be useful to still emit no-member and other checks for
381 | # the rest of the inferred objects.
382 | ignore-on-opaque-inference=yes
383 |
384 | # Show a hint with possible names when a member name was not found. The aspect
385 | # of finding the hint is based on edit distance.
386 | missing-member-hint=yes
387 |
388 | # The minimum edit distance a name should have in order to be considered a
389 | # similar match for a missing member name.
390 | missing-member-hint-distance=1
391 |
392 | # The total number of similar names that should be taken in consideration when
393 | # showing a hint for a missing member.
394 | missing-member-max-choices=1
395 |
396 | [SPELLING]
397 |
398 | # Spelling dictionary name. Available dictionaries: none. To make it working
399 | # install python-enchant package.
400 | spelling-dict=
401 |
402 | # List of comma separated words that should not be checked.
403 | spelling-ignore-words=
404 |
405 | # List of comma separated words that should be considered directives if they
406 | # appear and the beginning of a comment and should not be checked.
407 | spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:,pragma:,# noinspection
408 |
409 | # A path to a file that contains private dictionary; one word per line.
410 | spelling-private-dict-file=.pyenchant_pylint_custom_dict.txt
411 |
412 | # Tells whether to store unknown words to indicated private dictionary in
413 | # --spelling-private-dict-file option instead of raising a message.
414 | spelling-store-unknown-words=no
415 |
416 | # Limits count of emitted suggestions for spelling mistakes.
417 | max-spelling-suggestions=2
418 |
419 |
420 | [DESIGN]
421 |
422 | # Maximum number of arguments for function / method
423 | max-args=10
424 |
425 | # Maximum number of locals for function / method body
426 | max-locals=25
427 |
428 | # Maximum number of return / yield for function / method body
429 | max-returns=11
430 |
431 | # Maximum number of branch for function / method body
432 | max-branches=27
433 |
434 | # Maximum number of statements in function / method body
435 | max-statements=100
436 |
437 | # Maximum number of parents for a class (see R0901).
438 | max-parents=7
439 |
440 | # List of qualified class names to ignore when counting class parents (see R0901).
441 | ignored-parents=
442 |
443 | # Maximum number of attributes for a class (see R0902).
444 | max-attributes=11
445 |
446 | # Minimum number of public methods for a class (see R0903).
447 | min-public-methods=2
448 |
449 | # Maximum number of public methods for a class (see R0904).
450 | max-public-methods=25
451 |
452 | # Maximum number of boolean expressions in an if statement (see R0916).
453 | max-bool-expr=5
454 |
455 | # List of regular expressions of class ancestor names to
456 | # ignore when counting public methods (see R0903).
457 | exclude-too-few-public-methods=
458 |
459 | max-complexity=10
460 |
461 | [CLASSES]
462 |
463 | # List of method names used to declare (i.e. assign) instance attributes.
464 | defining-attr-methods=__init__,__new__,setUp,__post_init__
465 |
466 | # List of valid names for the first argument in a class method.
467 | valid-classmethod-first-arg=cls
468 |
469 | # List of valid names for the first argument in a metaclass class method.
470 | valid-metaclass-classmethod-first-arg=mcs
471 |
472 | # List of member names, which should be excluded from the protected access
473 | # warning.
474 | exclude-protected=_asdict,_fields,_replace,_source,_make
475 |
476 | # Warn about protected attribute access inside special methods
477 | check-protected-access-in-special-methods=no
478 |
479 | [IMPORTS]
480 |
481 | # List of modules that can be imported at any level, not just the top level
482 | # one.
483 | allow-any-import-level=
484 |
485 | # Allow wildcard imports from modules that define __all__.
486 | allow-wildcard-with-all=no
487 |
488 | # Analyse import fallback blocks. This can be used to support both Python 2 and
489 | # 3 compatible code, which means that the block might have code that exists
490 | # only in one or another interpreter, leading to false positives when analysed.
491 | analyse-fallback-blocks=no
492 |
493 | # Deprecated modules which should not be used, separated by a comma
494 | deprecated-modules=regsub,TERMIOS,Bastion,rexec
495 |
496 | # Create a graph of every (i.e. internal and external) dependencies in the
497 | # given file (report RP0402 must not be disabled)
498 | import-graph=
499 |
500 | # Create a graph of external dependencies in the given file (report RP0402 must
501 | # not be disabled)
502 | ext-import-graph=
503 |
504 | # Create a graph of internal dependencies in the given file (report RP0402 must
505 | # not be disabled)
506 | int-import-graph=
507 |
508 | # Force import order to recognize a module as part of the standard
509 | # compatibility libraries.
510 | known-standard-library=
511 |
512 | # Force import order to recognize a module as part of a third party library.
513 | known-third-party=enchant
514 |
515 | # Couples of modules and preferred modules, separated by a comma.
516 | preferred-modules=
517 |
518 |
519 | [EXCEPTIONS]
520 |
521 | # Exceptions that will emit a warning when being caught. Defaults to
522 | # "Exception"
523 | overgeneral-exceptions=Exception
524 |
525 |
526 | [TYPING]
527 |
528 | # Set to ``no`` if the app / library does **NOT** need to support runtime
529 | # introspection of type annotations. If you use type annotations
530 | # **exclusively** for type checking of an application, you're probably fine.
531 | # For libraries, evaluate if some users what to access the type hints at
532 | # runtime first, e.g., through ``typing.get_type_hints``. Applies to Python
533 | # versions 3.7 - 3.9
534 | runtime-typing = no
535 |
536 |
537 | [DEPRECATED_BUILTINS]
538 |
539 | # List of builtins function names that should not be used, separated by a comma
540 | bad-functions=map,input
541 |
542 |
543 | [REFACTORING]
544 |
545 | # Maximum number of nested blocks for function / method body
546 | max-nested-blocks=5
547 |
548 | # Complete name of functions that never returns. When checking for
549 | # inconsistent-return-statements if a never returning function is called then
550 | # it will be considered as an explicit return statement and no message will be
551 | # printed.
552 | never-returning-functions=sys.exit,argparse.parse_error
553 |
554 |
555 | [STRING]
556 |
557 | # This flag controls whether inconsistent-quotes generates a warning when the
558 | # character used as a quote delimiter is used inconsistently within a module.
559 | check-quote-consistency=no
560 |
561 | # This flag controls whether the implicit-str-concat should generate a warning
562 | # on implicit string concatenation in sequences defined over several lines.
563 | check-str-concat-over-line-jumps=no
564 |
565 |
566 | [CODE_STYLE]
567 |
568 | # Max line length for which to sill emit suggestions. Used to prevent optional
569 | # suggestions which would get split by a code formatter (e.g., black). Will
570 | # default to the setting for ``max-line-length``.
571 | #max-line-length-suggestions=
572 |
--------------------------------------------------------------------------------