MohammedAlakhras commited on
Commit
afce118
·
1 Parent(s): 0feaa16

Add application file

Browse files
.deepsource.toml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ version = 1
2
+
3
+ [[analyzers]]
4
+ name = "secrets"
5
+
6
+ [[analyzers]]
7
+ name = "python"
8
+
9
+ [analyzers.meta]
10
+ runtime_version = "3.x.x"
.github/FUNDING.yml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ github: EDM115
2
+ custom: ["https://paypal.me/8EDM115", "https://www.buymeacoffee.com/edm115", "https://t.me/EDM115bots/544", "https://edm115.shadd.eu.org/"]
.github/ISSUE_TEMPLATE/bug_report.md ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: Bug report
3
+ about: Report any bug you found
4
+ title: "[BUG] "
5
+ labels: bug
6
+ assignees: EDM115
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce**
14
+ Steps to reproduce the behavior:
15
+ 1. Do this
16
+ 2. Then that
17
+ 3. See error
18
+
19
+ **Expected behavior**
20
+ A clear and concise description of what you expected to happen.
21
+
22
+ **Screenshots**
23
+ If applicable, add screenshots to help explain your problem.
24
+
25
+ **Additional context**
26
+ Add any other context about the problem here.
.github/ISSUE_TEMPLATE/feature_request.md ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: "[FEATURE REQUEST] "
5
+ labels: enhancement
6
+ assignees: EDM115
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem ? Please describe :**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
.github/dependabot.yml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "pip"
9
+ directory: "/"
10
+ schedule:
11
+ interval: "daily"
12
+ assignees:
13
+ - "EDM115"
14
+ open-pull-requests-limit: 15
.github/workflows/black-code-style.yml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Renders the whole code using Black code style
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ jobs:
7
+ black:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - name: Checkout
11
+ uses: actions/checkout@v4
12
+ with:
13
+ persist-credentials: false
14
+ fetch-depth: 0
15
+ - name: Black-ify
16
+ uses: psf/black@stable
17
+ with:
18
+ options: "--verbose"
19
+ - name: Commit changes
20
+ run: |
21
+ d=`date '+%Y/%m/%dT%H:%M:%SZ'`
22
+ git config --local user.email ${{ secrets.MAIL }}
23
+ git config --local user.name ${{ secrets.USERNAME }}
24
+ git add -A
25
+ git commit -m "Code style changed to Black at ${d}"
26
+ - name: Push commit
27
+ uses: ad-m/github-push-action@v0.6.0
28
+ with:
29
+ force: true
30
+ directory: "."
31
+ branch: ${{ github.ref }}
32
+ github_token: ${{ secrets.GITHUB_TOKEN }}
.github/workflows/build-docker-image.yml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Build Docker Image
2
+
3
+ on:
4
+ # push:
5
+ # branches: [ main ]
6
+ # pull_request:
7
+ # branches: [ main ]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - name: Build the Docker image
17
+ run: docker build . --file Dockerfile --tag unzip-bot:$(date +%s)
.github/workflows/codeql-analysis.yml ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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:
15
+ # push:
16
+ # branches: [ master ]
17
+ # pull_request:
18
+ # # The branches below must be a subset of the branches above
19
+ # branches: [ master ]
20
+ # schedule:
21
+ # - cron: '0 4 * * 6'
22
+ workflow_dispatch:
23
+
24
+ jobs:
25
+ analyze:
26
+ name: Analyze
27
+ runs-on: ubuntu-latest
28
+ permissions:
29
+ actions: read
30
+ contents: read
31
+ security-events: write
32
+
33
+ strategy:
34
+ fail-fast: false
35
+ matrix:
36
+ language: [ 'python' ]
37
+
38
+ steps:
39
+ - name: Checkout repository
40
+ uses: actions/checkout@v4
41
+
42
+ # Initializes the CodeQL tools for scanning.
43
+ - name: Initialize CodeQL
44
+ uses: github/codeql-action/init@v2
45
+ with:
46
+ languages: ${{ matrix.language }}
47
+ # If you wish to specify custom queries, you can do so here or in a config file.
48
+ # By default, queries listed here will override any specified in a config file.
49
+ # Prefix the list here with "+" to use these queries and those in the config file.
50
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
51
+
52
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
53
+ # If this step fails, then you should remove it and run the build manually (see below)
54
+ - name: Autobuild
55
+ uses: github/codeql-action/autobuild@v2
56
+
57
+ # ℹ️ Command-line programs to run using the OS shell.
58
+ # 📚 https://git.io/JvXDl
59
+
60
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
61
+ # and modify them (or add more) to build your code if your project
62
+ # uses a compiled language
63
+
64
+ #- run: |
65
+ # make bootstrap
66
+ # make release
67
+
68
+ - name: Perform CodeQL Analysis
69
+ uses: github/codeql-action/analyze@v2
.github/workflows/publish-docker-image.yml ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Publish Docker Image
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ push_to_registries:
10
+ name: Push Docker image to GHCR & Docker Hub
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ packages: write
14
+ contents: read
15
+ steps:
16
+ - name: Check out the repo
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Log in to Docker Hub
20
+ uses: docker/login-action@v3
21
+ with:
22
+ username: ${{ secrets.DOCKER_USERNAME }}
23
+ password: ${{ secrets.DOCKER_TOKEN }}
24
+
25
+ - name: Log in to the GitHub Container registry
26
+ uses: docker/login-action@v3
27
+ with:
28
+ registry: ghcr.io
29
+ username: ${{ github.actor }}
30
+ password: ${{ secrets.GITHUB_TOKEN }}
31
+
32
+ - name: Extract metadata (tags, labels) for Docker
33
+ id: meta
34
+ uses: docker/metadata-action@v5
35
+ with:
36
+ images: |
37
+ edm115/unzip-bot
38
+ ghcr.io/${{ github.repository }}
39
+
40
+ - name: Build and push Docker images
41
+ uses: docker/build-push-action@v5
42
+ with:
43
+ context: .
44
+ push: true
45
+ tags: ${{ steps.meta.outputs.tags }}
46
+ labels: ${{ steps.meta.outputs.labels }}
.gitignore ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ .Python
8
+ build/
9
+ develop-eggs/
10
+ dist/
11
+ downloads/
12
+ eggs/
13
+ .eggs/
14
+ lib/
15
+ lib64/
16
+ parts/
17
+ sdist/
18
+ var/
19
+ wheels/
20
+ pip-wheel-metadata/
21
+ share/python-wheels/
22
+ *.egg-info/
23
+ .installed.cfg
24
+ *.egg
25
+ MANIFEST
26
+
27
+ # PyInstaller
28
+ # Usually these files are written by a python script from a template
29
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.py,cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+
51
+ # PyBuilder
52
+ target/
53
+
54
+ # Jupyter Notebook
55
+ .ipynb_checkpoints
56
+
57
+ # IPython
58
+ profile_default/
59
+ ipython_config.py
60
+
61
+ # pyenv
62
+ .python-version
63
+
64
+ # pipenv
65
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
66
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
67
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
68
+ # install all needed dependencies.
69
+ #Pipfile.lock
70
+
71
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
72
+ __pypackages__/
73
+
74
+ # Environments
75
+ .env
76
+ .venv
77
+ env/
78
+ venv/
79
+ ENV/
80
+ env.bak/
81
+ venv.bak/
82
+
83
+ # VS Code
84
+ .vscode
85
+ .vscode/
86
+ .vscode/*
87
+
88
+ # Secrets
89
+ *.session
.restyled.yml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ enabled: false
2
+ exclude:
3
+ - "unzipper/modules/ext_script/ext_helper.py"
4
+ - "unzipper/modules/ext_script/up_helper.py"
5
+ auto: false
6
+ pull_requests: true
7
+ commit_template: |
8
+ Restyled by ${restyler.name}
9
+ statuses:
10
+ differences: false
11
+ no_differences: true
12
+ error: true
13
+ labels:
14
+ - style
15
+ restylers:
16
+ - name: autopep8
17
+ - name: isort
18
+ - name: black
19
+ - name: yapf
.whitesource ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "scanSettings": {
3
+ "baseBranches": []
4
+ },
5
+ "checkRunSettings": {
6
+ "vulnerableCheckRunConclusionLevel": "failure",
7
+ "displayMode": "diff",
8
+ "useMendCheckNames": true
9
+ },
10
+ "issueSettings": {
11
+ "minSeverityLevel": "LOW",
12
+ "issueType": "DEPENDENCY"
13
+ }
14
+ }
CODE_OF_CONDUCT.md ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, religion, or sexual identity
10
+ and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ * Demonstrating empathy and kindness toward other people
21
+ * Being respectful of differing opinions, viewpoints, and experiences
22
+ * Giving and gracefully accepting constructive feedback
23
+ * Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ * Focusing on what is best not just for us as individuals, but for the
26
+ overall community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ * The use of sexualized language or imagery, and sexual attention or
31
+ advances of any kind
32
+ * Trolling, insulting or derogatory comments, and personal or political attacks
33
+ * Public or private harassment
34
+ * Publishing others' private information, such as a physical or email
35
+ address, without their explicit permission
36
+ * Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official e-mail address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ dev@edm115.eu.org.
64
+ All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+ All community leaders are obligated to respect the privacy and security of the
67
+ reporter of any incident.
68
+
69
+ ## Enforcement Guidelines
70
+
71
+ Community leaders will follow these Community Impact Guidelines in determining
72
+ the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+ ### 1. Correction
75
+
76
+ **Community Impact**: Use of inappropriate language or other behavior deemed
77
+ unprofessional or unwelcome in the community.
78
+
79
+ **Consequence**: A private, written warning from community leaders, providing
80
+ clarity around the nature of the violation and an explanation of why the
81
+ behavior was inappropriate. A public apology may be requested.
82
+
83
+ ### 2. Warning
84
+
85
+ **Community Impact**: A violation through a single incident or series
86
+ of actions.
87
+
88
+ **Consequence**: A warning with consequences for continued behavior. No
89
+ interaction with the people involved, including unsolicited interaction with
90
+ those enforcing the Code of Conduct, for a specified period of time. This
91
+ includes avoiding interactions in community spaces as well as external channels
92
+ like social media. Violating these terms may lead to a temporary or
93
+ permanent ban.
94
+
95
+ ### 3. Temporary Ban
96
+
97
+ **Community Impact**: A serious violation of community standards, including
98
+ sustained inappropriate behavior.
99
+
100
+ **Consequence**: A temporary ban from any sort of interaction or public
101
+ communication with the community for a specified period of time. No public or
102
+ private interaction with the people involved, including unsolicited interaction
103
+ with those enforcing the Code of Conduct, is allowed during this period.
104
+ Violating these terms may lead to a permanent ban.
105
+
106
+ ### 4. Permanent Ban
107
+
108
+ **Community Impact**: Demonstrating a pattern of violation of community
109
+ standards, including sustained inappropriate behavior, harassment of an
110
+ individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+ **Consequence**: A permanent ban from any sort of public interaction within
113
+ the community.
114
+
115
+ ## Attribution
116
+
117
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+ version 2.0, available at
119
+ https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120
+
121
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct
122
+ enforcement ladder](https://github.com/mozilla/diversity).
123
+
124
+ [homepage]: https://www.contributor-covenant.org
125
+
126
+ For answers to common questions about this code of conduct, see the FAQ at
127
+ https://www.contributor-covenant.org/faq. Translations are available at
128
+ https://www.contributor-covenant.org/translations.
CreateMongoDB.md ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # How to create a MongoDB URL in 10min ?
2
+
3
+ 1. Go to [MongoDB website](https://mongodb.com/cloud/atlas/register)
4
+ *(skip steps 2 and 3 if you already have an account, just create a new cluster)*
5
+ 1. On the account setup page, choose wisely your Organization name and Project name (you can't change that). Select Python as preferred language
6
+ 2. Choose `Create a cluster` on the `Shared Clusters` category (the free one)
7
+ 3. Choose `Azure` as provider and `Netherlands` as region, then click on `Create Cluster` (it takes 1-5 minutes, but don't close the window !)
8
+ 4. When it's done, click on `Network Access` on left panel
9
+ 1. `Add IP Address`
10
+ 2. `Allow access from anywhere` (because Heroku IP addresses changes everytime)
11
+ 3. `Confirm` (it can take up to 2 minutes)
12
+ 5. Click on `Clusters` on left panel
13
+ 1. `Connect`
14
+ 2. Create a Database User (remember the credentials you choose !)
15
+ 3. `Choose a connection method`
16
+ 4. `Connect with your application`
17
+ 5. Choose `Python` as Driver
18
+ 6. Choose `3.6 or later` as Version
19
+ 7. Copy the URL
20
+ 6. Replace `<password>` with the one you chosen at step 5.ii
21
+
22
+ ## You're done :partying_face:
23
+ ### Now use that as `MONGODB_URL`
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM archlinux:latest
2
+
3
+ RUN pacman -Syyu --noconfirm
4
+ RUN pacman -S --noconfirm python-pip zstd p7zip gcc git ffmpeg
5
+ RUN python -m venv /venv
6
+ ENV PATH="/venv/bin:$PATH"
7
+ RUN pip install -U pip setuptools wheel
8
+ RUN mkdir /app/
9
+ WORKDIR /app/
10
+ RUN git clone https://github.com/EDM115/unzip-bot.git /app/
11
+ COPY requirements.txt /app/requirements.txt
12
+ RUN pip install -U -r requirements.txt
13
+ CMD bash start.sh
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2022 - 2023 EDM115
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
Procfile ADDED
@@ -0,0 +1 @@
 
 
1
+ unzipper: bash start.sh
app.json ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Unzipper Bot",
3
+ "description": "A Telegram bot to extract various types of archives",
4
+ "logo": "https://telegra.ph/file/d4ba24682e030fc58613f.jpg",
5
+ "keywords": [
6
+ "7z",
7
+ "zip",
8
+ "rar",
9
+ "Telegram Bot",
10
+ "unzipper bot"
11
+ ],
12
+ "website": "https://edm115.dev/unzip",
13
+ "repository": "https://github.com/EDM115/unzip-bot",
14
+ "success_url": "https://t.me/EDM115bots",
15
+ "env": {
16
+ "APP_ID": {
17
+ "description": "Your APP_ID from my.telegram.org",
18
+ "required": true
19
+ },
20
+ "API_HASH": {
21
+ "description": "Your API_HASH from my.telegram.org",
22
+ "required": true
23
+ },
24
+ "BOT_OWNER": {
25
+ "description": "Telegram Id of your account",
26
+ "required": true
27
+ },
28
+ "BOT_TOKEN": {
29
+ "description": "Your Bot Token From @BotFather",
30
+ "required": true
31
+ },
32
+ "MONGODB_URL": {
33
+ "description": "Your MongoDB url, Get it from https://www.mongodb.com/",
34
+ "required": true
35
+ },
36
+ "LOGS_CHANNEL": {
37
+ "description": "ID of a channel, can also be a group",
38
+ "required": true
39
+ }
40
+ },
41
+ "addons": [],
42
+ "buildpacks": [
43
+ {
44
+ "url": "heroku/python"
45
+ }
46
+ ],
47
+ "formation": {
48
+ "worker": {
49
+ "quantity": 1,
50
+ "size": "eco"
51
+ }
52
+ },
53
+ "stack": "container"
54
+ }
bot_thumb.jpg ADDED
changelog.md ADDED
@@ -0,0 +1,427 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div align="center">
2
+
3
+ <h1><a href="https://github.com/EDM115/unzip-bot" target="_blank" rel="noreferrer"><img src="https://telegra.ph/file/d4ba24682e030fc58613f.jpg" alt="unzip-bot" width="40" height="40"/></a> Unarchiver Bot • Changelog</h1>
4
+
5
+ ## You will find here all the changes made with each version, in antichronological order
6
+
7
+ #### Convention : `vX.Y.Z`, where `X` stands for a major change and a lot of new features, `Y` for some new features and bug fixes, `Z` for testing stuff and undebugged things
8
+
9
+ </div>
10
+
11
+ ---
12
+
13
+ ### v6.3.2 **[LATEST STABLE RELEASE]**
14
+
15
+ - Fixed thumbnails not being saved
16
+ - Premium related stuff is moved to its own branch (buggy so yes)
17
+ - Fixed files being nearly all the time not uploaded
18
+ - Better logging
19
+ - Added [Mend Bolt](https://github.com/marketplace/whitesource-bolt)
20
+ - Downgraded pyromod to 1.5 again (too much errors, I know they had been fixed in 2.1.0 but still)
21
+ - Client specification in decorators instead of global @Client
22
+ - New maintenance logic
23
+ - Attempt to support files sent as TG links (may fail for topics, unaccessible chats and forward-restricted files)
24
+
25
+ ### v6.3.1
26
+
27
+ - Finally fixed [#133](https://github.com/EDM115/unzip-bot/issues/133)
28
+ - Attempt to create a premium user to upload +2Gb files
29
+ - Added `/maintenance`
30
+
31
+ ### v6.3.0
32
+
33
+ - Ongoing tasks are removed from the database after a restart
34
+ - Added a new command : /cleantasks
35
+ - Finally upgraded pyromod to v2
36
+ - Upgraded from python-3.11.3 to python-3.11.5
37
+ - Removed any trace of bayfiles upload since the service is dead
38
+ - Support for `.partx.rar` splitted archives
39
+ - Download files in 10 Mb chunks instead of 5 Mb
40
+ - Added maintenance on DB
41
+ - Aded VIP methods in DB + implementation of no-restrictions for VIP ([#205](https://github.com/EDM115/unzip-bot/issues/205))
42
+
43
+ ### v6.2.4
44
+
45
+ - Attempt to add some URL parsers (fail)
46
+ - Even more refactor
47
+ - Splitted files can be renamed
48
+ - Url are checked before extracting
49
+ - If a thumbnail fails to be uploaded to telegra.ph, the error message is no longer saved in the db (and on download, non url strings are skipped)
50
+ - `/broadcast` now shows how many users had been processed
51
+
52
+ ### v6.2.3
53
+
54
+ - Fixes little error on strings
55
+ - Closes a lot of issues opened by DeepSource (mostly style)
56
+ - Added a task limit (configurable in `config.py`)
57
+ - FloodWait is now handled correctly everywhere
58
+ - The bot is no longer blocking any task (finally)
59
+
60
+ ### v6.2.2
61
+
62
+ - Bugfix : No longer use of `subprocess.communicate()`, as it's thread blocking
63
+ - All strings are in `bot_data.py`, hope this should ease [#179](https://github.com/EDM115/unzip-bot/issues/179)
64
+ - Even less thread block : use of `async for` and `yield`
65
+ - Any file unreachable/with a size of 0B is skipped, thus avoiding the bot being stuck on an impossible task
66
+
67
+ ### v6.2.1
68
+
69
+ - Security fix : Merging files could lead to paths being swapped between users. It's now fixed
70
+
71
+ ### v6.2.0
72
+
73
+ - Added a new command : /merge (and /done)
74
+ - Allows to merge splitted archives in .XXX format
75
+ - Upload of thumbnails on telegra.ph now handles errors
76
+
77
+ ### v6.1.0
78
+
79
+ - URL's also shows a progressbar + ETA when possible
80
+ - Downloads are 28 times faster
81
+ - Some databases are cleared upon restart
82
+ - Attempt to implement [#137](https://github.com/EDM115/unzip-bot/issues/137)
83
+ - New boot sequence
84
+
85
+ ### v6.0.0
86
+
87
+ - Dependencies update
88
+ - tgz and zst archives are now supported
89
+ - Thumbnail change tasks are now removed from DB after completion
90
+ - Dockerfile have been updated : Add of ffmpeg and venv
91
+ - Uploading videos as media is fixed ! [#133](https://github.com/EDM115/unzip-bot/issues/133)
92
+ - Added Docker instructions on the README
93
+ - Added GitHub Actions for Docker publishing and deployment
94
+ - Updated the FUNDING.yml
95
+ - New command : /donate, plus donate button appears on /start and after a task is processed
96
+ - Tell users that they can rate the bot after a task is processed
97
+ - [#33](https://github.com/EDM115/unzip-bot/issues/33) is gone (no longer useless alerts)
98
+ - ETA is now correct
99
+ - Tried to add a way to cancel tasks, but it's not working
100
+ - Files above 2 GB are now splitted
101
+
102
+ ---
103
+
104
+ ### v5.3.1
105
+
106
+ - Added /gitpull command to try the latest updates (removed at each restart)
107
+ - /delthumb also works locally
108
+ - Logs the boot time on database
109
+ - Clears the logs on /restart (because in the end they're sent before actually restarting)
110
+ - /user2 now correctly format the link when an username is provided
111
+ - Users are warned when the bot have restarted
112
+ - So the ongoing tasks are also stored in the DB
113
+ - And so /stats shows how many tasks are ongoing
114
+ - X7 archives are now supported
115
+
116
+ ### v5.3.0
117
+
118
+ - Splitted archives are no longer processed (even .rar ones)
119
+ - Sending videos as media worked but now instantly fails for an unknown reason
120
+ - Heroku deployment file now complies to their drop of the free their
121
+ - Added THUMB_DEL buttons
122
+ - Added ZIPX support
123
+ - Added a Refresh button on /stats ([#143](https://github.com/EDM115/unzip-bot/issues/143))
124
+
125
+ ### v5.2.2
126
+
127
+ - Happy new year 2023 🎉
128
+ - Avoids double ban/unban
129
+ - Fixed extentions recognition
130
+ - Added a "Processing task" message
131
+
132
+
133
+ ### v5.2.1
134
+
135
+ - Added the website to /help
136
+ - Python 3.10 -> 3.11
137
+ - Added a new command : /report
138
+
139
+ ### v5.2.0
140
+
141
+ - Removal of the personal_only and beta branches, only master remains
142
+ - Added permalink to the profile on /user2
143
+ - Half refactor, a lot of errors and misuse of functions gone
144
+ - Added renovate[bot]
145
+ - Better new user formatting
146
+ - ban/unban also acts on main user_db
147
+ - Added support for IPSW archives on request
148
+
149
+ ### v5.1.2
150
+
151
+ - URL downloaded files finally have their original name
152
+ - Split goes stonks (lie)
153
+ - Prompting users to transload files I can't download
154
+ - What happens on the terminal is now on the logs
155
+ - Made /listdir and /sendfile for testing purposes
156
+ - Added issue templates
157
+ - /delthumb now also deletes it from the DB
158
+
159
+ ### v5.1.1
160
+
161
+ - **Huge code refactoring**
162
+ - Little fixes
163
+ - Still trying to split files
164
+ - Thumbnail support is permanant 🥳 Redownloads them at every server restart
165
+ - Clears correctly the thumbnails
166
+ - FloodWait correctly handled
167
+ - Bot starting happens on another file (so we can use async/await)
168
+
169
+ ### v5.1.0
170
+
171
+ - We fetch the file size *before* uploading
172
+ - We try to split files above 2 GB (fail)
173
+
174
+ ### v5.0.3
175
+
176
+ - Added /user2 and /self
177
+ - Added ability to just change the thumbnail of the file (archive or not)
178
+ - Also we can rename it
179
+
180
+ ### v5.0.2
181
+
182
+ - Heroku runtime shifted from Python 3.9.11 to 3.10.6
183
+ - Added wheel for faster deployment
184
+ - /getthumbs work
185
+
186
+ ### v5.0.1
187
+
188
+ - Made thumbnail support better (with buttons)
189
+ - Saves the thumbnail URL (telegra.ph) to the DB
190
+ - Buttons are side-by-side
191
+ - Checks if sent file is actually an archive (so we stop processing PDF and MKV 😭)
192
+ - Code style shifted to Black 🖤
193
+ - Upgraded to Pyrogram v2 (finally)
194
+ - The bot can process other things while extracting
195
+ - Better password handling
196
+ - Progressbar on uploads too
197
+ - Uploads as media by default
198
+ - Avoids splitted archives to be processed
199
+ - Better LOG_CHANNEL verification
200
+
201
+ ### v5.0.0
202
+
203
+ - Added extensions list (for verification)
204
+ - Medias are sent as native media
205
+ - Fixed ENTITY_BOUNDS_INVALID error
206
+ - Removed numpy as we don't use it
207
+ - Added requests
208
+ - Added development followup ([#38](https://github.com/EDM115/unzip-bot/issues/38))
209
+ - Uptime on /stats works correctly
210
+ - Simpler buttons
211
+ - Thumbnails on upload are officially supported 🥳
212
+ - Commands updates (no /setmode, /me become /info, addded stats for everyone)
213
+
214
+ ---
215
+
216
+ ### v4.5.0
217
+
218
+ - Attempt to add /merge and /cancel commands + linked callbacks. Actually failed
219
+
220
+ ### v4.4.5
221
+
222
+ - The logs are better. Putting the text message _before_ file, as it does with URL & replies to text message instead of file
223
+ - Made a way more permissive regex for URL
224
+ - Fixed exceptions on nearly all commands
225
+ - Performing a /restart send the logs automatically
226
+
227
+ ### v4.4.4
228
+
229
+ - Definitely fixed #NEW_USER
230
+ - Once again tweaks on BayFiles
231
+
232
+ ### v4.4.3
233
+
234
+ - Way better handling of `check_logs()` on start
235
+ - Fixed the #NEW_USER
236
+ - Few changes on BayFiles upload
237
+
238
+ ### v4.4.2
239
+
240
+ - A lot of changes on BayFiles upload :
241
+ - Errors sent to logs
242
+ - Formatting the results with size, url and filename
243
+ - Correct formatting of the errors
244
+ - Created get_cloud(), will improve it to let choose the upload platform for the user (bayfiles, anonfiles, …)
245
+
246
+ ### v4.4.1
247
+
248
+ - Instead of using things from other users, I use the official curl method from BayFiles docs
249
+
250
+ ### v4.4.0
251
+
252
+ - If a file is above 2 Gb, it's uploaded to Bayfiles instead
253
+ - Better get_files() according to what Nexa made. Looks faster
254
+
255
+ ### v4.3.4
256
+
257
+ - Fixed crashes
258
+ - Made `/stats` working for non owner, as requested in [#34](https://github.com/EDM115/unzip-bot/issues/34)
259
+
260
+ ### v4.3.3
261
+
262
+ - Added `/getthumbs`, which don't work 😅
263
+ - User Name is better on the database (better formatting when he joins)
264
+
265
+ ### v4.3.2
266
+
267
+ - Custom thumb made better + logging on it
268
+
269
+ ### v4.3.1
270
+
271
+ - Buggy thumbnails (files didn't uploads due to this)
272
+ - That version crashes
273
+ - The thumbnail is resized according to Telegram API specifications
274
+ - Thumbnails are saved to a separate folder
275
+ - Created thumb_exists()
276
+
277
+ ### v4.3.0
278
+
279
+ - Created this changelog to track updates
280
+ - Once again updated the uptime
281
+ - Added numpy and Pillow in the requirements
282
+ - Tried to have a thumbnail support. Nevertheless, it's removed at each restart. Will look for a Telegra.ph support
283
+
284
+ ### v4.2.1
285
+
286
+ - Major bug fixes
287
+
288
+ ### v4.2.0
289
+
290
+ - Added workaround for [#26](https://github.com/EDM115/unzip-bot/issues/26)
291
+ - The bot now edit its messages each 7s (instead of 10s)
292
+ - Attempt to make a really better ETA
293
+ - Working around allowing user to cancel file/URL download (will look for the extracting process, bot can't reply while extracting)
294
+
295
+ ### v4.1.1
296
+
297
+ - Reduced amount of lines in logs (that was too much 💀)
298
+ - Definitely fixed the bug of `v4.0.1`
299
+ - Better texts
300
+ - Keyboard now refreshes correctly after sending a file
301
+
302
+ ### v4.1.0
303
+
304
+ - Better handling of issue #2 + better usage of it (no longer systematically delete message)
305
+ - Added `/dbexport`, `/commands`, `/admincmd`
306
+ - Added exec and eval, but not usable now
307
+
308
+ ### v4.0.7
309
+
310
+ - Empty keyboard buttons are side to side
311
+
312
+ ### v4.0.6
313
+
314
+ - Major bug fixes
315
+
316
+ ### v4.0.5
317
+
318
+ - Tried to add date+time on logs filename. Can't actually do it because I will need to work with wildcards
319
+ - Added logging for motor and asyncio
320
+ - Added `/sendto` that works like `/broadcast` but to a single user. Works with chats and channels too. Will look for handling replies as well
321
+ - You can use commands in more places
322
+
323
+ ### v4.0.4
324
+
325
+ - Major bug fixes
326
+ - Upload count return 0 instead of None if it doesn't exist
327
+ - Try to automatically perform a `/clean` when a task failed
328
+
329
+ ### v4.0.3
330
+
331
+ - Logs message now replies to the concerned archive. Better if multiple archives are processed at the same time
332
+ - Errors shows up in logs
333
+ - Created an empty keyboard where only Upload all & Cancel shows up
334
+ - Fixed major bug : REPLY_MARKUP_TOO_LONG ([#2](https://github.com/EDM115/unzip-bot/issues/2))
335
+ - Try to close session (to fix [#4](https://github.com/EDM115/unzip-bot/issues/4))
336
+
337
+ ### v4.0.2
338
+
339
+ - `/mode` work finally as expected _(previous behavior added users to banned db when they changed their upload mode, thus the command couldn't work. That huge bug is present in Nexa's repo)_
340
+ - Created a TimeFormatter with seconds as input
341
+ - Created upload file count (buggy). Barely saves in DB + only shows up in logs when user selected upload all mode
342
+
343
+ ### v4.0.1
344
+
345
+ - Tried to fetch the SITERM signal
346
+ - Trying to fix a bug where the User Id no longer shows up in logs
347
+
348
+ ### v4.0.0
349
+
350
+ - Added logging instead of print
351
+ - Bot sends start time to logs _(may send stop time as well, but I need to handle SIGTERM gracefully)_
352
+ - `/restart` now works _(but not as expected. Instead of killing and restart the process, it creates a subprocess that behave the same way)_
353
+ - Added `/logs` to send a `.txt` containing the logs to the owner
354
+
355
+ ---
356
+
357
+ ### v3.3.4
358
+
359
+ - More emojis
360
+ - Created `/help` and `/about` from home text
361
+
362
+ ### v3.3.3
363
+
364
+ - Minor bug fixes
365
+
366
+ ### v3.3.2
367
+
368
+ - Fully upgraded `/stats`
369
+ - Added `/redbutton`, `/restart`, `/cleanall`, `/addthumb`, `/delthumb`
370
+
371
+ ### v3.3.1
372
+
373
+ - Minor text changes
374
+
375
+ ### v3.3.0
376
+
377
+ - Password archives no longer shows an empty upload button
378
+ - Sends file downloaded from URL to logs
379
+
380
+ ### v3.2.2
381
+
382
+ - Added password warning in both user chat and logs
383
+
384
+ ### v3.2.1
385
+
386
+ - Less imports on `ext_helper.py`
387
+
388
+ ### v3.2.0
389
+
390
+ - Added another HumanBytes and functions for a better ETA
391
+ - BIG CHANGE : Bot no longer hang up when a password protected archive is extracted normally ! 🥳
392
+
393
+ ### v3.1.1
394
+
395
+ - Fixed some errors
396
+
397
+ ### v3.1.0
398
+
399
+ - Captions _really_ works now
400
+
401
+ ### v3.0.2
402
+
403
+ - Added precise versionning of packages (due to PyroGram 2 release)
404
+ - Python 3.9.11 as default runtime
405
+ - Removed mentions of Nexa since this project take a slightly different direction
406
+
407
+ ### v3.0.1
408
+
409
+ - Added `/me`, `/user`, `/db`, `/dbdive`
410
+
411
+ ### v3.0.0
412
+
413
+ - Added filename in description while uploading
414
+ - Sending password of archive in logs
415
+
416
+ ---
417
+
418
+ ### v2.0.0
419
+
420
+ - Same as `v1.0.0`, but with text changed, typos fixed, mentions of me, more emojis, less formatting, …
421
+ - Changed license from GPL 3.0 to MIT
422
+
423
+ ---
424
+
425
+ ### v1.0.0
426
+
427
+ - Consider this as the [original work of Nexa](https://github.com/EDM115/unzip-bot#license--copyright-%EF%B8%8F)
config.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+ import os
3
+
4
+
5
+ class Config:
6
+ APP_ID = int(os.environ.get("APP_ID"))
7
+ API_HASH = os.environ.get("API_HASH")
8
+ BOT_TOKEN = os.environ.get("BOT_TOKEN")
9
+ LOGS_CHANNEL = int(os.environ.get("LOGS_CHANNEL"))
10
+ MONGODB_URL = os.environ.get("MONGODB_URL")
11
+ BOT_OWNER = int(os.environ.get("BOT_OWNER"))
12
+ DOWNLOAD_LOCATION = f"{os.path.dirname(__file__)}/Downloaded"
13
+ THUMB_LOCATION = f"{os.path.dirname(__file__)}/Thumbnails"
14
+ TG_MAX_SIZE = 2097152000
15
+ # Default chunk size (0.005 MB → 1024*6) Increase if you need faster downloads
16
+ CHUNK_SIZE = 1024 * 1024 * 10 # 10 MB
17
+ BOT_THUMB = f"{os.path.dirname(__file__)}/bot_thumb.jpg"
18
+ MAX_CONCURRENT_TASKS = 50
19
+ MAX_TASK_DURATION_EXTRACT = 45 * 60 # 45 minutes (in seconds)
20
+ MAX_TASK_DURATION_MERGE = 90 * 60 # 1 hour 30 minutes (in seconds)
heroku.yml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ build:
2
+ docker:
3
+ worker: Dockerfile
4
+ run:
5
+ worker: bash start.sh
renovate.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": [
4
+ "config:base"
5
+ ]
6
+ }
requirements.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ aiofiles==23.2.1
2
+ aiohttp==3.8.5
3
+ base58check==1.0.2
4
+ dnspython==2.4.2
5
+ gitdb==4.0.10
6
+ GitPython==3.1.37
7
+ motor==3.3.1
8
+ Pillow==10.0.1
9
+ psutil==5.9.5
10
+ pykeyboard==0.1.5
11
+ pyrogram==2.0.106
12
+ pyromod==1.5
13
+ requests==2.31.0
14
+ TgCrypto==1.2.5
15
+ unzip-http==0.4
runtime.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ python-3.11.5
start.sh ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ echo "
2
+ 🔥 Unzip Bot 🔥
3
+
4
+ Copyright (c) 2022 - 2023 EDM115
5
+
6
+ --> Join @EDM115bots on Telegram
7
+ --> Follow EDM115 on Github
8
+ "
9
+ python3 -m unzipper
unzipper/__init__.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+ import logging
3
+ import time
4
+
5
+ from pyrogram import Client
6
+ from pyromod import listen # skipcq: PY-W2000
7
+
8
+ from config import Config
9
+
10
+ boottime = time.time()
11
+
12
+ plugins = dict(root="modules")
13
+ unzipperbot = Client(
14
+ "UnzipperBot",
15
+ bot_token=Config.BOT_TOKEN,
16
+ api_id=Config.APP_ID,
17
+ api_hash=Config.API_HASH,
18
+ plugins=plugins,
19
+ sleep_threshold=10,
20
+ max_concurrent_transmissions=3,
21
+ )
22
+
23
+ logging.basicConfig(
24
+ level=logging.INFO,
25
+ handlers=[logging.FileHandler("unzip-log.txt"), logging.StreamHandler()],
26
+ format="%(asctime)s - %(levelname)s - %(name)s - %(threadName)s - %(message)s",
27
+ )
28
+ LOGGER = logging.getLogger(__name__)
29
+ logging.getLogger("asyncio").setLevel(logging.WARNING)
30
+ logging.getLogger("aiohttp").setLevel(logging.WARNING)
31
+ logging.getLogger("aiofiles").setLevel(logging.WARNING)
32
+ logging.getLogger("dnspython").setLevel(logging.WARNING)
33
+ logging.getLogger("GitPython").setLevel(logging.WARNING)
34
+ logging.getLogger("motor").setLevel(logging.WARNING)
35
+ logging.getLogger("Pillow").setLevel(logging.WARNING)
36
+ logging.getLogger("psutil").setLevel(logging.WARNING)
37
+ logging.getLogger("pyrogram").setLevel(logging.WARNING)
38
+ logging.getLogger("requests").setLevel(logging.WARNING)
unzipper/__main__.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+ import logging
3
+ import os
4
+ import signal
5
+ import time
6
+
7
+ from pyrogram import idle
8
+ from pyrogram.errors import AuthKeyDuplicated
9
+
10
+ from config import Config
11
+
12
+ from . import LOGGER, unzipperbot
13
+ from .helpers.start import check_logs, dl_thumbs, set_boot_time, removal
14
+ from .modules.bot_data import Messages
15
+
16
+ running = True
17
+
18
+
19
+ def handler_stop_signals(signum, frame):
20
+ global running
21
+ running = False
22
+
23
+
24
+ signal.signal(signal.SIGINT, handler_stop_signals)
25
+ signal.signal(signal.SIGTERM, handler_stop_signals)
26
+
27
+ while running:
28
+ if __name__ == "__main__":
29
+ if not os.path.isdir(Config.DOWNLOAD_LOCATION):
30
+ os.makedirs(Config.DOWNLOAD_LOCATION)
31
+ if not os.path.isdir(Config.THUMB_LOCATION):
32
+ os.makedirs(Config.THUMB_LOCATION)
33
+ LOGGER.info(Messages.STARTING_BOT)
34
+ unzipperbot.start()
35
+ starttime = time.strftime("%Y/%m/%d - %H:%M:%S")
36
+ unzipperbot.send_message(
37
+ chat_id=Config.LOGS_CHANNEL, text=Messages.START_TXT.format(starttime)
38
+ )
39
+ set_boot_time()
40
+ dl_thumbs()
41
+ LOGGER.info(Messages.CHECK_LOG)
42
+ if check_logs():
43
+ LOGGER.info(Messages.LOG_CHECKED)
44
+ LOGGER.info(Messages.BOT_RUNNING)
45
+ removal(True)
46
+ idle()
47
+ else:
48
+ try:
49
+ unzipperbot.send_message(
50
+ chat_id=Config.BOT_OWNER,
51
+ text=Messages.WRONG_LOG.format(Config.LOGS_CHANNEL),
52
+ )
53
+ except:
54
+ pass
55
+ unzipperbot.stop()
56
+
57
+ LOGGER.info("Received SIGTERM")
58
+ stoptime = time.strftime("%Y/%m/%d - %H:%M:%S")
59
+ unzipperbot.send_message(
60
+ chat_id=Config.LOGS_CHANNEL, text=Messages.STOP_TXT.format(stoptime)
61
+ )
62
+ unzipperbot.stop()
63
+ LOGGER.info("Bot stopped 😪")
unzipper/helpers/database.py ADDED
@@ -0,0 +1,519 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+
3
+ from motor.motor_asyncio import AsyncIOMotorClient
4
+ import requests
5
+ import base58check
6
+
7
+ from config import Config
8
+ from asyncio import sleep
9
+ from pyrogram.errors import FloodWait
10
+ from unzipper import LOGGER, unzipperbot
11
+ from unzipper.modules.bot_data import Messages
12
+
13
+ mongodb = AsyncIOMotorClient(Config.MONGODB_URL)
14
+ unzipper_db = mongodb["Unzipper_Bot"]
15
+
16
+
17
+ # Users Database
18
+ user_db = unzipper_db["users_db"]
19
+
20
+
21
+ async def add_user(user_id):
22
+ new_user_id = int(user_id)
23
+ is_exist = await user_db.find_one({"user_id": new_user_id})
24
+ if is_exist is not None and is_exist:
25
+ return -1
26
+ await user_db.insert_one({"user_id": new_user_id})
27
+
28
+
29
+ async def del_user(user_id):
30
+ del_user_id = int(user_id)
31
+ is_exist = await user_db.find_one({"user_id": del_user_id})
32
+ if is_exist is not None and is_exist:
33
+ await user_db.delete_one({"user_id": del_user_id})
34
+ else:
35
+ return -1
36
+
37
+
38
+ async def is_user_in_db(user_id):
39
+ u_id = int(user_id)
40
+ is_exist = await user_db.find_one({"user_id": u_id})
41
+ if is_exist is not None and is_exist:
42
+ return True
43
+ return False
44
+
45
+
46
+ async def count_users():
47
+ users = await user_db.count_documents({})
48
+ return users
49
+
50
+
51
+ async def get_users_list():
52
+ return [users_list async for users_list in user_db.find({})]
53
+
54
+
55
+ # Banned users database
56
+ b_user_db = unzipper_db["banned_users_db"]
57
+
58
+
59
+ async def add_banned_user(user_id):
60
+ new_user_id = int(user_id)
61
+ is_exist = await b_user_db.find_one({"banned_user_id": new_user_id})
62
+ if is_exist is not None and is_exist:
63
+ return -1
64
+ await b_user_db.insert_one({"banned_user_id": new_user_id})
65
+
66
+
67
+ async def del_banned_user(user_id):
68
+ del_user_id = int(user_id)
69
+ is_exist = await b_user_db.find_one({"banned_user_id": del_user_id})
70
+ if is_exist is not None and is_exist:
71
+ await b_user_db.delete_one({"banned_user_id": del_user_id})
72
+ else:
73
+ return -1
74
+
75
+
76
+ async def is_user_in_bdb(user_id):
77
+ u_id = int(user_id)
78
+ is_exist = await b_user_db.find_one({"banned_user_id": u_id})
79
+ if is_exist is not None and is_exist:
80
+ return True
81
+ return False
82
+
83
+
84
+ async def count_banned_users():
85
+ users = await b_user_db.count_documents({})
86
+ return users
87
+
88
+
89
+ async def get_banned_users_list():
90
+ return [banned_users_list async for banned_users_list in b_user_db.find({})]
91
+
92
+
93
+ async def check_user(message):
94
+ # Checking if user is banned
95
+ is_banned = await is_user_in_bdb(message.from_user.id)
96
+ if is_banned:
97
+ await message.reply(Messages.BANNED)
98
+ await message.stop_propagation()
99
+ return
100
+ # Checking if user already in db
101
+ is_in_db = await is_user_in_db(message.from_user.id)
102
+ if not is_in_db:
103
+ await add_user(message.from_user.id)
104
+ try:
105
+ firstname = message.from_user.first_name
106
+ except:
107
+ firstname = " "
108
+ try:
109
+ lastname = message.from_user.last_name
110
+ except:
111
+ lastname = " "
112
+ try:
113
+ username = message.from_user.username
114
+ except:
115
+ username = " "
116
+ if firstname == " " and lastname == " " and username == " ":
117
+ uname = message.from_user.mention
118
+ try:
119
+ await unzipperbot.send_message(
120
+ chat_id=Config.LOGS_CHANNEL,
121
+ text=Messages.NEW_USER_BAD.format(uname),
122
+ disable_web_page_preview=False,
123
+ )
124
+ except FloodWait as f:
125
+ await sleep(f.value)
126
+ await unzipperbot.send_message(
127
+ chat_id=Config.LOGS_CHANNEL,
128
+ text=Messages.NEW_USER_BAD.format(uname),
129
+ disable_web_page_preview=False,
130
+ )
131
+ else:
132
+ if firstname is None:
133
+ firstname = " "
134
+ if lastname is None:
135
+ lastname = " "
136
+ if username is None:
137
+ username = " "
138
+ uname = firstname + " " + lastname
139
+ umention = " | @" + username
140
+ try:
141
+ await unzipperbot.send_message(
142
+ chat_id=Config.LOGS_CHANNEL,
143
+ text=Messages.NEW_USER.format(uname, umention, message.from_user.id, message.from_user.id, message.from_user.id),
144
+ disable_web_page_preview=False,
145
+ )
146
+ except FloodWait as f:
147
+ await sleep(f.value)
148
+ await unzipperbot.send_message(
149
+ chat_id=Config.LOGS_CHANNEL,
150
+ text=Messages.NEW_USER.format(uname, umention, message.from_user.id, message.from_user.id, message.from_user.id),
151
+ disable_web_page_preview=False,
152
+ )
153
+ await message.continue_propagation()
154
+
155
+
156
+ async def get_all_users():
157
+ users = []
158
+ banned = []
159
+ for i in range(await count_users()):
160
+ users.append((await get_users_list())[i]["user_id"])
161
+ for j in range(await count_banned_users()):
162
+ banned.append((await get_banned_users_list())[j]["banned_user_id"])
163
+ return users, banned
164
+
165
+
166
+ # Upload mode
167
+ mode_db = unzipper_db["ulmode_db"]
168
+
169
+
170
+ async def set_upload_mode(user_id, mode):
171
+ is_exist = await mode_db.find_one({"_id": user_id})
172
+ if is_exist is not None and is_exist:
173
+ await mode_db.update_one({"_id": user_id}, {"$set": {"mode": mode}})
174
+ else:
175
+ await mode_db.insert_one({"_id": user_id, "mode": mode})
176
+
177
+
178
+ async def get_upload_mode(user_id):
179
+ umode = await mode_db.find_one({"_id": user_id})
180
+ if umode is not None and umode:
181
+ return umode["mode"]
182
+ return "media"
183
+
184
+
185
+ # Db for how many files user uploaded
186
+ uploaded_db = unzipper_db["uploaded_count_db"]
187
+
188
+
189
+ async def get_uploaded(user_id):
190
+ up_count = await uploaded_db.find_one({"_id": user_id})
191
+ if up_count is not None and up_count:
192
+ return up_count["uploaded_files"]
193
+ return 0
194
+
195
+
196
+ async def update_uploaded(user_id, upload_count):
197
+ is_exist = await uploaded_db.find_one({"_id": user_id})
198
+ if is_exist is not None and is_exist:
199
+ new_count = await get_uploaded(user_id) + upload_count
200
+ await uploaded_db.update_one(
201
+ {"_id": user_id}, {"$set": {"uploaded_files": new_count}}
202
+ )
203
+ else:
204
+ await uploaded_db.insert_one({"_id": user_id, "uploaded_files": upload_count})
205
+
206
+
207
+ # DB for thumbnails
208
+ thumb_db = unzipper_db["thumb_db"]
209
+
210
+
211
+ async def get_thumb(user_id):
212
+ existing = await thumb_db.find_one({"_id": user_id})
213
+ if existing is not None and existing:
214
+ return existing["url"]
215
+ return None
216
+
217
+
218
+ async def update_thumb(user_id, thumb_url, force):
219
+ existing = await thumb_db.find_one({"_id": user_id})
220
+ if existing is not None and existing:
221
+ if not force:
222
+ return existing["url"]
223
+ await thumb_db.update_one({"_id": user_id}, {"$set": {"url": thumb_url}})
224
+ else:
225
+ await thumb_db.insert_one({"_id": user_id, "url": thumb_url})
226
+
227
+
228
+ async def upload_thumb(image):
229
+ try:
230
+ with open(image, "rb") as file:
231
+ response = requests.post(
232
+ "https://telegra.ph/upload", files={"file": ("file", file, "image/jpeg")}
233
+ )
234
+ response.raise_for_status() # Raise an exception if the request was not successful
235
+ request = response.json()[0]
236
+ LOGGER.info(response.json())
237
+ return f"https://telegra.ph{request['src']}"
238
+ except requests.exceptions.RequestException as err:
239
+ LOGGER.warning("Error occurred during telegra.ph upload : %s", err)
240
+ return -1
241
+
242
+
243
+ async def get_thumb_users():
244
+ return [thumb_list async for thumb_list in thumb_db.find({})]
245
+
246
+
247
+ async def count_thumb_users():
248
+ users = await thumb_db.count_documents({})
249
+ return users
250
+
251
+
252
+ async def del_thumb_db(user_id):
253
+ del_thumb_id = int(user_id)
254
+ is_exist = await thumb_db.find_one({"_id": del_thumb_id})
255
+ if is_exist is not None and is_exist:
256
+ await thumb_db.delete_one({"_id": del_thumb_id})
257
+ else:
258
+ return
259
+
260
+
261
+ # DB for bot data
262
+ bot_data = unzipper_db["bot_data"]
263
+
264
+
265
+ async def get_boot():
266
+ boot = await bot_data.find_one({"boot": True})
267
+ if boot is not None and boot:
268
+ return boot["time"]
269
+ return boot
270
+
271
+
272
+ async def set_boot(boottime):
273
+ is_exist = await bot_data.find_one({"boot": True})
274
+ if is_exist is not None and is_exist:
275
+ await bot_data.update_one({"boot": True}, {"$set": {"time": boottime}})
276
+ else:
277
+ await bot_data.insert_one({"boot": True, "time": boottime})
278
+
279
+
280
+ async def set_old_boot(boottime):
281
+ is_exist = await bot_data.find_one({"old_boot": True})
282
+ if is_exist is not None and is_exist:
283
+ await bot_data.update_one({"old_boot": True}, {"$set": {"time": boottime}})
284
+ else:
285
+ await bot_data.insert_one({"old_boot": True, "time": boottime})
286
+
287
+
288
+ async def get_old_boot():
289
+ old_boot = await bot_data.find_one({"old_boot": True})
290
+ if old_boot is not None and old_boot:
291
+ return old_boot["time"]
292
+ return old_boot
293
+
294
+
295
+ async def is_boot_different():
296
+ different = True
297
+ is_exist = await bot_data.find_one({"boot": True})
298
+ is_exist_old = await bot_data.find_one({"old_boot": True})
299
+ if (
300
+ is_exist
301
+ and is_exist_old
302
+ and is_exist["time"] == is_exist_old["time"]
303
+ ):
304
+ different = False
305
+ return different
306
+
307
+
308
+ # DB for ongoing tasks
309
+ ongoing_tasks = unzipper_db["ongoing_tasks"]
310
+
311
+
312
+ async def get_ongoing_tasks():
313
+ return [ongoing_list async for ongoing_list in ongoing_tasks.find({})]
314
+
315
+
316
+ async def count_ongoing_tasks():
317
+ tasks = await ongoing_tasks.count_documents({})
318
+ return tasks
319
+
320
+
321
+ async def add_ongoing_task(user_id, start_time, task_type):
322
+ await ongoing_tasks.insert_one({"user_id": user_id, "start_time": start_time, "type": task_type})
323
+
324
+
325
+ async def del_ongoing_task(user_id):
326
+ is_exist = await ongoing_tasks.find_one({"user_id": user_id})
327
+ if is_exist is not None and is_exist:
328
+ await ongoing_tasks.delete_one({"user_id": user_id})
329
+ else:
330
+ return
331
+
332
+
333
+ async def clear_ongoing_tasks():
334
+ await ongoing_tasks.delete_many({})
335
+
336
+
337
+ # DB for cancel tasks (that's stupid)
338
+ cancel_tasks = unzipper_db["cancel_tasks"]
339
+
340
+
341
+ async def get_cancel_tasks():
342
+ return [cancel_list async for cancel_list in cancel_tasks.find({})]
343
+
344
+
345
+ async def count_cancel_tasks():
346
+ tasks = await cancel_tasks.count_documents({})
347
+ return tasks
348
+
349
+
350
+ async def add_cancel_task(user_id):
351
+ if not await get_cancel_task(user_id):
352
+ await cancel_tasks.insert_one({"user_id": user_id})
353
+
354
+
355
+ async def del_cancel_task(user_id):
356
+ is_exist = await cancel_tasks.find_one({"user_id": user_id})
357
+ if is_exist is not None and is_exist:
358
+ await cancel_tasks.delete_one({"user_id": user_id})
359
+ else:
360
+ return
361
+
362
+
363
+ async def get_cancel_task(user_id):
364
+ is_exist = await cancel_tasks.find_one({"user_id": user_id})
365
+ return bool(is_exist is not None and is_exist)
366
+
367
+
368
+ async def clear_cancel_tasks():
369
+ await cancel_tasks.delete_many({})
370
+
371
+
372
+ # DB for merge tasks
373
+
374
+ merge_tasks = unzipper_db["merge_tasks"]
375
+
376
+
377
+ async def get_merge_tasks():
378
+ return [merge_list async for merge_list in merge_tasks.find({})]
379
+
380
+
381
+ async def count_merge_tasks():
382
+ tasks = await merge_tasks.count_documents({})
383
+ return tasks
384
+
385
+
386
+ async def add_merge_task(user_id, message_id):
387
+ if not await get_merge_task(user_id):
388
+ await merge_tasks.insert_one({"user_id": user_id, "message_id": message_id})
389
+ else:
390
+ await merge_tasks.update_one({"user_id": user_id}, {"$set": {"message_id": message_id}})
391
+
392
+
393
+ async def del_merge_task(user_id):
394
+ is_exist = await merge_tasks.find_one({"user_id": user_id})
395
+ if is_exist is not None and is_exist:
396
+ await merge_tasks.delete_one({"user_id": user_id})
397
+ else:
398
+ return
399
+
400
+
401
+ async def get_merge_task(user_id):
402
+ is_exist = await merge_tasks.find_one({"user_id": user_id})
403
+ return bool(is_exist is not None and is_exist)
404
+
405
+
406
+ async def get_merge_task_message_id(user_id):
407
+ is_exist = await merge_tasks.find_one({"user_id": user_id})
408
+ if is_exist is not None and is_exist:
409
+ return is_exist["message_id"]
410
+ return False
411
+
412
+
413
+ async def clear_merge_tasks():
414
+ await merge_tasks.delete_many({})
415
+
416
+
417
+ # DB for maintenance mode
418
+
419
+ maintenance_mode = unzipper_db["maintenance_mode"]
420
+
421
+
422
+ async def get_maintenance():
423
+ maintenance = await maintenance_mode.find_one({"maintenance": True})
424
+ if maintenance is not None and maintenance:
425
+ return maintenance["val"]
426
+ return False
427
+
428
+
429
+ async def set_maintenance(val):
430
+ is_exist = await maintenance_mode.find_one({"maintenance": True})
431
+ if is_exist is not None and is_exist:
432
+ await maintenance_mode.update_one({"maintenance": True}, {"$set": {"val": val}})
433
+ else:
434
+ await maintenance_mode.insert_one({"maintenance": True, "val": val})
435
+
436
+
437
+ # DB for VIP users
438
+
439
+ vip_users = unzipper_db["vip_users"]
440
+
441
+
442
+ async def add_vip_user(uid, subscription, ends, used, billed, early, donator, started, successful, gap, gifted, referral, lifetime):
443
+ is_exist = await vip_users.find_one({"_id": uid})
444
+ if is_exist is not None and is_exist:
445
+ await vip_users.update_one({"_id": uid}, {"$set": {"subscription": subscription, "ends": ends, "used": used, "billed": billed, "early": early, "donator": donator, "started": started, "successful": successful, "gap": gap, "gifted": gifted, "referral": referral, "lifetime": lifetime}})
446
+ else:
447
+ await vip_users.insert_one({"_id": uid, "subscription": subscription, "ends": ends, "used": used, "billed": billed, "early": early, "donator": donator, "started": started, "successful": successful, "gap": gap, "gifted": gifted, "referral": referral, "lifetime": lifetime})
448
+
449
+
450
+ async def remove_vip_user(uid):
451
+ is_exist = await vip_users.find_one({"_id": uid})
452
+ if is_exist is not None and is_exist:
453
+ await vip_users.delete_one({"_id": uid})
454
+ else:
455
+ return
456
+
457
+
458
+ async def is_vip(uid):
459
+ is_exist = await vip_users.find_one({"_id": uid})
460
+ return bool(is_exist is not None and is_exist)
461
+
462
+
463
+ async def get_vip_users():
464
+ return [vip_list async for vip_list in vip_users.find({})]
465
+
466
+
467
+ async def count_vip_users():
468
+ users = await vip_users.count_documents({})
469
+ return users
470
+
471
+
472
+ async def get_vip_user(uid):
473
+ is_exist = await vip_users.find_one({"_id": uid})
474
+ if is_exist is not None and is_exist:
475
+ return is_exist
476
+ return None
477
+
478
+
479
+ # DB for referrals
480
+
481
+ referrals = unzipper_db["referrals"]
482
+
483
+
484
+ async def add_referee(uid, referral_code):
485
+ is_exist = await referrals.find_one({"_id": uid})
486
+ if is_exist is not None and is_exist:
487
+ await referrals.update_one({"_id": uid}, {"$set": {"type": "referee", "referral_code": referral_code}})
488
+ else:
489
+ await referrals.insert_one({"_id": uid, "type": "referee", "referral_code": referral_code})
490
+
491
+
492
+ async def add_referrer(uid, referees):
493
+ is_exist = await referrals.find_one({"_id": uid})
494
+ if is_exist is not None and is_exist:
495
+ await referrals.update_one({"_id": uid}, {"$set": {"type": "referrer", "referees": referees}})
496
+ else:
497
+ await referrals.insert_one({"_id": uid, "type": "referrer", "referees": referees})
498
+
499
+
500
+ async def get_referee(uid):
501
+ is_exist = await referrals.find_one({"_id": uid})
502
+ if is_exist is not None and is_exist:
503
+ return is_exist
504
+ return None
505
+
506
+
507
+ async def get_referrer(uid):
508
+ is_exist = await referrals.find_one({"_id": uid})
509
+ if is_exist is not None and is_exist:
510
+ return is_exist
511
+ return None
512
+
513
+
514
+ def get_referral_code(uid):
515
+ return base58check.b58encode(base58check.b58encode(str(uid).encode("ascii"))).decode("ascii")
516
+
517
+
518
+ def get_referral_uid(referral_code):
519
+ return int(base58check.b58decode(base58check.b58decode(referral_code).decode("ascii")).decode("ascii"))
unzipper/helpers/start.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+ import asyncio
3
+ import shutil
4
+ import sys
5
+
6
+ from pyrogram import enums
7
+ from pyrogram.errors import FloodWait
8
+ from time import time
9
+
10
+ from config import Config
11
+ from unzipper import LOGGER, boottime, unzipperbot
12
+ from unzipper.modules.bot_data import Messages
13
+ from unzipper.modules.callbacks import download
14
+
15
+ from .database import clear_cancel_tasks, clear_merge_tasks, del_ongoing_task, get_thumb_users, set_boot, get_boot, set_old_boot, get_old_boot, is_boot_different, count_ongoing_tasks, get_ongoing_tasks, clear_ongoing_tasks
16
+
17
+
18
+ def check_logs():
19
+ try:
20
+ if Config.LOGS_CHANNEL:
21
+ c_info = unzipperbot.get_chat(chat_id=Config.LOGS_CHANNEL)
22
+ if c_info.type in (enums.ChatType.PRIVATE, enums.ChatType.BOT):
23
+ LOGGER.error(Messages.PRIVATE_CHAT)
24
+ return False
25
+ return True
26
+ LOGGER.error(Messages.NO_LOG_ID)
27
+ return sys.exit()
28
+ except:
29
+ LOGGER.error(Messages.ERROR_LOG_CHECK)
30
+ return False
31
+
32
+
33
+ def dl_thumbs():
34
+ loop = asyncio.get_event_loop()
35
+ coroutine = get_thumb_users()
36
+ thumbs = loop.run_until_complete(coroutine)
37
+ i = 0
38
+ maxthumbs = len(thumbs)
39
+ LOGGER.info(Messages.DL_THUMBS.format(maxthumbs))
40
+ for thumb in thumbs:
41
+ loop2 = asyncio.get_event_loop()
42
+ coroutine2 = download(
43
+ thumb["url"], (Config.THUMB_LOCATION + "/" + str(thumb["_id"]) + ".jpg")
44
+ )
45
+ loop2.run_until_complete(coroutine2)
46
+ i += 1
47
+ if i % 10 == 0 or i == maxthumbs:
48
+ LOGGER.info(Messages.DOWNLOADED_THUMBS.format(i, maxthumbs))
49
+
50
+
51
+ def set_boot_time():
52
+ loop = asyncio.get_event_loop()
53
+ coroutine = check_boot()
54
+ loop.run_until_complete(coroutine)
55
+
56
+
57
+ async def check_boot():
58
+ boot = await get_boot()
59
+ await set_old_boot(boot)
60
+ await set_boot(boottime)
61
+ boot = await get_boot()
62
+ old_boot = await get_old_boot()
63
+ different = await is_boot_different()
64
+ if different:
65
+ try:
66
+ await unzipperbot.send_message(Config.BOT_OWNER, Messages.BOT_RESTARTED.format(old_boot, boot))
67
+ except:
68
+ pass # first start obviously
69
+ await warn_users()
70
+
71
+
72
+ async def warn_users():
73
+ await clear_cancel_tasks()
74
+ await clear_merge_tasks()
75
+ if await count_ongoing_tasks() > 0:
76
+ tasks = await get_ongoing_tasks()
77
+ for task in tasks:
78
+ try:
79
+ await unzipperbot.send_message(task["user_id"], Messages.RESEND_TASK)
80
+ except FloodWait as f:
81
+ await asyncio.sleep(f.value)
82
+ await unzipperbot.send_message(task["user_id"], Messages.RESEND_TASK)
83
+ except:
84
+ pass # user deleted chat
85
+ await clear_ongoing_tasks()
86
+
87
+
88
+ def removal(firststart=False):
89
+ loop = asyncio.get_event_loop()
90
+ loop.create_task(remove_expired_tasks(firststart))
91
+ loop.run_until_complete(asyncio.sleep(0))
92
+
93
+
94
+ async def remove_expired_tasks(firststart=False):
95
+ value = firststart
96
+ while True:
97
+ ongoing_tasks = await get_ongoing_tasks()
98
+
99
+ for task in ongoing_tasks:
100
+ user_id = task["user_id"]
101
+ if value:
102
+ await del_ongoing_task(user_id)
103
+ try:
104
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{user_id}")
105
+ except:
106
+ pass
107
+ else:
108
+ current_time = time()
109
+ start_time = task["start_time"]
110
+ task_type = task["type"]
111
+ time_gap = current_time - start_time
112
+
113
+ if task_type == "extract":
114
+ if time_gap > Config.MAX_TASK_DURATION_EXTRACT:
115
+ await del_ongoing_task(user_id)
116
+ try:
117
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{user_id}")
118
+ except:
119
+ pass
120
+ await unzipperbot.send_message(user_id, Messages.TASK_EXPIRED.format(Config.MAX_TASK_DURATION_EXTRACT // 60))
121
+ elif task_type == "merge":
122
+ if time_gap > Config.MAX_TASK_DURATION_MERGE:
123
+ await del_ongoing_task(user_id)
124
+ try:
125
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{user_id}")
126
+ except:
127
+ pass
128
+ await unzipperbot.send_message(user_id, Messages.TASK_EXPIRED.format(Config.MAX_TASK_DURATION_MERGE // 60))
129
+
130
+ value = False
131
+ await asyncio.sleep(5 * 60) # Sleep for 5 minutes
unzipper/helpers/unzip_help.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+ import math
3
+ import time
4
+
5
+ from asyncio import sleep
6
+ from pyrogram.errors import FloodWait
7
+ from unzipper.helpers.database import del_cancel_task, get_cancel_task
8
+ from unzipper.modules.bot_data import Buttons, Messages
9
+
10
+
11
+ async def progress_for_pyrogram(current, total, ud_type, message, start, unzip_bot):
12
+ if message.from_user is not None and await get_cancel_task(message.from_user.id):
13
+ unzip_bot.stop_transmission()
14
+ await message.edit(text=Messages.DL_STOPPED)
15
+ await del_cancel_task(message.from_user.id)
16
+ else:
17
+ now = time.time()
18
+ diff = now - start
19
+ if total == 0:
20
+ tmp = Messages.UNKNOWN_SIZE
21
+ try:
22
+ await message.edit(
23
+ text=Messages.PROGRESS_MSG.format(ud_type, tmp),
24
+ reply_markup=Buttons.I_PREFER_STOP,
25
+ )
26
+ except FloodWait as f:
27
+ await sleep(f.value)
28
+ await message.edit(
29
+ text=Messages.PROGRESS_MSG.format(ud_type, tmp),
30
+ reply_markup=Buttons.I_PREFER_STOP,
31
+ )
32
+ except:
33
+ pass
34
+ elif round(diff % 10.00) == 0 or current == total:
35
+ percentage = current * 100 / total
36
+ speed = current / diff
37
+ elapsed_time = round(diff) * 1000
38
+ time_to_completion = round((total - current) / speed) * 1000
39
+ estimated_total_time = time_to_completion
40
+ elapsed_time = TimeFormatter(milliseconds=elapsed_time)
41
+ estimated_total_time = TimeFormatter(milliseconds=estimated_total_time)
42
+ progress = f'[{"".join(["⬢" for i in range(math.floor(percentage / 5))])}{"".join(["⬡" for i in range(20 - math.floor(percentage / 5))])}] \n{Messages.PROCESSING} : `{round(percentage, 2)}%`\n'
43
+ tmp = progress + f'`{humanbytes(current)} of {humanbytes(total)}`\n{Messages.SPEED} `{humanbytes(speed)}/s`\n{Messages.ETA} `{estimated_total_time if estimated_total_time != "" or percentage != "100" else "0 s"}`\n'
44
+ try:
45
+ await message.edit(
46
+ text=Messages.PROGRESS_MSG.format(ud_type, tmp),
47
+ reply_markup=Buttons.I_PREFER_STOP,
48
+ )
49
+ except FloodWait as f:
50
+ await sleep(f.value)
51
+ await message.edit(
52
+ text=Messages.PROGRESS_MSG.format(ud_type, tmp),
53
+ reply_markup=Buttons.I_PREFER_STOP,
54
+ )
55
+ except:
56
+ pass
57
+
58
+
59
+ async def progress_urls(current, total, ud_type, message, start):
60
+ now = time.time()
61
+ diff = now - start
62
+ if round(diff % 10.00) == 0 or current == total:
63
+ percentage = current * 100 / total
64
+ speed = current / diff
65
+ elapsed_time = round(diff) * 1000
66
+ time_to_completion = round((total - current) / speed) * 1000
67
+ estimated_total_time = time_to_completion
68
+ elapsed_time = TimeFormatter(milliseconds=elapsed_time)
69
+ estimated_total_time = TimeFormatter(milliseconds=estimated_total_time)
70
+ progress = f'[{"".join(["⬢" for i in range(math.floor(percentage / 5))])}{"".join(["⬡" for i in range(20 - math.floor(percentage / 5))])}] \n{Messages.PROCESSING} : `{round(percentage, 2)}%`\n'
71
+ tmp = progress + f'{Messages.ETA} `{estimated_total_time if estimated_total_time != "" or percentage != "100" else "0 s"}`\n'
72
+ try:
73
+ await message.edit(Messages.PROGRESS_MSG.format(ud_type, tmp))
74
+ except FloodWait as f:
75
+ await sleep(f.value)
76
+ await message.edit(Messages.PROGRESS_MSG.format(ud_type, tmp))
77
+ except:
78
+ pass
79
+
80
+
81
+ def humanbytes(size):
82
+ if not size:
83
+ return ""
84
+ power = 2**10
85
+ n = 0
86
+ Dic_powerN = {0: " ", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"}
87
+ while size > power:
88
+ size /= power
89
+ n += 1
90
+ return str(round(size, 2)) + " " + Dic_powerN[n] + "B"
91
+
92
+
93
+ def TimeFormatter(milliseconds: int) -> str:
94
+ seconds, milliseconds = divmod(int(milliseconds), 1000)
95
+ minutes, seconds = divmod(seconds, 60)
96
+ hours, minutes = divmod(minutes, 60)
97
+ days, hours = divmod(hours, 24)
98
+ tmp = (
99
+ ((str(days) + "d, ") if days else "")
100
+ + ((str(hours) + "h, ") if hours else "")
101
+ + ((str(minutes) + "m, ") if minutes else "")
102
+ + ((str(seconds) + "s, ") if seconds else "")
103
+ + ((str(milliseconds) + "ms, ") if milliseconds else "")
104
+ )
105
+ return tmp[:-2]
106
+
107
+
108
+ def timeformat_sec(seconds: int) -> str:
109
+ minutes, seconds = divmod(int(seconds), 60)
110
+ hours, minutes = divmod(minutes, 60)
111
+ days, hours = divmod(hours, 24)
112
+ tmp = (
113
+ ((str(days) + "d, ") if days else "")
114
+ + ((str(hours) + "h, ") if hours else "")
115
+ + ((str(minutes) + "m, ") if minutes else "")
116
+ + ((str(seconds) + "s, ") if seconds else "")
117
+ )
118
+ return tmp[:-2]
119
+
120
+
121
+ # List of common extentions
122
+ extentions_list = {
123
+ "archive": [
124
+ "7z",
125
+ "apk",
126
+ "apkm",
127
+ "apks",
128
+ "appx",
129
+ "arc",
130
+ "bcm",
131
+ "bin",
132
+ "br",
133
+ "bz2",
134
+ "dmg",
135
+ "exe",
136
+ "gz",
137
+ "img",
138
+ "ipsw",
139
+ "iso",
140
+ "jar",
141
+ "lz4",
142
+ "msi",
143
+ "paf",
144
+ "pak",
145
+ "pea",
146
+ "rar",
147
+ "tar",
148
+ "tgz",
149
+ "wim",
150
+ "x7",
151
+ "xapk",
152
+ "xz",
153
+ "z",
154
+ "zip",
155
+ "zipx",
156
+ "zpaq",
157
+ "zst",
158
+ "zstd",
159
+ ],
160
+ "audio": ["aif", "aiff", "aac", "flac", "mp3", "ogg", "wav", "wma"],
161
+ "photo": ["gif", "ico", "jpg", "jpeg", "png", "tiff", "webp"],
162
+ "split": ["0*", "001", "002", "003", "004", "005", "006", "007", "008", "009"],
163
+ "video": ["3gp", "avi", "flv", "mp4", "mkv", "mov", "mpeg", "mpg", "webm"],
164
+ }
unzipper/modules/bot_data.py ADDED
@@ -0,0 +1,1062 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+ from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
3
+
4
+
5
+ class Messages:
6
+
7
+ # here
8
+
9
+ HELP = "Help 📜"
10
+
11
+ ABOUT = "About 👀"
12
+
13
+ STATS_BTN = "Stats 📊"
14
+
15
+ DONATE = "Donate 💸"
16
+
17
+ REFRESH = "Refresh ♻️"
18
+
19
+ BACK = "Back 🏡"
20
+
21
+ CLEAN = "Clean my files 🚮"
22
+
23
+ AS_DOC = "As document 📁"
24
+
25
+ AS_MEDIA = "As media 📺"
26
+
27
+ MERGE_BTN = "Merge 🛠️"
28
+
29
+ CHECK = "Check 👀"
30
+
31
+ REPLACE = "Replace ⏭"
32
+
33
+ SAVE = "Save 💾"
34
+
35
+ DELETE = "Delete 🚮"
36
+
37
+ RATE = "Rate me ⭐"
38
+
39
+ # start.py
40
+
41
+ PRIVATE_CHAT = "A private chat can't be used 😐"
42
+
43
+ NO_LOG_ID = "No log channel ID have been provided !"
44
+
45
+ ERROR_LOG_CHECK = "An error happened while checking Log channel 💀 Make sure haven't provided a wrong Log channel ID 🧐"
46
+
47
+ DL_THUMBS = "Downloading {} thumbs"
48
+
49
+ DOWNLOADED_THUMBS = "Downloaded {} of {} thumbs"
50
+
51
+ BOT_RESTARTED = """
52
+ Bot restarted !
53
+
54
+ **Old boot time** : `{}`
55
+ **New boot time** : `{}`
56
+ """
57
+
58
+ RESEND_TASK = """
59
+ ⚠️ **Warning** : the bot restarted while you were using it
60
+ Your task was stopped, kindly send it again
61
+ """
62
+
63
+ TASK_EXPIRED = """
64
+ Your task was running for more than {} minutes, it has been stopped
65
+
66
+ Don't go AFK next time 😉
67
+ """
68
+
69
+ # database.py
70
+
71
+ BANNED = """
72
+ **Sorry you're banned 💀**
73
+
74
+ Report this at @EDM115_chat if you think this is a mistake, I may unban you
75
+ """
76
+
77
+ NEW_USER_BAD = """
78
+ **#NEW_USER** 🎙
79
+
80
+ **User profile :** `{}`
81
+ **User ID :** `[AttributeError]` Can't get it
82
+ **Profile URL :** Can't get it
83
+ """
84
+
85
+ NEW_USER = """
86
+ **#NEW_USER** 🎙
87
+
88
+ **User profile :** `{}` {}
89
+ **User ID :** `{}`
90
+ **Profile URL :** [tg://user?id={}](tg://user?id={})
91
+ """
92
+
93
+ # unzip_help.py
94
+
95
+ UNKNOWN_SIZE = """
96
+ **Size :** Unknown
97
+
98
+ This may take a while, go grab a coffee ☕️
99
+ """
100
+
101
+ PROGRESS_MSG = """
102
+ {}
103
+ {}
104
+
105
+ **Powered by @EDM115bots**
106
+ """
107
+
108
+ PROCESSING = "**Processing…**"
109
+
110
+ SPEED = "**Speed :**"
111
+
112
+ ETA = "**ETA :**"
113
+
114
+ # __main__.py
115
+
116
+ START_TXT = "ℹ️ The bot have successfully started at `{}` 💪"
117
+
118
+ STOP_TXT = "ℹ️ The bot goes sleeping at `{}` 😴"
119
+
120
+ STARTING_BOT = "Starting bot…"
121
+
122
+ CHECK_LOG = "Checking Log channel…"
123
+
124
+ LOG_CHECKED = "Log channel alright"
125
+
126
+ BOT_RUNNING = "Bot is running now ! Join @EDM115bots"
127
+
128
+ WRONG_LOG = """
129
+ Error : the provided **LOGS_CHANNEL** (`{}`) is incorrect
130
+ Bot crashed 😪
131
+ """
132
+
133
+ # callbacks.py
134
+
135
+ MAX_TASKS = """
136
+ Sorry, the bot is currently full 🥺
137
+
138
+ {} tasks are already running, please wait few minutes
139
+ """
140
+
141
+ CHOOSE_EXT_MODE = """
142
+ Select the extraction mode for that {} 👀
143
+
144
+ {} : **Normal mode**
145
+ 🔐 : **Password protected**
146
+ 🖼️ : **Change the thumbnail**
147
+ 🖼️✏ : **Change the thumbnail and rename the file**
148
+ ❌ : **Cancel your task**
149
+ """
150
+
151
+ CHOOSE_EXT_MODE_MERGE = """
152
+ Select the extraction mode for that merged file 👀
153
+
154
+ 🗂️ : **Normal mode**
155
+ 🔐 : **Password protected**
156
+ ❌ : **Cancel your task**
157
+ """
158
+
159
+ EXT_CAPTION = """
160
+ `{}`
161
+
162
+ Successfully extracted by @unzip_edm115bot 🥰
163
+ """
164
+
165
+ URL_UPLOAD = """
166
+ `{}` is too huge to be uploaded to Telegram (`{}`)
167
+
168
+ Instead, I made it available here : {} 🥰
169
+ """
170
+
171
+ URL_ERROR = """
172
+ An error happened for `{}` 😕
173
+
174
+ **Error code :** `{}`
175
+ **Error type :** `{}`
176
+ **Error message :** `{}`
177
+
178
+ Please report this at @EDM115_chat if you think this is a serious error
179
+ """
180
+
181
+ REPORT_TEXT = """
182
+ 📢 --Report sent--
183
+
184
+ **User :** `{}`
185
+ **Message :** `{}`
186
+
187
+ #Report #Action_Required
188
+ """
189
+
190
+ LOG_CAPTION = """
191
+ **The file : ** `{}`
192
+
193
+ have been saved from the URL
194
+
195
+ `{}`
196
+ """
197
+
198
+ EXT_FAILED_TXT = """
199
+ **Extraction failed 😕**
200
+
201
+ **What to do ?**
202
+
203
+ • Please make sure archive isn’t corrupted
204
+ • Please make sure that you selected the right mode !
205
+ • Also check if you sent the right password (it's case sensitive)
206
+ • Maybe your archive format isn’t supported yet 😔
207
+
208
+
209
+ **⚠ IN ALL CASES ⚠**, please send **/clean**, else you couldn’t send any other task 🙂🔫 (may be fixed in the future)
210
+
211
+ Please report this at @EDM115_chat if you think this is a serious error
212
+ """
213
+
214
+ HOW_MANY_UPLOADED = "`{}` files were extracted from that archive"
215
+
216
+ PLS_REPLY = "You need to reply to a picture for saving it as custom thumbnail 🤓"
217
+
218
+ NO_MERGE_TASK = """
219
+ Bruh there's no merge task ongoing 🗿
220
+ Use **/merge** to start one
221
+ """
222
+
223
+ LOG_TXT = """
224
+ **Extract log 📝**
225
+
226
+ **User ID :** `{}`
227
+ **File name :** `{}`
228
+ **File size :** `{}`
229
+ """
230
+
231
+ PASS_TXT = """
232
+ **Password of the above archive is 🔑**
233
+
234
+ `{}`
235
+ """
236
+
237
+ DL_URL = """
238
+ **Trying to download… Please wait**
239
+
240
+ **URL :** `{}`
241
+
242
+ """
243
+
244
+ REFRESH_STATS = "Refreshing stats… ♻️"
245
+
246
+ ACTUAL_THUMB = "Your actual thumbnail"
247
+
248
+ START_TEXT = """
249
+ Hi **{}** 👋, I'm **Unarchiver bot** 🥰
250
+
251
+
252
+ I can extract archives like `zip`, `rar`, `tar`, …
253
+
254
+ **Made with ❤️ by @EDM115bots**
255
+
256
+ **/donate** if you can 🥺
257
+ """
258
+
259
+ HELP_TXT = """
260
+ **• How to extract 🤔**
261
+
262
+ **1)** Send the file or link that you want to extract
263
+ **2)** Click on extract button (If you sent a link use `🔗` button. If it's a file just use `🗂️` button)
264
+
265
+
266
+ **• How to change upload mode 🤔**
267
+ Send **/mode**
268
+
269
+
270
+ **Note :**
271
+ **1.** If your archive is password protected select `🔐` button
272
+ **2.** Please don’t send corrupted files ! If you sent one by mistake just send **/clean**
273
+ **3.** If your archive have +95 files in it then bot can’t show all of extracted files to select from (yet). So in that case if you can’t see your file in the buttons just click on `Upload all 📤` button. It will send all extracted files to you !
274
+
275
+
276
+ **• Got an error ?**
277
+ Visit edm115.eu.org/unzip#help
278
+
279
+
280
+ **• I wanna have help 🥺**
281
+
282
+ PM me at **@EDM115** or join the chat **@EDM115_chat**
283
+ """
284
+
285
+ ABOUT_TXT = """
286
+ **About Unarchiver bot [v6.3.2]**
287
+
288
+ • **Language :** [Python 3.11.5](https://www.python.org/)
289
+ • **Framework :** [Pyrogram 2.0.106](https://pyrogram.org/)
290
+ • **Source code :** [EDM115/unzip-bot](https://github.com/EDM115/unzip-bot)
291
+ • **Developer :** [EDM115](https://github.com/EDM115)
292
+
293
+ **[Rate me ⭐](https://t.me/BotsArchive/2705)**
294
+ Made with ❤️ by **@EDM115bots**
295
+ """
296
+
297
+ DONATE_TEXT = """
298
+ I'm going to be honest : **this bot costs me money**…
299
+ Nothing's free on this world, however I try to keep this bot for free for as many people as possible
300
+ I don't like to put restrictions, nor getting your PM's flooded with ads…
301
+
302
+ So if you can, donate :)
303
+ It helps out a ton, covers the costs (hosting, updating, … 👨‍💻)
304
+
305
+ --How ?--
306
+ • **[Paypal](https://www.paypal.me/8EDM115)**
307
+ • **[GitHub Sponsors](https://github.com/sponsors/EDM115)**
308
+ • **[Directly in Telegram](https://t.me/EDM115bots/544)**
309
+ • **[BuyMeACoffee](https://www.buymeacoffee.com/edm115)**
310
+ • **[Send cryptos (not recommended)](https://edm115.shadd.eu.org/)**
311
+
312
+ Thanks for your contribution 😊
313
+
314
+ --Side note :--
315
+ Donation doesn't count as a VIP subscription. Check **/vip** for more info
316
+ """
317
+
318
+ VIP_INFO = """
319
+ Wanna help the developer of this __amazing__ bot ?
320
+ Here's how : Become a VIP user and benefit from extra perks !
321
+
322
+ **VIP perks :**
323
+ - No max tasks limit
324
+ - No AFK timeout
325
+ - Get a better support
326
+ - Upload files up to 4Gb
327
+ - Early access to new features
328
+ - Access a second bot exclusive to VIPs __(subject to conditions)__
329
+ - And more…
330
+
331
+ **What's the price ?**
332
+ - `1$/month`
333
+ - `10$/year`
334
+
335
+ **How to become a VIP ?**
336
+ 1) Send **/pay** to the bot
337
+ 2) Choose your subscription
338
+ 3) Send a screenshot of your payment to **@EDM115**
339
+ 4) Enjoy your VIP perks !
340
+
341
+ **What happens when my subscription ends ?**
342
+ If you choosed GitHub Sponsors, Telegram Donate or BuyMeACoffee, you will be automatically renewed until you cancel it
343
+ If you choosed PayPal, you will have to redo the 4 above steps
344
+ You will be notified few days before you subscription ends so you can check if you want to renew it or not
345
+
346
+ **I wanna cancel my subscription**
347
+ Just send **/stoppay** and follow the instructions according to the platform you selected
348
+ Your payment will be cancelled and you will keep your VIP perks until the end of your subscription
349
+ (i.e. if you paid for 1 month, from 05/01/2024 to 05/02/2024 and you cancel your subscription on 15/01/2024, your perks will stay until 05/02/2024)
350
+
351
+ **What is the referral system ?**
352
+ Referrals have benefits for both sides :)
353
+ - For the referrer : you get 1 month of VIP for free for each 3 new VIPs you bring
354
+ - For the referred : you get 1 month of VIP for free if you take the monthly subscription, and 3 months for free if you take the yearly subscription
355
+ How to input the referral code ? Just send **/pay** to the bot and follow the instructions
356
+ """
357
+
358
+ VIP_REQUIRED_MESSAGE = """
359
+ Use this command as a reply to a messsage, where you have the following (ONE PER LINE) :
360
+ The user ID (int)
361
+ When the subscription starts (in %Y-%m-%dT%H:%M:%SZ format)
362
+ When the subscription ends (same)
363
+ Which platform had been used [paypal, telegram, sponsor, bmac]
364
+ At which frequency the user is billed [monthly, yearly]
365
+ Is the user a early supporter (can be obtained only the first 3 months) [True, False]
366
+ Is the user also a donator [True, False]
367
+ When does the user ever started a subscription (date)
368
+ How many successful payments had been done (int)
369
+ Is there been any gap between payments [True, False]
370
+ If the user had been gifted a Premium plan [True, False]
371
+ The user referral code (str)
372
+ Is this a lifetime free subscription [True, False]
373
+ """
374
+
375
+ VIP_ADDED_USER = """
376
+ The following user had been added with the following infos :
377
+ User ID : `{}`
378
+ Start date : `{}`
379
+ End date : `{}`
380
+ Platform : `{}`
381
+ Frequency : `{}`
382
+ Early supporter : `{}`
383
+ Donator : `{}`
384
+ First subscription date : `{}`
385
+ Successful payments : `{}`
386
+ Gap between payments : `{}`
387
+ Gifted : `{}`
388
+ Referral code : `{}`
389
+ Lifetime : `{}`
390
+ """
391
+
392
+ CLEAN_TXT = """
393
+ **Are sure want to clean your task 🤔**
394
+
395
+ Note : This action cannot be undone !
396
+ """
397
+
398
+ SELECT_UPLOAD_MODE_TXT = """
399
+ Select your upload mode 👇
400
+
401
+ **Current upload mode is :** `{}`
402
+ """
403
+
404
+ CHANGED_UPLOAD_MODE_TXT = "**Successfully changed upload mode to** `{}` ✅"
405
+
406
+ EXISTING_THUMB = """
407
+ A thumbnail already have been saved 😅 What you wanna do ?
408
+ • Checking the actual thumbnail
409
+ • Replace it with the new one you just sent
410
+ • Cancel
411
+ """
412
+
413
+ SAVING_THUMB = "Are you sure you want to save this thumbnail 🤔"
414
+
415
+ SAVED_THUMBNAIL = "**Successfully saved this thumbnail ✅**"
416
+
417
+ DEL_CONFIRM_THUMB = """
418
+ Do you really want to delete your thumbnail ?
419
+ • Check the actual thumbnail
420
+ • Delete it
421
+ • Cancel
422
+ """
423
+
424
+ DEL_CONFIRM_THUMB_2 = "Do you really want to delete your thumbnail ?"
425
+
426
+ DELETED_THUMB = "**Successfully removed your thumbnail ✅**"
427
+
428
+ ERROR_THUMB_RENAME = "Error on thumb rename"
429
+
430
+ ERROR_THUMB_UPDATE = "Error while updating thumb URL on DB"
431
+
432
+ ERROR_TELEGRAPH_UPLOAD = "Error on Telegra.ph upload"
433
+
434
+ ERROR_THUMB_DEL = "Error on thumb deletion in DB : {}"
435
+
436
+ AFTER_OK_DL_TXT = """
437
+ **Successfully downloaded ✅**
438
+
439
+ **Download time :** `{}`
440
+ **Status :** Testing the archive… Please wait
441
+ """
442
+
443
+ AFTER_OK_MERGE_DL_TXT = """
444
+ **Successfully downloaded all {} files ✅**
445
+
446
+ **Download time :** `{}`
447
+ **Status :** Merging the archive… Please wait
448
+ """
449
+
450
+ AFTER_OK_MERGE_TXT = """
451
+ **Successfully merged ✅**
452
+
453
+ **Merge time :** `{}`
454
+ **Status :** Processing the archive… Please wait
455
+ """
456
+
457
+ AFTER_OK_TEST_TXT = """
458
+ **Successfully tested ✅**
459
+
460
+ **Test time :** `{}`
461
+ **Status :** Extracting the archive… Please wait
462
+ """
463
+
464
+ EXT_OK_TXT = """
465
+ **Extraction successful ✅**
466
+
467
+ **Extraction time :** `{}`
468
+ **Status :** Processing the extracted files… Please wait
469
+ """
470
+
471
+ ERROR_TXT = """
472
+ **Error happened 😕**
473
+
474
+ `{}`
475
+
476
+ Please report this at @EDM115_chat if you think this is a serious error
477
+ """
478
+
479
+ CANCELLED_TXT = "**{} ✅**"
480
+
481
+ DL_STOPPED = "✅ The download of your file have successfully been cancelled 😌"
482
+
483
+ PROCESSING_TASK = "**✅ Processing your task… Please wait**"
484
+
485
+ ERROR_GET_MSG = "Error on getting messages from user : {}"
486
+
487
+ PROCESS_MSGS = "**Processing {} messages… Please wait**"
488
+
489
+ DL_FILES = """
490
+ **Trying to download file {}/{}… Please wait**
491
+
492
+ """
493
+
494
+ PROCESS_MERGE = """
495
+ Processing an user query…
496
+
497
+ User ID : {}
498
+ Task : #Merge
499
+
500
+ File : {}
501
+ """
502
+
503
+ PLS_SEND_PASSWORD = "**Please send me the password 🔑**"
504
+
505
+ PASSWORD_PROTECTED = "That archive is password protected 😡 **Don't fool me !** "
506
+
507
+ SELECT_FILES = "Select files to upload 👇"
508
+
509
+ UNABLE_GATHER_FILES = """
510
+ Unable to gather the files to upload 😥
511
+ Choose either to upload everything, or cancel the process
512
+ """
513
+
514
+ FATAL_ERROR = "Fatal error : uncorrect archive format"
515
+
516
+ USER_QUERY = """
517
+ Processing an user query…
518
+
519
+ User ID : {}
520
+ """
521
+
522
+ INVALID_URL = "That's not a valid url 💀"
523
+
524
+ NOT_AN_ARCHIVE = """
525
+ That's not an archive 💀
526
+
527
+ **Try to @transload it**
528
+ """
529
+
530
+ DEF_NOT_AN_ARCHIVE = """
531
+ This file is NOT an archive 😐
532
+ If you believe it's an error, send the file to **@EDM115**
533
+ """
534
+
535
+ PROCESSING2 = "`Processing… ⏳`"
536
+
537
+ UNZIP_HTTP = "Can't use unzip_http on {} : {}"
538
+
539
+ ERR_DL = "Error on download : {}"
540
+
541
+ CANT_DL_URL = "**Sorry, I can't download that URL 😭 Try to @transload it**"
542
+
543
+ GIVE_ARCHIVE = "Give me an archive to extract 😐"
544
+
545
+ ITS_SPLITTED = """
546
+ This file is splitted
547
+ Use the **/merge** command
548
+ """
549
+
550
+ SPL_RZ = "Splitted RAR/ZIP files in .rxx or .zxx format can't be processed yet"
551
+
552
+ TRY_DL = """
553
+ **Trying to download… Please wait**
554
+
555
+ """
556
+
557
+ QUERY_PARSE_ERR = """
558
+ Fatal query parsing error 💀
559
+
560
+ Please contact @EDM115_chat with details and screenshots
561
+ """
562
+
563
+ GIVE_NEW_NAME = """
564
+ Current file name : `{}`
565
+
566
+ Please send the new file name (**--INCLUDE THE FILE EXTENTION !--**)
567
+ """
568
+
569
+ SPLITTING = "**Splitting {}… Please wait**"
570
+
571
+ ERR_SPLIT = "An error occured while splitting a file above 2 Gb 😥"
572
+
573
+ SEND_ALL_PARTS = "Trying to send all parts of {} to you… Please wait"
574
+
575
+ UPLOADED = """
576
+ **Successfully uploaded ✅**
577
+
578
+ **Join @EDM115bots ❤️**
579
+ """
580
+
581
+ NO_FILE_LEFT = "There's no file left to upload"
582
+
583
+ SENDING_FILE = "Sending that file to you… Please wait"
584
+
585
+ SEND_ALL_FILES = "Trying to send all files to you… Please wait"
586
+
587
+ REFRESHING = "Refreshing… ⏳"
588
+
589
+ CANCELLED = "**Cancelled successfully ✅**"
590
+
591
+ PROCESS_CANCELLED = "❌ Process cancelled"
592
+
593
+ # commands.py
594
+
595
+ PROCESS_RUNNING = """
596
+ Already one process is running, don't spam 😐
597
+
598
+ Wanna clear your files from my server ? Then just send **/clean** command
599
+ """
600
+
601
+ SPLIT_NOPE = "Those type of splitted files can't be processed yet"
602
+
603
+ UNVALID = "Send a valid archive/URL"
604
+
605
+ MERGE = """
606
+ You have splitted archives to process ?
607
+ Send me **all** the splitted files (.001, .002, .00×, …)
608
+
609
+ **AFTER** you sent them all, send **/done** and click on the `Merge 🛠️` button
610
+ """
611
+
612
+ DONE = """
613
+ If you have sent **ALL** the files, you can click on the `Merge 🛠️` button below
614
+
615
+ If you sent /done by mistake and haven't sent all the files yet, just ignore this message and re-send **/done** when ALL the files are sent
616
+ """
617
+
618
+ STATS = """
619
+ **💫 Current bot stats 💫**
620
+
621
+ **💾 Disk usage :**
622
+ ↳ **Total Disk Space :** `{}`
623
+ ↳ **Used :** `{} - {}%`
624
+ ↳ **Free :** `{}`
625
+ ↳ **Ongoing tasks :** `{}`
626
+
627
+ **🎛 Hardware usage :**
628
+ ↳ **CPU usage :** `{}%`
629
+ ↳ **RAM usage :** `{}%`
630
+ ↳ **Uptime :** `{}`
631
+ """
632
+
633
+ STATS_OWNER = """
634
+ **💫 Current bot stats 💫**
635
+
636
+ **👥 Users :**
637
+ ↳ **Users in database :** `{}`
638
+ ↳ **Total banned users :** `{}`
639
+
640
+ **💾 Disk usage :**
641
+ ↳ **Total Disk Space :** `{}`
642
+ ↳ **Used :** `{} - {}%`
643
+ ↳ **Free :** `{}`
644
+ ↳ **Ongoing tasks :** `{}`
645
+
646
+ **🌐 Network usage :**
647
+ ↳ **Uploaded :** `{}`
648
+ ↳ **Downloaded :** `{}`
649
+
650
+ **🎛 Hardware usage :**
651
+ ↳ **CPU usage :** `{}%`
652
+ ↳ **RAM usage :** `{}%`
653
+ ↳ **Uptime :** `{}`
654
+ """
655
+
656
+ BC_REPLY = "Reply to a message to broadcast it 📡"
657
+
658
+ BC_START = """
659
+ Broadcasting has started, this may take a while 😪
660
+
661
+ Users : {}/{}
662
+ """
663
+
664
+ BC_DONE = """
665
+ **Broadcast completed ✅**
666
+
667
+ **Total users :** `{}`
668
+ **Successful responses :** `{}`
669
+ **Failed responses :** `{}`
670
+ """
671
+
672
+ SEND_REPLY = "Reply to a message to send it 📡"
673
+
674
+ PROVIDE_UID = "Please provide an user ID"
675
+
676
+ PROVIDE_UID2 = "Please provide an user ID/username"
677
+
678
+ SENDING = "Sending it, please wait… 😪"
679
+
680
+ SEND_SUCCESS = "Message successfully sent to `{}`"
681
+
682
+ SEND_FAILED = """
683
+ It failed 😣 Retry
684
+
685
+ If it fails again, it means that {} haven't started the bot yet (or deleted the chat), or he's private/banned/whatever
686
+ """
687
+
688
+ REPORT_REPLY = "Reply to a message to report it to @EDM115"
689
+
690
+ REPORT_DONE = """
691
+ Report sucessfully sent ! An answer will arrive soon
692
+
693
+ Note : if you need to reply to replies, always use that /report command (or join **@EDM115_chat**)
694
+ """
695
+
696
+ BAN_ID = "Give an user id to ban 😈"
697
+
698
+ ALREADY_BANNED = """
699
+ {} have already been banned
700
+
701
+
702
+ """
703
+
704
+ ALREADY_REMOVED = "{} have already been removed from the user database"
705
+
706
+ BANNED = """
707
+ **Successfully banned that user ✅**
708
+
709
+ **User ID :** `{}`
710
+ """
711
+
712
+ UNBAN_ID = "Give an user id to unban 😇"
713
+
714
+ ALREADY_ADDED = """
715
+ {} is already in the user database
716
+
717
+
718
+ """
719
+
720
+ ALREADY_UNBANNED = "{} have already been deleted from banned users database"
721
+
722
+ UNBANNED = """
723
+ **Successfully unbanned that user ✅**
724
+
725
+ **User ID :** `{}`
726
+ """
727
+
728
+ INFO = "Send a text (shorter possible) from any user/chat. And you will have infos about it 👀"
729
+
730
+ USER = "This is a WIP command that would allow you to get more stats about your utilisation of me 🤓"
731
+
732
+ UNABLE_FETCH = "Unable to fetch"
733
+
734
+ USER_INFO = """
735
+ **User ID :** `{}`
736
+ `{}` files uploaded
737
+
738
+
739
+ WIP
740
+ """
741
+
742
+ UID_UNAME_INVALID = "Error happened, The user ID/username is probably invalid"
743
+
744
+ USER2_INFO = """
745
+ `{}`
746
+
747
+ **Direct link to profile :** tg://user?id={}
748
+ """
749
+
750
+ MAINTENANCE = """
751
+ Do you want the bot to go in maintenance mode 🤔
752
+ Current state : `{}`
753
+ """
754
+
755
+ MAINTENANCE_ASK = """
756
+ False : No maintenance
757
+ True : Maintenance
758
+ Send the appropriate string
759
+ """
760
+
761
+ MAINTENANCE_DONE = "Successfully changed maintenance mode to `{}`"
762
+
763
+ MAINTENANCE_ON = "Maintenance mode is currently **ON**\nTasks can't be processed. Come back later"
764
+
765
+ MAINTENANCE_FAIL = "Provide one of the values"
766
+
767
+ NO_THUMBS = "No thumbnails on the server yet"
768
+
769
+ ERASE_ALL = """
770
+ 🚧 WIP 🚧
771
+
772
+ **Cleaning…**
773
+ """
774
+
775
+ CLEANED = "The whole server have been cleaned 😌"
776
+
777
+ NOT_CLEANED = "An error happened during /cleanall 😕"
778
+
779
+ ERASE_TASKS = "Deleting {} tasks… Please wait"
780
+
781
+ ERASE_TASKS_SUCCESS = "Successfully deleted {} tasks ✅"
782
+
783
+ LOG_SENT = "Log file sent to {}"
784
+
785
+ DELETED_FOLDER = "Deleted {} folder successfully"
786
+
787
+ RESTARTED_AT = "**ℹ️ Bot restarted successfully at **`{}`"
788
+
789
+ RESTARTING = "{} : Restarting…"
790
+
791
+ PULLING = "Pulling updates… ⌛"
792
+
793
+ PULLED = "✅ Pulled changes, restarting…"
794
+
795
+ NO_PULL = "Nothing to pull 😅"
796
+
797
+ COMMANDS_LIST = """
798
+ Here is the list of the commands you can use (only in private btw) :
799
+
800
+ **{send any file or URL}** : Prompt the extract dialog
801
+ **/start** : To know if I'm online
802
+ **/help** : Gives a simple help
803
+ **/about** : Know more about me
804
+ **/donate** : Know how you can contribute to this bot
805
+ **/clean** : Remove your files from my server. Also useful if a task failed
806
+ **/mode** : Change your upload mode (either `doc` or `media`)
807
+ **/stats** : Know all the current stats about me. If you're running on Heroku, it's reset every day
808
+ **/merge** : Merge splitted archives together
809
+ **/done** : After you sent all the splitted archives, use this to merge them
810
+ **/info** : Get full info about a [Message](https://docs.pyrogram.org/api/types/Message) (info returned by Pyrogram)
811
+ **/addthumb** : Upload with a custom thumbnail (not permanant yet)
812
+ **/delthumb** : Removes your thumbnail
813
+ **/report** : Used by replying to a message, sends it to the bot owner (useful for bug report, or any question)
814
+ **/commands** : This message
815
+
816
+ **/admincmd** : Only if you are the Owner
817
+ """
818
+
819
+ ADMINCMD = """
820
+ Here's all the commands that only the owner (you) can use :
821
+
822
+ **/gitpull** : Pulls the latest changes from GitHub
823
+ **/broadcast** : Send something to all the users
824
+ **/sendto {user_id}** : Same as broadcast but for a single user. Don't handle replies for now…
825
+ **/ban {user_id}** : Ban an user. He no longer can use your bot, except if…
826
+ **/unban {user_id}** : …you unban him. All his stats and settings stays saved after a ban
827
+ **/user {user_id}** : Know more about the use of your bot by a single user
828
+ **/user2 {user_id}** : Get full info about an [User](https://docs.pyrogram.org/api/types/User) (info returned by Pyrogram)
829
+ **/self** : Get full info about me (info returned by Pyrogram)
830
+ **/redbutton** : Will fully restart bot + server
831
+ **/cleanall** : Same as `/clean`, but for the whole server
832
+ **/logs** : Send you the logs (all of them). Useful for bug tracking. Send them to **@EDM115** if you don't understand them/need help
833
+ **/restart** : Does a basic restart, less intrusive as the `/redbutton` one
834
+ **/dbexport** : Exports the whole database as CSV
835
+ **/admincmd** : This message
836
+ **/commands** : For all the other commands
837
+ """
838
+
839
+ # cloud_upload.py
840
+
841
+ ERROR_UP_BAYFILES = "Error happened on BayFiles upload (check connection, or retry later)"
842
+
843
+ # custom_thumbnail.py
844
+
845
+ ALBUM = "{} tried to save a thumbnail from an album"
846
+
847
+ ALBUM_NOPE = "You can't use an album. Reply to a single picture sent as photo (not as document)"
848
+
849
+ DL_THUMB = "Downloading thumbnail of {}…"
850
+
851
+ THUMB_SAVED = "Thumbnail saved"
852
+
853
+ THUMB_FAILED = "Failed to generate thumb"
854
+
855
+ THUMB_ERROR = "Error happened 😕 Try again later"
856
+
857
+ NO_THUMB = "You already have no thumbnail 😅"
858
+
859
+ # ext_helper.py
860
+
861
+ UP_ALL = "Upload all 📤"
862
+
863
+ CANCEL_IT = "❌ Cancel"
864
+
865
+ # up_helper.py
866
+
867
+ TRY_UP = """
868
+ **Trying to upload {}… Please wait**
869
+
870
+ """
871
+
872
+ CANT_FIND = "Sorry ! I can't find that file {} 💀"
873
+
874
+ TOO_LARGE = "URL file is too large to send in telegram 😥"
875
+
876
+ ARCHIVE_GONE = "Archive has gone from servers before uploading 😥"
877
+
878
+ EMPTY_FILE = "The file {} is empty/unreachable"
879
+
880
+ CHECK_MSG = """
881
+ **Verifying the file… Please wait**
882
+
883
+ """
884
+
885
+
886
+ # List of error messages from p7zip
887
+ ERROR_MSGS = ["Error", "Can't open as archive"]
888
+
889
+
890
+ # Inline buttons
891
+ class Buttons:
892
+ START_BUTTON = InlineKeyboardMarkup(
893
+ [
894
+ [
895
+ InlineKeyboardButton(Messages.HELP, callback_data="helpcallback"),
896
+ InlineKeyboardButton(Messages.ABOUT, callback_data="aboutcallback"),
897
+ ],
898
+ [
899
+ InlineKeyboardButton(Messages.STATS_BTN, callback_data="statscallback"),
900
+ InlineKeyboardButton(Messages.DONATE, callback_data="donatecallback"),
901
+ ]
902
+ ]
903
+ )
904
+
905
+ REFRESH_BUTTON = InlineKeyboardMarkup(
906
+ [
907
+ [
908
+ InlineKeyboardButton(Messages.REFRESH, callback_data="statscallback|refresh"),
909
+ InlineKeyboardButton(Messages.BACK, callback_data="megoinhome"),
910
+ ]
911
+ ]
912
+ )
913
+
914
+ CHOOSE_E_F__BTNS = InlineKeyboardMarkup(
915
+ [
916
+ [
917
+ InlineKeyboardButton(
918
+ "🗂️", callback_data="extract_file|tg_file|no_pass"
919
+ ),
920
+ InlineKeyboardButton(
921
+ "🔐", callback_data="extract_file|tg_file|with_pass"
922
+ ),
923
+ ],
924
+ [
925
+ InlineKeyboardButton("🖼️", callback_data="extract_file|tg_file|thumb"),
926
+ InlineKeyboardButton(
927
+ "🖼️✏", callback_data="extract_file|tg_file|thumbrename"
928
+ ),
929
+ ],
930
+ [InlineKeyboardButton("❌", callback_data="cancel_dis")],
931
+ ]
932
+ )
933
+
934
+ CHOOSE_E_F_M__BTNS = InlineKeyboardMarkup(
935
+ [
936
+ [
937
+ InlineKeyboardButton(
938
+ "🗂️", callback_data="merged|no_pass"
939
+ ),
940
+ InlineKeyboardButton(
941
+ "🔐", callback_data="merged|with_pass"
942
+ ),
943
+ ],
944
+ [InlineKeyboardButton("❌", callback_data="cancel_dis")],
945
+ ]
946
+ )
947
+
948
+ CHOOSE_E_U__BTNS = InlineKeyboardMarkup(
949
+ [
950
+ [
951
+ InlineKeyboardButton("🔗", callback_data="extract_file|url|no_pass"),
952
+ InlineKeyboardButton("🔐", callback_data="extract_file|url|with_pass"),
953
+ ],
954
+ [
955
+ InlineKeyboardButton("🖼️", callback_data="extract_file|url|thumb"),
956
+ InlineKeyboardButton(
957
+ "🖼️✏", callback_data="extract_file|url|thumbrename"
958
+ ),
959
+ ],
960
+ [InlineKeyboardButton("❌", callback_data="cancel_dis")],
961
+ ]
962
+ )
963
+
964
+ RENAME = InlineKeyboardMarkup(
965
+ [
966
+ [
967
+ InlineKeyboardButton("✏", callback_data="renameit"),
968
+ InlineKeyboardButton("🙅‍♂️", callback_data="norename"),
969
+ ]
970
+ ]
971
+ )
972
+
973
+ CLN_BTNS = InlineKeyboardMarkup(
974
+ [
975
+ [
976
+ InlineKeyboardButton(Messages.CLEAN, callback_data="cancel_dis"),
977
+ InlineKeyboardButton(Messages.CANCEL_IT, callback_data="nobully"),
978
+ ]
979
+ ]
980
+ )
981
+
982
+ ME_GOIN_HOME = InlineKeyboardMarkup(
983
+ [[InlineKeyboardButton(Messages.BACK, callback_data="megoinhome")]]
984
+ )
985
+
986
+ SET_UPLOAD_MODE_BUTTONS = InlineKeyboardMarkup(
987
+ [
988
+ [
989
+ InlineKeyboardButton(Messages.AS_DOC, callback_data="set_mode|doc"),
990
+ InlineKeyboardButton(Messages.AS_MEDIA, callback_data="set_mode|media")
991
+ ],
992
+ ]
993
+ )
994
+
995
+ I_PREFER_STOP = InlineKeyboardMarkup(
996
+ [[InlineKeyboardButton(Messages.CANCEL_IT, callback_data="canceldownload")]]
997
+ )
998
+
999
+ MERGE_THEM_ALL = InlineKeyboardMarkup(
1000
+ [
1001
+ [
1002
+ InlineKeyboardButton(Messages.MERGE_BTN, callback_data="merge_this"),
1003
+ InlineKeyboardButton(Messages.CANCEL_IT, callback_data="cancel_dis"),
1004
+ ]
1005
+ ]
1006
+ )
1007
+
1008
+ THUMB_REPLACEMENT = InlineKeyboardMarkup(
1009
+ [
1010
+ [
1011
+ InlineKeyboardButton(Messages.CHECK, callback_data="check_thumb"),
1012
+ InlineKeyboardButton(Messages.REPLACE, callback_data="save_thumb|replace"),
1013
+ ],
1014
+ [InlineKeyboardButton(Messages.CANCEL_IT, callback_data="nope_thumb")],
1015
+ ]
1016
+ )
1017
+
1018
+ THUMB_FINAL = InlineKeyboardMarkup(
1019
+ [
1020
+ [
1021
+ InlineKeyboardButton(Messages.REPLACE, callback_data="save_thumb|replace"),
1022
+ InlineKeyboardButton(Messages.CANCEL_IT, callback_data="nope_thumb"),
1023
+ ]
1024
+ ]
1025
+ )
1026
+
1027
+ THUMB_SAVE = InlineKeyboardMarkup(
1028
+ [
1029
+ [
1030
+ InlineKeyboardButton(Messages.SAVE, callback_data="save_thumb|save"),
1031
+ InlineKeyboardButton(Messages.CANCEL_IT, callback_data="nope_thumb"),
1032
+ ]
1033
+ ]
1034
+ )
1035
+
1036
+ THUMB_DEL = InlineKeyboardMarkup(
1037
+ [
1038
+ [
1039
+ InlineKeyboardButton(Messages.CHECK, callback_data="check_before_del"),
1040
+ InlineKeyboardButton(Messages.DELETE, callback_data="del_thumb"),
1041
+ ],
1042
+ [InlineKeyboardButton(Messages.CANCEL_IT, callback_data="nope_thumb")],
1043
+ ]
1044
+ )
1045
+
1046
+ THUMB_DEL_2 = InlineKeyboardMarkup(
1047
+ [
1048
+ [
1049
+ InlineKeyboardButton(Messages.DELETE, callback_data="del_thumb"),
1050
+ InlineKeyboardButton(Messages.CANCEL_IT, callback_data="nope_thumb")
1051
+ ],
1052
+ ]
1053
+ )
1054
+
1055
+ RATE_ME = InlineKeyboardMarkup(
1056
+ [
1057
+ [
1058
+ InlineKeyboardButton(Messages.RATE, url="https://t.me/BotsArchive/2705"),
1059
+ InlineKeyboardButton(Messages.DONATE, callback_data="donatecallback")
1060
+ ],
1061
+ ]
1062
+ )
unzipper/modules/callbacks.py ADDED
@@ -0,0 +1,1233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+ import asyncio
3
+ import concurrent.futures
4
+ import os
5
+ import re
6
+ import shutil
7
+ from fnmatch import fnmatch
8
+ from time import time
9
+ from urllib.parse import unquote
10
+
11
+ from aiofiles import open as openfile
12
+ from aiohttp import ClientSession, InvalidURL
13
+ from pyrogram import Client
14
+ from pyrogram.errors import ReplyMarkupTooLong
15
+ from pyrogram.types import CallbackQuery
16
+ import unzip_http
17
+
18
+ from config import Config
19
+ from unzipper import LOGGER, unzipperbot
20
+ from unzipper.helpers.database import (
21
+ add_cancel_task,
22
+ del_cancel_task,
23
+ del_merge_task,
24
+ del_thumb_db,
25
+ get_cancel_task,
26
+ get_maintenance,
27
+ get_merge_task_message_id,
28
+ get_ongoing_tasks,
29
+ set_upload_mode,
30
+ update_thumb,
31
+ update_uploaded,
32
+ upload_thumb,
33
+ add_ongoing_task,
34
+ del_ongoing_task,
35
+ count_ongoing_tasks,
36
+ )
37
+ from unzipper.helpers.unzip_help import (
38
+ TimeFormatter,
39
+ extentions_list,
40
+ humanbytes,
41
+ progress_for_pyrogram,
42
+ )
43
+
44
+ from .bot_data import ERROR_MSGS, Buttons, Messages
45
+ from .commands import https_url_regex, get_stats
46
+ from .ext_script.custom_thumbnail import silent_del
47
+ from .ext_script.ext_helper import (
48
+ _test_with_7z_helper,
49
+ extr_files,
50
+ get_files,
51
+ make_keyboard,
52
+ make_keyboard_empty,
53
+ merge_files,
54
+ split_files,
55
+ )
56
+ from .ext_script.up_helper import answer_query, get_size, send_file, send_url_logs
57
+ from .ext_script.url_parser import gdrive_dl
58
+
59
+ split_file_pattern = r"\.(?:z\d+|r\d{2})$"
60
+ rar_file_pattern = r"\.part\d+\.rar$"
61
+ telegram_url_pattern = r"(?:http[s]?:\/\/)?(?:www\.)?t\.me\/([a-zA-Z0-9_]+)\/(\d+)"
62
+
63
+
64
+ async def download(url, path):
65
+ try:
66
+ async with ClientSession() as session, session.get(url, timeout=None, allow_redirects=True) as resp, openfile(path, mode="wb") as file:
67
+ async for chunk in resp.content.iter_chunked(Config.CHUNK_SIZE):
68
+ await file.write(chunk)
69
+ await session.close()
70
+ except InvalidURL:
71
+ LOGGER.error(Messages.INVALID_URL)
72
+ except:
73
+ LOGGER.error(Messages.ERR_DL.format(url))
74
+
75
+
76
+ async def download_with_progress(url, path, message, unzip_bot):
77
+ async with ClientSession() as session, session.get(url, timeout=None, allow_redirects=True) as resp, openfile(path, mode="wb") as file:
78
+ total_size = int(resp.headers.get("Content-Length", 0))
79
+ current_size = 0
80
+ start_time = time()
81
+
82
+ async for chunk in resp.content.iter_chunked(Config.CHUNK_SIZE):
83
+ if message.from_user is not None and await get_cancel_task(message.from_user.id):
84
+ await session.close()
85
+ await message.edit(text=Messages.DL_STOPPED)
86
+ await del_cancel_task(message.from_user.id)
87
+ return False
88
+
89
+ await file.write(chunk)
90
+ current_size += len(chunk)
91
+ await progress_for_pyrogram(current_size, total_size, Messages.DL_URL.format(url), message, start_time, unzip_bot)
92
+
93
+ await session.close()
94
+
95
+
96
+ def get_zip_http(url):
97
+ rzf = unzip_http.RemoteZipFile(url)
98
+ paths = rzf.namelist()
99
+ return rzf, paths
100
+
101
+
102
+ async def async_generator(iterable):
103
+ for item in iterable:
104
+ yield item
105
+
106
+
107
+ # Callbacks
108
+ @unzipperbot.on_callback_query()
109
+ async def unzipper_cb(unzip_bot: Client, query: CallbackQuery):
110
+ uid = query.from_user.id
111
+ if uid != Config.BOT_OWNER: # skipcq: PTC-W0048
112
+ if await count_ongoing_tasks() >= Config.MAX_CONCURRENT_TASKS:
113
+ ogtasks = await get_ongoing_tasks()
114
+ if not any(ogtask["user_id"] == uid for ogtask in ogtasks):
115
+ await unzip_bot.send_message(
116
+ chat_id=uid,
117
+ text=Messages.MAX_TASKS.format(Config.MAX_CONCURRENT_TASKS),
118
+ )
119
+ return
120
+
121
+ if uid != Config.BOT_OWNER and await get_maintenance():
122
+ await answer_query(query, Messages.MAINTENANCE_ON)
123
+ return
124
+
125
+ sent_files = 0
126
+ global log_msg
127
+
128
+ if query.data == "megoinhome":
129
+ await query.edit_message_text(
130
+ text=Messages.START_TEXT.format(query.from_user.mention),
131
+ reply_markup=Buttons.START_BUTTON,
132
+ )
133
+
134
+ elif query.data == "helpcallback":
135
+ await query.edit_message_text(
136
+ text=Messages.HELP_TXT,
137
+ reply_markup=Buttons.ME_GOIN_HOME
138
+ )
139
+
140
+ elif query.data == "aboutcallback":
141
+ await query.edit_message_text(
142
+ text=Messages.ABOUT_TXT,
143
+ reply_markup=Buttons.ME_GOIN_HOME,
144
+ disable_web_page_preview=True,
145
+ )
146
+
147
+ elif query.data == "donatecallback":
148
+ await query.edit_message_text(
149
+ text=Messages.DONATE_TEXT,
150
+ reply_markup=Buttons.ME_GOIN_HOME,
151
+ disable_web_page_preview=True,
152
+ )
153
+
154
+ elif query.data.startswith("statscallback"):
155
+ if query.data.endswith("refresh"):
156
+ await query.edit_message_text(text=Messages.REFRESH_STATS)
157
+ text_stats = await get_stats(query.from_user.id)
158
+ await query.edit_message_text(
159
+ text=text_stats,
160
+ reply_markup=Buttons.REFRESH_BUTTON,
161
+ )
162
+
163
+ elif query.data == "canceldownload":
164
+ await add_cancel_task(query.from_user.id)
165
+
166
+ elif query.data == "check_thumb":
167
+ user_id = query.from_user.id
168
+ thumb_location = Config.THUMB_LOCATION + "/" + str(user_id) + ".jpg"
169
+ await unzip_bot.send_photo(
170
+ chat_id=user_id,
171
+ photo=thumb_location,
172
+ caption=Messages.ACTUAL_THUMB
173
+ )
174
+ await unzip_bot.delete_messages(
175
+ chat_id=user_id,
176
+ message_ids=query.message.id
177
+ )
178
+ await unzip_bot.send_message(
179
+ chat_id=user_id,
180
+ text=Messages.EXISTING_THUMB,
181
+ reply_markup=Buttons.THUMB_FINAL,
182
+ )
183
+
184
+ elif query.data == "check_before_del":
185
+ user_id = query.from_user.id
186
+ thumb_location = Config.THUMB_LOCATION + "/" + str(user_id) + ".jpg"
187
+ await unzip_bot.send_photo(
188
+ chat_id=user_id,
189
+ photo=thumb_location,
190
+ caption=Messages.ACTUAL_THUMB
191
+ )
192
+ await unzip_bot.delete_messages(
193
+ chat_id=user_id,
194
+ message_ids=query.message.id
195
+ )
196
+ await unzip_bot.send_message(
197
+ chat_id=user_id,
198
+ text=Messages.DEL_CONFIRM_THUMB_2,
199
+ reply_markup=Buttons.THUMB_DEL_2,
200
+ )
201
+
202
+ elif query.data.startswith("save_thumb"):
203
+ user_id = query.from_user.id
204
+ replace = query.data.split("|")[1]
205
+ if replace == "replace":
206
+ await silent_del(user_id)
207
+ thumb_location = Config.THUMB_LOCATION + "/" + str(user_id) + ".jpg"
208
+ final_thumb = Config.THUMB_LOCATION + "/waiting_" + str(user_id) + ".jpg"
209
+ try:
210
+ os.rename(final_thumb, thumb_location)
211
+ except:
212
+ LOGGER.warning(Messages.ERROR_THUMB_RENAME)
213
+ try:
214
+ thumb_url = await upload_thumb(thumb_location)
215
+ try:
216
+ if thumb_url != -1 and re.match(https_url_regex, thumb_url):
217
+ await update_thumb(query.from_user.id, thumb_url, force=True)
218
+ except:
219
+ LOGGER.error(Messages.ERROR_THUMB_UPDATE)
220
+ except:
221
+ LOGGER.error(Messages.ERROR_TELEGRAPH_UPLOAD)
222
+ await answer_query(query, Messages.SAVED_THUMBNAIL)
223
+
224
+ elif query.data == "del_thumb":
225
+ user_id = query.from_user.id
226
+ thumb_location = Config.THUMB_LOCATION + "/" + str(user_id) + ".jpg"
227
+ try:
228
+ await del_thumb_db(user_id)
229
+ except Exception as e:
230
+ LOGGER.error(Messages.ERROR_THUMB_DEL.format(e))
231
+ try:
232
+ os.remove(thumb_location)
233
+ except:
234
+ pass
235
+ await query.edit_message_text(text=Messages.DELETED_THUMB)
236
+
237
+ elif query.data == "nope_thumb":
238
+ user_id = query.from_user.id
239
+ del_1 = Config.THUMB_LOCATION + "/not_resized_" + str(user_id) + ".jpg"
240
+ del_2 = Config.THUMB_LOCATION + "/waiting_" + str(user_id) + ".jpg"
241
+ try:
242
+ os.remove(del_1)
243
+ except:
244
+ pass
245
+ try:
246
+ os.remove(del_2)
247
+ except:
248
+ pass
249
+ await query.edit_message_text(
250
+ text=Messages.CANCELLED_TXT.format(Messages.PROCESS_CANCELLED))
251
+
252
+ elif query.data.startswith("set_mode"):
253
+ user_id = query.from_user.id
254
+ mode = query.data.split("|")[1]
255
+ await set_upload_mode(user_id, mode)
256
+ await answer_query(
257
+ query,
258
+ Messages.CHANGED_UPLOAD_MODE_TXT.format(mode)
259
+ )
260
+
261
+ elif query.data == "merge_this":
262
+ user_id = query.from_user.id
263
+ m_id = query.message.id
264
+ start_time = time()
265
+ await add_ongoing_task(user_id, start_time, "merge")
266
+ s_id = await get_merge_task_message_id(user_id)
267
+ merge_msg = await query.message.edit(Messages.PROCESSING_TASK)
268
+ download_path = f"{Config.DOWNLOAD_LOCATION}/{user_id}/merge"
269
+ if s_id and (m_id - s_id) > 1:
270
+ files_array = list(range(s_id, m_id))
271
+ try:
272
+ messages_array = await unzip_bot.get_messages(user_id, files_array)
273
+ except Exception as e:
274
+ LOGGER.error(Messages.ERROR_GET_MSG.format(e))
275
+ await answer_query(query, Messages.ERROR_TXT.format(e))
276
+ await del_ongoing_task(user_id)
277
+ await del_merge_task(user_id)
278
+ try:
279
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{user_id}")
280
+ except:
281
+ pass
282
+ return
283
+ length = len(messages_array)
284
+ if not os.path.isdir(download_path):
285
+ os.makedirs(download_path)
286
+ rs_time = time()
287
+ newarray = []
288
+ await merge_msg.edit(Messages.PROCESS_MSGS.format(length))
289
+ for message in messages_array:
290
+ if message.document is None:
291
+ pass
292
+ else:
293
+ if message.from_user.id == user_id: # avoid getting files from other users, tho idk why this could happen
294
+ newarray.append(message)
295
+ length = len(newarray)
296
+ if length == 0:
297
+ await answer_query(query, Messages.NO_MERGE_TASK)
298
+ await del_ongoing_task(user_id)
299
+ await del_merge_task(user_id)
300
+ try:
301
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{user_id}")
302
+ except:
303
+ pass
304
+ return
305
+ i = 0
306
+ async_newarray = async_generator(newarray)
307
+ async for message in async_newarray:
308
+ i += 1
309
+ fname = message.document.file_name
310
+ await message.forward(chat_id=Config.LOGS_CHANNEL)
311
+ location = f"{download_path}/{fname}"
312
+ s_time = time()
313
+ await message.download(
314
+ file_name=location,
315
+ progress=progress_for_pyrogram,
316
+ progress_args=(
317
+ Messages.DL_FILES.format(i, length),
318
+ merge_msg,
319
+ s_time,
320
+ unzip_bot,
321
+ ),
322
+ )
323
+ e_time = time()
324
+ dltime = TimeFormatter(round(e_time - rs_time) * 1000)
325
+ if dltime == "":
326
+ dltime = "1 s"
327
+ await merge_msg.edit(Messages.AFTER_OK_MERGE_DL_TXT.format(i, dltime))
328
+ await merge_msg.edit(
329
+ text=Messages.CHOOSE_EXT_MODE_MERGE,
330
+ reply_markup=Buttons.CHOOSE_E_F_M__BTNS,
331
+ )
332
+ await del_merge_task(user_id)
333
+ else:
334
+ await answer_query(query, Messages.NO_MERGE_TASK)
335
+ await del_ongoing_task(user_id)
336
+ await del_merge_task(user_id)
337
+ try:
338
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{user_id}")
339
+ except:
340
+ pass
341
+
342
+ elif query.data.startswith("merged"):
343
+ user_id = query.from_user.id
344
+ download_path = f"{Config.DOWNLOAD_LOCATION}/{user_id}/merge"
345
+ ext_files_dir = f"{Config.DOWNLOAD_LOCATION}/{user_id}/extracted"
346
+ os.makedirs(ext_files_dir)
347
+ try:
348
+ files = await get_files(download_path)
349
+ file = files[0]
350
+ except IndexError:
351
+ await answer_query(query, Messages.NO_MERGE_TASK)
352
+ await del_ongoing_task(user_id)
353
+ await del_merge_task(user_id)
354
+ try:
355
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{user_id}")
356
+ except:
357
+ pass
358
+ return
359
+ splitted_data = query.data.split("|")
360
+ log_msg = await unzip_bot.send_message(
361
+ chat_id=Config.LOGS_CHANNEL,
362
+ text=Messages.PROCESS_MERGE.format(user_id, ".".join(file.split("/")[-1].split(".")[:-1])),
363
+ )
364
+ try:
365
+ await query.message.edit(Messages.PROCESSING_TASK)
366
+ except:
367
+ pass
368
+ if splitted_data[1] == "with_pass":
369
+ password = await unzip_bot.ask(
370
+ chat_id=query.message.chat.id,
371
+ text=Messages.PLS_SEND_PASSWORD,
372
+ )
373
+ ext_s_time = time()
374
+ extractor = await merge_files(
375
+ iinput=file,
376
+ ooutput=ext_files_dir,
377
+ password=password.text,
378
+ )
379
+ ext_e_time = time()
380
+ else:
381
+ # Can't test the archive apparently
382
+ ext_s_time = time()
383
+ extractor = await merge_files(iinput=file, ooutput=ext_files_dir)
384
+ ext_e_time = time()
385
+ # Checks if there is an error happened while extracting the archive
386
+ if any(err in extractor for err in ERROR_MSGS):
387
+ try:
388
+ await query.message.edit(Messages.EXT_FAILED_TXT)
389
+ shutil.rmtree(ext_files_dir)
390
+ shutil.rmtree(download_path)
391
+ await del_ongoing_task(user_id)
392
+ except:
393
+ try:
394
+ await query.message.delete()
395
+ except:
396
+ pass
397
+ await unzip_bot.send_message(
398
+ chat_id=query.message.chat.id,
399
+ text=Messages.EXT_FAILED_TXT
400
+ )
401
+ shutil.rmtree(ext_files_dir)
402
+ await del_ongoing_task(user_id)
403
+ return
404
+ # Check if user was dumb 😐
405
+ paths = await get_files(path=ext_files_dir)
406
+ if not paths:
407
+ await unzip_bot.send_message(
408
+ chat_id=query.message.chat.id,
409
+ text=Messages.PASSWORD_PROTECTED,
410
+ )
411
+ await answer_query(
412
+ query,
413
+ Messages.EXT_FAILED_TXT,
414
+ unzip_client=unzip_bot
415
+ )
416
+ shutil.rmtree(ext_files_dir)
417
+ shutil.rmtree(download_path)
418
+ await del_ongoing_task(user_id)
419
+ return
420
+
421
+ try:
422
+ shutil.rmtree(download_path)
423
+ except:
424
+ pass
425
+
426
+ # Upload extracted files
427
+ extrtime = TimeFormatter(round(ext_e_time - ext_s_time) * 1000)
428
+ if extrtime == "":
429
+ extrtime = "1s"
430
+ await answer_query(
431
+ query,
432
+ Messages.EXT_OK_TXT.format(extrtime),
433
+ unzip_client=unzip_bot
434
+ )
435
+
436
+ try:
437
+ i_e_buttons = await make_keyboard(
438
+ paths=paths,
439
+ user_id=user_id,
440
+ chat_id=query.message.chat.id,
441
+ unziphttp=False
442
+ )
443
+ try:
444
+ await query.message.edit(Messages.SELECT_FILES, reply_markup=i_e_buttons)
445
+ except ReplyMarkupTooLong:
446
+ empty_buttons = await make_keyboard_empty(
447
+ user_id=user_id,
448
+ chat_id=query.message.chat.id,
449
+ unziphttp=False
450
+ )
451
+ await query.message.edit(
452
+ Messages.UNABLE_GATHER_FILES,
453
+ reply_markup=empty_buttons,
454
+ )
455
+ except:
456
+ try:
457
+ await query.message.delete()
458
+ i_e_buttons = await make_keyboard(
459
+ paths=paths,
460
+ user_id=user_id,
461
+ chat_id=query.message.chat.id,
462
+ unziphttp=False
463
+ )
464
+ await unzip_bot.send_message(
465
+ chat_id=query.message.chat.id,
466
+ text=Messages.SELECT_FILES,
467
+ reply_markup=i_e_buttons,
468
+ )
469
+ except:
470
+ try:
471
+ await query.message.delete()
472
+ empty_buttons = await make_keyboard_empty(
473
+ user_id=user_id,
474
+ chat_id=query.message.chat.id,
475
+ unziphttp=False
476
+ )
477
+ await unzip_bot.send_message(
478
+ chat_id=query.message.chat.id,
479
+ text=Messages.UNABLE_GATHER_FILES,
480
+ reply_markup=empty_buttons,
481
+ )
482
+ except:
483
+ await answer_query(
484
+ query,
485
+ Messages.EXT_FAILED_TXT,
486
+ unzip_client=unzip_bot
487
+ )
488
+ shutil.rmtree(ext_files_dir)
489
+ LOGGER.error(Messages.FATAL_ERROR)
490
+ await del_ongoing_task(user_id)
491
+ return
492
+
493
+ elif query.data.startswith("extract_file"):
494
+ user_id = query.from_user.id
495
+ start_time = time()
496
+ await add_ongoing_task(user_id, start_time, "extract")
497
+ download_path = f"{Config.DOWNLOAD_LOCATION}/{user_id}"
498
+ ext_files_dir = f"{download_path}/extracted"
499
+ r_message = query.message.reply_to_message
500
+ splitted_data = query.data.split("|")
501
+ try:
502
+ await query.message.edit(Messages.PROCESSING_TASK)
503
+ except:
504
+ pass
505
+ log_msg = await unzip_bot.send_message(
506
+ chat_id=Config.LOGS_CHANNEL,
507
+ text=Messages.USER_QUERY.format(user_id)
508
+ )
509
+ global archive_msg
510
+
511
+ try:
512
+ if splitted_data[1] == "url":
513
+ url = r_message.text
514
+ # Double check
515
+ if not re.match(https_url_regex, url):
516
+ await del_ongoing_task(user_id)
517
+ await query.message.edit(Messages.INVALID_URL)
518
+ return
519
+ if re.match(telegram_url_pattern, url):
520
+ r_message = await unzip_bot.get_messages(
521
+ chat_id=url.split("/")[-2],
522
+ message_ids=int(url.split("/")[-1])
523
+ )
524
+ splitted_data[1] = "tg_file"
525
+ if splitted_data[1] == "url":
526
+ s = ClientSession()
527
+ if "drive.google.com" in url:
528
+ url = await gdrive_dl(url)
529
+ if url is None:
530
+ await del_ongoing_task(user_id)
531
+ await query.message.edit(Messages.INVALID_URL)
532
+ return
533
+ async with s as session:
534
+ # Get the file size
535
+ unzip_head = await session.head(url, allow_redirects=True)
536
+ f_size = unzip_head.headers.get("content-length")
537
+ u_file_size = f_size if f_size else "undefined"
538
+ await log_msg.edit(Messages.LOG_TXT.format(user_id, url, u_file_size))
539
+ archive_msg = log_msg
540
+ # Checks if file is an archive using content-type header
541
+ unzip_resp = await session.get(url, timeout=None, allow_redirects=True)
542
+ if "application/" not in unzip_resp.headers.get("content-type"):
543
+ await del_ongoing_task(user_id)
544
+ await query.message.edit(Messages.NOT_AN_ARCHIVE)
545
+ return
546
+ rfnamebro = unquote(url.split("/")[-1])
547
+ if unzip_resp.status == 200:
548
+ # Makes download dir
549
+ os.makedirs(download_path)
550
+ s_time = time()
551
+ fname = unquote(os.path.splitext(url)[1])
552
+ fext = fname.split(".")[-1].casefold()
553
+ if (
554
+ splitted_data[2] not in ["thumb", "thumbrename"]
555
+ and fext not in extentions_list["archive"]
556
+ ):
557
+ await del_ongoing_task(user_id)
558
+ await query.message.edit(Messages.DEF_NOT_AN_ARCHIVE)
559
+ try:
560
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{user_id}")
561
+ except:
562
+ pass
563
+ return
564
+ archive = f"{download_path}/archive_from_{user_id}{fname}"
565
+ location = archive
566
+ await answer_query(
567
+ query,
568
+ Messages.PROCESSING2,
569
+ unzip_client=unzip_bot
570
+ )
571
+ # HTTP server must send Accept-Ranges: bytes and Content-Length in headers
572
+ if fext == "zip" and "accept-ranges" in unzip_resp.headers and "content-length" in unzip_resp.headers:
573
+ try:
574
+ loop = asyncio.get_event_loop()
575
+ with concurrent.futures.ThreadPoolExecutor() as pool:
576
+ rzf, paths = await loop.run_in_executor(pool, get_zip_http, url)
577
+ try:
578
+ i_e_buttons = await make_keyboard(
579
+ paths=paths,
580
+ user_id=user_id,
581
+ chat_id=query.message.chat.id,
582
+ unziphttp=True,
583
+ rzfile=rzf,
584
+ )
585
+ try:
586
+ await query.message.edit(
587
+ Messages.SELECT_FILES,
588
+ reply_markup=i_e_buttons
589
+ )
590
+ except ReplyMarkupTooLong:
591
+ empty_buttons = await make_keyboard_empty(
592
+ user_id=user_id, chat_id=query.message.chat.id, unziphttp=True, rzfile=rzf)
593
+ await query.message.edit(
594
+ Messages.UNABLE_GATHER_FILES,
595
+ reply_markup=empty_buttons,
596
+ )
597
+ except:
598
+ try:
599
+ await query.message.delete()
600
+ i_e_buttons = await make_keyboard(
601
+ paths=paths,
602
+ user_id=user_id,
603
+ chat_id=query.message.chat.id,
604
+ unziphttp=True,
605
+ rzfile=rzf,
606
+ )
607
+ await unzip_bot.send_message(
608
+ chat_id=query.message.chat.id,
609
+ text=Messages.SELECT_FILES,
610
+ reply_markup=i_e_buttons,
611
+ )
612
+ except:
613
+ try:
614
+ await query.message.delete()
615
+ empty_buttons = await make_keyboard_empty(
616
+ user_id=user_id,
617
+ chat_id=query.message.chat.id,
618
+ unziphttp=True,
619
+ rzfile=rzf
620
+ )
621
+ await unzip_bot.send_message(
622
+ chat_id=query.message.chat.id,
623
+ text=Messages.UNABLE_GATHER_FILES,
624
+ reply_markup=empty_buttons,
625
+ )
626
+ except:
627
+ pass
628
+ except Exception as e:
629
+ LOGGER.error(Messages.UNZIP_HTTP.format(url, e))
630
+ try:
631
+ dled = await download_with_progress(url, archive, query.message, unzip_bot)
632
+ except Exception as e:
633
+ dled = False
634
+ LOGGER.error(Messages.ERR_DL.format(e))
635
+ if isinstance(dled, bool) and not dled:
636
+ return
637
+ e_time = time()
638
+ # Send copy in logs in case url has gone
639
+ # paths = await get_files(path=archive)
640
+ await send_url_logs(
641
+ unzip_bot=unzip_bot,
642
+ c_id=Config.LOGS_CHANNEL,
643
+ doc_f=archive,
644
+ source=url,
645
+ message=query.message,
646
+ )
647
+ else:
648
+ await del_ongoing_task(user_id)
649
+ await query.message.edit(Messages.CANT_DL_URL)
650
+ try:
651
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{user_id}")
652
+ except:
653
+ pass
654
+ return
655
+
656
+ elif splitted_data[1] == "tg_file":
657
+ if r_message.document is None:
658
+ await del_ongoing_task(user_id)
659
+ await query.message.edit(Messages.GIVE_ARCHIVE)
660
+ return
661
+ fname = r_message.document.file_name
662
+ rfnamebro = fname
663
+ archive_msg = await r_message.forward(chat_id=Config.LOGS_CHANNEL)
664
+ await log_msg.edit(Messages.LOG_TXT.format(
665
+ user_id,
666
+ fname,
667
+ humanbytes(r_message.document.file_size)
668
+ ))
669
+ # Checks if it's actually an archive
670
+ # fext = (pathlib.Path(fname).suffix).casefold()
671
+ if splitted_data[2] not in ["thumb", "thumbrename"]:
672
+ fext = fname.split(".")[-1].casefold()
673
+ if (
674
+ fnmatch(fext, extentions_list["split"][0])
675
+ or fext in extentions_list["split"] or bool(re.search(rar_file_pattern, fname))
676
+ ):
677
+ await query.message.edit(Messages.ITS_SPLITTED)
678
+ return
679
+ if bool(re.search(split_file_pattern, fname)):
680
+ await del_ongoing_task(user_id)
681
+ await query.message.edit(Messages.SPL_RZ)
682
+ return
683
+ if fext not in extentions_list["archive"]:
684
+ await del_ongoing_task(user_id)
685
+ await query.message.edit(Messages.DEF_NOT_AN_ARCHIVE)
686
+ return
687
+ # Makes download dir
688
+ os.makedirs(download_path)
689
+ s_time = time()
690
+ location = f"{download_path}/archive_from_{user_id}{os.path.splitext(fname)[1]}"
691
+ archive = await r_message.download(
692
+ file_name=location,
693
+ progress=progress_for_pyrogram,
694
+ progress_args=(
695
+ Messages.TRY_DL,
696
+ query.message,
697
+ s_time,
698
+ unzip_bot,
699
+ ),
700
+ )
701
+ e_time = time()
702
+ else:
703
+ await del_ongoing_task(user_id)
704
+ await answer_query(
705
+ query,
706
+ Messages.QUERY_PARSE_ERR,
707
+ answer_only=True,
708
+ unzip_client=unzip_bot,
709
+ )
710
+ return
711
+
712
+ if splitted_data[2].startswith("thumb"):
713
+ await query.message.edit(Messages.PROCESSING2)
714
+ archive_name = location.split("/")[-1]
715
+ if "rename" in splitted_data[2]:
716
+ newname = await unzip_bot.ask(
717
+ chat_id=user_id,
718
+ text=Messages.GIVE_NEW_NAME.format(rfnamebro),
719
+ )
720
+ renamed = location.replace(archive_name, newname.text)
721
+ else:
722
+ renamed = location.replace(archive_name, rfnamebro)
723
+ try:
724
+ os.rename(location, renamed)
725
+ except OSError as e:
726
+ await del_ongoing_task(user_id)
727
+ LOGGER.error(e)
728
+ return
729
+ newfname = renamed.split("/")[-1]
730
+ fsize = await get_size(renamed)
731
+ if fsize <= Config.TG_MAX_SIZE:
732
+ await send_file(
733
+ unzip_bot=unzip_bot,
734
+ c_id=user_id,
735
+ doc_f=renamed,
736
+ query=query,
737
+ full_path=renamed,
738
+ log_msg=log_msg,
739
+ split=False,
740
+ )
741
+ await query.message.delete()
742
+ await del_ongoing_task(user_id)
743
+ return shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{user_id}")
744
+ await query.message.edit(Messages.SPLITTING.format(newfname))
745
+ splitteddir = f"{Config.DOWNLOAD_LOCATION}/splitted/{user_id}"
746
+ os.makedirs(splitteddir)
747
+ ooutput = f"{splitteddir}/{newfname}"
748
+ if await (user_id):
749
+ splittedfiles = await split_files(renamed, ooutput, Config.TG_PREMIUM_SIZE)
750
+ else:
751
+ splittedfiles = await split_files(renamed, ooutput, Config.TG_MAX_SIZE)
752
+ if not splittedfiles:
753
+ try:
754
+ shutil.rmtree(splitteddir)
755
+ except:
756
+ pass
757
+ await del_ongoing_task(user_id)
758
+ await query.message.edit(Messages.ERR_SPLIT)
759
+ return
760
+ await query.message.edit(Messages.SEND_ALL_PARTS.format(newfname))
761
+ async_splittedfiles = async_generator(splittedfiles)
762
+ async for file in async_splittedfiles:
763
+ sent_files += 1
764
+ await send_file(
765
+ unzip_bot=unzip_bot,
766
+ c_id=user_id,
767
+ doc_f=file,
768
+ query=query,
769
+ full_path=splitteddir,
770
+ log_msg=log_msg,
771
+ split=True,
772
+ )
773
+ try:
774
+ shutil.rmtree(splitteddir)
775
+ shutil.rmtree(renamed.replace(newfname, ""))
776
+ except:
777
+ pass
778
+ await del_ongoing_task(user_id)
779
+ try:
780
+ await query.message.edit(
781
+ text=Messages.UPLOADED,
782
+ reply_markup=Buttons.RATE_ME
783
+ )
784
+ except:
785
+ await unzip_bot.send_message(
786
+ chat_id=user_id,
787
+ text=Messages.UPLOADED,
788
+ reply_markup=Buttons.RATE_ME
789
+ )
790
+ return
791
+
792
+ dltime = TimeFormatter(round(e_time - s_time) * 1000)
793
+ if dltime == "":
794
+ dltime = "1s"
795
+ await answer_query(
796
+ query,
797
+ Messages.AFTER_OK_DL_TXT.format(dltime),
798
+ unzip_client=unzip_bot
799
+ )
800
+
801
+ # Attempt to fetch password protected archives
802
+ if splitted_data[2] == "with_pass":
803
+ password = await unzip_bot.ask(
804
+ chat_id=query.message.chat.id,
805
+ text=Messages.PLS_SEND_PASSWORD
806
+ )
807
+ ext_s_time = time()
808
+ extractor = await extr_files(
809
+ path=ext_files_dir,
810
+ archive_path=archive,
811
+ password=password.text,
812
+ )
813
+ ext_e_time = time()
814
+ await archive_msg.reply(Messages.PASS_TXT.format(password.text))
815
+ else:
816
+ ext_s_time = time()
817
+ tested = await _test_with_7z_helper(archive)
818
+ ext_t_time = time()
819
+ testtime = TimeFormatter(round(ext_t_time - ext_s_time) * 1000)
820
+ if testtime == "":
821
+ testtime = "1s"
822
+ await answer_query(
823
+ query,
824
+ Messages.AFTER_OK_TEST_TXT.format(testtime),
825
+ unzip_client=unzip_bot
826
+ )
827
+ if tested:
828
+ extractor = await extr_files(
829
+ path=ext_files_dir,
830
+ archive_path=archive
831
+ )
832
+ ext_e_time = time()
833
+ else:
834
+ extractor = "Error"
835
+ ext_e_time = time()
836
+ # Checks if there is an error happened while extracting the archive
837
+ if any(err in extractor for err in ERROR_MSGS):
838
+ try:
839
+ await query.message.edit(Messages.EXT_FAILED_TXT)
840
+ shutil.rmtree(ext_files_dir)
841
+ await del_ongoing_task(user_id)
842
+ await log_msg.reply(Messages.EXT_FAILED_TXT)
843
+ return
844
+ except:
845
+ try:
846
+ await query.message.delete()
847
+ except:
848
+ pass
849
+ await unzip_bot.send_message(
850
+ chat_id=query.message.chat.id,
851
+ text=Messages.EXT_FAILED_TXT
852
+ )
853
+ shutil.rmtree(ext_files_dir)
854
+ await del_ongoing_task(user_id)
855
+ await archive_msg.reply(Messages.EXT_FAILED_TXT)
856
+ return
857
+ # Check if user was dumb 😐
858
+ paths = await get_files(path=ext_files_dir)
859
+ if not paths:
860
+ await archive_msg.reply(Messages.PASSWORD_PROTECTED)
861
+ await unzip_bot.send_message(
862
+ chat_id=query.message.chat.id,
863
+ text=Messages.PASSWORD_PROTECTED,
864
+ )
865
+ await answer_query(
866
+ query,
867
+ Messages.EXT_FAILED_TXT,
868
+ unzip_client=unzip_bot
869
+ )
870
+ shutil.rmtree(ext_files_dir)
871
+ await del_ongoing_task(user_id)
872
+ return
873
+
874
+ # Upload extracted files
875
+ extrtime = TimeFormatter(round(ext_e_time - ext_s_time) * 1000)
876
+ if extrtime == "":
877
+ extrtime = "1s"
878
+ await answer_query(
879
+ query,
880
+ Messages.EXT_OK_TXT.format(extrtime),
881
+ unzip_client=unzip_bot
882
+ )
883
+
884
+ try:
885
+ i_e_buttons = await make_keyboard(
886
+ paths=paths,
887
+ user_id=user_id,
888
+ chat_id=query.message.chat.id,
889
+ unziphttp=False
890
+ )
891
+ try:
892
+ await query.message.edit(Messages.SELECT_FILES, reply_markup=i_e_buttons)
893
+ except ReplyMarkupTooLong:
894
+ empty_buttons = await make_keyboard_empty(
895
+ user_id=user_id,
896
+ chat_id=query.message.chat.id,
897
+ unziphttp=False
898
+ )
899
+ await query.message.edit(
900
+ Messages.UNABLE_GATHER_FILES,
901
+ reply_markup=empty_buttons,
902
+ )
903
+ except:
904
+ try:
905
+ await query.message.delete()
906
+ i_e_buttons = await make_keyboard(
907
+ paths=paths,
908
+ user_id=user_id,
909
+ chat_id=query.message.chat.id,
910
+ unziphttp=False
911
+ )
912
+ await unzip_bot.send_message(
913
+ chat_id=query.message.chat.id,
914
+ text=Messages.SELECT_FILES,
915
+ reply_markup=i_e_buttons,
916
+ )
917
+ except:
918
+ try:
919
+ await query.message.delete()
920
+ empty_buttons = await make_keyboard_empty(
921
+ user_id=user_id,
922
+ chat_id=query.message.chat.id,
923
+ unziphttp=False
924
+ )
925
+ await unzip_bot.send_message(
926
+ chat_id=query.message.chat.id,
927
+ text=Messages.UNABLE_GATHER_FILES,
928
+ reply_markup=empty_buttons,
929
+ )
930
+ except:
931
+ await answer_query(
932
+ query,
933
+ Messages.EXT_FAILED_TXT,
934
+ unzip_client=unzip_bot
935
+ )
936
+ await archive_msg.reply(Messages.EXT_FAILED_TXT)
937
+ shutil.rmtree(ext_files_dir)
938
+ LOGGER.error(Messages.FATAL_ERROR)
939
+ await del_ongoing_task(user_id)
940
+ return
941
+
942
+ except Exception as e:
943
+ await del_ongoing_task(user_id)
944
+ try:
945
+ try:
946
+ await query.message.edit(Messages.ERROR_TXT.format(e))
947
+ except:
948
+ await unzip_bot.send_message(
949
+ chat_id=query.message.chat.id,
950
+ text=Messages.ERROR_TXT.format(e))
951
+ await archive_msg.reply(Messages.ERROR_TXT.format(e))
952
+ shutil.rmtree(ext_files_dir)
953
+ try:
954
+ await ClientSession().close()
955
+ except:
956
+ pass
957
+ LOGGER.error(e)
958
+ except Exception as err:
959
+ LOGGER.error(err)
960
+ await archive_msg.reply(err)
961
+
962
+ elif query.data.startswith("ext_f"):
963
+ LOGGER.info(query.data)
964
+ user_id = query.from_user.id
965
+ spl_data = query.data.split("|")
966
+ file_path = f"{Config.DOWNLOAD_LOCATION}/{spl_data[1]}/extracted"
967
+ try:
968
+ urled = spl_data[4] if isinstance(spl_data[4], bool) else False
969
+ except:
970
+ urled = False
971
+ if urled:
972
+ paths = spl_data[5].namelist()
973
+ else:
974
+ paths = await get_files(path=file_path)
975
+ if not paths and not urled:
976
+ if os.path.isdir(f"{Config.DOWNLOAD_LOCATION}/{spl_data[1]}"):
977
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{spl_data[1]}")
978
+ await del_ongoing_task(user_id)
979
+ await query.message.edit(
980
+ text=Messages.NO_FILE_LEFT,
981
+ reply_markup=Buttons.RATE_ME
982
+ )
983
+ return
984
+ LOGGER.info("ext_f paths : " + str(paths))
985
+ try:
986
+ await query.answer(Messages.SENDING_FILE)
987
+ except:
988
+ pass
989
+ sent_files += 1
990
+ if urled:
991
+ file = spl_data[5].open(paths[int(spl_data[3])])
992
+ else:
993
+ file = paths[int(spl_data[3])]
994
+ fsize = await get_size(file)
995
+ split = False
996
+ if fsize <= Config.TG_MAX_SIZE:
997
+ await send_file(
998
+ unzip_bot=unzip_bot,
999
+ c_id=spl_data[2],
1000
+ doc_f=file,
1001
+ query=query,
1002
+ full_path=f"{Config.DOWNLOAD_LOCATION}/{spl_data[1]}",
1003
+ log_msg=log_msg,
1004
+ split=False,
1005
+ )
1006
+ else:
1007
+ split = True
1008
+ if split:
1009
+ fname = file.split('/')[-1]
1010
+ smessage = await unzip_bot.send_message(
1011
+ chat_id=user_id,
1012
+ text=Messages.SPLITTING.format(fname)
1013
+ )
1014
+ splitteddir = f"{Config.DOWNLOAD_LOCATION}/splitted/{user_id}"
1015
+ os.makedirs(splitteddir)
1016
+ ooutput = f"{splitteddir}/{fname}"
1017
+ splittedfiles = await split_files(file, ooutput, Config.TG_MAX_SIZE)
1018
+ LOGGER.info(splittedfiles)
1019
+ if not splittedfiles:
1020
+ try:
1021
+ shutil.rmtree(splitteddir)
1022
+ except:
1023
+ pass
1024
+ await del_ongoing_task(user_id)
1025
+ await smessage.edit(Messages.ERR_SPLIT)
1026
+ return
1027
+ await smessage.edit(Messages.SEND_ALL_PARTS.format(fname))
1028
+ async_splittedfiles = async_generator(splittedfiles)
1029
+ async for file in async_splittedfiles:
1030
+ sent_files += 1
1031
+ await send_file(
1032
+ unzip_bot=unzip_bot,
1033
+ c_id=user_id,
1034
+ doc_f=file,
1035
+ query=query,
1036
+ full_path=splitteddir,
1037
+ log_msg=log_msg,
1038
+ split=True,
1039
+ )
1040
+ try:
1041
+ shutil.rmtree(splitteddir)
1042
+ os.remove(file)
1043
+ except:
1044
+ pass
1045
+ try:
1046
+ await smessage.delete()
1047
+ except:
1048
+ pass
1049
+
1050
+ await query.message.edit(Messages.REFRESHING)
1051
+ if urled:
1052
+ rpaths = paths.remove(paths[int(spl_data[3])])
1053
+ else:
1054
+ rpaths = await get_files(path=file_path)
1055
+ if not rpaths:
1056
+ try:
1057
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{spl_data[1]}")
1058
+ except:
1059
+ pass
1060
+ await del_ongoing_task(user_id)
1061
+ await query.message.edit(
1062
+ text=Messages.NO_FILE_LEFT,
1063
+ reply_markup=Buttons.RATE_ME
1064
+ )
1065
+ return
1066
+ if urled:
1067
+ try:
1068
+ i_e_buttons = await make_keyboard(
1069
+ paths=rpaths,
1070
+ user_id=query.from_user.id,
1071
+ chat_id=query.message.chat.id,
1072
+ unziphttp=True,
1073
+ rzfile=spl_data[5],
1074
+ )
1075
+ await query.message.edit(Messages.SELECT_FILES, reply_markup=i_e_buttons)
1076
+ except ReplyMarkupTooLong:
1077
+ empty_buttons = await make_keyboard_empty(
1078
+ user_id=user_id,
1079
+ chat_id=query.message.chat.id,
1080
+ unziphttp=True,
1081
+ rzfile=spl_data[5]
1082
+ )
1083
+ await query.message.edit(
1084
+ Messages.UNABLE_GATHER_FILES,
1085
+ reply_markup=empty_buttons,
1086
+ )
1087
+ else:
1088
+ try:
1089
+ i_e_buttons = await make_keyboard(
1090
+ paths=rpaths,
1091
+ user_id=query.from_user.id,
1092
+ chat_id=query.message.chat.id,
1093
+ unziphttp=False
1094
+ )
1095
+ await query.message.edit(Messages.SELECT_FILES, reply_markup=i_e_buttons)
1096
+ except ReplyMarkupTooLong:
1097
+ empty_buttons = await make_keyboard_empty(
1098
+ user_id=user_id,
1099
+ chat_id=query.message.chat.id,
1100
+ unziphttp=False
1101
+ )
1102
+ await query.message.edit(
1103
+ Messages.UNABLE_GATHER_FILES,
1104
+ reply_markup=empty_buttons,
1105
+ )
1106
+ await update_uploaded(user_id, upload_count=sent_files)
1107
+
1108
+ elif query.data.startswith("ext_a"):
1109
+ LOGGER.info(query.data)
1110
+ user_id = query.from_user.id
1111
+ spl_data = query.data.split("|")
1112
+ file_path = f"{Config.DOWNLOAD_LOCATION}/{spl_data[1]}/extracted"
1113
+ try:
1114
+ urled = spl_data[4] if isinstance(spl_data[3], bool) else False
1115
+ except:
1116
+ urled = False
1117
+ if urled:
1118
+ paths = spl_data[4].namelist()
1119
+ else:
1120
+ paths = await get_files(path=file_path)
1121
+ LOGGER.info("ext_a paths : " + str(paths))
1122
+ if not paths and not urled:
1123
+ try:
1124
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{spl_data[1]}")
1125
+ except:
1126
+ pass
1127
+ await del_ongoing_task(user_id)
1128
+ await query.message.edit(
1129
+ text=Messages.NO_FILE_LEFT,
1130
+ reply_markup=Buttons.RATE_ME
1131
+ )
1132
+ return
1133
+ await query.message.edit(Messages.SEND_ALL_FILES)
1134
+ async_paths = async_generator(paths)
1135
+ async for file in async_paths:
1136
+ sent_files += 1
1137
+ if urled:
1138
+ file = spl_data[4].open(file)
1139
+ fsize = Config.TG_MAX_SIZE + 1
1140
+ # secutity as we can't retrieve the file size from URL
1141
+ else:
1142
+ fsize = await get_size(file)
1143
+ split = False
1144
+ if fsize <= Config.TG_MAX_SIZE:
1145
+ await send_file(
1146
+ unzip_bot=unzip_bot,
1147
+ c_id=spl_data[2],
1148
+ doc_f=file,
1149
+ query=query,
1150
+ full_path=f"{Config.DOWNLOAD_LOCATION}/{spl_data[1]}",
1151
+ log_msg=log_msg,
1152
+ split=False,
1153
+ )
1154
+ else:
1155
+ split = True
1156
+ if split:
1157
+ fname = file.split('/')[-1]
1158
+ smessage = await unzip_bot.send_message(
1159
+ chat_id=user_id,
1160
+ text=Messages.SPLITTING.format(fname)
1161
+ )
1162
+ splitteddir = f"{Config.DOWNLOAD_LOCATION}/splitted/{user_id}"
1163
+ os.makedirs(splitteddir)
1164
+ ooutput = f"{splitteddir}/{fname}"
1165
+ splittedfiles = await split_files(file, ooutput, Config.TG_MAX_SIZE)
1166
+ LOGGER.info(splittedfiles)
1167
+ if not splittedfiles:
1168
+ try:
1169
+ shutil.rmtree(splitteddir)
1170
+ except:
1171
+ pass
1172
+ await del_ongoing_task(user_id)
1173
+ await smessage.edit(Messages.ERR_SPLIT)
1174
+ return
1175
+ await smessage.edit(Messages.SEND_ALL_PARTS.format(fname))
1176
+ async_splittedfiles = async_generator(splittedfiles)
1177
+ async for file in async_splittedfiles:
1178
+ sent_files += 1
1179
+ await send_file(
1180
+ unzip_bot=unzip_bot,
1181
+ c_id=user_id,
1182
+ doc_f=file,
1183
+ query=query,
1184
+ full_path=splitteddir,
1185
+ log_msg=log_msg,
1186
+ split=True,
1187
+ )
1188
+ try:
1189
+ shutil.rmtree(splitteddir)
1190
+ except:
1191
+ pass
1192
+ try:
1193
+ await smessage.delete()
1194
+ except:
1195
+ pass
1196
+
1197
+ await query.message.edit(
1198
+ text=Messages.UPLOADED,
1199
+ reply_markup=Buttons.RATE_ME
1200
+ )
1201
+ await log_msg.reply(Messages.HOW_MANY_UPLOADED.format(sent_files))
1202
+ await update_uploaded(user_id, upload_count=sent_files)
1203
+ await del_ongoing_task(user_id)
1204
+ try:
1205
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{spl_data[1]}")
1206
+ except Exception as e:
1207
+ await query.message.edit(Messages.ERROR_TXT.format(e))
1208
+ await archive_msg.reply(Messages.ERROR_TXT.format(e))
1209
+
1210
+ elif query.data == "cancel_dis":
1211
+ uid = query.from_user.id
1212
+ await del_ongoing_task(uid)
1213
+ await del_merge_task(uid)
1214
+ try:
1215
+ await query.message.edit(Messages.CANCELLED_TXT.format(Messages.PROCESS_CANCELLED))
1216
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{uid}")
1217
+ await update_uploaded(
1218
+ user_id=uid,
1219
+ upload_count=sent_files
1220
+ )
1221
+ try:
1222
+ await log_msg.reply(Messages.HOW_MANY_UPLOADED.format(sent_files))
1223
+ except:
1224
+ return
1225
+ except:
1226
+ await unzip_bot.send_message(
1227
+ chat_id=uid,
1228
+ text=Messages.CANCELLED_TXT.format(Messages.PROCESS_CANCELLED)
1229
+ )
1230
+ return
1231
+
1232
+ elif query.data == "nobully":
1233
+ await query.message.edit(Messages.CANCELLED)
unzipper/modules/commands.py ADDED
@@ -0,0 +1,819 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+ import os
3
+ import re
4
+ import shutil
5
+ import time
6
+ from asyncio import sleep
7
+ from sys import executable
8
+
9
+ import git
10
+ import psutil
11
+ from pyrogram import enums, filters
12
+ from pyrogram.errors import FloodWait, RPCError
13
+ from pyrogram.types import Message
14
+
15
+ from config import Config
16
+ from unzipper import LOGGER, boottime, unzipperbot
17
+ from unzipper.helpers.database import (
18
+ add_merge_task,
19
+ add_user,
20
+ add_banned_user,
21
+ add_vip_user,
22
+ check_user,
23
+ count_banned_users,
24
+ count_users,
25
+ del_banned_user,
26
+ del_ongoing_task,
27
+ del_user,
28
+ get_maintenance,
29
+ get_merge_task,
30
+ get_ongoing_tasks,
31
+ get_upload_mode,
32
+ get_uploaded,
33
+ get_users_list,
34
+ count_ongoing_tasks,
35
+ set_maintenance,
36
+ )
37
+ from unzipper.helpers.unzip_help import humanbytes, timeformat_sec
38
+ from unzipper.modules.ext_script.custom_thumbnail import add_thumb, del_thumb
39
+ from unzipper.modules.ext_script.ext_helper import get_files
40
+
41
+ from .bot_data import Buttons, Messages
42
+
43
+ # Regex for urls
44
+ https_url_regex = r"((http|https)\:\/\/)?[a-zA-Z0-9\.\/\?\:@\-_=#]+\.([a-zA-Z]){2,6}([a-zA-Z0-9\.\&\/\?\:@\-_=#])*"
45
+
46
+
47
+ @unzipperbot.on_message(filters.private)
48
+ async def _(_, message: Message):
49
+ await check_user(message)
50
+ uid = message.from_user.id
51
+ if uid != Config.BOT_OWNER and await get_maintenance():
52
+ await message.reply(Messages.MAINTENANCE_ON)
53
+ return
54
+ if uid == Config.BOT_OWNER:
55
+ return
56
+ if await count_ongoing_tasks() >= Config.MAX_CONCURRENT_TASKS:
57
+ ogtasks = await get_ongoing_tasks()
58
+ if not any(uid == task["user_id"] for task in ogtasks):
59
+ try:
60
+ await message.reply(
61
+ text=Messages.MAX_TASKS.format(Config.MAX_CONCURRENT_TASKS),
62
+ )
63
+ except:
64
+ await unzipperbot.send_message(
65
+ chat_id=uid,
66
+ text=Messages.MAX_TASKS.format(Config.MAX_CONCURRENT_TASKS),
67
+ )
68
+ return
69
+
70
+
71
+ @unzipperbot.on_message(filters.command("start"))
72
+ async def start_bot(_, message: Message):
73
+ try:
74
+ await message.reply_text(
75
+ text=Messages.START_TEXT.format(message.from_user.mention),
76
+ reply_markup=Buttons.START_BUTTON,
77
+ disable_web_page_preview=True,
78
+ )
79
+ except FloodWait as f:
80
+ await sleep(f.value)
81
+ await start_bot(_, message)
82
+
83
+
84
+ @unzipperbot.on_message(filters.private & filters.command("clean"))
85
+ async def clean_my_files(_, message: Message):
86
+ try:
87
+ await message.reply_text(text=Messages.CLEAN_TXT, reply_markup=Buttons.CLN_BTNS)
88
+ except FloodWait as f:
89
+ await sleep(f.value)
90
+ await clean_my_files(_, message)
91
+
92
+
93
+ @unzipperbot.on_message(filters.command("help"))
94
+ async def help_me(_, message: Message):
95
+ try:
96
+ await message.reply_text(text=Messages.HELP_TXT, reply_markup=Buttons.ME_GOIN_HOME)
97
+ except FloodWait as f:
98
+ await sleep(f.value)
99
+ await help_me(_, message)
100
+
101
+
102
+ @unzipperbot.on_message(filters.command("about"))
103
+ async def about_me(_, message: Message):
104
+ try:
105
+ await message.reply_text(
106
+ text=Messages.ABOUT_TXT,
107
+ reply_markup=Buttons.ME_GOIN_HOME,
108
+ disable_web_page_preview=True,
109
+ )
110
+ except FloodWait as f:
111
+ await sleep(f.value)
112
+ await about_me(_, message)
113
+
114
+
115
+ @unzipperbot.on_message(
116
+ filters.incoming & filters.private & filters.document
117
+ | filters.regex(https_url_regex)
118
+ )
119
+ async def extract_archive(_, message: Message):
120
+ try:
121
+ if message.chat.type != enums.ChatType.PRIVATE:
122
+ return
123
+ unzip_msg = await message.reply(Messages.PROCESSING2, reply_to_message_id=message.id)
124
+ user_id = message.from_user.id
125
+ download_path = f"{Config.DOWNLOAD_LOCATION}/{user_id}"
126
+ if os.path.isdir(download_path):
127
+ await unzip_msg.edit(Messages.PROCESS_RUNNING)
128
+ return
129
+ if await get_merge_task(user_id):
130
+ await unzip_msg.delete()
131
+ return
132
+ if message.text and (re.match(https_url_regex, message.text)):
133
+ await unzip_msg.edit(
134
+ text=Messages.CHOOSE_EXT_MODE.format("URL", "🔗"),
135
+ reply_markup=Buttons.CHOOSE_E_U__BTNS,
136
+ )
137
+ elif message.document:
138
+ await unzip_msg.edit(
139
+ text=Messages.CHOOSE_EXT_MODE.format("file", "🗂️"),
140
+ reply_markup=Buttons.CHOOSE_E_F__BTNS,
141
+ )
142
+ else:
143
+ await unzip_msg.edit(Messages.UNVALID)
144
+ except FloodWait as f:
145
+ await sleep(f.value)
146
+ await extract_archive(_, message)
147
+
148
+
149
+ @unzipperbot.on_message(filters.private & filters.command("cancel"))
150
+ async def cancel_task_by_user(_, message):
151
+ idtodel = message.id - 1
152
+ try:
153
+ await unzipperbot.delete_messages(chat_id=message.from_user.id, message_ids=idtodel)
154
+ except:
155
+ pass
156
+ await message.reply(Messages.CANCELLED)
157
+
158
+
159
+ @unzipperbot.on_message(filters.private & filters.command("merge"))
160
+ async def merging(_, message: Message):
161
+ try:
162
+ merge_msg = await message.reply(Messages.MERGE)
163
+ await add_merge_task(message.from_user.id, merge_msg.id)
164
+ except FloodWait as f:
165
+ await sleep(f.value)
166
+ await merging(_, message)
167
+
168
+
169
+ @unzipperbot.on_message(filters.private & filters.command("done"))
170
+ async def done_merge(_, message: Message):
171
+ try:
172
+ await message.reply(
173
+ Messages.DONE,
174
+ reply_markup=Buttons.MERGE_THEM_ALL
175
+ )
176
+ except FloodWait as f:
177
+ await sleep(f.value)
178
+ await done_merge(_, message)
179
+
180
+
181
+ @unzipperbot.on_message(filters.private & filters.command("mode"))
182
+ async def set_mode_for_user(_, message: Message):
183
+ try:
184
+ upload_mode = await get_upload_mode(message.from_user.id)
185
+ await message.reply(
186
+ text=Messages.SELECT_UPLOAD_MODE_TXT.format(upload_mode),
187
+ reply_markup=Buttons.SET_UPLOAD_MODE_BUTTONS,
188
+ )
189
+ except FloodWait as f:
190
+ await sleep(f.value)
191
+ await set_mode_for_user(_, message)
192
+
193
+
194
+ async def get_stats(id):
195
+ total, used, free = shutil.disk_usage(".")
196
+ total = humanbytes(total)
197
+ used = humanbytes(used)
198
+ free = humanbytes(free)
199
+ sent = humanbytes(psutil.net_io_counters().bytes_sent)
200
+ recv = humanbytes(psutil.net_io_counters().bytes_recv)
201
+ cpu_usage = psutil.cpu_percent(interval=0.2)
202
+ ram_usage = psutil.virtual_memory().percent
203
+ disk_usage = psutil.disk_usage("/").percent
204
+ uptime = timeformat_sec(time.time() - boottime)
205
+ total_users = await count_users()
206
+ total_banned_users = await count_banned_users()
207
+ ongoing_tasks = await count_ongoing_tasks()
208
+
209
+ if id == Config.BOT_OWNER:
210
+ stats_string = Messages.STATS_OWNER.format(
211
+ total_users,
212
+ total_banned_users,
213
+ total,
214
+ used,
215
+ disk_usage,
216
+ free,
217
+ ongoing_tasks,
218
+ sent,
219
+ recv,
220
+ cpu_usage,
221
+ ram_usage,
222
+ uptime,
223
+ )
224
+ else:
225
+ stats_string = Messages.STATS.format(
226
+ total,
227
+ used,
228
+ disk_usage,
229
+ free,
230
+ ongoing_tasks,
231
+ cpu_usage,
232
+ ram_usage,
233
+ uptime,
234
+ )
235
+
236
+ return stats_string
237
+
238
+
239
+ @unzipperbot.on_message(filters.command("stats"))
240
+ async def send_stats(_, message: Message):
241
+ try:
242
+ stats_msg = await message.reply(Messages.PROCESSING2)
243
+ stats_txt = await get_stats(message.from_user.id)
244
+ await stats_msg.edit(text=stats_txt, reply_markup=Buttons.REFRESH_BUTTON)
245
+ except FloodWait as f:
246
+ await sleep(f.value)
247
+ await send_stats(_, message)
248
+
249
+
250
+ async def _do_broadcast(message, user):
251
+ try:
252
+ await message.copy(chat_id=int(user))
253
+ return 200
254
+ except FloodWait as f:
255
+ await sleep(f.value)
256
+ return _do_broadcast(message, user)
257
+ except Exception:
258
+ await del_user(user)
259
+ return 400
260
+
261
+
262
+ @unzipperbot.on_message(filters.command("broadcast") & filters.user(Config.BOT_OWNER))
263
+ async def broadcast_this(_, message: Message):
264
+ bc_msg = await message.reply(Messages.PROCESSING2)
265
+ r_msg = message.reply_to_message
266
+ if not r_msg:
267
+ await bc_msg.edit(Messages.BC_REPLY)
268
+ return
269
+ users_list = await get_users_list()
270
+ success_no = 0
271
+ failed_no = 0
272
+ done_no = 0
273
+ total_users = await count_users()
274
+ await bc_msg.edit(Messages.BC_START.format(done_no, total_users))
275
+ for user in users_list:
276
+ b_cast = await _do_broadcast(message=r_msg, user=user["user_id"])
277
+ if b_cast == 200:
278
+ success_no += 1
279
+ else:
280
+ failed_no += 1
281
+ done_no += 1
282
+ if done_no % 10 == 0 or done_no == total_users:
283
+ try:
284
+ await bc_msg.edit(Messages.BC_START.format(done_no, total_users))
285
+ except FloodWait:
286
+ pass
287
+ try:
288
+ await bc_msg.edit(Messages.BC_DONE.format(
289
+ total_users,
290
+ success_no,
291
+ failed_no,
292
+ ))
293
+ except FloodWait as f:
294
+ await sleep(f.value)
295
+ await bc_msg.edit(Messages.BC_DONE.format(
296
+ total_users,
297
+ success_no,
298
+ failed_no,
299
+ ))
300
+
301
+
302
+ @unzipperbot.on_message(filters.command("sendto") & filters.user(Config.BOT_OWNER))
303
+ async def send_this(_, message: Message):
304
+ sd_msg = await message.reply(Messages.PROCESSING2)
305
+ r_msg = message.reply_to_message
306
+ if not r_msg:
307
+ await sd_msg.edit(Messages.SEND_REPLY)
308
+ return
309
+ try:
310
+ user_id = message.text.split(None, 1)[1]
311
+ except:
312
+ await sd_msg.edit(Messages.PROVIDE_UID)
313
+ return
314
+ await sd_msg.edit(Messages.SENDING)
315
+ send = await _do_broadcast(message=r_msg, user=user_id)
316
+ if send == 200:
317
+ await sd_msg.edit(Messages.SEND_SUCCESS.format(user_id))
318
+ else:
319
+ await sd_msg.edit(Messages.SEND_FAILED.format(user_id))
320
+
321
+
322
+ @unzipperbot.on_message(filters.command("report"))
323
+ async def report_this(_, message: Message):
324
+ sd_msg = await message.reply(Messages.PROCESSING2)
325
+ r_msg = message.reply_to_message
326
+ u_id = message.from_user.id
327
+ if not r_msg:
328
+ await sd_msg.edit(Messages.REPORT_REPLY)
329
+ return
330
+ await sd_msg.edit(Messages.SENDING)
331
+ await unzipperbot.send_message(
332
+ chat_id=Config.LOGS_CHANNEL,
333
+ text=Messages.REPORT_TEXT.format(u_id, r_msg.text.markdown),
334
+ )
335
+ await sd_msg.edit(Messages.REPORT_DONE)
336
+
337
+
338
+ @unzipperbot.on_message(filters.command("ban") & filters.user(Config.BOT_OWNER))
339
+ async def ban_user(_, message: Message):
340
+ ban_msg = await message.reply(Messages.PROCESSING2)
341
+ try:
342
+ user_id = message.text.split(None, 1)[1]
343
+ except:
344
+ await ban_msg.edit(Messages.BAN_ID)
345
+ return
346
+ bdb = await add_banned_user(user_id)
347
+ db = await del_user(user_id)
348
+ text = ""
349
+ if bdb == -1:
350
+ text += Messages.ALREADY_BANNED.format(user_id)
351
+ if db == -1:
352
+ text += Messages.ALREADY_REMOVED.format(user_id)
353
+ if text != "":
354
+ await ban_msg.edit(text)
355
+ else:
356
+ await ban_msg.edit(Messages.BANNED.format(user_id))
357
+
358
+
359
+ @unzipperbot.on_message(filters.command("unban") & filters.user(Config.BOT_OWNER))
360
+ async def unban_user(_, message: Message):
361
+ unban_msg = await message.reply(Messages.PROCESSING2)
362
+ try:
363
+ user_id = message.text.split(None, 1)[1]
364
+ except:
365
+ await unban_msg.edit(Messages.UNBAN_ID)
366
+ return
367
+ db = await add_user(user_id)
368
+ bdb = await del_banned_user(user_id)
369
+ text = ""
370
+ if db == -1:
371
+ text += Messages.ALREADY_ADDED.format(user_id)
372
+ if bdb == -1:
373
+ text += Messages.ALREADY_UNBANNED.format(user_id)
374
+ if text != "":
375
+ await unban_msg.edit(text)
376
+ else:
377
+ await unban_msg.edit(Messages.UNBANNED.format(user_id))
378
+
379
+
380
+ @unzipperbot.on_message(filters.private & filters.command("info"))
381
+ async def me_stats(_, message: Message):
382
+ me_info = await unzipperbot.ask(
383
+ chat_id=message.chat.id,
384
+ text=Messages.INFO,
385
+ )
386
+ await unzipperbot.send_message(chat_id=message.chat.id, text=f"`{me_info}`")
387
+
388
+
389
+ @unzipperbot.on_message(filters.command("user") & filters.user(Config.BOT_OWNER))
390
+ async def info_user(_, message: Message):
391
+ await message.reply(Messages.USER)
392
+ info_user_msg = await message.reply(Messages.PROCESSING2)
393
+ try:
394
+ user_id = message.text.split(None, 1)[1]
395
+ except:
396
+ await info_user_msg.edit(Messages.PROVIDE_UID)
397
+ return
398
+ up_count = get_uploaded(user_id)
399
+ if up_count == "":
400
+ up_count = Messages.UNABLE_FETCH
401
+ await info_user_msg.edit(Messages.USER_INFO.format(user_id, up_count))
402
+
403
+
404
+ @unzipperbot.on_message(filters.command("user2") & filters.user(Config.BOT_OWNER))
405
+ async def info_user2(_, message: Message):
406
+ user2_msg = await message.reply(Messages.PROCESSING2)
407
+ try:
408
+ user_id = message.text.split(None, 1)[1]
409
+ except:
410
+ await user2_msg.edit(Messages.PROVIDE_UID2)
411
+ return
412
+ try:
413
+ infos = await unzipperbot.get_users(user_id)
414
+ except:
415
+ await user2_msg.edit(Messages.UID_UNAME_INVALID)
416
+ return
417
+ if not isinstance(user_id, int):
418
+ try:
419
+ user_id = infos.id
420
+ except:
421
+ pass
422
+ await user2_msg.edit(Messages.USER2_INFO.format(infos, user_id))
423
+
424
+
425
+ @unzipperbot.on_message(filters.command("self") & filters.user(Config.BOT_OWNER))
426
+ async def info_self(_, message: Message):
427
+ self_infos = await unzipperbot.get_me()
428
+ await message.reply(f"`{self_infos}`")
429
+
430
+
431
+ @unzipperbot.on_message(
432
+ filters.private & filters.command("getthumbs") & filters.user(Config.BOT_OWNER)
433
+ )
434
+ async def get_all_thumbs(_, message: Message):
435
+ paths = await get_files(path=Config.THUMB_LOCATION)
436
+ if not paths:
437
+ await message.reply(Messages.NO_THUMBS)
438
+ for doc_f in paths:
439
+ try:
440
+ await unzipperbot.send_document(
441
+ chat_id=message.chat.id,
442
+ document=doc_f,
443
+ file_name=doc_f.split("/")[-1],
444
+ reply_to_message_id=message.id,
445
+ caption=Messages.EXT_CAPTION.format(doc_f),
446
+ )
447
+ except FloodWait as f:
448
+ await sleep(f.value)
449
+ await unzipperbot.send_document(
450
+ chat_id=message.chat.id,
451
+ document=doc_f,
452
+ file_name=doc_f.split("/")[-1],
453
+ reply_to_message_id=message.id,
454
+ caption=Messages.EXT_CAPTION.format(doc_f),
455
+ )
456
+ except RPCError as e:
457
+ LOGGER.error(e)
458
+
459
+
460
+ @unzipperbot.on_message(
461
+ filters.private & filters.command("redbutton") & filters.user(Config.BOT_OWNER)
462
+ )
463
+ async def red_alert(_, message: Message):
464
+ await message.reply("🚧 WIP 🚧")
465
+ # restart the whole bot, maybe using execl
466
+ # but also need to stop currently ongoing processes…
467
+
468
+
469
+ @unzipperbot.on_message(filters.private & filters.command("maintenance") & filters.user(Config.BOT_OWNER))
470
+ async def maintenance_mode(_, message: Message):
471
+ mstatus = await get_maintenance()
472
+ text = Messages.MAINTENANCE.format(mstatus) + "\n\n" + Messages.MAINTENANCE_ASK
473
+ mess = await message.reply(text)
474
+ try:
475
+ newstate = message.text.split(None, 1)[1]
476
+ except:
477
+ await mess.edit(Messages.MAINTENANCE_FAIL)
478
+ return
479
+ if newstate not in ["True", "False"]:
480
+ await mess.edit(Messages.MAINTENANCE_FAIL)
481
+ return
482
+ await set_maintenance(newstate == "True")
483
+ await message.reply(Messages.MAINTENANCE_DONE.format(newstate))
484
+
485
+
486
+ @unzipperbot.on_message(filters.private & filters.command("addthumb"))
487
+ async def thumb_add(_, message: Message):
488
+ await add_thumb(unzipperbot, message)
489
+
490
+
491
+ @unzipperbot.on_message(filters.private & filters.command("delthumb"))
492
+ async def thumb_del(_, message: Message):
493
+ await del_thumb(message)
494
+
495
+
496
+ @unzipperbot.on_message(
497
+ filters.private & filters.command("cleanall") & filters.user(Config.BOT_OWNER)
498
+ )
499
+ async def del_everything(_, message: Message):
500
+ cleaner = await message.reply(Messages.ERASE_ALL)
501
+ try:
502
+ shutil.rmtree(Config.DOWNLOAD_LOCATION)
503
+ await cleaner.edit(Messages.CLEANED)
504
+ os.mkdir(Config.DOWNLOAD_LOCATION)
505
+ except:
506
+ await cleaner.edit(Messages.NOT_CLEANED)
507
+
508
+
509
+ @unzipperbot.on_message(
510
+ filters.private & filters.command("cleantasks") & filters.user(Config.BOT_OWNER)
511
+ )
512
+ async def del_tasks(_, message: Message):
513
+ ongoing_tasks = await get_ongoing_tasks()
514
+ number = len(ongoing_tasks)
515
+ cleaner = await message.reply(Messages.ERASE_TASKS.format(number))
516
+
517
+ for task in ongoing_tasks:
518
+ user_id = task["user_id"]
519
+ await del_ongoing_task(user_id)
520
+ try:
521
+ shutil.rmtree(f"{Config.DOWNLOAD_LOCATION}/{user_id}")
522
+ except:
523
+ pass
524
+
525
+ await cleaner.edit(Messages.ERASE_TASKS_SUCCESS.format(number))
526
+
527
+
528
+ async def send_logs(user_id):
529
+ with open("unzip-log.txt", "rb") as doc_f:
530
+ try:
531
+ await unzipperbot.send_document(
532
+ chat_id=user_id,
533
+ document=doc_f,
534
+ file_name=doc_f.name,
535
+ )
536
+ LOGGER.info(Messages.LOG_SENT.format(user_id))
537
+ except FloodWait as f:
538
+ await sleep(f.value)
539
+ await unzipperbot.send_document(
540
+ chat_id=user_id,
541
+ document=doc_f,
542
+ file_name=doc_f.name,
543
+ )
544
+ except RPCError as e:
545
+ await unzipperbot.send_message(chat_id=user_id, text=e)
546
+
547
+
548
+ def clear_logs():
549
+ open('file.txt', 'w').close()
550
+
551
+
552
+ @unzipperbot.on_message(
553
+ filters.private & filters.command("logs") & filters.user(Config.BOT_OWNER)
554
+ )
555
+ async def logz(_, message: Message):
556
+ await send_logs(message.from_user.id)
557
+
558
+
559
+ @unzipperbot.on_message(
560
+ filters.private & filters.command("restart") & filters.user(Config.BOT_OWNER)
561
+ )
562
+ async def restart(_, message: Message):
563
+ try:
564
+ folder_to_del = os.path.dirname(os.path.abspath(Config.DOWNLOAD_LOCATION))
565
+ shutil.rmtree(Config.DOWNLOAD_LOCATION)
566
+ LOGGER.info(Messages.DELETED_FOLDER.format(folder_to_del))
567
+ except:
568
+ pass
569
+ restarttime = time.strftime("%Y/%m/%d - %H:%M:%S")
570
+ await message.reply_text(
571
+ Messages.RESTARTED_AT.format(restarttime), quote=True
572
+ )
573
+ await send_logs(message.from_user.id)
574
+ LOGGER.info(Messages.RESTARTING.format(message.from_user.id))
575
+ clear_logs()
576
+ os.execl(executable, executable, "-m", "unzipper")
577
+
578
+
579
+ @unzipperbot.on_message(
580
+ filters.private & filters.command("gitpull") & filters.user(Config.BOT_OWNER)
581
+ )
582
+ async def pull_updates(_, message: Message):
583
+ git_reply = await message.reply(Messages.PULLING)
584
+ repo = git.Repo("/app")
585
+ current = repo.head.commit
586
+ repo.remotes.origin.pull()
587
+ time.sleep(2)
588
+ if current != repo.head.commit:
589
+ await git_reply.edit(Messages.PULLED)
590
+ await restart(_, message)
591
+ else:
592
+ await git_reply.edit(Messages.NO_PULL)
593
+
594
+
595
+ @unzipperbot.on_message(filters.command("donate"))
596
+ async def donate_help(_, message: Message):
597
+ await message.reply(Messages.DONATE_TEXT)
598
+
599
+
600
+ @unzipperbot.on_message(filters.command("vip"))
601
+ async def vip_help(_, message: Message):
602
+ await message.reply(Messages.VIP_INFO)
603
+
604
+
605
+ @unzipperbot.on_message(
606
+ filters.private & filters.command("addvip") & filters.user(Config.BOT_OWNER)
607
+ )
608
+ async def add_vip(_, message: Message):
609
+ if message.reply_to_message is None:
610
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
611
+ return
612
+ message = message.reply_to_message
613
+ messagearray = message.text.splitlines()
614
+ if len(messagearray) != 13:
615
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
616
+ return
617
+ if not messagearray[0].isdigit():
618
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
619
+ return
620
+ user_id = int(messagearray[0])
621
+ dateregex = r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z"
622
+ if not re.match(dateregex, messagearray[1]):
623
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
624
+ return
625
+ subscription = messagearray[1]
626
+ if not re.match(dateregex, messagearray[2]):
627
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
628
+ return
629
+ ends = messagearray[2]
630
+ if messagearray[3] not in ["paypal", "telegram", "sponsor", "bmac"]:
631
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
632
+ return
633
+ used = messagearray[3]
634
+ if messagearray[4] not in ["monthly", "yearly"]:
635
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
636
+ return
637
+ billed = messagearray[4]
638
+ if messagearray[5] not in ["True", "False"]:
639
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
640
+ return
641
+ early = messagearray[5] == "True"
642
+ if messagearray[6] not in ["True", "False"]:
643
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
644
+ return
645
+ donator = messagearray[6] == "True"
646
+ if not re.match(dateregex, messagearray[7]):
647
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
648
+ return
649
+ started = messagearray[7]
650
+ if not messagearray[8].isdigit():
651
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
652
+ return
653
+ successful = int(messagearray[8])
654
+ if messagearray[9] not in ["True", "False"]:
655
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
656
+ return
657
+ gap = messagearray[9] == "True"
658
+ if messagearray[10] not in ["True", "False"]:
659
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
660
+ return
661
+ gifted = messagearray[10] == "True"
662
+ if not re.match(r"[a-zA-Z0-9]{1,34}", messagearray[11]):
663
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
664
+ return
665
+ referral = messagearray[11]
666
+ if messagearray[12] not in ["True", "False"]:
667
+ await message.reply(Messages.VIP_REQUIRED_MESSAGE)
668
+ return
669
+ lifetime = messagearray[12] == "True"
670
+ await add_vip_user(user_id, subscription, ends, used, billed, early, donator, started, successful, gap, gifted, referral, lifetime)
671
+ await message.reply(Messages.VIP_ADDED_USER.format(user_id, subscription, ends, used, billed, early, donator, started, successful, gap, gifted, referral, lifetime))
672
+
673
+
674
+ @unzipperbot.on_message(filters.command("delvip") & filters.user(Config.BOT_OWNER))
675
+ async def del_vip(_, message: Message):
676
+ del_msg = await message.reply(Messages.PROVIDE_UID)
677
+ try:
678
+ user_id = message.text.split(None, 1)[1]
679
+ except:
680
+ await del_msg.edit(Messages.UNBAN_ID)
681
+ return
682
+ db = await add_user(user_id)
683
+ bdb = await del_banned_user(user_id)
684
+ text = ""
685
+ if db == -1:
686
+ text += Messages.ALREADY_ADDED.format(user_id)
687
+ if bdb == -1:
688
+ text += Messages.ALREADY_UNBANNED.format(user_id)
689
+ if text != "":
690
+ await del_msg.edit(text)
691
+ else:
692
+ await del_msg.edit(Messages.UNBANNED.format(user_id))
693
+
694
+
695
+ @unzipperbot.on_message(
696
+ filters.private & filters.command("dbexport") & filters.user(Config.BOT_OWNER)
697
+ )
698
+ async def export_db(_, message):
699
+ await message.reply("🚧 WIP 🚧")
700
+ # Will use https://www.mongodb.com/docs/database-tools/mongoexport/ on command to export as CSV
701
+
702
+
703
+ @unzipperbot.on_message(filters.command("commands"))
704
+ async def getall_cmds(_, message):
705
+ await message.reply(
706
+ Messages.COMMANDS_LIST,
707
+ disable_web_page_preview=True,
708
+ )
709
+
710
+
711
+ @unzipperbot.on_message(filters.command("admincmd") & filters.user(Config.BOT_OWNER))
712
+ async def getadmin_cmds(_, message):
713
+ await message.reply(
714
+ Messages.ADMINCMD,
715
+ disable_web_page_preview=True,
716
+ )
717
+
718
+
719
+ disabled = """ async def exec_message_f(client, message):
720
+ if message.from_user.id in AUTH_CHANNEL:
721
+ DELAY_BETWEEN_EDITS = 0.3
722
+ PROCESS_RUN_TIME = 100
723
+ cmd = message.text.split(" ", maxsplit=1)[1]
724
+
725
+ reply_to_id = message.message_id
726
+ if message.reply_to_message:
727
+ reply_to_id = message.reply_to_message.message_id
728
+
729
+ start_time = time.time() + PROCESS_RUN_TIME
730
+ process = await asyncio.create_subprocess_shell(
731
+ cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
732
+ )
733
+ stdout, stderr = await process.communicate()
734
+ e = stderr.decode()
735
+ if not e:
736
+ e = "No Error"
737
+ o = stdout.decode()
738
+ if not o:
739
+ o = "No Output"
740
+ else:
741
+ _o = o.split("\n")
742
+ o = "`\n".join(_o)
743
+ OUTPUT = f"**QUERY:**\n__Command:__\n`{cmd}` \n__PID:__\n`{process.pid}`\n\n**stderr:** \n`{e}`\n**Output:**\n{o}"
744
+
745
+ if len(OUTPUT) > MAX_MESSAGE_LENGTH:
746
+ with io.BytesIO(str.encode(OUTPUT)) as out_file:
747
+ out_file.name = "exec.text"
748
+ await client.send_document(
749
+ chat_id=message.chat.id,
750
+ document=out_file,
751
+ caption=cmd,
752
+ disable_notification=True,
753
+ reply_to_message_id=reply_to_id,
754
+ )
755
+ await message.delete()
756
+ else:
757
+ await message.reply_text(OUTPUT)
758
+
759
+ async def eval_message_f(client, message):
760
+ if message.from_user.id in AUTH_CHANNEL:
761
+ status_message = await message.reply_text("Processing ...")
762
+ cmd = message.text.split(" ", maxsplit=1)[1]
763
+
764
+ reply_to_id = message.message_id
765
+ if message.reply_to_message:
766
+ reply_to_id = message.reply_to_message.message_id
767
+
768
+ old_stderr = sys.stderr
769
+ old_stdout = sys.stdout
770
+ redirected_output = sys.stdout = io.StringIO()
771
+ redirected_error = sys.stderr = io.StringIO()
772
+ stdout, stderr, exc = None, None, None
773
+
774
+ try:
775
+ await aexec(cmd, client, message)
776
+ except Exception:
777
+ exc = traceback.format_exc()
778
+
779
+ stdout = redirected_output.getvalue()
780
+ stderr = redirected_error.getvalue()
781
+ sys.stdout = old_stdout
782
+ sys.stderr = old_stderr
783
+
784
+ evaluation = ""
785
+ if exc:
786
+ evaluation = exc
787
+ elif stderr:
788
+ evaluation = stderr
789
+ elif stdout:
790
+ evaluation = stdout
791
+ else:
792
+ evaluation = "Success"
793
+
794
+ final_output = (
795
+ "<b>EVAL</b>: <code>{}</code>\n\n<b>OUTPUT</b>:\n<code>{}</code> \n".format(
796
+ cmd, evaluation.strip()
797
+ )
798
+ )
799
+
800
+ if len(final_output) > MAX_MESSAGE_LENGTH:
801
+ with open("eval.text", "w+", encoding="utf8") as out_file:
802
+ out_file.write(str(final_output))
803
+ await message.reply_document(
804
+ document="eval.text",
805
+ caption=cmd,
806
+ disable_notification=True,
807
+ reply_to_message_id=reply_to_id,
808
+ )
809
+ os.remove("eval.text")
810
+ await status_message.delete()
811
+ else:
812
+ await status_message.edit(final_output)
813
+
814
+ async def aexec(code, client, message):
815
+ exec(
816
+ f"async def __aexec(client, message): "
817
+ + "".join(f"\n {l}" for l in code.split("\n"))
818
+ )
819
+ return await locals()["__aexec"](client, message) """
unzipper/modules/ext_script/custom_thumbnail.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+ import os
3
+
4
+ from PIL import Image
5
+
6
+ from config import Config
7
+ from unzipper import LOGGER
8
+ from unzipper.modules.bot_data import Buttons, Messages
9
+ from pyrogram.errors import FloodWait
10
+ from asyncio import sleep
11
+
12
+
13
+ async def silent_del(user_id):
14
+ try:
15
+ thumb_location = Config.THUMB_LOCATION + "/" + str(user_id) + ".jpg"
16
+ os.remove(thumb_location)
17
+ except:
18
+ pass
19
+
20
+
21
+ async def add_thumb(_, message):
22
+ try:
23
+ user_id = str(message.from_user.id)
24
+ if message.reply_to_message is not None:
25
+ reply_message = message.reply_to_message
26
+ if reply_message.media_group_id is not None: # album sent
27
+ LOGGER.info(Messages.ALBUM.format(user_id))
28
+ await message.reply(Messages.ALBUM_NOPE)
29
+ return
30
+ thumb_location = Config.THUMB_LOCATION + "/" + user_id + ".jpg"
31
+ pre_thumb = Config.THUMB_LOCATION + "/not_resized_" + user_id + ".jpg"
32
+ final_thumb = Config.THUMB_LOCATION + "/waiting_" + user_id + ".jpg"
33
+ if os.path.exists(thumb_location) and os.path.isfile(thumb_location):
34
+ await message.reply(
35
+ text=Messages.EXISTING_THUMB, reply_markup=Buttons.THUMB_REPLACEMENT
36
+ )
37
+ else:
38
+ await message.reply(
39
+ text=Messages.SAVING_THUMB, reply_markup=Buttons.THUMB_SAVE
40
+ )
41
+ LOGGER.info(Messages.DL_THUMB.format(user_id))
42
+ file = await _.download_media(message=reply_message)
43
+ await _.send_document(
44
+ chat_id=Config.LOGS_CHANNEL,
45
+ document=file,
46
+ file_name=file.split("/")[-1],
47
+ caption=Messages.EXT_CAPTION.format(file),
48
+ )
49
+ os.rename(file, pre_thumb)
50
+ size = 320, 320
51
+ try:
52
+ with Image.open(pre_thumb) as previous:
53
+ previous.thumbnail(size, Image.Resampling.LANCZOS)
54
+ previous.save(final_thumb, "JPEG")
55
+ LOGGER.info(Messages.THUMB_SAVED)
56
+ except:
57
+ LOGGER.info(Messages.THUMB_FAILED)
58
+ try:
59
+ os.remove(pre_thumb)
60
+ except:
61
+ pass
62
+ try:
63
+ os.remove(final_thumb)
64
+ except:
65
+ pass
66
+ await message.reply(Messages.THUMB_ERROR)
67
+ else:
68
+ await _.send_message(
69
+ chat_id=message.chat.id,
70
+ text=Messages.PLS_REPLY,
71
+ reply_to_message_id=message.id,
72
+ )
73
+ except FloodWait as f:
74
+ await sleep(f.value)
75
+ return await add_thumb(_, message)
76
+
77
+
78
+ async def del_thumb(message):
79
+ try:
80
+ uid = message.from_user.id
81
+ thumb_location = Config.THUMB_LOCATION + "/" + str(uid) + ".jpg"
82
+ if not os.path.exists(thumb_location):
83
+ await message.reply(text=Messages.NO_THUMB)
84
+ else:
85
+ await message.reply(text=Messages.DEL_CONFIRM_THUMB, reply_markup=Buttons.THUMB_DEL)
86
+ except FloodWait as f:
87
+ await sleep(f.value)
88
+ return await del_thumb(message)
89
+
90
+
91
+ async def thumb_exists(chat_id):
92
+ thumb_location = Config.THUMB_LOCATION + "/" + str(chat_id) + ".jpg"
93
+ return os.path.exists(thumb_location)
unzipper/modules/ext_script/ext_helper.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+ import os
3
+ from asyncio import get_running_loop
4
+ from functools import partial
5
+ import subprocess
6
+
7
+ from pykeyboard import InlineKeyboard
8
+ from pyrogram.types import InlineKeyboardButton
9
+
10
+ from unzipper import LOGGER
11
+ from unzipper.modules.bot_data import Messages
12
+
13
+
14
+ def __run_cmds_unzipper(command):
15
+ ext_cmd = subprocess.Popen(
16
+ command["cmd"],
17
+ stdout=subprocess.PIPE,
18
+ stderr=subprocess.PIPE,
19
+ shell=True
20
+ )
21
+ ext_out = ext_cmd.stdout.read()[:-1].decode("utf-8").rstrip('\n')
22
+ LOGGER.info(ext_out)
23
+ if ext_cmd.stderr:
24
+ ext_cmd.stderr.close()
25
+ if ext_cmd.stdout:
26
+ ext_cmd.stdout.close()
27
+ return ext_out
28
+
29
+
30
+ async def run_cmds_on_cr(func, **kwargs):
31
+ loop = get_running_loop()
32
+ return await loop.run_in_executor(None, partial(func, kwargs))
33
+
34
+
35
+ # Extract with 7z
36
+ async def _extract_with_7z_helper(path, archive_path, password=None):
37
+ if password:
38
+ command = f'7z x -o{path} -p"{password}" {archive_path} -y'
39
+ else:
40
+ command = f"7z x -o{path} {archive_path} -y"
41
+ return await run_cmds_on_cr(__run_cmds_unzipper, cmd=command)
42
+
43
+
44
+ async def _test_with_7z_helper(archive_path):
45
+ command = f'7z t {archive_path} -p"IAmVeryProbablySureThatThisPasswordWillNeverBeUsedElseItsVeryStrangeAAAAAAAAAAAAAAAAAAA" -y' # skipcq: FLK-E501
46
+ return "Everything is Ok" in await run_cmds_on_cr(__run_cmds_unzipper, cmd=command)
47
+
48
+
49
+ # Extract with zstd (for .zst files)
50
+ async def _extract_with_zstd(path, archive_path):
51
+ command = f"zstd -f --output-dir-flat {path} -d {archive_path}"
52
+ return await run_cmds_on_cr(__run_cmds_unzipper, cmd=command)
53
+
54
+
55
+ # Main function to extract files
56
+ async def extr_files(path, archive_path, password=None):
57
+ file_path = os.path.splitext(archive_path)[1]
58
+ if file_path == ".zst":
59
+ os.mkdir(path)
60
+ return await _extract_with_zstd(path, archive_path)
61
+ return await _extract_with_7z_helper(path, archive_path, password)
62
+
63
+
64
+ # Split files
65
+ async def split_files(iinput, ooutput, size):
66
+ command = f'7z a -tzip -mx=0 "{ooutput}" "{iinput}" -v{size}b'
67
+ await run_cmds_on_cr(__run_cmds_unzipper, cmd=command)
68
+ spdir = ooutput.replace("/" + ooutput.split("/")[-1], "")
69
+ return await get_files(spdir)
70
+
71
+
72
+ # Merge files
73
+ async def merge_files(iinput, ooutput, password=None):
74
+ if password:
75
+ command = f'7z x -o"{ooutput}" -p"{password}" "{iinput}" -y'
76
+ else:
77
+ command = f'7z x -o"{ooutput}" "{iinput}" -y'
78
+ return await run_cmds_on_cr(__run_cmds_unzipper, cmd=command)
79
+
80
+
81
+ # Get files in directory as a list
82
+ async def get_files(path):
83
+ path_list = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk(path)] for val in sublist] # skipcq: FLK-E501
84
+ return sorted(path_list)
85
+
86
+
87
+ # Make keyboard
88
+ async def make_keyboard(paths, user_id, chat_id, unziphttp, rzfile=None):
89
+ num = 0
90
+ i_kbd = InlineKeyboard(row_width=1)
91
+ data = []
92
+ if unziphttp:
93
+ data.append(InlineKeyboardButton(
94
+ Messages.UP_ALL,
95
+ f"ext_a|{user_id}|{chat_id}|{unziphttp}|{rzfile}"
96
+ ))
97
+ else:
98
+ data.append(InlineKeyboardButton(
99
+ Messages.UP_ALL,
100
+ f"ext_a|{user_id}|{chat_id}|{unziphttp}"
101
+ ))
102
+ data.append(InlineKeyboardButton(Messages.CANCEL_IT, "cancel_dis"))
103
+ for file in paths:
104
+ if num > 96:
105
+ break
106
+ if unziphttp:
107
+ data.append(InlineKeyboardButton(
108
+ f"{num} - {os.path.basename(file)}".encode("utf-8").decode("utf-8"),
109
+ f"ext_f|{user_id}|{chat_id}|{num}|{unziphttp}|{rzfile}",
110
+ ))
111
+ else:
112
+ data.append(InlineKeyboardButton(
113
+ f"{num} - {os.path.basename(file)}".encode("utf-8").decode("utf-8"),
114
+ f"ext_f|{user_id}|{chat_id}|{num}|{unziphttp}",
115
+ ))
116
+ num += 1
117
+ i_kbd.add(*data)
118
+ return i_kbd
119
+
120
+
121
+ async def make_keyboard_empty(user_id, chat_id, unziphttp, rzfile=None):
122
+ i_kbd = InlineKeyboard(row_width=2)
123
+ data = []
124
+ if unziphttp:
125
+ data.append(InlineKeyboardButton(
126
+ Messages.UP_ALL,
127
+ f"ext_a|{user_id}|{chat_id}|{unziphttp}|{rzfile}"
128
+ ))
129
+ else:
130
+ data.append(InlineKeyboardButton(
131
+ Messages.UP_ALL,
132
+ f"ext_a|{user_id}|{chat_id}|{unziphttp}"
133
+ ))
134
+ data.append(InlineKeyboardButton(Messages.CANCEL_IT, "cancel_dis"))
135
+ i_kbd.add(*data)
136
+ return i_kbd
unzipper/modules/ext_script/up_helper.py ADDED
@@ -0,0 +1,310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023 EDM115
2
+ import os
3
+ import pathlib
4
+ import re
5
+ import shutil
6
+ import subprocess
7
+ import asyncio
8
+ from time import time
9
+
10
+ from pyrogram.errors import FloodWait
11
+
12
+ from config import Config
13
+ from unzipper import LOGGER
14
+ from unzipper import unzipperbot
15
+ from unzipper.helpers.database import get_upload_mode
16
+ from unzipper.helpers.unzip_help import extentions_list, progress_urls
17
+ from unzipper.helpers.unzip_help import progress_for_pyrogram
18
+ from unzipper.modules.bot_data import Messages
19
+ from unzipper.modules.ext_script.custom_thumbnail import thumb_exists
20
+
21
+
22
+ # To get video duration and thumbnail
23
+ async def run_shell_cmds(command):
24
+ run = subprocess.Popen(
25
+ command,
26
+ stdout=subprocess.PIPE,
27
+ stderr=subprocess.PIPE,
28
+ shell=True
29
+ )
30
+ shell_output = run.stdout.read()[:-1].decode("utf-8").rstrip('\n')
31
+ LOGGER.info(shell_output)
32
+ if run.stderr:
33
+ run.stderr.close()
34
+ if run.stdout:
35
+ run.stdout.close()
36
+ return shell_output
37
+
38
+
39
+ # Get file size
40
+ async def get_size(doc_f):
41
+ try:
42
+ fsize = os.stat(doc_f).st_size
43
+ return fsize
44
+ except:
45
+ return -1
46
+
47
+
48
+ # Send file to a user
49
+ async def send_file(unzip_bot, c_id, doc_f, query, full_path, log_msg, split):
50
+ fsize = await get_size(doc_f)
51
+ if fsize == -1 or fsize == 0: # File not found or empty
52
+ try:
53
+ await unzipperbot.send_message(c_id, Messages.EMPTY_FILE.format(os.path.basename(doc_f)))
54
+ except:
55
+ pass
56
+ return
57
+ try:
58
+ ul_mode = await get_upload_mode(c_id)
59
+ fname = os.path.basename(doc_f)
60
+ fext = ((pathlib.Path(os.path.abspath(doc_f)).suffix).casefold().replace(".", ""))
61
+ thumbornot = await thumb_exists(c_id)
62
+ upmsg = await unzipperbot.send_message(c_id, Messages.PROCESSING2)
63
+ if ul_mode == "media" and fext in extentions_list["audio"]:
64
+ if thumbornot:
65
+ thumb_image = Config.THUMB_LOCATION + "/" + str(c_id) + ".jpg"
66
+ sentfile = await unzip_bot.send_audio(
67
+ chat_id=c_id,
68
+ audio=doc_f,
69
+ caption=Messages.EXT_CAPTION.format(fname),
70
+ thumb=thumb_image,
71
+ progress=progress_for_pyrogram,
72
+ progress_args=(
73
+ Messages.TRY_UP.format(fname),
74
+ upmsg,
75
+ time(),
76
+ unzip_bot,
77
+ ),
78
+ )
79
+ else:
80
+ sentfile = await unzip_bot.send_audio(
81
+ chat_id=c_id,
82
+ audio=doc_f,
83
+ caption=Messages.EXT_CAPTION.format(fname),
84
+ progress=progress_for_pyrogram,
85
+ progress_args=(
86
+ Messages.TRY_UP.format(fname),
87
+ upmsg,
88
+ time(),
89
+ unzip_bot,
90
+ ),
91
+ )
92
+ elif ul_mode == "media" and fext in extentions_list["photo"]:
93
+ # impossible to use a thumb here :(
94
+ sentfile = await unzip_bot.send_photo(
95
+ chat_id=c_id,
96
+ photo=doc_f,
97
+ caption=Messages.EXT_CAPTION.format(fname),
98
+ progress=progress_for_pyrogram,
99
+ progress_args=(
100
+ Messages.TRY_UP.format(fname),
101
+ upmsg,
102
+ time(),
103
+ unzip_bot,
104
+ ),
105
+ )
106
+ elif ul_mode == "media" and fext in extentions_list["video"]:
107
+ vid_duration = await run_shell_cmds(
108
+ f"ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 {doc_f}"
109
+ )
110
+ if thumbornot:
111
+ thumb_image = Config.THUMB_LOCATION + "/" + str(c_id) + ".jpg"
112
+ sentfile = await unzip_bot.send_video(
113
+ chat_id=c_id,
114
+ video=doc_f,
115
+ caption=Messages.EXT_CAPTION.format(fname),
116
+ duration=int(vid_duration) if vid_duration.isnumeric() else 0,
117
+ thumb=thumb_image,
118
+ progress=progress_for_pyrogram,
119
+ progress_args=(
120
+ Messages.TRY_UP.format(fname),
121
+ upmsg,
122
+ time(),
123
+ unzip_bot,
124
+ ),
125
+ )
126
+ else:
127
+ thmb_pth = (
128
+ f"{Config.THUMB_LOCATION}/thumbnail_{os.path.basename(doc_f)}.jpg"
129
+ )
130
+ if os.path.exists(thmb_pth):
131
+ os.remove(thmb_pth)
132
+ try:
133
+ await run_shell_cmds(
134
+ f"ffmpeg -ss 00:00:00.00 -i {doc_f} -vf 'scale=320:320:force_original_aspect_ratio=decrease' -vframes 1 {thmb_pth}"
135
+ )
136
+ except Exception as e:
137
+ LOGGER.warning(e)
138
+ shutil.copy(Config.BOT_THUMB, thmb_pth)
139
+ try:
140
+ sentfile = await unzip_bot.send_video(
141
+ chat_id=c_id,
142
+ video=doc_f,
143
+ caption=Messages.EXT_CAPTION.format(fname),
144
+ duration=int(vid_duration) if vid_duration.isnumeric() else 0,
145
+ thumb=str(thmb_pth),
146
+ progress=progress_for_pyrogram,
147
+ progress_args=(
148
+ Messages.TRY_UP.format(fname),
149
+ upmsg,
150
+ time(),
151
+ unzip_bot,
152
+ ),
153
+ )
154
+ try:
155
+ os.remove(thmb_pth)
156
+ except:
157
+ pass
158
+ except:
159
+ try:
160
+ sentfile = await unzip_bot.send_video(
161
+ chat_id=c_id,
162
+ video=doc_f,
163
+ caption=Messages.EXT_CAPTION.format(fname),
164
+ duration=0,
165
+ thumb=str(Config.BOT_THUMB),
166
+ progress=progress_for_pyrogram,
167
+ progress_args=(
168
+ Messages.TRY_UP.format(fname),
169
+ upmsg,
170
+ time(),
171
+ unzip_bot,
172
+ ),
173
+ )
174
+ except:
175
+ sentfile = await unzip_bot.send_document(
176
+ chat_id=c_id,
177
+ document=doc_f,
178
+ caption=Messages.EXT_CAPTION.format(fname),
179
+ force_document=True,
180
+ progress=progress_for_pyrogram,
181
+ progress_args=(
182
+ Messages.TRY_UP.format(fname),
183
+ upmsg,
184
+ time(),
185
+ unzip_bot,
186
+ ),
187
+ )
188
+ else:
189
+ if thumbornot:
190
+ thumb_image = Config.THUMB_LOCATION + "/" + str(c_id) + ".jpg"
191
+ sentfile = await unzip_bot.send_document(
192
+ chat_id=c_id,
193
+ document=doc_f,
194
+ thumb=thumb_image,
195
+ caption=Messages.EXT_CAPTION.format(fname),
196
+ force_document=True,
197
+ progress=progress_for_pyrogram,
198
+ progress_args=(
199
+ Messages.TRY_UP.format(fname),
200
+ upmsg,
201
+ time(),
202
+ unzip_bot,
203
+ ),
204
+ )
205
+ else:
206
+ sentfile = await unzip_bot.send_document(
207
+ chat_id=c_id,
208
+ document=doc_f,
209
+ caption=Messages.EXT_CAPTION.format(fname),
210
+ force_document=True,
211
+ progress=progress_for_pyrogram,
212
+ progress_args=(
213
+ Messages.TRY_UP.format(fname),
214
+ upmsg,
215
+ time(),
216
+ unzip_bot,
217
+ ),
218
+ )
219
+ await upmsg.delete()
220
+ os.remove(doc_f)
221
+ except FloodWait as f:
222
+ await asyncio.sleep(f.value)
223
+ return await send_file(unzip_bot, c_id, doc_f, query, full_path, log_msg, split)
224
+ except FileNotFoundError:
225
+ try:
226
+ await unzipperbot.send_message(c_id, Messages.CANT_FIND.format(os.path.basename(doc_f)))
227
+ except:
228
+ pass
229
+ return
230
+ except BaseException as e:
231
+ LOGGER.error(e)
232
+ shutil.rmtree(full_path)
233
+
234
+
235
+ async def forward_file(message, cid):
236
+ try:
237
+ await unzipperbot.copy_message(
238
+ chat_id=cid,
239
+ from_chat_id=message.chat.id,
240
+ message_id=message.id,
241
+ )
242
+ except FloodWait as f:
243
+ await asyncio.sleep(f.value)
244
+ return await forward_file(message, cid)
245
+
246
+
247
+ async def send_url_logs(unzip_bot, c_id, doc_f, source, message):
248
+ try:
249
+ u_file_size = os.stat(doc_f).st_size
250
+ if Config.TG_MAX_SIZE < int(u_file_size):
251
+ await unzip_bot.send_message(
252
+ chat_id=c_id,
253
+ text=Messages.TOO_LARGE
254
+ )
255
+ return
256
+ fname = os.path.basename(doc_f)
257
+ await unzip_bot.send_document(
258
+ chat_id=c_id,
259
+ document=doc_f,
260
+ caption=Messages.LOG_CAPTION.format(fname, source),
261
+ progress=progress_urls,
262
+ progress_args=(
263
+ Messages.CHECK_MSG,
264
+ message,
265
+ time(),
266
+ ),
267
+ )
268
+ except FloodWait as f:
269
+ await asyncio.sleep(f.value)
270
+ return send_url_logs(unzip_bot, c_id, doc_f, source, message)
271
+ except FileNotFoundError:
272
+ await unzip_bot.send_message(
273
+ chat_id=Config.LOGS_CHANNEL,
274
+ text=Messages.ARCHIVE_GONE,
275
+ )
276
+ except BaseException:
277
+ pass
278
+
279
+
280
+ async def merge_splitted_archives(user_id, path):
281
+ cmd = f"cd {path} && cat * > MERGED_{user_id}.zip"
282
+ await run_shell_cmds(cmd)
283
+
284
+
285
+ # Function to remove basic markdown characters from a string
286
+ async def rm_mark_chars(text: str):
287
+ return re.sub("[*`_]", "", text)
288
+
289
+
290
+ # Function to answer queries
291
+ async def answer_query(
292
+ query, message_text: str, answer_only: bool = False, unzip_client=None, buttons=None
293
+ ):
294
+ try:
295
+ if answer_only:
296
+ await query.answer(await rm_mark_chars(message_text), show_alert=True)
297
+ else:
298
+ await query.message.edit(message_text, reply_markup=buttons)
299
+ except:
300
+ try:
301
+ if unzip_client:
302
+ await unzip_client.send_message(
303
+ chat_id=query.message.chat.id, text=message_text, reply_markup=buttons
304
+ )
305
+ else:
306
+ await unzipperbot.send_message(
307
+ chat_id=query.message.chat.id, text=message_text, reply_markup=buttons
308
+ )
309
+ except:
310
+ pass
unzipper/modules/ext_script/url_parser.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ from unzipper import LOGGER
3
+
4
+
5
+ anonfilesBaseSites = ["anonfiles.com", "hotfile.io", "bayfiles.com", "megaupload.nz", "letsupload.cc", "filechan.org", "myfile.is", "vshare.is", "rapidshare.nu", "lolabits.se", "openload.cc", "share-online.is", "upvid.cc"]
6
+
7
+
8
+ async def get_gdrive_id(gdrive_url):
9
+ gdrive_url = gdrive_url.strip().rstrip('/')
10
+
11
+ if "drive.google.com" in gdrive_url:
12
+ if "/file/d/" in gdrive_url:
13
+ start_index = gdrive_url.find("/file/d/") + len("/file/d/")
14
+ end_index = gdrive_url.find("/", start_index)
15
+ file_id = gdrive_url[start_index:end_index]
16
+ return file_id
17
+
18
+ if "/open?id=" in gdrive_url:
19
+ start_index = gdrive_url.find("/open?id=") + len("/open?id=")
20
+ file_id = gdrive_url[start_index:]
21
+ return file_id
22
+
23
+ if "/file/d/" in gdrive_url and "/view" in gdrive_url:
24
+ start_index = gdrive_url.find("/file/d/") + len("/file/d/")
25
+ end_index = gdrive_url.find("/view", start_index)
26
+ file_id = gdrive_url[start_index:end_index]
27
+ return file_id
28
+
29
+ if "/uc?id=" in gdrive_url:
30
+ start_index = gdrive_url.find("/uc?id=") + len("/uc?id=")
31
+ end_index = gdrive_url.find("&", start_index)
32
+ file_id = gdrive_url[start_index:end_index]
33
+ return file_id
34
+
35
+ if "/uc?export=download&id=" in gdrive_url:
36
+ start_index = gdrive_url.find("/uc?export=download&id=") + len("/uc?export=download&id=")
37
+ end_index = gdrive_url.find("&", start_index)
38
+ file_id = gdrive_url[start_index:end_index]
39
+ return file_id
40
+
41
+ raise ValueError("Invalid or unrecognized Google Drive URL format")
42
+
43
+
44
+ async def gdrive_dl(url):
45
+ try:
46
+ file_id = await get_gdrive_id(url)
47
+ downloadable_link = f"https://drive.google.com/uc?id={file_id}&export=download"
48
+ return downloadable_link
49
+ except ValueError as e:
50
+ LOGGER.warning(e)
51
+ return None
52
+
53
+
54
+ async def yandisk_dl(url):
55
+ try:
56
+ file_id = url.split("/")[-1]
57
+ downloadable_link = f"https://cloud-api.yandex.net/v1/disk/public/resources/download?public_key={file_id}"
58
+ r = requests.get(downloadable_link)
59
+ download_link = r.json()["href"]
60
+ return download_link
61
+ except Exception as e:
62
+ LOGGER.warning(e)
63
+ return None
64
+
65
+
66
+ async def onedrive_dl(url):
67
+ try:
68
+ file_id = url.split("/")[-2]
69
+ downloadable_link = f"https://api.onedrive.com/v1.0/shares/u!{file_id}/root/content"
70
+ return downloadable_link
71
+ except Exception as e:
72
+ LOGGER.warning(e)
73
+ return None
74
+
75
+
76
+ async def mediafire_dl(url):
77
+ try:
78
+ file_id = url.split("/")[-2]
79
+ downloadable_link = f"https://download{file_id}.mediafire.com/file/{file_id}/file"
80
+ return downloadable_link
81
+ except Exception as e:
82
+ LOGGER.warning(e)
83
+ return None
84
+
85
+
86
+ async def anonfiles_dl(url):
87
+ basesite = url.split("/")[2].replace("www.", "")
88
+ if basesite not in anonfilesBaseSites:
89
+ return None
90
+ try:
91
+ file_id = url.split("/")[-1]
92
+ downloadable_link = f"https://api.anonfiles.com/v2/file/{file_id}/info"
93
+ r = requests.get(downloadable_link)
94
+ download_link = r.json()["data"]["file"]["url"]["full"]
95
+ return download_link
96
+ except Exception as e:
97
+ LOGGER.warning(e)
98
+ return None
99
+
100
+
101
+ async def krakenfiles_dl(url):
102
+ try:
103
+ file_id = url.split("/")[-1]
104
+ downloadable_link = f"https://krakenfiles.com/view/{file_id}"
105
+ r = requests.get(downloadable_link)
106
+ download_link = r.url
107
+ return download_link
108
+ except Exception as e:
109
+ LOGGER.warning(e)
110
+ return None
111
+
112
+
113
+ async def wetransfer_dl(url):
114
+ try:
115
+ file_id = url.split("/")[-1]
116
+ downloadable_link = f"https://wetransfer.com/api/ui/transfers/{file_id}/download"
117
+ r = requests.get(downloadable_link)
118
+ download_link = r.json()["direct_link"]
119
+ return download_link
120
+ except Exception as e:
121
+ LOGGER.warning(e)
122
+ return None