mega345evolutions commited on
Commit
cb570da
·
verified ·
1 Parent(s): 2fb5485

Upload 233 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .env.sample +12 -0
  2. .gitattributes +10 -0
  3. .github/ISSUE_TEMPLATE/bug-reports.yml +68 -0
  4. .github/ISSUE_TEMPLATE/config.yml +8 -0
  5. .github/PULL_REQUEST_TEMPLATE/pull_request_template.md +9 -0
  6. .github/stalebot.yml +13 -0
  7. .github/workflows/codeql-analysis.yml +35 -0
  8. .github/workflows/pylint.yaml +45 -0
  9. .github/workflows/stringanalysis.yml +33 -0
  10. .gitignore +47 -0
  11. CODE_OF_CONDUCT.md +128 -0
  12. CONTRIBUTING.md +23 -0
  13. Dockerfile +20 -0
  14. LICENSE +661 -0
  15. app.json +56 -0
  16. assistant/__init__.py +34 -0
  17. assistant/callbackstuffs.py +1318 -0
  18. assistant/games.py +331 -0
  19. assistant/initial.py +84 -0
  20. assistant/inlinestuff.py +623 -0
  21. assistant/localization.py +47 -0
  22. assistant/manager/__init__.py +11 -0
  23. assistant/manager/_help.py +134 -0
  24. assistant/manager/_on_adds.py +23 -0
  25. assistant/manager/admins.py +50 -0
  26. assistant/manager/afk.py +110 -0
  27. assistant/manager/misc.py +64 -0
  28. assistant/manager/stickermanager.py +180 -0
  29. assistant/pmbot.py +150 -0
  30. assistant/start.py +240 -0
  31. assistant/ytdl.py +307 -0
  32. docker-compose.yml +14 -0
  33. heroku.yml +3 -0
  34. install-termux +16 -0
  35. installer.sh +241 -0
  36. multi_client.py +39 -0
  37. okteto-pipeline.yml +3 -0
  38. plugins/__init__.py +105 -0
  39. plugins/_chatactions.py +253 -0
  40. plugins/_help.py +136 -0
  41. plugins/_inline.py +455 -0
  42. plugins/_ultroid.py +66 -0
  43. plugins/_userlogs.py +299 -0
  44. plugins/_wspr.py +204 -0
  45. plugins/admintools.py +471 -0
  46. plugins/afk.py +165 -0
  47. plugins/aiwrapper.py +445 -0
  48. plugins/antiflood.py +121 -0
  49. plugins/asstcmd.py +100 -0
  50. plugins/audiotools.py +163 -0
.env.sample ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Don't use quotes( " and ' )
2
+
3
+ API_ID=
4
+ API_HASH=
5
+ SESSION=
6
+ REDIS_URI=
7
+ REDIS_PASSWORD=
8
+
9
+ # [OPTIONAL]
10
+
11
+ LOG_CHANNEL=
12
+ BOT_TOKEN=
.gitattributes CHANGED
@@ -33,3 +33,13 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ resources/extras/logo_readme.jpg filter=lfs diff=lfs merge=lfs -text
37
+ resources/extras/ultroid.jpg filter=lfs diff=lfs merge=lfs -text
38
+ resources/fonts/11.otf filter=lfs diff=lfs merge=lfs -text
39
+ resources/fonts/3.ttf filter=lfs diff=lfs merge=lfs -text
40
+ resources/fonts/default.ttf filter=lfs diff=lfs merge=lfs -text
41
+ resources/fonts/DroidSansMono.ttf filter=lfs diff=lfs merge=lfs -text
42
+ resources/fonts/Quivira.otf filter=lfs diff=lfs merge=lfs -text
43
+ resources/fonts/Roboto-Italic.ttf filter=lfs diff=lfs merge=lfs -text
44
+ resources/fonts/Roboto-Medium.ttf filter=lfs diff=lfs merge=lfs -text
45
+ resources/fonts/Roboto-Regular.ttf filter=lfs diff=lfs merge=lfs -text
.github/ISSUE_TEMPLATE/bug-reports.yml ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Bug Report
2
+ description: File a bug report
3
+ title: "[BUG REPORT]"
4
+ labels: [bug]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Thanks for taking the time to fill out this bug report!
10
+ - type: input
11
+ id: contact
12
+ attributes:
13
+ label: Contact Details
14
+ description: Your telegram username or email ID.
15
+ placeholder: ex. @username or email@example.com
16
+ validations:
17
+ required: false
18
+ - type: textarea
19
+ id: what-happened
20
+ attributes:
21
+ label: What happened?
22
+ description: Also tell us, what did you expect to happen?
23
+ placeholder: Tell us what you see!
24
+ value: "A bug happened!"
25
+ validations:
26
+ required: true
27
+ - type: dropdown
28
+ id: version
29
+ attributes:
30
+ label: Version
31
+ description: What version of Ultroid are you running?
32
+ options:
33
+ - "0.5"
34
+ - "0.6"
35
+ - "0.7"
36
+ - "> 0.7"
37
+
38
+ validations:
39
+ required: true
40
+ - type: dropdown
41
+ id: hosted
42
+ attributes:
43
+ label: Hosted On
44
+ description: You are hosting Ultroid on Which Platform?
45
+ options:
46
+ - Vps
47
+ - Heroku
48
+ - Railway
49
+ - Qovery
50
+ - Termux
51
+ - Other
52
+ - Didn't Deployed Yet
53
+ validations:
54
+ required: true
55
+ - type: textarea
56
+ id: logs
57
+ attributes:
58
+ label: Relevant logs output
59
+ description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
60
+ render: shell
61
+ - type: checkboxes
62
+ id: terms
63
+ attributes:
64
+ label: Acknowledgement
65
+ description: By submitting this issue, you agree that you have read the [docs](https://ultroid.tech) and gone through the [deploy tutorial](https://www.youtube.com/watch?v=9wF7k9qA0Q4) at first.
66
+ options:
67
+ - label: I have followed all of the above steps.
68
+ required: true
.github/ISSUE_TEMPLATE/config.yml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Ultroid Support
4
+ url: https://t.me/UltroidSupportChat
5
+ about: Please drop your doubts or queries there for an immediate response.
6
+ - name: Documentation
7
+ url: https://ultroid.tech/
8
+ about: Basic documentation for setting up.
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # Title
2
+
3
+ ### Detailed Description [Optional] :
4
+ detailed_description_here
5
+
6
+ ### Issue Number [Optional] :
7
+ Issue Number here if your pull request close any issue or is linked to.
8
+
9
+ ----
.github/stalebot.yml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Number of days of inactivity before an issue becomes stale
2
+ daysUntilStale: 50
3
+ # Number of days of inactivity before a stale issue is closed
4
+ daysUntilClose: 7
5
+ # Label to use when marking an issue as stale
6
+ staleLabel: inactive
7
+ # Comment to post when marking an issue as stale. Set to `false` to disable
8
+ markComment: >
9
+ This issue has been automatically marked as stale because it has not had
10
+ any recent activity. It will be closed if no further activity occurs. Thank you
11
+ for your contributions.
12
+ # Comment to post when closing a stale issue. Set to `false` to disable
13
+ closeComment: false
.github/workflows/codeql-analysis.yml ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: CodeQL Analysis
2
+
3
+ on:
4
+ push:
5
+ branches-ignore:
6
+ - 'dependabot/**'
7
+ pull_request:
8
+ branches: [ '**' ]
9
+ schedule:
10
+ - cron: '0 8 * * *'
11
+ workflow_dispatch:
12
+
13
+ jobs:
14
+ analyze:
15
+ name: Analyze
16
+ runs-on: ubuntu-latest
17
+
18
+ steps:
19
+ - name: Checkout repository
20
+ uses: actions/checkout@v2
21
+
22
+ - name: Cache CodeQL database
23
+ uses: actions/cache@v2
24
+ with:
25
+ path: ~/.codeql
26
+ key: ${{ runner.os }}-codeql-${{ hashFiles('**/qlpack.yml') }}
27
+ restore-keys: ${{ runner.os }}-codeql-
28
+
29
+ - name: Initialize CodeQL
30
+ uses: github/codeql-action/init@v2
31
+ with:
32
+ languages: 'python'
33
+
34
+ - name: Perform CodeQL Analysis
35
+ uses: github/codeql-action/analyze@v2
.github/workflows/pylint.yaml ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: PyLint
2
+ on:
3
+ push:
4
+ branches: [ dev ]
5
+ paths:
6
+ - "**.py"
7
+ jobs:
8
+ PEP8:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v2
12
+ - name: Setup Python
13
+ uses: actions/setup-python@v1
14
+ with:
15
+ python-version: 3.8
16
+ cache: "pip"
17
+ - name: Install Python lint libraries
18
+ run: pip install autopep8 autoflake isort black
19
+ - name: Check for showstoppers
20
+ run: |
21
+ autopep8 --verbose --in-place --recursive --aggressive --aggressive assistant/*.py
22
+ autopep8 --verbose --in-place --recursive --aggressive --aggressive assistant/manager/*.py
23
+ autopep8 --verbose --in-place --recursive --aggressive --aggressive plugins/*.py
24
+ - name: Remove unused imports and variables
25
+ run: |
26
+ autoflake --in-place --recursive --remove-all-unused-imports --remove-unused-variables --ignore-init-module-imports assistant/*.py
27
+ autoflake --in-place --recursive --remove-all-unused-imports --remove-unused-variables --ignore-init-module-imports assistant/manager/*.py
28
+ autoflake --in-place --recursive --remove-all-unused-imports --remove-unused-variables --ignore-init-module-imports plugins/*.py
29
+ - name: lint with isort and black
30
+ run: |
31
+ isort assistant/*.py
32
+ isort assistant/manager/*.py
33
+ black --fast assistant/*.py
34
+ black assistant/manager/*.py
35
+ isort plugins/*.py
36
+ black --fast plugins/*.py
37
+ - uses: stefanzweifel/git-auto-commit-action@v4
38
+ with:
39
+ commit_message: 'pylint: auto fixes'
40
+ commit_options: '--no-verify'
41
+ repository: .
42
+ commit_user_name: buddhhu
43
+ commit_user_email: 48654350+buddhhu@users.noreply.github.com
44
+ commit_author: Amit Sharma <48654350+buddhhu@users.noreply.github.com>
45
+
.github/workflows/stringanalysis.yml ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Strings Analysis
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: dev
7
+ paths: strings/strings/*
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ analyze:
12
+ name: Checkout Strings
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - name: Setup Python
17
+ uses: actions/setup-python@v2
18
+ with:
19
+ python-version: 3.9.x
20
+ - name: Install Stuff
21
+ run: pip install pyyaml
22
+ - name: dl stuff
23
+ run: wget -O stringizer.py https://gist.githubusercontent.com/New-dev0/6dfc8a177418565a776167764b2fe0e4/raw/f515e9892a331110c1565eb109eb712eb64423bd/stringzer.py
24
+ - name: do stuff
25
+ run: python stringizer.py && rm stringizer.py
26
+ - uses: stefanzweifel/git-auto-commit-action@v4
27
+ with:
28
+ commit_message: 'Strings: Auto Count'
29
+ commit_options: '--no-verify'
30
+ repository: .
31
+ commit_user_name: TeamUltroid
32
+ commit_user_email: 48654350+buddhhu@users.noreply.github.com
33
+ commit_author: Amit Sharma <48654350+buddhhu@users.noreply.github.com>
.gitignore ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .env
2
+ *.session-journal
3
+ *.session
4
+ build
5
+ test*
6
+ *.mp3
7
+ *.webm
8
+ *.webp
9
+ *.mp4
10
+ *.tgs
11
+ *.txt
12
+ /*.jpg
13
+ /*.png
14
+ /*.mp4
15
+ *.log
16
+ target/npmlist.json
17
+ package-lock.json
18
+ ultroid.json
19
+ resources/extras/thumbnail.jpg
20
+ resources/auth/
21
+
22
+ # Directories
23
+ addons/
24
+ vcbot/
25
+ __pycache__/
26
+ venv/
27
+ node_modules/
28
+ glitch_me/
29
+ src/glitch-me
30
+ .idea/
31
+ .vscode/
32
+ temp/
33
+ bin-debug/
34
+ bin-release/
35
+ [Oo]bj/
36
+ [Bb]in/
37
+ .settings/
38
+ *.swf
39
+ *.air
40
+ *.ipa
41
+ *.apk
42
+
43
+ # temporary files
44
+ *.raw
45
+
46
+ # fly.io configs
47
+ fly.toml
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
+ teamultroid@ultroid.tech.
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.
CONTRIBUTING.md ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Welcome To Contributing Guide
2
+ Any contribution done to this repository will be appreciated! 🎉
3
+
4
+ ### About Ultroid
5
+ - **Ultroid** is Telegram Userbot, which allow you to perform many task easily.
6
+ - It's base core is a pip package named [`pyUltroid`](https://GitHub.com/TeamUltroid/pyUltroid).
7
+ - This repository, contains Ultroid's official plugin files.
8
+ - moreover, Ultroid User also have an option to get more plugins available at [`UltroidAddons`](https://github.com/TeamUltroid/UltroidAddons)
9
+
10
+ ### Issues
11
+ - Search for existing Similar issues.
12
+ - You can open a issue first before contributing, If it contain some major changes like `bug fixes` or `feature addition`.
13
+ - If you are busy or getting trouble while making changes, you can just open the issue (explaining it).
14
+
15
+ ### Contributing
16
+ - You can [fork](https://github.com/TeamUltroid/Ultroid/fork) this repository.
17
+ - You can commits your changes now.
18
+ - You can [Git-Squash](https://docs.github.com/en/get-started/using-git/about-git-rebase) your commits, if they are more. [Optional]
19
+ - At last, You are ready to [`make a Pull Request`](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request).
20
+ - Make sure that, your `Pull Request` contain an explaining title and issue number if it closes any open issue.
21
+
22
+ ### Thanks
23
+ - Congrats! 💫 you have successfully made your Contribution to Ultroid.
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
4
+ # PLease read the GNU Affero General Public License in <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
5
+
6
+ FROM theteamultroid/ultroid:main
7
+
8
+ # set timezone
9
+ ENV TZ=Asia/Kolkata
10
+ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
11
+
12
+ COPY installer.sh .
13
+
14
+ RUN bash installer.sh
15
+
16
+ # changing workdir
17
+ WORKDIR "/root/TeamUltroid"
18
+
19
+ # start the bot.
20
+ CMD ["bash", "startup"]
LICENSE ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU AFFERO GENERAL PUBLIC LICENSE
2
+ Version 3, 19 November 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU Affero General Public License is a free, copyleft license for
11
+ software and other kinds of works, specifically designed to ensure
12
+ cooperation with the community in the case of network server software.
13
+
14
+ The licenses for most software and other practical works are designed
15
+ to take away your freedom to share and change the works. By contrast,
16
+ our General Public Licenses are intended to guarantee your freedom to
17
+ share and change all versions of a program--to make sure it remains free
18
+ software for all its users.
19
+
20
+ When we speak of free software, we are referring to freedom, not
21
+ price. Our General Public Licenses are designed to make sure that you
22
+ have the freedom to distribute copies of free software (and charge for
23
+ them if you wish), that you receive source code or can get it if you
24
+ want it, that you can change the software or use pieces of it in new
25
+ free programs, and that you know you can do these things.
26
+
27
+ Developers that use our General Public Licenses protect your rights
28
+ with two steps: (1) assert copyright on the software, and (2) offer
29
+ you this License which gives you legal permission to copy, distribute
30
+ and/or modify the software.
31
+
32
+ A secondary benefit of defending all users' freedom is that
33
+ improvements made in alternate versions of the program, if they
34
+ receive widespread use, become available for other developers to
35
+ incorporate. Many developers of free software are heartened and
36
+ encouraged by the resulting cooperation. However, in the case of
37
+ software used on network servers, this result may fail to come about.
38
+ The GNU General Public License permits making a modified version and
39
+ letting the public access it on a server without ever releasing its
40
+ source code to the public.
41
+
42
+ The GNU Affero General Public License is designed specifically to
43
+ ensure that, in such cases, the modified source code becomes available
44
+ to the community. It requires the operator of a network server to
45
+ provide the source code of the modified version running there to the
46
+ users of that server. Therefore, public use of a modified version, on
47
+ a publicly accessible server, gives the public access to the source
48
+ code of the modified version.
49
+
50
+ An older license, called the Affero General Public License and
51
+ published by Affero, was designed to accomplish similar goals. This is
52
+ a different license, not a version of the Affero GPL, but Affero has
53
+ released a new version of the Affero GPL which permits relicensing under
54
+ this license.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ TERMS AND CONDITIONS
60
+
61
+ 0. Definitions.
62
+
63
+ "This License" refers to version 3 of the GNU Affero General Public License.
64
+
65
+ "Copyright" also means copyright-like laws that apply to other kinds of
66
+ works, such as semiconductor masks.
67
+
68
+ "The Program" refers to any copyrightable work licensed under this
69
+ License. Each licensee is addressed as "you". "Licensees" and
70
+ "recipients" may be individuals or organizations.
71
+
72
+ To "modify" a work means to copy from or adapt all or part of the work
73
+ in a fashion requiring copyright permission, other than the making of an
74
+ exact copy. The resulting work is called a "modified version" of the
75
+ earlier work or a work "based on" the earlier work.
76
+
77
+ A "covered work" means either the unmodified Program or a work based
78
+ on the Program.
79
+
80
+ To "propagate" a work means to do anything with it that, without
81
+ permission, would make you directly or secondarily liable for
82
+ infringement under applicable copyright law, except executing it on a
83
+ computer or modifying a private copy. Propagation includes copying,
84
+ distribution (with or without modification), making available to the
85
+ public, and in some countries other activities as well.
86
+
87
+ To "convey" a work means any kind of propagation that enables other
88
+ parties to make or receive copies. Mere interaction with a user through
89
+ a computer network, with no transfer of a copy, is not conveying.
90
+
91
+ An interactive user interface displays "Appropriate Legal Notices"
92
+ to the extent that it includes a convenient and prominently visible
93
+ feature that (1) displays an appropriate copyright notice, and (2)
94
+ tells the user that there is no warranty for the work (except to the
95
+ extent that warranties are provided), that licensees may convey the
96
+ work under this License, and how to view a copy of this License. If
97
+ the interface presents a list of user commands or options, such as a
98
+ menu, a prominent item in the list meets this criterion.
99
+
100
+ 1. Source Code.
101
+
102
+ The "source code" for a work means the preferred form of the work
103
+ for making modifications to it. "Object code" means any non-source
104
+ form of a work.
105
+
106
+ A "Standard Interface" means an interface that either is an official
107
+ standard defined by a recognized standards body, or, in the case of
108
+ interfaces specified for a particular programming language, one that
109
+ is widely used among developers working in that language.
110
+
111
+ The "System Libraries" of an executable work include anything, other
112
+ than the work as a whole, that (a) is included in the normal form of
113
+ packaging a Major Component, but which is not part of that Major
114
+ Component, and (b) serves only to enable use of the work with that
115
+ Major Component, or to implement a Standard Interface for which an
116
+ implementation is available to the public in source code form. A
117
+ "Major Component", in this context, means a major essential component
118
+ (kernel, window system, and so on) of the specific operating system
119
+ (if any) on which the executable work runs, or a compiler used to
120
+ produce the work, or an object code interpreter used to run it.
121
+
122
+ The "Corresponding Source" for a work in object code form means all
123
+ the source code needed to generate, install, and (for an executable
124
+ work) run the object code and to modify the work, including scripts to
125
+ control those activities. However, it does not include the work's
126
+ System Libraries, or general-purpose tools or generally available free
127
+ programs which are used unmodified in performing those activities but
128
+ which are not part of the work. For example, Corresponding Source
129
+ includes interface definition files associated with source files for
130
+ the work, and the source code for shared libraries and dynamically
131
+ linked subprograms that the work is specifically designed to require,
132
+ such as by intimate data communication or control flow between those
133
+ subprograms and other parts of the work.
134
+
135
+ The Corresponding Source need not include anything that users
136
+ can regenerate automatically from other parts of the Corresponding
137
+ Source.
138
+
139
+ The Corresponding Source for a work in source code form is that
140
+ same work.
141
+
142
+ 2. Basic Permissions.
143
+
144
+ All rights granted under this License are granted for the term of
145
+ copyright on the Program, and are irrevocable provided the stated
146
+ conditions are met. This License explicitly affirms your unlimited
147
+ permission to run the unmodified Program. The output from running a
148
+ covered work is covered by this License only if the output, given its
149
+ content, constitutes a covered work. This License acknowledges your
150
+ rights of fair use or other equivalent, as provided by copyright law.
151
+
152
+ You may make, run and propagate covered works that you do not
153
+ convey, without conditions so long as your license otherwise remains
154
+ in force. You may convey covered works to others for the sole purpose
155
+ of having them make modifications exclusively for you, or provide you
156
+ with facilities for running those works, provided that you comply with
157
+ the terms of this License in conveying all material for which you do
158
+ not control copyright. Those thus making or running the covered works
159
+ for you must do so exclusively on your behalf, under your direction
160
+ and control, on terms that prohibit them from making any copies of
161
+ your copyrighted material outside their relationship with you.
162
+
163
+ Conveying under any other circumstances is permitted solely under
164
+ the conditions stated below. Sublicensing is not allowed; section 10
165
+ makes it unnecessary.
166
+
167
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168
+
169
+ No covered work shall be deemed part of an effective technological
170
+ measure under any applicable law fulfilling obligations under article
171
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
172
+ similar laws prohibiting or restricting circumvention of such
173
+ measures.
174
+
175
+ When you convey a covered work, you waive any legal power to forbid
176
+ circumvention of technological measures to the extent such circumvention
177
+ is effected by exercising rights under this License with respect to
178
+ the covered work, and you disclaim any intention to limit operation or
179
+ modification of the work as a means of enforcing, against the work's
180
+ users, your or third parties' legal rights to forbid circumvention of
181
+ technological measures.
182
+
183
+ 4. Conveying Verbatim Copies.
184
+
185
+ You may convey verbatim copies of the Program's source code as you
186
+ receive it, in any medium, provided that you conspicuously and
187
+ appropriately publish on each copy an appropriate copyright notice;
188
+ keep intact all notices stating that this License and any
189
+ non-permissive terms added in accord with section 7 apply to the code;
190
+ keep intact all notices of the absence of any warranty; and give all
191
+ recipients a copy of this License along with the Program.
192
+
193
+ You may charge any price or no price for each copy that you convey,
194
+ and you may offer support or warranty protection for a fee.
195
+
196
+ 5. Conveying Modified Source Versions.
197
+
198
+ You may convey a work based on the Program, or the modifications to
199
+ produce it from the Program, in the form of source code under the
200
+ terms of section 4, provided that you also meet all of these conditions:
201
+
202
+ a) The work must carry prominent notices stating that you modified
203
+ it, and giving a relevant date.
204
+
205
+ b) The work must carry prominent notices stating that it is
206
+ released under this License and any conditions added under section
207
+ 7. This requirement modifies the requirement in section 4 to
208
+ "keep intact all notices".
209
+
210
+ c) You must license the entire work, as a whole, under this
211
+ License to anyone who comes into possession of a copy. This
212
+ License will therefore apply, along with any applicable section 7
213
+ additional terms, to the whole of the work, and all its parts,
214
+ regardless of how they are packaged. This License gives no
215
+ permission to license the work in any other way, but it does not
216
+ invalidate such permission if you have separately received it.
217
+
218
+ d) If the work has interactive user interfaces, each must display
219
+ Appropriate Legal Notices; however, if the Program has interactive
220
+ interfaces that do not display Appropriate Legal Notices, your
221
+ work need not make them do so.
222
+
223
+ A compilation of a covered work with other separate and independent
224
+ works, which are not by their nature extensions of the covered work,
225
+ and which are not combined with it such as to form a larger program,
226
+ in or on a volume of a storage or distribution medium, is called an
227
+ "aggregate" if the compilation and its resulting copyright are not
228
+ used to limit the access or legal rights of the compilation's users
229
+ beyond what the individual works permit. Inclusion of a covered work
230
+ in an aggregate does not cause this License to apply to the other
231
+ parts of the aggregate.
232
+
233
+ 6. Conveying Non-Source Forms.
234
+
235
+ You may convey a covered work in object code form under the terms
236
+ of sections 4 and 5, provided that you also convey the
237
+ machine-readable Corresponding Source under the terms of this License,
238
+ in one of these ways:
239
+
240
+ a) Convey the object code in, or embodied in, a physical product
241
+ (including a physical distribution medium), accompanied by the
242
+ Corresponding Source fixed on a durable physical medium
243
+ customarily used for software interchange.
244
+
245
+ b) Convey the object code in, or embodied in, a physical product
246
+ (including a physical distribution medium), accompanied by a
247
+ written offer, valid for at least three years and valid for as
248
+ long as you offer spare parts or customer support for that product
249
+ model, to give anyone who possesses the object code either (1) a
250
+ copy of the Corresponding Source for all the software in the
251
+ product that is covered by this License, on a durable physical
252
+ medium customarily used for software interchange, for a price no
253
+ more than your reasonable cost of physically performing this
254
+ conveying of source, or (2) access to copy the
255
+ Corresponding Source from a network server at no charge.
256
+
257
+ c) Convey individual copies of the object code with a copy of the
258
+ written offer to provide the Corresponding Source. This
259
+ alternative is allowed only occasionally and noncommercially, and
260
+ only if you received the object code with such an offer, in accord
261
+ with subsection 6b.
262
+
263
+ d) Convey the object code by offering access from a designated
264
+ place (gratis or for a charge), and offer equivalent access to the
265
+ Corresponding Source in the same way through the same place at no
266
+ further charge. You need not require recipients to copy the
267
+ Corresponding Source along with the object code. If the place to
268
+ copy the object code is a network server, the Corresponding Source
269
+ may be on a different server (operated by you or a third party)
270
+ that supports equivalent copying facilities, provided you maintain
271
+ clear directions next to the object code saying where to find the
272
+ Corresponding Source. Regardless of what server hosts the
273
+ Corresponding Source, you remain obligated to ensure that it is
274
+ available for as long as needed to satisfy these requirements.
275
+
276
+ e) Convey the object code using peer-to-peer transmission, provided
277
+ you inform other peers where the object code and Corresponding
278
+ Source of the work are being offered to the general public at no
279
+ charge under subsection 6d.
280
+
281
+ A separable portion of the object code, whose source code is excluded
282
+ from the Corresponding Source as a System Library, need not be
283
+ included in conveying the object code work.
284
+
285
+ A "User Product" is either (1) a "consumer product", which means any
286
+ tangible personal property which is normally used for personal, family,
287
+ or household purposes, or (2) anything designed or sold for incorporation
288
+ into a dwelling. In determining whether a product is a consumer product,
289
+ doubtful cases shall be resolved in favor of coverage. For a particular
290
+ product received by a particular user, "normally used" refers to a
291
+ typical or common use of that class of product, regardless of the status
292
+ of the particular user or of the way in which the particular user
293
+ actually uses, or expects or is expected to use, the product. A product
294
+ is a consumer product regardless of whether the product has substantial
295
+ commercial, industrial or non-consumer uses, unless such uses represent
296
+ the only significant mode of use of the product.
297
+
298
+ "Installation Information" for a User Product means any methods,
299
+ procedures, authorization keys, or other information required to install
300
+ and execute modified versions of a covered work in that User Product from
301
+ a modified version of its Corresponding Source. The information must
302
+ suffice to ensure that the continued functioning of the modified object
303
+ code is in no case prevented or interfered with solely because
304
+ modification has been made.
305
+
306
+ If you convey an object code work under this section in, or with, or
307
+ specifically for use in, a User Product, and the conveying occurs as
308
+ part of a transaction in which the right of possession and use of the
309
+ User Product is transferred to the recipient in perpetuity or for a
310
+ fixed term (regardless of how the transaction is characterized), the
311
+ Corresponding Source conveyed under this section must be accompanied
312
+ by the Installation Information. But this requirement does not apply
313
+ if neither you nor any third party retains the ability to install
314
+ modified object code on the User Product (for example, the work has
315
+ been installed in ROM).
316
+
317
+ The requirement to provide Installation Information does not include a
318
+ requirement to continue to provide support service, warranty, or updates
319
+ for a work that has been modified or installed by the recipient, or for
320
+ the User Product in which it has been modified or installed. Access to a
321
+ network may be denied when the modification itself materially and
322
+ adversely affects the operation of the network or violates the rules and
323
+ protocols for communication across the network.
324
+
325
+ Corresponding Source conveyed, and Installation Information provided,
326
+ in accord with this section must be in a format that is publicly
327
+ documented (and with an implementation available to the public in
328
+ source code form), and must require no special password or key for
329
+ unpacking, reading or copying.
330
+
331
+ 7. Additional Terms.
332
+
333
+ "Additional permissions" are terms that supplement the terms of this
334
+ License by making exceptions from one or more of its conditions.
335
+ Additional permissions that are applicable to the entire Program shall
336
+ be treated as though they were included in this License, to the extent
337
+ that they are valid under applicable law. If additional permissions
338
+ apply only to part of the Program, that part may be used separately
339
+ under those permissions, but the entire Program remains governed by
340
+ this License without regard to the additional permissions.
341
+
342
+ When you convey a copy of a covered work, you may at your option
343
+ remove any additional permissions from that copy, or from any part of
344
+ it. (Additional permissions may be written to require their own
345
+ removal in certain cases when you modify the work.) You may place
346
+ additional permissions on material, added by you to a covered work,
347
+ for which you have or can give appropriate copyright permission.
348
+
349
+ Notwithstanding any other provision of this License, for material you
350
+ add to a covered work, you may (if authorized by the copyright holders of
351
+ that material) supplement the terms of this License with terms:
352
+
353
+ a) Disclaiming warranty or limiting liability differently from the
354
+ terms of sections 15 and 16 of this License; or
355
+
356
+ b) Requiring preservation of specified reasonable legal notices or
357
+ author attributions in that material or in the Appropriate Legal
358
+ Notices displayed by works containing it; or
359
+
360
+ c) Prohibiting misrepresentation of the origin of that material, or
361
+ requiring that modified versions of such material be marked in
362
+ reasonable ways as different from the original version; or
363
+
364
+ d) Limiting the use for publicity purposes of names of licensors or
365
+ authors of the material; or
366
+
367
+ e) Declining to grant rights under trademark law for use of some
368
+ trade names, trademarks, or service marks; or
369
+
370
+ f) Requiring indemnification of licensors and authors of that
371
+ material by anyone who conveys the material (or modified versions of
372
+ it) with contractual assumptions of liability to the recipient, for
373
+ any liability that these contractual assumptions directly impose on
374
+ those licensors and authors.
375
+
376
+ All other non-permissive additional terms are considered "further
377
+ restrictions" within the meaning of section 10. If the Program as you
378
+ received it, or any part of it, contains a notice stating that it is
379
+ governed by this License along with a term that is a further
380
+ restriction, you may remove that term. If a license document contains
381
+ a further restriction but permits relicensing or conveying under this
382
+ License, you may add to a covered work material governed by the terms
383
+ of that license document, provided that the further restriction does
384
+ not survive such relicensing or conveying.
385
+
386
+ If you add terms to a covered work in accord with this section, you
387
+ must place, in the relevant source files, a statement of the
388
+ additional terms that apply to those files, or a notice indicating
389
+ where to find the applicable terms.
390
+
391
+ Additional terms, permissive or non-permissive, may be stated in the
392
+ form of a separately written license, or stated as exceptions;
393
+ the above requirements apply either way.
394
+
395
+ 8. Termination.
396
+
397
+ You may not propagate or modify a covered work except as expressly
398
+ provided under this License. Any attempt otherwise to propagate or
399
+ modify it is void, and will automatically terminate your rights under
400
+ this License (including any patent licenses granted under the third
401
+ paragraph of section 11).
402
+
403
+ However, if you cease all violation of this License, then your
404
+ license from a particular copyright holder is reinstated (a)
405
+ provisionally, unless and until the copyright holder explicitly and
406
+ finally terminates your license, and (b) permanently, if the copyright
407
+ holder fails to notify you of the violation by some reasonable means
408
+ prior to 60 days after the cessation.
409
+
410
+ Moreover, your license from a particular copyright holder is
411
+ reinstated permanently if the copyright holder notifies you of the
412
+ violation by some reasonable means, this is the first time you have
413
+ received notice of violation of this License (for any work) from that
414
+ copyright holder, and you cure the violation prior to 30 days after
415
+ your receipt of the notice.
416
+
417
+ Termination of your rights under this section does not terminate the
418
+ licenses of parties who have received copies or rights from you under
419
+ this License. If your rights have been terminated and not permanently
420
+ reinstated, you do not qualify to receive new licenses for the same
421
+ material under section 10.
422
+
423
+ 9. Acceptance Not Required for Having Copies.
424
+
425
+ You are not required to accept this License in order to receive or
426
+ run a copy of the Program. Ancillary propagation of a covered work
427
+ occurring solely as a consequence of using peer-to-peer transmission
428
+ to receive a copy likewise does not require acceptance. However,
429
+ nothing other than this License grants you permission to propagate or
430
+ modify any covered work. These actions infringe copyright if you do
431
+ not accept this License. Therefore, by modifying or propagating a
432
+ covered work, you indicate your acceptance of this License to do so.
433
+
434
+ 10. Automatic Licensing of Downstream Recipients.
435
+
436
+ Each time you convey a covered work, the recipient automatically
437
+ receives a license from the original licensors, to run, modify and
438
+ propagate that work, subject to this License. You are not responsible
439
+ for enforcing compliance by third parties with this License.
440
+
441
+ An "entity transaction" is a transaction transferring control of an
442
+ organization, or substantially all assets of one, or subdividing an
443
+ organization, or merging organizations. If propagation of a covered
444
+ work results from an entity transaction, each party to that
445
+ transaction who receives a copy of the work also receives whatever
446
+ licenses to the work the party's predecessor in interest had or could
447
+ give under the previous paragraph, plus a right to possession of the
448
+ Corresponding Source of the work from the predecessor in interest, if
449
+ the predecessor has it or can get it with reasonable efforts.
450
+
451
+ You may not impose any further restrictions on the exercise of the
452
+ rights granted or affirmed under this License. For example, you may
453
+ not impose a license fee, royalty, or other charge for exercise of
454
+ rights granted under this License, and you may not initiate litigation
455
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
456
+ any patent claim is infringed by making, using, selling, offering for
457
+ sale, or importing the Program or any portion of it.
458
+
459
+ 11. Patents.
460
+
461
+ A "contributor" is a copyright holder who authorizes use under this
462
+ License of the Program or a work on which the Program is based. The
463
+ work thus licensed is called the contributor's "contributor version".
464
+
465
+ A contributor's "essential patent claims" are all patent claims
466
+ owned or controlled by the contributor, whether already acquired or
467
+ hereafter acquired, that would be infringed by some manner, permitted
468
+ by this License, of making, using, or selling its contributor version,
469
+ but do not include claims that would be infringed only as a
470
+ consequence of further modification of the contributor version. For
471
+ purposes of this definition, "control" includes the right to grant
472
+ patent sublicenses in a manner consistent with the requirements of
473
+ this License.
474
+
475
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
476
+ patent license under the contributor's essential patent claims, to
477
+ make, use, sell, offer for sale, import and otherwise run, modify and
478
+ propagate the contents of its contributor version.
479
+
480
+ In the following three paragraphs, a "patent license" is any express
481
+ agreement or commitment, however denominated, not to enforce a patent
482
+ (such as an express permission to practice a patent or covenant not to
483
+ sue for patent infringement). To "grant" such a patent license to a
484
+ party means to make such an agreement or commitment not to enforce a
485
+ patent against the party.
486
+
487
+ If you convey a covered work, knowingly relying on a patent license,
488
+ and the Corresponding Source of the work is not available for anyone
489
+ to copy, free of charge and under the terms of this License, through a
490
+ publicly available network server or other readily accessible means,
491
+ then you must either (1) cause the Corresponding Source to be so
492
+ available, or (2) arrange to deprive yourself of the benefit of the
493
+ patent license for this particular work, or (3) arrange, in a manner
494
+ consistent with the requirements of this License, to extend the patent
495
+ license to downstream recipients. "Knowingly relying" means you have
496
+ actual knowledge that, but for the patent license, your conveying the
497
+ covered work in a country, or your recipient's use of the covered work
498
+ in a country, would infringe one or more identifiable patents in that
499
+ country that you have reason to believe are valid.
500
+
501
+ If, pursuant to or in connection with a single transaction or
502
+ arrangement, you convey, or propagate by procuring conveyance of, a
503
+ covered work, and grant a patent license to some of the parties
504
+ receiving the covered work authorizing them to use, propagate, modify
505
+ or convey a specific copy of the covered work, then the patent license
506
+ you grant is automatically extended to all recipients of the covered
507
+ work and works based on it.
508
+
509
+ A patent license is "discriminatory" if it does not include within
510
+ the scope of its coverage, prohibits the exercise of, or is
511
+ conditioned on the non-exercise of one or more of the rights that are
512
+ specifically granted under this License. You may not convey a covered
513
+ work if you are a party to an arrangement with a third party that is
514
+ in the business of distributing software, under which you make payment
515
+ to the third party based on the extent of your activity of conveying
516
+ the work, and under which the third party grants, to any of the
517
+ parties who would receive the covered work from you, a discriminatory
518
+ patent license (a) in connection with copies of the covered work
519
+ conveyed by you (or copies made from those copies), or (b) primarily
520
+ for and in connection with specific products or compilations that
521
+ contain the covered work, unless you entered into that arrangement,
522
+ or that patent license was granted, prior to 28 March 2007.
523
+
524
+ Nothing in this License shall be construed as excluding or limiting
525
+ any implied license or other defenses to infringement that may
526
+ otherwise be available to you under applicable patent law.
527
+
528
+ 12. No Surrender of Others' Freedom.
529
+
530
+ If conditions are imposed on you (whether by court order, agreement or
531
+ otherwise) that contradict the conditions of this License, they do not
532
+ excuse you from the conditions of this License. If you cannot convey a
533
+ covered work so as to satisfy simultaneously your obligations under this
534
+ License and any other pertinent obligations, then as a consequence you may
535
+ not convey it at all. For example, if you agree to terms that obligate you
536
+ to collect a royalty for further conveying from those to whom you convey
537
+ the Program, the only way you could satisfy both those terms and this
538
+ License would be to refrain entirely from conveying the Program.
539
+
540
+ 13. Remote Network Interaction; Use with the GNU General Public License.
541
+
542
+ Notwithstanding any other provision of this License, if you modify the
543
+ Program, your modified version must prominently offer all users
544
+ interacting with it remotely through a computer network (if your version
545
+ supports such interaction) an opportunity to receive the Corresponding
546
+ Source of your version by providing access to the Corresponding Source
547
+ from a network server at no charge, through some standard or customary
548
+ means of facilitating copying of software. This Corresponding Source
549
+ shall include the Corresponding Source for any work covered by version 3
550
+ of the GNU General Public License that is incorporated pursuant to the
551
+ following paragraph.
552
+
553
+ Notwithstanding any other provision of this License, you have
554
+ permission to link or combine any covered work with a work licensed
555
+ under version 3 of the GNU General Public License into a single
556
+ combined work, and to convey the resulting work. The terms of this
557
+ License will continue to apply to the part which is the covered work,
558
+ but the work with which it is combined will remain governed by version
559
+ 3 of the GNU General Public License.
560
+
561
+ 14. Revised Versions of this License.
562
+
563
+ The Free Software Foundation may publish revised and/or new versions of
564
+ the GNU Affero General Public License from time to time. Such new versions
565
+ will be similar in spirit to the present version, but may differ in detail to
566
+ address new problems or concerns.
567
+
568
+ Each version is given a distinguishing version number. If the
569
+ Program specifies that a certain numbered version of the GNU Affero General
570
+ Public License "or any later version" applies to it, you have the
571
+ option of following the terms and conditions either of that numbered
572
+ version or of any later version published by the Free Software
573
+ Foundation. If the Program does not specify a version number of the
574
+ GNU Affero General Public License, you may choose any version ever published
575
+ by the Free Software Foundation.
576
+
577
+ If the Program specifies that a proxy can decide which future
578
+ versions of the GNU Affero General Public License can be used, that proxy's
579
+ public statement of acceptance of a version permanently authorizes you
580
+ to choose that version for the Program.
581
+
582
+ Later license versions may give you additional or different
583
+ permissions. However, no additional obligations are imposed on any
584
+ author or copyright holder as a result of your choosing to follow a
585
+ later version.
586
+
587
+ 15. Disclaimer of Warranty.
588
+
589
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597
+
598
+ 16. Limitation of Liability.
599
+
600
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608
+ SUCH DAMAGES.
609
+
610
+ 17. Interpretation of Sections 15 and 16.
611
+
612
+ If the disclaimer of warranty and limitation of liability provided
613
+ above cannot be given local legal effect according to their terms,
614
+ reviewing courts shall apply local law that most closely approximates
615
+ an absolute waiver of all civil liability in connection with the
616
+ Program, unless a warranty or assumption of liability accompanies a
617
+ copy of the Program in return for a fee.
618
+
619
+ END OF TERMS AND CONDITIONS
620
+
621
+ How to Apply These Terms to Your New Programs
622
+
623
+ If you develop a new program, and you want it to be of the greatest
624
+ possible use to the public, the best way to achieve this is to make it
625
+ free software which everyone can redistribute and change under these terms.
626
+
627
+ To do so, attach the following notices to the program. It is safest
628
+ to attach them to the start of each source file to most effectively
629
+ state the exclusion of warranty; and each file should have at least
630
+ the "copyright" line and a pointer to where the full notice is found.
631
+
632
+ <one line to give the program's name and a brief idea of what it does.>
633
+ Copyright (C) <year> <name of author>
634
+
635
+ This program is free software: you can redistribute it and/or modify
636
+ it under the terms of the GNU Affero General Public License as published by
637
+ the Free Software Foundation, either version 3 of the License, or
638
+ (at your option) any later version.
639
+
640
+ This program is distributed in the hope that it will be useful,
641
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
642
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643
+ GNU Affero General Public License for more details.
644
+
645
+ You should have received a copy of the GNU Affero General Public License
646
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
647
+
648
+ Also add information on how to contact you by electronic and paper mail.
649
+
650
+ If your software can interact with users remotely through a computer
651
+ network, you should also make sure that it provides a way for users to
652
+ get its source. For example, if your program is a web application, its
653
+ interface could display a "Source" link that leads users to an archive
654
+ of the code. There are many ways you could offer source, and different
655
+ solutions will be better for different programs; see section 13 for the
656
+ specific requirements.
657
+
658
+ You should also get your employer (if you work as a programmer) or school,
659
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
660
+ For more information on this, and how to apply and follow the GNU AGPL, see
661
+ <https://www.gnu.org/licenses/>.
app.json ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Ultroid UserBot",
3
+ "description": "Pluggable telegram userbot, made in python using Telethon.",
4
+ "logo": "https://graph.org/file/031957757a4f6a5191040.jpg",
5
+ "keywords": [
6
+ "Telethon",
7
+ "telegram",
8
+ "userbot",
9
+ "python",
10
+ "ultroid"
11
+ ],
12
+ "repository": "https://github.com/TeamUltroid/Ultroid",
13
+ "website": "https://ultroid.tech",
14
+ "success_url": "https://t.me/TeamUltroid",
15
+ "stack": "container",
16
+ "env": {
17
+ "API_ID": {
18
+ "description": "You api id, from my.telegram.org or @ScrapperRoBot.",
19
+ "value": "",
20
+ "required": false
21
+ },
22
+ "API_HASH": {
23
+ "description": "You api hash, from my.telegram.org or @ScrapperRoBot.",
24
+ "value": "",
25
+ "required": false
26
+ },
27
+ "SESSION": {
28
+ "description": "Session String (telethon or pyrogram) for your telegram user account. The userbot will NOT work without a session string!!",
29
+ "value": ""
30
+ },
31
+ "REDIS_URI": {
32
+ "description": "Redis endpoint URL, from redislabs.com",
33
+ "value": ""
34
+ },
35
+ "REDIS_PASSWORD": {
36
+ "description": "Redis endpoint password, from redislabs.com",
37
+ "value": ""
38
+ },
39
+ "HEROKU_API": {
40
+ "description": "Heroku API token. Mandatory for Heroku Deploy...",
41
+ "value": "",
42
+ "required": false
43
+ },
44
+ "HEROKU_APP_NAME": {
45
+ "description": "Name of your Heroku app, given in the first blank on this page. To be added if deploying to heroku ONLY.",
46
+ "value": "",
47
+ "required": false
48
+ }
49
+ },
50
+ "formation": {
51
+ "ultroid": {
52
+ "quantity": 1,
53
+ "size": "eco"
54
+ }
55
+ }
56
+ }
assistant/__init__.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from telethon import Button, custom
9
+
10
+ from plugins import ATRA_COL, InlinePlugin
11
+ from pyUltroid import *
12
+ from pyUltroid import _ult_cache
13
+ from pyUltroid._misc import owner_and_sudos
14
+ from pyUltroid._misc._assistant import asst_cmd, callback, in_pattern
15
+ from pyUltroid.fns.helper import *
16
+ from pyUltroid.fns.tools import get_stored_file
17
+ from strings import get_languages, get_string
18
+
19
+ OWNER_NAME = ultroid_bot.full_name
20
+ OWNER_ID = ultroid_bot.uid
21
+
22
+ AST_PLUGINS = {}
23
+
24
+
25
+ async def setit(event, name, value):
26
+ try:
27
+ udB.set_key(name, value)
28
+ except BaseException as er:
29
+ LOGS.exception(er)
30
+ return await event.edit("`Something Went Wrong`")
31
+
32
+
33
+ def get_back_button(name):
34
+ return [Button.inline("« Bᴀᴄᴋ", data=f"{name}")]
assistant/callbackstuffs.py ADDED
@@ -0,0 +1,1318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ import ast
9
+ import asyncio
10
+ import re
11
+ import sys
12
+ import time
13
+ from asyncio.exceptions import TimeoutError as AsyncTimeOut
14
+ from os import execl, remove
15
+ from random import choice
16
+
17
+ from bs4 import BeautifulSoup as bs
18
+
19
+ try:
20
+ from pyUltroid.fns.gDrive import GDriveManager
21
+ except ImportError:
22
+ GDriveManager = None
23
+ from telethon import Button, events
24
+ from catbox import CatboxUploader
25
+ from telethon.tl.types import MessageMediaWebPage
26
+ from telethon.utils import get_peer_id
27
+
28
+ from pyUltroid.fns.helper import fast_download, progress
29
+ from pyUltroid.fns.tools import Carbon, async_searcher, get_paste, telegraph_client
30
+ from pyUltroid.startup.loader import Loader
31
+
32
+ from . import *
33
+
34
+ # --------------------------------------------------------------------#
35
+ telegraph = telegraph_client()
36
+ GDrive = GDriveManager() if GDriveManager else None
37
+ uploader = CatboxUploader()
38
+ # --------------------------------------------------------------------#
39
+
40
+ def text_to_url(event):
41
+ """function to get media url (with|without) Webpage"""
42
+ if isinstance(event.media, MessageMediaWebPage):
43
+ webpage = event.media.webpage
44
+ if not isinstance(webpage, types.WebPageEmpty) and webpage.type in ["photo"]:
45
+ return webpage.display_url
46
+ return event.text
47
+
48
+
49
+ # --------------------------------------------------------------------#
50
+
51
+ _buttons = {
52
+ "otvars": {
53
+ "text": "Other Variables to set for @TeamUltroid:",
54
+ "buttons": [
55
+ [
56
+ Button.inline("Tᴀɢ Lᴏɢɢᴇʀ", data="taglog"),
57
+ Button.inline("SᴜᴘᴇʀFʙᴀɴ", data="cbs_sfban"),
58
+ ],
59
+ [
60
+ Button.inline("Sᴜᴅᴏ Mᴏᴅᴇ", data="sudo"),
61
+ Button.inline("Hᴀɴᴅʟᴇʀ", data="hhndlr"),
62
+ ],
63
+ [
64
+ Button.inline("Exᴛʀᴀ Pʟᴜɢɪɴs", data="plg"),
65
+ Button.inline("Aᴅᴅᴏɴs", data="eaddon"),
66
+ ],
67
+ [
68
+ Button.inline("Eᴍᴏᴊɪ ɪɴ Hᴇʟᴘ", data="emoj"),
69
+ Button.inline("Sᴇᴛ ɢDʀɪᴠᴇ", data="gdrive"),
70
+ ],
71
+ [
72
+ Button.inline("Iɴʟɪɴᴇ Pɪᴄ", data="inli_pic"),
73
+ Button.inline("Sᴜᴅᴏ HNDLR", data="shndlr"),
74
+ ],
75
+ [Button.inline("Dᴜᴀʟ Mᴏᴅᴇ", "cbs_oofdm")],
76
+ [Button.inline("« Bᴀᴄᴋ", data="setter")],
77
+ ],
78
+ },
79
+ "sfban": {
80
+ "text": "SuperFban Settings:",
81
+ "buttons": [
82
+ [Button.inline("FBᴀɴ Gʀᴏᴜᴘ", data="sfgrp")],
83
+ [Button.inline("Exᴄʟᴜᴅᴇ Fᴇᴅs", data="abs_sfexf")],
84
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_otvars")],
85
+ ],
86
+ },
87
+ "apauto": {
88
+ "text": "This'll auto approve on outgoing messages",
89
+ "buttons": [
90
+ [Button.inline("Aᴜᴛᴏ Aᴘᴘʀᴏᴠᴇ ON", data="apon")],
91
+ [Button.inline("Aᴜᴛᴏ Aᴘᴘʀᴏᴠᴇ OFF", data="apof")],
92
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_pmcstm")],
93
+ ],
94
+ },
95
+ "alvcstm": {
96
+ "text": f"Customise your {HNDLR}alive. Choose from the below options -",
97
+ "buttons": [
98
+ [Button.inline("Aʟɪᴠᴇ Tᴇxᴛ", data="abs_alvtx")],
99
+ [Button.inline("Aʟɪᴠᴇ ᴍᴇᴅɪᴀ", data="alvmed")],
100
+ [Button.inline("Dᴇʟᴇᴛᴇ Aʟɪᴠᴇ Mᴇᴅɪᴀ", data="delmed")],
101
+ [Button.inline("« Bᴀᴄᴋ", data="setter")],
102
+ ],
103
+ },
104
+ "pmcstm": {
105
+ "text": "Customise your PMPERMIT Settings -",
106
+ "buttons": [
107
+ [
108
+ Button.inline("Pᴍ Tᴇxᴛ", data="pmtxt"),
109
+ Button.inline("Pᴍ Mᴇᴅɪᴀ", data="pmmed"),
110
+ ],
111
+ [
112
+ Button.inline("Aᴜᴛᴏ Aᴘᴘʀᴏᴠᴇ", data="cbs_apauto"),
113
+ Button.inline("PMLOGGER", data="pml"),
114
+ ],
115
+ [
116
+ Button.inline("Sᴇᴛ Wᴀʀɴs", data="swarn"),
117
+ Button.inline("Dᴇʟᴇᴛᴇ Pᴍ Mᴇᴅɪᴀ", data="delpmmed"),
118
+ ],
119
+ [Button.inline("PMPermit Type", data="cbs_pmtype")],
120
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_ppmset")],
121
+ ],
122
+ },
123
+ "pmtype": {
124
+ "text": "Select the type of PMPermit needed.",
125
+ "buttons": [
126
+ [Button.inline("Inline", data="inpm_in")],
127
+ [Button.inline("Normal", data="inpm_no")],
128
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_pmcstm")],
129
+ ],
130
+ },
131
+ "ppmset": {
132
+ "text": "PMPermit Settings:",
133
+ "buttons": [
134
+ [Button.inline("Tᴜʀɴ PMPᴇʀᴍɪᴛ Oɴ", data="pmon")],
135
+ [Button.inline("Tᴜʀɴ PMPᴇʀᴍɪᴛ Oғғ", data="pmoff")],
136
+ [Button.inline("Cᴜsᴛᴏᴍɪᴢᴇ PMPᴇʀᴍɪᴛ", data="cbs_pmcstm")],
137
+ [Button.inline("« Bᴀᴄᴋ", data="setter")],
138
+ ],
139
+ },
140
+ "chatbot": {
141
+ "text": "From This Feature U can chat with ppls Via ur Assistant Bot.\n[More info](https://t.me/UltroidUpdates/2)",
142
+ "buttons": [
143
+ [
144
+ Button.inline("Cʜᴀᴛ Bᴏᴛ Oɴ", data="onchbot"),
145
+ Button.inline("Cʜᴀᴛ Bᴏᴛ Oғғ", data="ofchbot"),
146
+ ],
147
+ [
148
+ Button.inline("Bᴏᴛ Wᴇʟᴄᴏᴍᴇ", data="bwel"),
149
+ Button.inline("Bᴏᴛ Wᴇʟᴄᴏᴍᴇ Mᴇᴅɪᴀ", data="botmew"),
150
+ ],
151
+ [Button.inline("Bᴏᴛ Iɴғᴏ Tᴇxᴛ", data="botinfe")],
152
+ [Button.inline("Fᴏʀᴄᴇ Sᴜʙsᴄʀɪʙᴇ", data="pmfs")],
153
+ [Button.inline("« Bᴀᴄᴋ", data="setter")],
154
+ ],
155
+ },
156
+ "vcb": {
157
+ "text": "From This Feature U can play songs in group voice chat\n\n[moreinfo](https://t.me/UltroidUpdates/4)",
158
+ "buttons": [
159
+ [Button.inline("VC Sᴇssɪᴏɴ", data="abs_vcs")],
160
+ [Button.inline("« Bᴀᴄᴋ", data="setter")],
161
+ ],
162
+ },
163
+ "oofdm": {
164
+ "text": "About [Dual Mode](https://t.me/UltroidUpdates/18)",
165
+ "buttons": [
166
+ [
167
+ Button.inline("Dᴜᴀʟ Mᴏᴅᴇ Oɴ", "dmof"),
168
+ Button.inline("Dᴜᴀʟ Mᴏᴅᴇ Oғғ", "dmof"),
169
+ ],
170
+ [Button.inline("Dᴜᴀʟ Mᴏᴅᴇ Hɴᴅʟʀ", "dmhn")],
171
+ [Button.inline("« Back", data="cbs_otvars")],
172
+ ],
173
+ },
174
+ "apiset": {
175
+ "text": get_string("ast_1"),
176
+ "buttons": [
177
+ [Button.inline("Remove.bg API", data="abs_rmbg")],
178
+ [Button.inline("DEEP API", data="abs_dapi")],
179
+ [Button.inline("OCR API", data="abs_oapi")],
180
+ [Button.inline("« Back", data="setter")],
181
+ ],
182
+ },
183
+ }
184
+
185
+ _convo = {
186
+ "rmbg": {
187
+ "var": "RMBG_API",
188
+ "name": "Remove.bg API Key",
189
+ "text": get_string("ast_2"),
190
+ "back": "cbs_apiset",
191
+ },
192
+ "dapi": {
193
+ "var": "DEEP_AI",
194
+ "name": "Deep AI Api Key",
195
+ "text": "Get Your Deep Api from deepai.org and send here.",
196
+ "back": "cbs_apiset",
197
+ },
198
+ "oapi": {
199
+ "var": "OCR_API",
200
+ "name": "Ocr Api Key",
201
+ "text": "Get Your OCR api from ocr.space and send that Here.",
202
+ "back": "cbs_apiset",
203
+ },
204
+ "pmlgg": {
205
+ "var": "PMLOGGROUP",
206
+ "name": "Pm Log Group",
207
+ "text": "Send chat id of chat which you want to save as Pm log Group.",
208
+ "back": "pml",
209
+ },
210
+ "vcs": {
211
+ "var": "VC_SESSION",
212
+ "name": "Vc Session",
213
+ "text": "**Vc session**\nEnter the New session u generated for vc bot.\n\nUse /cancel to terminate the operation.",
214
+ "back": "cbs_vcb",
215
+ },
216
+ "settag": {
217
+ "var": "TAG_LOG",
218
+ "name": "Tag Log Group",
219
+ "text": f"Make a group, add your assistant and make it admin.\nGet the `{HNDLR}id` of that group and send it here for tag logs.\n\nUse /cancel to cancel.",
220
+ "back": "taglog",
221
+ },
222
+ "alvtx": {
223
+ "var": "ALIVE_TEXT",
224
+ "name": "Alive Text",
225
+ "text": "**Alive Text**\nEnter the new alive text.\n\nUse /cancel to terminate the operation.",
226
+ "back": "cbs_alvcstm",
227
+ },
228
+ "sfexf": {
229
+ "var": "EXCLUDE_FED",
230
+ "name": "Excluded Fed",
231
+ "text": "Send the Fed IDs you want to exclude in the ban. Split by a space.\neg`id1 id2 id3`\nSet is as `None` if you dont want any.\nUse /cancel to go back.",
232
+ "back": "cbs_sfban",
233
+ },
234
+ }
235
+
236
+
237
+ TOKEN_FILE = "resources/auths/auth_token.txt"
238
+
239
+
240
+ @callback(
241
+ re.compile(
242
+ "sndplug_(.*)",
243
+ ),
244
+ owner=True,
245
+ )
246
+ async def send(eve):
247
+ key, name = (eve.data_match.group(1)).decode("UTF-8").split("_")
248
+ thumb = "resources/extras/inline.jpg"
249
+ await eve.answer("■ Sending ■")
250
+ data = f"uh_{key}_"
251
+ index = None
252
+ if "|" in name:
253
+ name, index = name.split("|")
254
+ key = "plugins" if key == "Official" else key.lower()
255
+ plugin = f"{key}/{name}.py"
256
+ _ = f"pasta-{plugin}"
257
+ if index is not None:
258
+ data += f"|{index}"
259
+ _ += f"|{index}"
260
+ buttons = [
261
+ [
262
+ Button.inline(
263
+ "« Pᴀsᴛᴇ »",
264
+ data=_,
265
+ )
266
+ ],
267
+ [
268
+ Button.inline("« Bᴀᴄᴋ", data=data),
269
+ ],
270
+ ]
271
+ try:
272
+ await eve.edit(file=plugin, thumb=thumb, buttons=buttons)
273
+ except Exception as er:
274
+ await eve.answer(str(er), alert=True)
275
+
276
+
277
+ heroku_api, app_name = Var.HEROKU_API, Var.HEROKU_APP_NAME
278
+
279
+
280
+ @callback("updatenow", owner=True)
281
+ async def update(eve):
282
+ repo = Repo()
283
+ ac_br = repo.active_branch
284
+ ups_rem = repo.remote("upstream")
285
+ if heroku_api:
286
+ import heroku3
287
+
288
+ try:
289
+ heroku = heroku3.from_key(heroku_api)
290
+ heroku_app = None
291
+ heroku_applications = heroku.apps()
292
+ except BaseException as er:
293
+ LOGS.exception(er)
294
+ return await eve.edit("`Wrong HEROKU_API.`")
295
+ for app in heroku_applications:
296
+ if app.name == app_name:
297
+ heroku_app = app
298
+ if not heroku_app:
299
+ await eve.edit("`Wrong HEROKU_APP_NAME.`")
300
+ repo.__del__()
301
+ return
302
+ await eve.edit(get_string("clst_1"))
303
+ ups_rem.fetch(ac_br)
304
+ repo.git.reset("--hard", "FETCH_HEAD")
305
+ heroku_git_url = heroku_app.git_url.replace(
306
+ "https://", f"https://api:{heroku_api}@"
307
+ )
308
+
309
+ if "heroku" in repo.remotes:
310
+ remote = repo.remote("heroku")
311
+ remote.set_url(heroku_git_url)
312
+ else:
313
+ remote = repo.create_remote("heroku", heroku_git_url)
314
+ try:
315
+ remote.push(refspec=f"HEAD:refs/heads/{ac_br}", force=True)
316
+ except GitCommandError as error:
317
+ await eve.edit(f"`Here is the error log:\n{error}`")
318
+ repo.__del__()
319
+ return
320
+ await eve.edit("`Successfully Updated!\nRestarting, please wait...`")
321
+ else:
322
+ await eve.edit(get_string("clst_1"))
323
+ call_back()
324
+ await bash("git pull && pip3 install -r requirements.txt")
325
+ await bash("pip3 install -r requirements.txt --break-system-packages")
326
+ execl(sys.executable, sys.executable, "-m", "pyUltroid")
327
+
328
+ @callback(re.compile("changes(.*)"), owner=True)
329
+ async def changes(okk):
330
+ match = okk.data_match.group(1).decode("utf-8")
331
+ await okk.answer(get_string("clst_3"))
332
+ repo = Repo.init()
333
+ button = [[Button.inline("Update Now", data="updatenow")]]
334
+ changelog, tl_chnglog = await gen_chlog(
335
+ repo, f"HEAD..upstream/{repo.active_branch}"
336
+ )
337
+ cli = "\n\nClick the below button to update!"
338
+ if not match:
339
+ try:
340
+ if len(tl_chnglog) > 700:
341
+ tl_chnglog = f"{tl_chnglog[:700]}..."
342
+ button.append([Button.inline("View Complete", "changesall")])
343
+ await okk.edit("• Writing Changelogs 📝 •")
344
+ img = await Carbon(
345
+ file_name="changelog",
346
+ code=tl_chnglog,
347
+ backgroundColor=choice(ATRA_COL),
348
+ language="md",
349
+ )
350
+ return await okk.edit(
351
+ f"**• Ultroid Userbot •**{cli}", file=img, buttons=button
352
+ )
353
+ except Exception as er:
354
+ LOGS.exception(er)
355
+ changelog_str = changelog + cli
356
+ if len(changelog_str) > 1024:
357
+ await okk.edit(get_string("upd_4"))
358
+ await asyncio.sleep(2)
359
+ with open("ultroid_updates.txt", "w+") as file:
360
+ file.write(tl_chnglog)
361
+ await okk.edit(
362
+ get_string("upd_5"),
363
+ file="ultroid_updates.txt",
364
+ buttons=button,
365
+ )
366
+ remove("ultroid_updates.txt")
367
+ return
368
+ await okk.edit(
369
+ changelog_str,
370
+ buttons=button,
371
+ parse_mode="html",
372
+ )
373
+
374
+
375
+ @callback(
376
+ re.compile(
377
+ "pasta-(.*)",
378
+ ),
379
+ owner=True,
380
+ )
381
+ async def _(e):
382
+ ok = (e.data_match.group(1)).decode("UTF-8")
383
+ index = None
384
+ if "|" in ok:
385
+ ok, index = ok.split("|")
386
+ with open(ok, "r") as hmm:
387
+ _, data = await get_paste(hmm.read())
388
+ if not data.get("link"):
389
+ return await e.answer(key[:30], alert=True)
390
+ if not key.startswith("http"):
391
+ link, raw = data["link"], data["raw"]
392
+ else:
393
+ link = key
394
+ raw = f"{key}/raw"
395
+ if ok.startswith("addons"):
396
+ key = "Addons"
397
+ elif ok.startswith("vcbot"):
398
+ key = "VCBot"
399
+ else:
400
+ key = "Official"
401
+ data = f"uh_{key}_"
402
+ if index is not None:
403
+ data += f"|{index}"
404
+ await e.edit(
405
+ "",
406
+ buttons=[
407
+ [Button.url("Lɪɴᴋ", link), Button.url("Rᴀᴡ", raw)],
408
+ [Button.inline("« Bᴀᴄᴋ", data=data)],
409
+ ],
410
+ )
411
+
412
+
413
+ @callback(re.compile("cbs_(.*)"), owner=True)
414
+ async def _edit_to(event):
415
+ match = event.data_match.group(1).decode("utf-8")
416
+ data = _buttons.get(match)
417
+ if not data:
418
+ return
419
+ await event.edit(data["text"], buttons=data["buttons"], link_preview=False)
420
+
421
+
422
+ @callback(re.compile("abs_(.*)"), owner=True)
423
+ async def convo_handler(event: events.CallbackQuery):
424
+ match = event.data_match.group(1).decode("utf-8")
425
+ if not _convo.get(match):
426
+ return
427
+ await event.delete()
428
+ get_ = _convo[match]
429
+ back = get_["back"]
430
+ async with event.client.conversation(event.sender_id) as conv:
431
+ await conv.send_message(get_["text"])
432
+ response = await conv.get_response()
433
+ themssg = response.message
434
+ try:
435
+ themssg = ast.literal_eval(themssg)
436
+ except Exception:
437
+ pass
438
+ if themssg == "/cancel":
439
+ return await conv.send_message(
440
+ "Cancelled!!",
441
+ buttons=get_back_button(back),
442
+ )
443
+ await setit(event, get_["var"], themssg)
444
+ await conv.send_message(
445
+ f"{get_['name']} changed to `{themssg}`",
446
+ buttons=get_back_button(back),
447
+ )
448
+
449
+
450
+ @callback("authorise", owner=True)
451
+ async def _(e):
452
+ if not e.is_private:
453
+ return
454
+ url = GDrive._create_token_file()
455
+ await e.edit("Go to the below link and send the code!")
456
+ async with asst.conversation(e.sender_id) as conv:
457
+ await conv.send_message(url)
458
+ code = await conv.get_response()
459
+ if GDrive._create_token_file(code=code.text):
460
+ await conv.send_message(
461
+ "`Success!\nYou are all set to use Google Drive with Ultroid Userbot.`",
462
+ buttons=Button.inline("Main Menu", data="setter"),
463
+ )
464
+ else:
465
+ await conv.send_message("Wrong code! Click authorise again.")
466
+
467
+
468
+ @callback("folderid", owner=True, func=lambda x: x.is_private)
469
+ async def _(e):
470
+ if not e.is_private:
471
+ return
472
+ msg = (
473
+ "Send your FOLDER ID\n\n"
474
+ + "For FOLDER ID:\n"
475
+ + "1. Open Google Drive App.\n"
476
+ + "2. Create Folder.\n"
477
+ + "3. Make that folder public.\n"
478
+ + "4. Send link of that folder."
479
+ )
480
+ await e.delete()
481
+ async with asst.conversation(e.sender_id, timeout=150) as conv:
482
+ await conv.send_message(msg)
483
+ repl = await conv.get_response()
484
+ id = repl.text
485
+ if id.startswith("https"):
486
+ id = id.split("?id=")[-1]
487
+ udB.set_key("GDRIVE_FOLDER_ID", id)
488
+ await repl.reply(
489
+ "`Success.`",
490
+ buttons=get_back_button("gdrive"),
491
+ )
492
+
493
+
494
+ @callback("gdrive", owner=True)
495
+ async def _(e):
496
+ if not e.is_private:
497
+ return
498
+ await e.edit(
499
+ "Click Authorise and send the code.\n\nYou can use your own CLIENT ID and SECRET by [this](https://t.me/UltroidUpdates/37)",
500
+ buttons=[
501
+ [
502
+ Button.inline("Folder ID", data="folderid"),
503
+ Button.inline("Authorise", data="authorise"),
504
+ ],
505
+ [Button.inline("« Back", data="cbs_otvars")],
506
+ ],
507
+ link_preview=False,
508
+ )
509
+
510
+
511
+ @callback("dmof", owner=True)
512
+ async def rhwhe(e):
513
+ if udB.get_key("DUAL_MODE"):
514
+ udB.del_key("DUAL_MODE")
515
+ key = "Off"
516
+ else:
517
+ udB.set_key("DUAL_MODE", "True")
518
+ key = "On"
519
+ Msg = f"Dual Mode : {key}"
520
+ await e.edit(Msg, buttons=get_back_button("cbs_otvars"))
521
+
522
+
523
+ @callback("dmhn", owner=True)
524
+ async def hndlrr(event):
525
+ await event.delete()
526
+ pru = event.sender_id
527
+ var = "DUAL_HNDLR"
528
+ name = "Dual Handler"
529
+ CH = udB.get_key(var) or "/"
530
+ async with event.client.conversation(pru) as conv:
531
+ await conv.send_message(
532
+ f"Send The Symbol Which u want as Handler/Trigger to use your Assistant bot\nUr Current Handler is [ `{CH}` ]\n\n use /cancel to cancel.",
533
+ )
534
+ response = conv.wait_event(events.NewMessage(chats=pru))
535
+ response = await response
536
+ themssg = response.message.message
537
+ if themssg == "/cancel":
538
+ await conv.send_message(
539
+ "Cancelled!!",
540
+ buttons=get_back_button("cbs_otvars"),
541
+ )
542
+ elif len(themssg) > 1:
543
+ await conv.send_message(
544
+ "Incorrect Handler",
545
+ buttons=get_back_button("cbs_otvars"),
546
+ )
547
+ else:
548
+ await setit(event, var, themssg)
549
+ await conv.send_message(
550
+ f"{name} changed to {themssg}",
551
+ buttons=get_back_button("cbs_otvars"),
552
+ )
553
+
554
+
555
+ @callback("emoj", owner=True)
556
+ async def emoji(event):
557
+ await event.delete()
558
+ pru = event.sender_id
559
+ var = "EMOJI_IN_HELP"
560
+ name = f"Emoji in `{HNDLR}help` menu"
561
+ async with event.client.conversation(pru) as conv:
562
+ await conv.send_message("Send emoji u want to set 🙃.\n\nUse /cancel to cancel.")
563
+ response = conv.wait_event(events.NewMessage(chats=pru))
564
+ response = await response
565
+ themssg = response.message.message
566
+ if themssg == "/cancel":
567
+ await conv.send_message(
568
+ "Cancelled!!",
569
+ buttons=get_back_button("cbs_otvars"),
570
+ )
571
+ elif themssg.startswith(("/", HNDLR)):
572
+ await conv.send_message(
573
+ "Incorrect Emoji",
574
+ buttons=get_back_button("cbs_otvars"),
575
+ )
576
+ else:
577
+ await setit(event, var, themssg)
578
+ await conv.send_message(
579
+ f"{name} changed to {themssg}\n",
580
+ buttons=get_back_button("cbs_otvars"),
581
+ )
582
+
583
+
584
+ @callback("plg", owner=True)
585
+ async def pluginch(event):
586
+ await event.delete()
587
+ pru = event.sender_id
588
+ var = "PLUGIN_CHANNEL"
589
+ name = "Plugin Channel"
590
+ async with event.client.conversation(pru) as conv:
591
+ await conv.send_message(
592
+ "Send id or username of a channel from where u want to install all plugins\n\nOur Channel~ @ultroidplugins\n\nUse /cancel to cancel.",
593
+ )
594
+ response = conv.wait_event(events.NewMessage(chats=pru))
595
+ response = await response
596
+ themssg = response.message.message
597
+ if themssg == "/cancel":
598
+ await conv.send_message(
599
+ "Cancelled!!",
600
+ buttons=get_back_button("cbs_otvars"),
601
+ )
602
+ elif themssg.startswith(("/", HNDLR)):
603
+ await conv.send_message(
604
+ "Incorrect channel",
605
+ buttons=get_back_button("cbs_otvars"),
606
+ )
607
+ else:
608
+ await setit(event, var, themssg)
609
+ await conv.send_message(
610
+ f"{name} changed to {themssg}\n After Setting All Things Do Restart",
611
+ buttons=get_back_button("cbs_otvars"),
612
+ )
613
+
614
+
615
+ @callback("hhndlr", owner=True)
616
+ async def hndlrr(event):
617
+ await event.delete()
618
+ pru = event.sender_id
619
+ var = "HNDLR"
620
+ name = "Handler/ Trigger"
621
+ async with event.client.conversation(pru) as conv:
622
+ await conv.send_message(
623
+ f"Send The Symbol Which u want as Handler/Trigger to use bot\nUr Current Handler is [ `{HNDLR}` ]\n\n use /cancel to cancel.",
624
+ )
625
+ response = conv.wait_event(events.NewMessage(chats=pru))
626
+ response = await response
627
+ themssg = response.message.message
628
+ if themssg == "/cancel":
629
+ await conv.send_message(
630
+ "Cancelled!!",
631
+ buttons=get_back_button("cbs_otvars"),
632
+ )
633
+ elif len(themssg) > 1:
634
+ await conv.send_message(
635
+ "Incorrect Handler",
636
+ buttons=get_back_button("cbs_otvars"),
637
+ )
638
+ elif themssg.startswith(("/", "#", "@")):
639
+ await conv.send_message(
640
+ "This cannot be used as handler",
641
+ buttons=get_back_button("cbs_otvars"),
642
+ )
643
+ else:
644
+ await setit(event, var, themssg)
645
+ await conv.send_message(
646
+ f"{name} changed to {themssg}",
647
+ buttons=get_back_button("cbs_otvars"),
648
+ )
649
+
650
+
651
+ @callback("shndlr", owner=True)
652
+ async def hndlrr(event):
653
+ await event.delete()
654
+ pru = event.sender_id
655
+ var = "SUDO_HNDLR"
656
+ name = "Sudo Handler"
657
+ async with event.client.conversation(pru) as conv:
658
+ await conv.send_message(
659
+ "Send The Symbol Which u want as Sudo Handler/Trigger to use bot\n\n use /cancel to cancel."
660
+ )
661
+
662
+ response = conv.wait_event(events.NewMessage(chats=pru))
663
+ response = await response
664
+ themssg = response.message.message
665
+ if themssg == "/cancel":
666
+ await conv.send_message(
667
+ "Cancelled!!",
668
+ buttons=get_back_button("cbs_otvars"),
669
+ )
670
+ elif len(themssg) > 1:
671
+ await conv.send_message(
672
+ "Incorrect Handler",
673
+ buttons=get_back_button("cbs_otvars"),
674
+ )
675
+ elif themssg.startswith(("/", "#", "@")):
676
+ await conv.send_message(
677
+ "This cannot be used as handler",
678
+ buttons=get_back_button("cbs_otvars"),
679
+ )
680
+ else:
681
+ await setit(event, var, themssg)
682
+ await conv.send_message(
683
+ f"{name} changed to {themssg}",
684
+ buttons=get_back_button("cbs_otvars"),
685
+ )
686
+
687
+
688
+ @callback("taglog", owner=True)
689
+ async def tagloggrr(e):
690
+ BUTTON = [
691
+ [Button.inline("SET TAG LOG", data="abs_settag")],
692
+ [Button.inline("DELETE TAG LOG", data="deltag")],
693
+ get_back_button("cbs_otvars"),
694
+ ]
695
+ await e.edit(
696
+ "Choose Options",
697
+ buttons=BUTTON,
698
+ )
699
+
700
+
701
+ @callback("deltag", owner=True)
702
+ async def _(e):
703
+ udB.del_key("TAG_LOG")
704
+ await e.answer("Done!!! Tag Logger has been turned Off")
705
+
706
+
707
+ @callback("eaddon", owner=True)
708
+ async def pmset(event):
709
+ BT = (
710
+ [Button.inline("Aᴅᴅᴏɴs Oғғ", data="edof")]
711
+ if udB.get_key("ADDONS")
712
+ else [Button.inline("Aᴅᴅᴏɴs Oɴ", data="edon")]
713
+ )
714
+
715
+ await event.edit(
716
+ "ADDONS~ Extra Plugins:",
717
+ buttons=[
718
+ BT,
719
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_otvars")],
720
+ ],
721
+ )
722
+
723
+
724
+ @callback("edon", owner=True)
725
+ async def eddon(event):
726
+ var = "ADDONS"
727
+ await setit(event, var, "True")
728
+ await event.edit(
729
+ "Done! ADDONS has been turned on!!\n\n After Setting All Things Do Restart",
730
+ buttons=get_back_button("eaddon"),
731
+ )
732
+
733
+
734
+ @callback("edof", owner=True)
735
+ async def eddof(event):
736
+ udB.set_key("ADDONS", "False")
737
+ await event.edit(
738
+ "Done! ADDONS has been turned off!! After Setting All Things Do Restart",
739
+ buttons=get_back_button("eaddon"),
740
+ )
741
+
742
+
743
+ @callback("sudo", owner=True)
744
+ async def pmset(event):
745
+ BT = (
746
+ [Button.inline("Sᴜᴅᴏ Mᴏᴅᴇ Oғғ", data="ofsudo")]
747
+ if udB.get_key("SUDO")
748
+ else [Button.inline("Sᴜᴅᴏ Mᴏᴅᴇ Oɴ", data="onsudo")]
749
+ )
750
+
751
+ await event.edit(
752
+ f"SUDO MODE ~ Some peoples can use ur Bot which u selected. To know More use `{HNDLR}help sudo`",
753
+ buttons=[
754
+ BT,
755
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_otvars")],
756
+ ],
757
+ )
758
+
759
+
760
+ @callback("onsudo", owner=True)
761
+ async def eddon(event):
762
+ var = "SUDO"
763
+ await setit(event, var, "True")
764
+ await event.edit(
765
+ "Done! SUDO MODE has been turned on!!\n\n After Setting All Things Do Restart",
766
+ buttons=get_back_button("sudo"),
767
+ )
768
+
769
+
770
+ @callback("ofsudo", owner=True)
771
+ async def eddof(event):
772
+ var = "SUDO"
773
+ await setit(event, var, "False")
774
+ await event.edit(
775
+ "Done! SUDO MODE has been turned off!! After Setting All Things Do Restart",
776
+ buttons=get_back_button("sudo"),
777
+ )
778
+
779
+
780
+ @callback("sfgrp", owner=True)
781
+ async def sfgrp(event):
782
+ await event.delete()
783
+ name = "FBan Group ID"
784
+ var = "FBAN_GROUP_ID"
785
+ pru = event.sender_id
786
+ async with asst.conversation(pru) as conv:
787
+ await conv.send_message(
788
+ f"Make a group, add @MissRose_Bot, send `{HNDLR}id`, copy that and send it here.\nUse /cancel to go back.",
789
+ )
790
+ response = conv.wait_event(events.NewMessage(chats=pru))
791
+ response = await response
792
+ themssg = response.message.message
793
+ if themssg == "/cancel":
794
+ return await conv.send_message(
795
+ "Cancelled!!",
796
+ buttons=get_back_button("cbs_sfban"),
797
+ )
798
+ await setit(event, var, themssg)
799
+ await conv.send_message(
800
+ f"{name} changed to {themssg}",
801
+ buttons=get_back_button("cbs_sfban"),
802
+ )
803
+
804
+
805
+ @callback("alvmed", owner=True)
806
+ async def media(event):
807
+ await event.delete()
808
+ pru = event.sender_id
809
+ var = "ALIVE_PIC"
810
+ name = "Alive Media"
811
+ async with event.client.conversation(pru) as conv:
812
+ await conv.send_message(
813
+ "**Alive Media**\nSend me a pic/gif/media to set as alive media.\n\nUse /cancel to terminate the operation.",
814
+ )
815
+ response = await conv.get_response()
816
+ try:
817
+ themssg = response.message
818
+ if themssg == "/cancel":
819
+ return await conv.send_message(
820
+ "Operation cancelled!!",
821
+ buttons=get_back_button("cbs_alvcstm"),
822
+ )
823
+ except BaseException as er:
824
+ LOGS.exception(er)
825
+ if (
826
+ not (response.text).startswith("/")
827
+ and response.text != ""
828
+ and (not response.media or isinstance(response.media, MessageMediaWebPage))
829
+ ):
830
+ url = text_to_url(response)
831
+ elif response.sticker:
832
+ url = response.file.id
833
+ else:
834
+ media = await event.client.download_media(response, "alvpc")
835
+ try:
836
+ url = uploader.upload_file(media)
837
+ remove(media)
838
+ except BaseException as er:
839
+ LOGS.exception(er)
840
+ return await conv.send_message(
841
+ "Terminated.",
842
+ buttons=get_back_button("cbs_alvcstm"),
843
+ )
844
+ await setit(event, var, url)
845
+ await conv.send_message(
846
+ f"{name} has been set.",
847
+ buttons=get_back_button("cbs_alvcstm"),
848
+ )
849
+
850
+
851
+ @callback("delmed", owner=True)
852
+ async def dell(event):
853
+ try:
854
+ udB.del_key("ALIVE_PIC")
855
+ return await event.edit(
856
+ get_string("clst_5"), buttons=get_back_button("cbs_alabs_vcstm")
857
+ )
858
+ except BaseException as er:
859
+ LOGS.exception(er)
860
+ return await event.edit(
861
+ get_string("clst_4"),
862
+ buttons=get_back_button("cbs_alabs_vcstm"),
863
+ )
864
+
865
+
866
+ @callback("inpm_in", owner=True)
867
+ async def inl_on(event):
868
+ var = "INLINE_PM"
869
+ await setit(event, var, "True")
870
+ await event.edit(
871
+ "Done!! PMPermit type has been set to inline!",
872
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_pmtype")]],
873
+ )
874
+
875
+
876
+ @callback("inpm_no", owner=True)
877
+ async def inl_on(event):
878
+ var = "INLINE_PM"
879
+ await setit(event, var, "False")
880
+ await event.edit(
881
+ "Done!! PMPermit type has been set to normal!",
882
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_pmtype")]],
883
+ )
884
+
885
+
886
+ @callback("pmtxt", owner=True)
887
+ async def name(event):
888
+ await event.delete()
889
+ pru = event.sender_id
890
+ var = "PM_TEXT"
891
+ name = "PM Text"
892
+ async with event.client.conversation(pru) as conv:
893
+ await conv.send_message(
894
+ "**PM Text**\nEnter the new Pmpermit text.\n\nu can use `{name}` `{fullname}` `{count}` `{mention}` `{username}` to get this from user Too\n\nUse /cancel to terminate the operation.",
895
+ )
896
+ response = conv.wait_event(events.NewMessage(chats=pru))
897
+ response = await response
898
+ themssg = response.message.message
899
+ if themssg == "/cancel":
900
+ return await conv.send_message(
901
+ "Cancelled!!",
902
+ buttons=get_back_button("cbs_pmcstm"),
903
+ )
904
+ if len(themssg) > 4090:
905
+ return await conv.send_message(
906
+ "Message too long!\nGive a shorter message please!!",
907
+ buttons=get_back_button("cbs_pmcstm"),
908
+ )
909
+ await setit(event, var, themssg)
910
+ await conv.send_message(
911
+ f"{name} changed to {themssg}\n\nAfter Setting All Things Do restart",
912
+ buttons=get_back_button("cbs_pmcstm"),
913
+ )
914
+
915
+
916
+ @callback("swarn", owner=True)
917
+ async def name(event):
918
+ m = range(1, 10)
919
+ tultd = [Button.inline(f"{x}", data=f"wrns_{x}") for x in m]
920
+ lst = list(zip(tultd[::3], tultd[1::3], tultd[2::3]))
921
+ lst.append([Button.inline("« Bᴀᴄᴋ", data="cbs_pmcstm")])
922
+ await event.edit(
923
+ "Select the number of warnings for a user before getting blocked in PMs.",
924
+ buttons=lst,
925
+ )
926
+
927
+
928
+ @callback(re.compile(b"wrns_(.*)"), owner=True)
929
+ async def set_wrns(event):
930
+ value = int(event.data_match.group(1).decode("UTF-8"))
931
+ if dn := udB.set_key("PMWARNS", value):
932
+ await event.edit(
933
+ f"PM Warns Set to {value}.\nNew users will have {value} chances in PMs before getting banned.",
934
+ buttons=get_back_button("cbs_pmcstm"),
935
+ )
936
+ else:
937
+ await event.edit(
938
+ f"Something went wrong, please check your {HNDLR}logs!",
939
+ buttons=get_back_button("cbs_pmcstm"),
940
+ )
941
+
942
+
943
+ @callback("pmmed", owner=True)
944
+ async def media(event):
945
+ await event.delete()
946
+ pru = event.sender_id
947
+ var = "PMPIC"
948
+ name = "PM Media"
949
+ async with event.client.conversation(pru) as conv:
950
+ await conv.send_message(
951
+ "**PM Media**\nSend me a pic/gif/sticker/link to set as pmpermit media.\n\nUse /cancel to terminate the operation.",
952
+ )
953
+ response = await conv.get_response()
954
+ try:
955
+ themssg = response.message
956
+ if themssg == "/cancel":
957
+ return await conv.send_message(
958
+ "Operation cancelled!!",
959
+ buttons=get_back_button("cbs_pmcstm"),
960
+ )
961
+ except BaseException as er:
962
+ LOGS.exception(er)
963
+ media = await event.client.download_media(response, "pmpc")
964
+ if (
965
+ not (response.text).startswith("/")
966
+ and response.text != ""
967
+ and (not response.media or isinstance(response.media, MessageMediaWebPage))
968
+ ):
969
+ url = text_to_url(response)
970
+ elif response.sticker:
971
+ url = response.file.id
972
+ else:
973
+ try:
974
+ url = uploader.upload_file(media)
975
+ remove(media)
976
+ except BaseException as er:
977
+ LOGS.exception(er)
978
+ return await conv.send_message(
979
+ "Terminated.",
980
+ buttons=get_back_button("cbs_pmcstm"),
981
+ )
982
+ await setit(event, var, url)
983
+ await conv.send_message(
984
+ f"{name} has been set.",
985
+ buttons=get_back_button("cbs_pmcstm"),
986
+ )
987
+
988
+
989
+ @callback("delpmmed", owner=True)
990
+ async def dell(event):
991
+ try:
992
+ udB.del_key("PMPIC")
993
+ return await event.edit(
994
+ get_string("clst_5"), buttons=get_back_button("cbs_pmcstm")
995
+ )
996
+ except BaseException as er:
997
+ LOGS.exception(er)
998
+ return await event.edit(
999
+ get_string("clst_4"),
1000
+ buttons=[[Button.inline("« Sᴇᴛᴛɪɴɢs", data="setter")]],
1001
+ )
1002
+
1003
+
1004
+ @callback("apon", owner=True)
1005
+ async def apon(event):
1006
+ var = "AUTOAPPROVE"
1007
+ await setit(event, var, "True")
1008
+ await event.edit(
1009
+ "Done!! AUTOAPPROVE Started!!",
1010
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_apauto")]],
1011
+ )
1012
+
1013
+
1014
+ @callback("apof", owner=True)
1015
+ async def apof(event):
1016
+ try:
1017
+ udB.set_key("AUTOAPPROVE", "False")
1018
+ return await event.edit(
1019
+ "Done! AUTOAPPROVE Stopped!!",
1020
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_apauto")]],
1021
+ )
1022
+ except BaseException as er:
1023
+ LOGS.exception(er)
1024
+ return await event.edit(
1025
+ get_string("clst_4"),
1026
+ buttons=[[Button.inline("« Sᴇᴛᴛɪɴɢs", data="setter")]],
1027
+ )
1028
+
1029
+
1030
+ @callback("pml", owner=True)
1031
+ async def l_vcs(event):
1032
+ BT = (
1033
+ [Button.inline("PMLOGGER OFF", data="pmlogof")]
1034
+ if udB.get_key("PMLOG")
1035
+ else [Button.inline("PMLOGGER ON", data="pmlog")]
1036
+ )
1037
+
1038
+ await event.edit(
1039
+ "PMLOGGER This Will Forward Ur Pm to Ur Private Group -",
1040
+ buttons=[
1041
+ BT,
1042
+ [Button.inline("PᴍLᴏɢɢᴇʀ Gʀᴏᴜᴘ", "abs_pmlgg")],
1043
+ [Button.inline("« Bᴀᴄᴋ", data="cbs_pmcstm")],
1044
+ ],
1045
+ )
1046
+
1047
+
1048
+ @callback("pmlog", owner=True)
1049
+ async def pmlog(event):
1050
+ await setit(event, "PMLOG", "True")
1051
+ await event.edit(
1052
+ "Done!! PMLOGGER Started!!",
1053
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="pml")]],
1054
+ )
1055
+
1056
+
1057
+ @callback("pmlogof", owner=True)
1058
+ async def pmlogof(event):
1059
+ try:
1060
+ udB.del_key("PMLOG")
1061
+ return await event.edit(
1062
+ "Done! PMLOGGER Stopped!!",
1063
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="pml")]],
1064
+ )
1065
+ except BaseException as er:
1066
+ LOGS.exception(er)
1067
+ return await event.edit(
1068
+ get_string("clst_4"),
1069
+ buttons=[[Button.inline("« Sᴇᴛᴛɪɴɢs", data="setter")]],
1070
+ )
1071
+
1072
+
1073
+ @callback("pmon", owner=True)
1074
+ async def pmonn(event):
1075
+ var = "PMSETTING"
1076
+ await setit(event, var, "True")
1077
+ await event.edit(
1078
+ "Done! PMPermit has been turned on!!",
1079
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_ppmset")]],
1080
+ )
1081
+
1082
+
1083
+ @callback("pmoff", owner=True)
1084
+ async def pmofff(event):
1085
+ var = "PMSETTING"
1086
+ await setit(event, var, "False")
1087
+ await event.edit(
1088
+ "Done! PMPermit has been turned off!!",
1089
+ buttons=[[Button.inline("« Bᴀᴄᴋ", data="cbs_ppmset")]],
1090
+ )
1091
+
1092
+
1093
+ @callback("botmew", owner=True)
1094
+ async def hhh(e):
1095
+ async with e.client.conversation(e.chat_id) as conv:
1096
+ await conv.send_message("Send Any Media to keep at your Bot's welcome ")
1097
+ msg = await conv.get_response()
1098
+ if not msg.media or msg.text.startswith("/"):
1099
+ return await conv.send_message(
1100
+ "Terminated!", buttons=get_back_button("cbs_chatbot")
1101
+ )
1102
+ udB.set_key("STARTMEDIA", msg.file.id)
1103
+ await conv.send_message("Done!", buttons=get_back_button("cbs_chatbot"))
1104
+
1105
+
1106
+ @callback("botinfe", owner=True)
1107
+ async def hhh(e):
1108
+ async with e.client.conversation(e.chat_id) as conv:
1109
+ await conv.send_message(
1110
+ "Send message to set to Display, when user Press Info button in Bot Welcome!\n\nsend `False` to completely remove that button.."
1111
+ )
1112
+ msg = await conv.get_response()
1113
+ if msg.media or msg.text.startswith("/"):
1114
+ return await conv.send_message(
1115
+ "Terminated!", buttons=get_back_button("cbs_chatbot")
1116
+ )
1117
+ udB.set_key("BOT_INFO_START", msg.text)
1118
+ await conv.send_message("Done!", buttons=get_back_button("cbs_chatbot"))
1119
+
1120
+
1121
+ @callback("pmfs", owner=True)
1122
+ async def heheh(event):
1123
+ Ll = []
1124
+ err = ""
1125
+ async with event.client.conversation(event.chat_id) as conv:
1126
+ await conv.send_message(
1127
+ "• Send The Chat Id(s), which you want user to Join Before using Chat/Pm Bot\n\n• Send /clear to disable PmBot Force sub..\n• • Send /cancel to stop this process.."
1128
+ )
1129
+ await conv.send_message(
1130
+ "Example : \n`-1001234567\n-100778888`\n\nFor Multiple Chat(s)."
1131
+ )
1132
+ try:
1133
+ msg = await conv.get_response()
1134
+ except AsyncTimeOut:
1135
+ return await conv.send_message("**• TimeUp!**\nStart from /start back.")
1136
+ if not msg.text or msg.text.startswith("/"):
1137
+ timyork = "Cancelled!"
1138
+ if msg.text == "/clear":
1139
+ udB.del_key("PMBOT_FSUB")
1140
+ timyork = "Done! Force Subscribe Stopped\nRestart your Bot!"
1141
+ return await conv.send_message(
1142
+ "Cancelled!", buttons=get_back_button("cbs_chatbot")
1143
+ )
1144
+ for chat in msg.message.split("\n"):
1145
+ if chat.startswith("-") or chat.isdigit():
1146
+ chat = int(chat)
1147
+ try:
1148
+ CHSJSHS = await event.client.get_entity(chat)
1149
+ Ll.append(get_peer_id(CHSJSHS))
1150
+ except Exception as er:
1151
+ err += f"**{chat}** : {er}\n"
1152
+ if err:
1153
+ return await conv.send_message(err)
1154
+ udB.set_key("PMBOT_FSUB", str(Ll))
1155
+ await conv.send_message(
1156
+ "Done!\nRestart Your Bot.", buttons=get_back_button("cbs_chatbot")
1157
+ )
1158
+
1159
+
1160
+ @callback("bwel", owner=True)
1161
+ async def name(event):
1162
+ await event.delete()
1163
+ pru = event.sender_id
1164
+ var = "STARTMSG"
1165
+ name = "Bot Welcome Message:"
1166
+ async with event.client.conversation(pru) as conv:
1167
+ await conv.send_message(
1168
+ "**BOT WELCOME MSG**\nEnter the msg which u want to show when someone start your assistant Bot.\nYou Can use `{me}` , `{mention}` Parameters Too\nUse /cancel to terminate the operation.",
1169
+ )
1170
+ response = conv.wait_event(events.NewMessage(chats=pru))
1171
+ response = await response
1172
+ themssg = response.message.message
1173
+ if themssg == "/cancel":
1174
+ return await conv.send_message(
1175
+ "Cancelled!!",
1176
+ buttons=get_back_button("cbs_chatbot"),
1177
+ )
1178
+ await setit(event, var, themssg)
1179
+ await conv.send_message(
1180
+ f"{name} changed to {themssg}",
1181
+ buttons=get_back_button("cbs_chatbot"),
1182
+ )
1183
+
1184
+
1185
+ @callback("onchbot", owner=True)
1186
+ async def chon(event):
1187
+ var = "PMBOT"
1188
+ await setit(event, var, "True")
1189
+ Loader(path="assistant/pmbot.py", key="PM Bot").load()
1190
+ if AST_PLUGINS.get("pmbot"):
1191
+ for i, e in AST_PLUGINS["pmbot"]:
1192
+ event.client.remove_event_handler(i)
1193
+ for i, e in AST_PLUGINS["pmbot"]:
1194
+ event.client.add_event_handler(i, events.NewMessage(**e))
1195
+ await event.edit(
1196
+ "Done! Now u Can Chat With People Via This Bot",
1197
+ buttons=[Button.inline("« Bᴀᴄᴋ", data="cbs_chatbot")],
1198
+ )
1199
+
1200
+
1201
+ @callback("ofchbot", owner=True)
1202
+ async def chon(event):
1203
+ var = "PMBOT"
1204
+ await setit(event, var, "False")
1205
+ if AST_PLUGINS.get("pmbot"):
1206
+ for i, e in AST_PLUGINS["pmbot"]:
1207
+ event.client.remove_event_handler(i)
1208
+ await event.edit(
1209
+ "Done! Chat People Via This Bot Stopped.",
1210
+ buttons=[Button.inline("« Bᴀᴄᴋ", data="cbs_chatbot")],
1211
+ )
1212
+
1213
+
1214
+ @callback("inli_pic", owner=True)
1215
+ async def media(event):
1216
+ await event.delete()
1217
+ pru = event.sender_id
1218
+ var = "INLINE_PIC"
1219
+ name = "Inline Media"
1220
+ async with event.client.conversation(pru) as conv:
1221
+ await conv.send_message(
1222
+ "**Inline Media**\nSend me a pic/gif/ or link to set as inline media.\n\nUse /cancel to terminate the operation.",
1223
+ )
1224
+ response = await conv.get_response()
1225
+ try:
1226
+ themssg = response.message
1227
+ if themssg == "/cancel":
1228
+ return await conv.send_message(
1229
+ "Operation cancelled!!",
1230
+ buttons=get_back_button("setter"),
1231
+ )
1232
+ except BaseException as er:
1233
+ LOGS.exception(er)
1234
+ media = await event.client.download_media(response, "inlpic")
1235
+ if (
1236
+ not (response.text).startswith("/")
1237
+ and response.text != ""
1238
+ and (not response.media or isinstance(response.media, MessageMediaWebPage))
1239
+ ):
1240
+ url = text_to_url(response)
1241
+ else:
1242
+ try:
1243
+ url = uploader.upload_file(media)
1244
+ remove(media)
1245
+ except BaseException as er:
1246
+ LOGS.exception(er)
1247
+ return await conv.send_message(
1248
+ "Terminated.",
1249
+ buttons=get_back_button("setter"),
1250
+ )
1251
+ await setit(event, var, url)
1252
+ await conv.send_message(
1253
+ f"{name} has been set.",
1254
+ buttons=get_back_button("setter"),
1255
+ )
1256
+
1257
+
1258
+ FD_MEDIA = {}
1259
+
1260
+
1261
+ @callback(re.compile("fd(.*)"), owner=True)
1262
+ async def fdroid_dler(event):
1263
+ uri = event.data_match.group(1).decode("utf-8")
1264
+ if FD_MEDIA.get(uri):
1265
+ return await event.edit(file=FD_MEDIA[uri])
1266
+ await event.answer("• Starting Download •", alert=True)
1267
+ await event.edit("• Downloading.. •")
1268
+ URL = f"https://f-droid.org/packages/{uri}"
1269
+ conte = await async_searcher(URL, re_content=True)
1270
+ BSC = bs(conte, "html.parser", from_encoding="utf-8")
1271
+ dl_ = BSC.find("p", "package-version-download").find("a")["href"]
1272
+ title = BSC.find("h3", "package-name").text.strip()
1273
+ thumb = BSC.find("img", "package-icon")["src"]
1274
+ if thumb.startswith("/"):
1275
+ thumb = f"https://f-droid.org{thumb}"
1276
+ thumb, _ = await fast_download(thumb, filename=f"{uri}.png")
1277
+ s_time = time.time()
1278
+ file, _ = await fast_download(
1279
+ dl_,
1280
+ filename=f"{title}.apk",
1281
+ progress_callback=lambda d, t: asyncio.get_event_loop().create_task(
1282
+ progress(
1283
+ d,
1284
+ t,
1285
+ event,
1286
+ s_time,
1287
+ "Downloading...",
1288
+ )
1289
+ ),
1290
+ )
1291
+
1292
+ time.time()
1293
+ n_file = await event.client.fast_uploader(
1294
+ file, show_progress=True, event=event, message="Uploading...", to_delete=True
1295
+ )
1296
+ buttons = Button.switch_inline("Search Back", query="fdroid", same_peer=True)
1297
+ try:
1298
+ msg = await event.edit(
1299
+ f"**• [{title}]({URL}) •**", file=n_file, thumb=thumb, buttons=buttons
1300
+ )
1301
+ except Exception as er:
1302
+ LOGS.exception(er)
1303
+ try:
1304
+ msg = await event.client.edit_message(
1305
+ await event.get_input_chat(),
1306
+ event.message_id,
1307
+ f"**• [{title}]({URL}) •**",
1308
+ buttons=buttons,
1309
+ thumb=thumb,
1310
+ file=n_file,
1311
+ )
1312
+ except Exception as er:
1313
+ os.remove(thumb)
1314
+ LOGS.exception(er)
1315
+ return await event.edit(f"**ERROR**: `{er}`", buttons=buttons)
1316
+ if msg and hasattr(msg, "media"):
1317
+ FD_MEDIA.update({uri: msg.media})
1318
+ os.remove(thumb)
assistant/games.py ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ """
9
+ • `{i}akinator` | `/akinator`
10
+ Start akinator game from Userbot/Assistant
11
+
12
+ • `/startgame`
13
+ Open Portal for Games
14
+ """
15
+
16
+ import asyncio
17
+ import re, uuid, operator
18
+ from random import choice, shuffle
19
+
20
+ from akipy.async_akipy import Akinator
21
+ from telethon.errors.rpcerrorlist import BotMethodInvalidError
22
+ from telethon.events import Raw
23
+ from telethon.tl.types import InputMediaPoll, Poll, PollAnswer, UpdateMessagePollVote
24
+
25
+ from pyUltroid._misc._decorators import ultroid_cmd
26
+ from logging import getLogger
27
+ from html import unescape
28
+ from telethon.tl.types import TextWithEntities
29
+ from pyUltroid.fns.helper import inline_mention
30
+ from pyUltroid.fns.tools import async_searcher
31
+ from telethon.errors import ChatSendStickersForbiddenError
32
+
33
+ from . import * # Ensure this import matches your project structure
34
+
35
+ games = {}
36
+ aki_photo = "https://graph.org/file/3cc8825c029fd0cab9edc.jpg"
37
+
38
+ akipyLOGS = getLogger("akipy")
39
+
40
+ @ultroid_cmd(pattern="akinator")
41
+ async def akina(e):
42
+ sta = Akinator()
43
+ games[e.chat_id] = {e.id: sta}
44
+ LOGS.info(f"Game started for chat {e.chat_id} with ID {e.id}.")
45
+ try:
46
+ m = await e.client.inline_query(asst.me.username, f"aki_{e.chat_id}_{e.id}")
47
+ await m[0].click(e.chat_id)
48
+ akipyLOGS.info(f"Clicked inline result for chat {e.chat_id}")
49
+ except BotMethodInvalidError as err:
50
+ akipyLOGS.error(f"BotMethodInvalidError: {err}")
51
+ await asst.send_file(
52
+ e.chat_id,
53
+ aki_photo,
54
+ buttons=Button.inline(get_string("aki_2"), data=f"aki_{e.chat_id}_{e.id}"),
55
+ )
56
+ except Exception as er:
57
+ akipyLOGS.error(f"Unexpected error: {er}")
58
+ return await e.eor(f"ERROR : {er}")
59
+ if e.out:
60
+ await e.delete()
61
+
62
+
63
+ @asst_cmd(pattern="akinator", owner=True)
64
+ async def _akokk(e):
65
+ await akina(e)
66
+
67
+
68
+ @callback(re.compile("aki_(.*)"), owner=True)
69
+ async def doai(e):
70
+ adt = e.pattern_match.group(1).strip().decode("utf-8")
71
+ dt = adt.split("_")
72
+ ch = int(dt[0])
73
+ mid = int(dt[1])
74
+ await e.edit(get_string("com_1"))
75
+ try:
76
+ await games[ch][mid].start_game(child_mode=False)
77
+ bts = [Button.inline(o, f"aka_{adt}_{o}") for o in ["Yes", "No", "Idk"]]
78
+ cts = [Button.inline(o, f"aka_{adt}_{o}") for o in ["Probably", "Probably Not"]]
79
+ bts = [bts, cts]
80
+ await e.edit(f"Q. {games[ch][mid].question}", buttons=bts)
81
+ except KeyError:
82
+ return await e.answer(get_string("aki_1"), alert=True)
83
+
84
+
85
+ @callback(re.compile("aka_(.*)"), owner=True)
86
+ async def okah(e):
87
+ try:
88
+ mk = e.pattern_match.group(1).decode("utf-8").split("_")
89
+ #akipyLOGS.info(f"Parsed values: {mk}")
90
+
91
+ if len(mk) < 3:
92
+ akipyLOGS.error("Pattern match did not return enough parts.")
93
+ return await e.answer("Invalid data received.", alert=True)
94
+
95
+ ch = int(mk[0])
96
+ mid = int(mk[1])
97
+ ans = mk[2]
98
+
99
+ gm = games[ch][mid]
100
+ await gm.answer(ans)
101
+
102
+ # Check for the final guess in the API response
103
+ if gm.name_proposition and gm.description_proposition:
104
+ gm.win = True
105
+ text = f"It's {gm.name_proposition}\n{gm.description_proposition}"
106
+ await e.edit(text, file=gm.photo)
107
+ else:
108
+ # Game is not won yet, continue asking questions
109
+ buttons = [
110
+ [Button.inline(o, f"aka_{ch}_{mid}_{o}") for o in ["Yes", "No", "Idk"]],
111
+ [Button.inline(o, f"aka_{ch}_{mid}_{o}") for o in ["Probably", "Probably Not"]],
112
+ ]
113
+ await e.edit(gm.question, buttons=buttons)
114
+
115
+ except KeyError:
116
+ await e.answer(get_string("aki_3"))
117
+ except Exception as ex:
118
+ akipyLOGS.error(f"An unexpected error occurred: {ex}")
119
+
120
+
121
+ @in_pattern(re.compile("aki_?(.*)"), owner=True)
122
+ async def eiagx(e):
123
+ bts = Button.inline(get_string("aki_2"), data=e.text)
124
+ ci = types.InputWebDocument(aki_photo, 0, "image/jpeg", [])
125
+ ans = [
126
+ await e.builder.article(
127
+ "Akinator",
128
+ type="photo",
129
+ content=ci,
130
+ text="Akinator",
131
+ thumb=ci,
132
+ buttons=bts,
133
+ include_media=True,
134
+ )
135
+ ]
136
+ await e.answer(ans)
137
+
138
+
139
+ # ----------------------- Main Command ------------------- #
140
+
141
+ GIMAGE = "https://graph.org/file/1c51015bae5205a65fd69.jpg"
142
+
143
+ @asst_cmd(pattern="startgame", owner=True)
144
+ async def magic(event):
145
+ buttons = [
146
+ [Button.inline("Trivia Quiz", "trzia")],
147
+ [Button.inline("Cancel ❌", "delit")],
148
+ ]
149
+ await event.reply(
150
+ get_string("games_1"),
151
+ file=GIMAGE,
152
+ buttons=buttons,
153
+ )
154
+
155
+
156
+ # -------------------------- Trivia ----------------------- #
157
+
158
+ TR_BTS = {}
159
+ DIFI_KEYS = ["Easy", "Medium", "Hard"]
160
+ TRIVIA_CHATS = {}
161
+ POLLS = {}
162
+ CONGO_STICKER = [
163
+ "CAADAgADSgIAAladvQrJasZoYBh68AI",
164
+ "CAADAgADXhIAAuyZKUl879mlR_dkOwI",
165
+ "CAADAgADpQAD9wLID-xfZCDwOI5LAg",
166
+ "CAADAgADjAADECECEFZM-SrKO9GgAg",
167
+ "CAADAgADSwIAAj-VzArAzNCDiGWAHAI",
168
+ "CAADAgADhQADwZxgDIuMHR9IU10iAg",
169
+ "CAADAgADiwMAAsSraAuoe2BwYu1sdQI",
170
+ ]
171
+
172
+
173
+ @callback("delit", owner=True)
174
+ async def delete_it(event):
175
+ await event.delete()
176
+
177
+
178
+ @callback(re.compile("ctdown(.*)"), owner=True)
179
+ async def ct_spam(e):
180
+ n = e.data_match.group(1).decode("utf-8")
181
+ await e.answer(f"Wait {n} seconds..", alert=True)
182
+
183
+
184
+ @callback(re.compile("trzia(.*)"), owner=True)
185
+ async def choose_cata(event):
186
+ match = event.data_match.group(1).decode("utf-8")
187
+ if not match:
188
+ if TR_BTS.get("category"):
189
+ buttons = TR_BTS["category"]
190
+ else:
191
+ req = (
192
+ await async_searcher(
193
+ "https://opentdb.com/api_category.php", re_json=True
194
+ )
195
+ )["trivia_categories"]
196
+ btt = []
197
+ for i in req:
198
+ name = i["name"]
199
+ if ":" in name:
200
+ name = name.split(":")[1]
201
+ btt.append(Button.inline(name, f"trziad_{i['id']}"))
202
+ buttons = list(zip(btt[::2], btt[1::2]))
203
+ if len(btt) % 2 == 1:
204
+ buttons.append((btt[-1],))
205
+ buttons.append([Button.inline("Cancel ❌", "delit")])
206
+ TR_BTS.update({"category": buttons})
207
+ text = get_string("games_2")
208
+ elif match[0] == "d":
209
+ cat = match[1:]
210
+ buttons = [[Button.inline(i, f"trziac{cat}_{i}") for i in DIFI_KEYS]]
211
+ buttons.append(get_back_button("trzia"))
212
+ text = get_string("games_3")
213
+ elif match[0] == "c":
214
+ m = match[1:]
215
+ buttons = [[Button.inline(str(i), f"trziat{m}_{i}") for i in range(10, 70, 20)]]
216
+ text = get_string("games_4")
217
+ elif match[0] == "t":
218
+ m_ = match[1:]
219
+ buttons = [
220
+ [Button.inline(str(i), f"trzias{m_}_{i}") for i in [10, 30, 60, 120]]
221
+ ]
222
+ text = get_string("games_5")
223
+ elif match[0] == "s":
224
+ chat = event.chat_id
225
+ cat, le, nu, in_ = match[2:].split("_")
226
+ msg = await event.edit(get_string("games_6").format(le, nu))
227
+ for i in reversed(range(5)):
228
+ msg = await msg.edit(buttons=Button.inline(f"{i} ⏰", f"ctdown{i}"))
229
+ await asyncio.sleep(1)
230
+ await msg.edit(
231
+ msg.text + "\n\n• Send /cancel to stop the Quiz...", buttons=None
232
+ )
233
+ qsss = await async_searcher(
234
+ f"https://opentdb.com/api.php?amount={nu}&category={cat}&difficulty={le.lower()}",
235
+ re_json=True,
236
+ )
237
+ qs = qsss["results"]
238
+ if not qs:
239
+ await event.respond("Sorry, No Question Found for the given Criteria..")
240
+ await event.delete()
241
+ return
242
+ TRIVIA_CHATS.update({chat: {}})
243
+ for copper, q in enumerate(qs):
244
+ if TRIVIA_CHATS[chat].get("cancel") is not None:
245
+ break
246
+ ansi = str(uuid.uuid1()).split("-")[0].encode()
247
+ opts = [PollAnswer(TextWithEntities(unescape(q["correct_answer"]), entities=[]), ansi)]
248
+ [
249
+ opts.append(
250
+ PollAnswer(TextWithEntities(unescape(a), entities=[]), str(uuid.uuid1()).split("-")[0].encode())
251
+ )
252
+ for a in q["incorrect_answers"]
253
+ ]
254
+ shuffle(opts)
255
+ poll = InputMediaPoll(
256
+ Poll(
257
+ 0,
258
+ TextWithEntities(
259
+ f"[{copper+1}]. " + unescape(q["question"]),
260
+ entities=[]
261
+ ),
262
+ answers=opts,
263
+ public_voters=True,
264
+ quiz=True,
265
+ close_period=int(in_),
266
+ ),
267
+ correct_answers=[ansi],
268
+ solution="Join @TeamUltroid",
269
+ solution_entities=[],
270
+ )
271
+ m_ = await event.client.send_message(chat, file=poll)
272
+ POLLS.update({m_.poll.poll.id: {"chat": m_.chat_id, "answer": ansi}})
273
+ await asyncio.sleep(int(in_))
274
+ if not TRIVIA_CHATS[chat]:
275
+ await event.respond(
276
+ "No-One Got Any Score in the Quiz!\nBetter Luck Next Time!"
277
+ )
278
+ else:
279
+ try:
280
+ await event.respond(file=choice(CONGO_STICKER))
281
+ except ChatSendStickersForbiddenError:
282
+ pass
283
+ LBD = "🎯 **Scoreboard of the Quiz.**\n\n"
284
+ TRC = TRIVIA_CHATS[chat]
285
+ if "cancel" in TRC.keys():
286
+ del TRC["cancel"]
287
+ for userid, user_score in dict(
288
+ sorted(TRC.items(), key=operator.itemgetter(1), reverse=True)
289
+ ).items():
290
+ user = inline_mention(await event.client.get_entity(userid))
291
+ LBD += f"••• {user} - {user_score}\n"
292
+ await event.respond(LBD)
293
+ del TRIVIA_CHATS[chat]
294
+ list_ = list(POLLS.copy().keys())
295
+ for key in list_:
296
+ if POLLS[key]["chat"] == chat:
297
+ del POLLS[key]
298
+ return
299
+ await event.edit(text, buttons=buttons)
300
+
301
+
302
+ @asst.on(
303
+ Raw(UpdateMessagePollVote, func=lambda x: TRIVIA_CHATS and POLLS.get(x.poll_id))
304
+ )
305
+ async def pollish(eve: UpdateMessagePollVote):
306
+ if POLLS.get(eve.poll_id)["chat"] not in TRIVIA_CHATS.keys():
307
+ return
308
+ if not eve.options:
309
+ # Consider as correct answer if no options selected
310
+ chat = POLLS.get(eve.poll_id)["chat"]
311
+ user = eve.peer.user_id
312
+ if not TRIVIA_CHATS.get(chat, {}).get(user):
313
+ TRIVIA_CHATS[chat][user] = 1
314
+ else:
315
+ TRIVIA_CHATS[chat][user] += 1
316
+ return
317
+ if POLLS[eve.poll_id]["answer"] != eve.options[0]:
318
+ return
319
+ chat = POLLS.get(eve.poll_id)["chat"]
320
+ user = eve.peer.user_id
321
+ if not TRIVIA_CHATS.get(chat, {}).get(user):
322
+ TRIVIA_CHATS[chat][user] = 1
323
+ else:
324
+ TRIVIA_CHATS[chat][user] += 1
325
+
326
+
327
+ @asst_cmd("cancel", owner=True, func=lambda x: TRIVIA_CHATS.get(x.chat_id))
328
+ async def cancelish(event):
329
+ chat = TRIVIA_CHATS.get(event.chat_id)
330
+ chat.update({"cancel": True})
331
+ await event.respond("Cancelled!")
assistant/initial.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ import re
9
+
10
+ from . import *
11
+
12
+ STRINGS = {
13
+ 1: """🎇 **Thanks for Deploying Ultroid Userbot!**
14
+
15
+ • Here, are the Some Basic stuff from, where you can Know, about its Usage.""",
16
+ 2: """🎉** About Ultroid**
17
+
18
+ 🧿 Ultroid is Pluggable and powerful Telethon Userbot, made in Python from Scratch. It is Aimed to Increase Security along with Addition of Other Useful Features.
19
+
20
+ ❣ Made by **@TeamUltroid**""",
21
+ 3: """**💡• FAQs •**
22
+
23
+ -> [Username Tracker](https://t.me/UltroidUpdates/24)
24
+ -> [Keeping Custom Addons Repo](https://t.me/UltroidUpdates/28)
25
+ -> [Disabling Deploy message](https://t.me/UltroidUpdates/27)
26
+ -> [Setting up TimeZone](https://t.me/UltroidUpdates/22)
27
+ -> [About Inline PmPermit](https://t.me/UltroidUpdates/21)
28
+ -> [About Dual Mode](https://t.me/UltroidUpdates/18)
29
+ -> [Custom Thumbnail](https://t.me/UltroidUpdates/13)
30
+ -> [About FullSudo](https://t.me/UltroidUpdates/11)
31
+ -> [Setting Up PmBot](https://t.me/UltroidUpdates/2)
32
+ -> [Also Check](https://t.me/UltroidUpdates/14)
33
+
34
+ **• To Know About Updates**
35
+ - Join @TeamUltroid.""",
36
+ 4: f"""• `To Know All Available Commands`
37
+
38
+ - `{HNDLR}help`
39
+ - `{HNDLR}cmds`""",
40
+ 5: """• **For Any Other Query or Suggestion**
41
+ - Move to **@UltroidSupportChat**.
42
+
43
+ • Thanks for Reaching till END.""",
44
+ }
45
+
46
+
47
+ @callback(re.compile("initft_(\\d+)"))
48
+ async def init_depl(e):
49
+ CURRENT = int(e.data_match.group(1))
50
+ if CURRENT == 5:
51
+ return await e.edit(
52
+ STRINGS[5],
53
+ buttons=Button.inline("<< Back", "initbk_4"),
54
+ link_preview=False,
55
+ )
56
+
57
+ await e.edit(
58
+ STRINGS[CURRENT],
59
+ buttons=[
60
+ Button.inline("<<", f"initbk_{str(CURRENT - 1)}"),
61
+ Button.inline(">>", f"initft_{str(CURRENT + 1)}"),
62
+ ],
63
+ link_preview=False,
64
+ )
65
+
66
+
67
+ @callback(re.compile("initbk_(\\d+)"))
68
+ async def ineiq(e):
69
+ CURRENT = int(e.data_match.group(1))
70
+ if CURRENT == 1:
71
+ return await e.edit(
72
+ STRINGS[1],
73
+ buttons=Button.inline("Start Back >>", "initft_2"),
74
+ link_preview=False,
75
+ )
76
+
77
+ await e.edit(
78
+ STRINGS[CURRENT],
79
+ buttons=[
80
+ Button.inline("<<", f"initbk_{str(CURRENT - 1)}"),
81
+ Button.inline(">>", f"initft_{str(CURRENT + 1)}"),
82
+ ],
83
+ link_preview=False,
84
+ )
assistant/inlinestuff.py ADDED
@@ -0,0 +1,623 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ import base64
9
+ import inspect
10
+ from datetime import datetime
11
+ from html import unescape
12
+ from random import choice
13
+ from re import compile as re_compile
14
+
15
+ from bs4 import BeautifulSoup as bs
16
+ from telethon import Button
17
+ from telethon.tl.alltlobjects import LAYER, tlobjects
18
+ from telethon.tl.types import DocumentAttributeAudio as Audio
19
+ from telethon.tl.types import InputWebDocument as wb
20
+
21
+ from pyUltroid.fns.misc import google_search
22
+ from pyUltroid.fns.tools import (
23
+ _webupload_cache,
24
+ async_searcher,
25
+ get_ofox,
26
+ saavn_search,
27
+ webuploader,
28
+ )
29
+
30
+ from . import *
31
+ from . import _ult_cache
32
+
33
+ SUP_BUTTONS = [
34
+ [
35
+ Button.url("• Repo •", url="https://github.com/TeamUltroid/Ultroid"),
36
+ Button.url("• Support •", url="t.me/UltroidSupportChat"),
37
+ ],
38
+ ]
39
+
40
+ ofox = "https://graph.org/file/231f0049fcd722824f13b.jpg"
41
+ gugirl = "https://graph.org/file/0df54ae4541abca96aa11.jpg"
42
+ ultpic = "https://graph.org/file/4136aa1650bc9d4109cc5.jpg"
43
+
44
+ apis = [
45
+ "QUl6YVN5QXlEQnNZM1dSdEI1WVBDNmFCX3c4SkF5NlpkWE5jNkZV",
46
+ "QUl6YVN5QkYwenhMbFlsUE1wOXh3TVFxVktDUVJxOERnZHJMWHNn",
47
+ "QUl6YVN5RGRPS253blB3VklRX2xiSDVzWUU0Rm9YakFLSVFWMERR",
48
+ ]
49
+
50
+
51
+ @in_pattern("ofox", owner=True)
52
+ async def _(e):
53
+ try:
54
+ match = e.text.split(" ", maxsplit=1)[1]
55
+ except IndexError:
56
+ kkkk = e.builder.article(
57
+ title="Enter Device Codename",
58
+ thumb=wb(ofox, 0, "image/jpeg", []),
59
+ text="**OFᴏx🦊Rᴇᴄᴏᴠᴇʀʏ**\n\nYou didn't search anything",
60
+ buttons=Button.switch_inline("Sᴇᴀʀᴄʜ Aɢᴀɪɴ", query="ofox ", same_peer=True),
61
+ )
62
+ return await e.answer([kkkk])
63
+ device, releases = await get_ofox(match)
64
+ if device.get("detail") is None:
65
+ fox = []
66
+ fullname = device["full_name"]
67
+ codename = device["codename"]
68
+ str(device["supported"])
69
+ maintainer = device["maintainer"]["name"]
70
+ link = f"https://orangefox.download/device/{codename}"
71
+ for data in releases["data"]:
72
+ release = data["type"]
73
+ version = data["version"]
74
+ size = humanbytes(data["size"])
75
+ release_date = datetime.utcfromtimestamp(data["date"]).strftime("%Y-%m-%d")
76
+ text = f"[\xad]({ofox})**OʀᴀɴɢᴇFᴏx Rᴇᴄᴏᴠᴇʀʏ Fᴏʀ**\n\n"
77
+ text += f"` Fᴜʟʟ Nᴀᴍᴇ: {fullname}`\n"
78
+ text += f"` Cᴏᴅᴇɴᴀᴍᴇ: {codename}`\n"
79
+ text += f"` Mᴀɪɴᴛᴀɪɴᴇʀ: {maintainer}`\n"
80
+ text += f"` Bᴜɪʟᴅ Tʏᴘᴇ: {release}`\n"
81
+ text += f"` Vᴇʀsɪᴏɴ: {version}`\n"
82
+ text += f"` Sɪᴢᴇ: {size}`\n"
83
+ text += f"` Bᴜɪʟᴅ Dᴀᴛᴇ: {release_date}`"
84
+ fox.append(
85
+ await e.builder.article(
86
+ title=f"{fullname}",
87
+ description=f"{version}\n{release_date}",
88
+ text=text,
89
+ thumb=wb(ofox, 0, "image/jpeg", []),
90
+ link_preview=True,
91
+ buttons=[
92
+ Button.url("Dᴏᴡɴʟᴏᴀᴅ", url=f"{link}"),
93
+ Button.switch_inline(
94
+ "Sᴇᴀʀᴄʜ Aɢᴀɪɴ", query="ofox ", same_peer=True
95
+ ),
96
+ ],
97
+ )
98
+ )
99
+ await e.answer(
100
+ fox, switch_pm="OrangeFox Recovery Search.", switch_pm_param="start"
101
+ )
102
+ else:
103
+ await e.answer(
104
+ [], switch_pm="OrangeFox Recovery Search.", switch_pm_param="start"
105
+ )
106
+
107
+
108
+ @in_pattern("fl2lnk ?(.*)", owner=True)
109
+ async def _(e):
110
+ match = e.pattern_match.group(1)
111
+ chat_id, msg_id = match.split(":")
112
+ filename = _webupload_cache[int(chat_id)][int(msg_id)]
113
+ if "/" in filename:
114
+ filename = filename.split("/")[-1]
115
+ __cache = f"{chat_id}:{msg_id}"
116
+ buttons = [
117
+ [
118
+ Button.inline("anonfiles", data=f"flanonfiles//{__cache}"),
119
+ Button.inline("transfer", data=f"fltransfer//{__cache}"),
120
+ ],
121
+ [
122
+ Button.inline("bayfiles", data=f"flbayfiles//{__cache}"),
123
+ Button.inline("x0.at", data=f"flx0.at//{__cache}"),
124
+ ],
125
+ [
126
+ Button.inline("file.io", data=f"flfile.io//{__cache}"),
127
+ Button.inline("siasky", data=f"flsiasky//{__cache}"),
128
+ ],
129
+ ]
130
+ try:
131
+ lnk = [
132
+ await e.builder.article(
133
+ title=f"Upload {filename}",
134
+ text=f"**File:**\n{filename}",
135
+ buttons=buttons,
136
+ )
137
+ ]
138
+ except BaseException as er:
139
+ LOGS.exception(er)
140
+ lnk = [
141
+ await e.builder.article(
142
+ title="fl2lnk",
143
+ text="File not found",
144
+ )
145
+ ]
146
+ await e.answer(lnk, switch_pm="File to Link.", switch_pm_param="start")
147
+
148
+
149
+ @callback(
150
+ re_compile(
151
+ "fl(.*)",
152
+ ),
153
+ owner=True,
154
+ )
155
+ async def _(e):
156
+ t = (e.data).decode("UTF-8")
157
+ data = t[2:]
158
+ host = data.split("//")[0]
159
+ chat_id, msg_id = data.split("//")[1].split(":")
160
+ filename = _webupload_cache[int(chat_id)][int(msg_id)]
161
+ if "/" in filename:
162
+ filename = filename.split("/")[-1]
163
+ await e.edit(f"Uploading `{filename}` on {host}")
164
+ link = (await webuploader(chat_id, msg_id, host)).strip().replace("\n", "")
165
+ await e.edit(f"Uploaded `{filename}` on {host}.", buttons=Button.url("View", link))
166
+
167
+
168
+ @in_pattern("repo", owner=True)
169
+ async def repo(e):
170
+ res = [
171
+ await e.builder.article(
172
+ title="Ultroid Userbot",
173
+ description="Userbot | Telethon",
174
+ thumb=wb(ultpic, 0, "image/jpeg", []),
175
+ text="• **ULTROID USERBOT** •",
176
+ buttons=SUP_BUTTONS,
177
+ ),
178
+ ]
179
+ await e.answer(res, switch_pm="Ultroid Repo.", switch_pm_param="start")
180
+
181
+
182
+ @in_pattern("go", owner=True)
183
+ async def gsearch(q_event):
184
+ try:
185
+ match = q_event.text.split(maxsplit=1)[1]
186
+ except IndexError:
187
+ return await q_event.answer(
188
+ [], switch_pm="Google Search. Enter a query!", switch_pm_param="start"
189
+ )
190
+ searcher = []
191
+ gresults = await google_search(match)
192
+ for i in gresults:
193
+ try:
194
+ title = i["title"]
195
+ link = i["link"]
196
+ desc = i["description"]
197
+ searcher.append(
198
+ await q_event.builder.article(
199
+ title=title,
200
+ description=desc,
201
+ thumb=wb(gugirl, 0, "image/jpeg", []),
202
+ text=f"**Gᴏᴏɢʟᴇ Sᴇᴀʀᴄʜ**\n\n**••Tɪᴛʟᴇ••**\n`{title}`\n\n**••Dᴇsᴄʀɪᴘᴛɪᴏɴ••**\n`{desc}`",
203
+ link_preview=False,
204
+ buttons=[
205
+ [Button.url("Lɪɴᴋ", url=f"{link}")],
206
+ [
207
+ Button.switch_inline(
208
+ "Sᴇᴀʀᴄʜ Aɢᴀɪɴ",
209
+ query="go ",
210
+ same_peer=True,
211
+ ),
212
+ Button.switch_inline(
213
+ "Sʜᴀʀᴇ",
214
+ query=f"go {match}",
215
+ same_peer=False,
216
+ ),
217
+ ],
218
+ ],
219
+ ),
220
+ )
221
+ except IndexError:
222
+ break
223
+ await q_event.answer(searcher, switch_pm="Google Search.", switch_pm_param="start")
224
+
225
+
226
+ @in_pattern("mods", owner=True)
227
+ async def _(e):
228
+ try:
229
+ quer = e.text.split(" ", maxsplit=1)[1]
230
+ except IndexError:
231
+ return await e.answer(
232
+ [], switch_pm="Mod Apps Search. Enter app name!", switch_pm_param="start"
233
+ )
234
+ start = 0 * 3 + 1
235
+ da = base64.b64decode(choice(apis)).decode("ascii")
236
+ url = f"https://www.googleapis.com/customsearch/v1?key={da}&cx=25b3b50edb928435b&q={quer}&start={start}"
237
+ data = await async_searcher(url, re_json=True)
238
+ search_items = data.get("items", [])
239
+ modss = []
240
+ for a in search_items:
241
+ title = a.get("title")
242
+ desc = a.get("snippet")
243
+ link = a.get("link")
244
+ text = f"**••Tɪᴛʟᴇ••** `{title}`\n\n"
245
+ text += f"**Dᴇsᴄʀɪᴘᴛɪᴏɴ** `{desc}`"
246
+ modss.append(
247
+ await e.builder.article(
248
+ title=title,
249
+ description=desc,
250
+ text=text,
251
+ link_preview=True,
252
+ buttons=[
253
+ [Button.url("Dᴏᴡɴʟᴏᴀᴅ", url=f"{link}")],
254
+ [
255
+ Button.switch_inline(
256
+ "Mᴏʀᴇ Mᴏᴅs",
257
+ query="mods ",
258
+ same_peer=True,
259
+ ),
260
+ Button.switch_inline(
261
+ "Sʜᴀʀᴇ",
262
+ query=f"mods {quer}",
263
+ same_peer=False,
264
+ ),
265
+ ],
266
+ ],
267
+ ),
268
+ )
269
+ await e.answer(modss, switch_pm="Search Mod Applications.", switch_pm_param="start")
270
+
271
+
272
+ APP_CACHE = {}
273
+ RECENTS = {}
274
+ PLAY_API = "https://googleplay.onrender.com/api/apps?q="
275
+
276
+
277
+ @in_pattern("app", owner=True)
278
+ async def _(e):
279
+ try:
280
+ f = e.text.split(maxsplit=1)[1].lower()
281
+ except IndexError:
282
+ get_string("instu_1")
283
+ res = []
284
+ if APP_CACHE and RECENTS.get(e.sender_id):
285
+ res.extend(
286
+ APP_CACHE[a][0] for a in RECENTS[e.sender_id] if APP_CACHE.get(a)
287
+ )
288
+ return await e.answer(
289
+ res, switch_pm=get_string("instu_2"), switch_pm_param="start"
290
+ )
291
+ try:
292
+ return await e.answer(
293
+ APP_CACHE[f], switch_pm="Application Searcher.", switch_pm_param="start"
294
+ )
295
+ except KeyError:
296
+ pass
297
+ foles = []
298
+ url = PLAY_API + f.replace(" ", "+")
299
+ aap = await async_searcher(url, re_json=True)
300
+ for z in aap["results"][:50]:
301
+ url = "https://play.google.com/store/apps/details?id=" + z["appId"]
302
+ name = z["title"]
303
+ desc = unescape(z["summary"])[:300].replace("<br>", "\n") + "..."
304
+ dev = z["developer"]["devId"]
305
+ text = f"**••Aᴘᴘ Nᴀᴍᴇ••** [{name}]({url})\n"
306
+ text += f"**••Dᴇᴠᴇʟᴏᴘᴇʀ••** `{dev}`\n"
307
+ text += f"**••Dᴇsᴄʀɪᴘᴛɪᴏɴ••**\n`{desc}`"
308
+ foles.append(
309
+ await e.builder.article(
310
+ title=name,
311
+ description=dev,
312
+ thumb=wb(z["icon"], 0, "image/jpeg", []),
313
+ text=text,
314
+ link_preview=True,
315
+ buttons=[
316
+ [Button.url("Lɪɴᴋ", url=url)],
317
+ [
318
+ Button.switch_inline(
319
+ "Mᴏʀᴇ Aᴘᴘs",
320
+ query="app ",
321
+ same_peer=True,
322
+ ),
323
+ Button.switch_inline(
324
+ "Sʜᴀʀᴇ",
325
+ query=f"app {f}",
326
+ same_peer=False,
327
+ ),
328
+ ],
329
+ ],
330
+ ),
331
+ )
332
+ APP_CACHE.update({f: foles})
333
+ if RECENTS.get(e.sender_id):
334
+ RECENTS[e.sender_id].append(f)
335
+ else:
336
+ RECENTS.update({e.sender_id: [f]})
337
+ await e.answer(foles, switch_pm="Application Searcher.", switch_pm_param="start")
338
+
339
+
340
+ PISTON_URI = "https://emkc.org/api/v2/piston/"
341
+ PISTON_LANGS = {}
342
+
343
+
344
+ @in_pattern("run", owner=True)
345
+ async def piston_run(event):
346
+ try:
347
+ lang = event.text.split()[1]
348
+ code = event.text.split(maxsplit=2)[2]
349
+ except IndexError:
350
+ result = await event.builder.article(
351
+ title="Bad Query",
352
+ description="Usage: [Language] [code]",
353
+ thumb=wb(
354
+ "https://graph.org/file/e33c57fc5f1044547e4d8.jpg", 0, "image/jpeg", []
355
+ ),
356
+ text=f'**Inline Usage**\n\n`@{asst.me.username} run python print("hello world")`\n\n[Language List](https://graph.org/Ultroid-09-01-6)',
357
+ )
358
+ return await event.answer([result])
359
+ if not PISTON_LANGS:
360
+ se = await async_searcher(f"{PISTON_URI}runtimes", re_json=True)
361
+ PISTON_LANGS.update({lang.pop("language"): lang for lang in se})
362
+ if lang in PISTON_LANGS.keys():
363
+ version = PISTON_LANGS[lang]["version"]
364
+ else:
365
+ result = await event.builder.article(
366
+ title="Unsupported Language",
367
+ description="Usage: [Language] [code]",
368
+ thumb=wb(
369
+ "https://graph.org/file/e33c57fc5f1044547e4d8.jpg", 0, "image/jpeg", []
370
+ ),
371
+ text=f'**Inline Usage**\n\n`@{asst.me.username} run python print("hello world")`\n\n[Language List](https://graph.org/Ultroid-09-01-6)',
372
+ )
373
+ return await event.answer([result])
374
+ output = await async_searcher(
375
+ f"{PISTON_URI}execute",
376
+ post=True,
377
+ json={
378
+ "language": lang,
379
+ "version": version,
380
+ "files": [{"content": code}],
381
+ },
382
+ re_json=True,
383
+ )
384
+
385
+ output = output["run"]["output"] or get_string("instu_4")
386
+ if len(output) > 3000:
387
+ output = f"{output[:3000]}..."
388
+ result = await event.builder.article(
389
+ title="Result",
390
+ description=output,
391
+ text=f"• **Language:**\n`{lang}`\n\n• **Code:**\n`{code}`\n\n• **Result:**\n`{output}`",
392
+ thumb=wb(
393
+ "https://graph.org/file/871ee4a481f58117dccc4.jpg", 0, "image/jpeg", []
394
+ ),
395
+ buttons=Button.switch_inline("Fork", query=event.text, same_peer=True),
396
+ )
397
+ await event.answer([result], switch_pm="• Piston •", switch_pm_param="start")
398
+
399
+
400
+ FDROID_ = {}
401
+
402
+
403
+ @in_pattern("fdroid", owner=True)
404
+ async def do_magic(event):
405
+ try:
406
+ match = event.text.split(" ", maxsplit=1)[1].lower()
407
+ except IndexError:
408
+ return await event.answer(
409
+ [], switch_pm="Enter Query to Search", switch_pm_param="start"
410
+ )
411
+ if FDROID_.get(match):
412
+ return await event.answer(
413
+ FDROID_[match], switch_pm=f"• Results for {match}", switch_pm_param="start"
414
+ )
415
+ link = "https://search.f-droid.org/?q=" + match.replace(" ", "+")
416
+ content = await async_searcher(link, re_content=True)
417
+ BSC = bs(content, "html.parser", from_encoding="utf-8")
418
+ ress = []
419
+ for dat in BSC.find_all("a", "package-header")[:10]:
420
+ image = dat.find("img", "package-icon")["src"]
421
+ if image.endswith("/"):
422
+ image = "https://graph.org/file/a8dd4a92c5a53a89d0eff.jpg"
423
+ title = dat.find("h4", "package-name").text.strip()
424
+ desc = dat.find("span", "package-summary").text.strip()
425
+ text = f"• **Name :** `{title}`\n\n"
426
+ text += f"• **Description :** `{desc}`\n"
427
+ text += f"• **License :** `{dat.find('span', 'package-license').text.strip()}`"
428
+ imga = wb(image, 0, "image/jpeg", [])
429
+ ress.append(
430
+ await event.builder.article(
431
+ title=title,
432
+ type="photo",
433
+ description=desc,
434
+ text=text,
435
+ content=imga,
436
+ thumb=imga,
437
+ include_media=True,
438
+ buttons=[
439
+ Button.inline(
440
+ "• Download •", "fd" + dat["href"].split("packages/")[-1]
441
+ ),
442
+ Button.switch_inline("• Share •", query=event.text),
443
+ ],
444
+ )
445
+ )
446
+ msg = f"Showing {len(ress)} Results!" if ress else "No Results Found"
447
+ FDROID_.update({match: ress})
448
+ await event.answer(ress, switch_pm=msg, switch_pm_param="start")
449
+
450
+
451
+ # Thanks to OpenSource
452
+ _bearer_collected = [
453
+ "AAAAAAAAAAAAAAAAAAAAALIKKgEAAAAA1DRuS%2BI7ZRKiagD6KHYmreaXomo%3DP5Vaje4UTtEkODg0fX7nCh5laSrchhtLxeyEqxXpv0w9ZKspLD",
454
+ "AAAAAAAAAAAAAAAAAAAAAL5iUAEAAAAAmo6FYRjqdKlI3cNziIm%2BHUQB9Xs%3DS31pj0mxARMTOk2g9dvQ1yP9wknvY4FPBPUlE00smJcncw4dPR",
455
+ "AAAAAAAAAAAAAAAAAAAAAN6sVgEAAAAAMMjMMWrwgGyv7YQOWN%2FSAsO5SGM%3Dg8MG9Jq93Rlllaok6eht7HvRCruN4Vpzp4NaVsZaaHHWSTzKI8",
456
+ ]
457
+
458
+
459
+ @in_pattern("twitter", owner=True)
460
+ async def twitter_search(event):
461
+ try:
462
+ match = event.text.split(maxsplit=1)[1].lower()
463
+ except IndexError:
464
+ return await event.answer(
465
+ [], switch_pm="Enter Query to Search", switch_pm_param="start"
466
+ )
467
+ try:
468
+ return await event.answer(
469
+ _ult_cache["twitter"][match],
470
+ switch_pm="• Twitter Search •",
471
+ switch_pm_param="start",
472
+ )
473
+ except KeyError:
474
+ pass
475
+ headers = {"Authorization": f"bearer {choice(_bearer_collected)}"}
476
+ res = await async_searcher(
477
+ f"https://api.twitter.com/1.1/users/search.json?q={match}",
478
+ headers=headers,
479
+ re_json=True,
480
+ )
481
+ reso = []
482
+ for user in res:
483
+ thumb = wb(user["profile_image_url_https"], 0, "image/jpeg", [])
484
+ if user.get("profile_banner_url"):
485
+ url = user["profile_banner_url"]
486
+ text = f"[\xad]({url})• **Name :** `{user['name']}`\n"
487
+ else:
488
+ text = f"• **Name :** `{user['name']}`\n"
489
+ text += f"• **Description :** `{user['description']}`\n"
490
+ text += f"• **Username :** `@{user['screen_name']}`\n"
491
+ text += f"• **Followers :** `{user['followers_count']}` • **Following :** `{user['friends_count']}`\n"
492
+ pro_ = "https://twitter.com/" + user["screen_name"]
493
+ text += f"• **Link :** [Click Here]({pro_})\n_"
494
+ reso.append(
495
+ await event.builder.article(
496
+ title=user["name"],
497
+ description=user["description"],
498
+ url=pro_,
499
+ text=text,
500
+ thumb=thumb,
501
+ )
502
+ )
503
+ swi_ = f"🐦 Showing {len(reso)} Results!" if reso else "No User Found :("
504
+ await event.answer(reso, switch_pm=swi_, switch_pm_param="start")
505
+ if _ult_cache.get("twitter"):
506
+ _ult_cache["twitter"].update({match: reso})
507
+ else:
508
+ _ult_cache.update({"twitter": {match: reso}})
509
+
510
+
511
+ _savn_cache = {}
512
+
513
+
514
+ @in_pattern("saavn", owner=True)
515
+ async def savn_s(event):
516
+ try:
517
+ query = event.text.split(maxsplit=1)[1].lower()
518
+ except IndexError:
519
+ return await event.answer(
520
+ [], switch_pm="Enter Query to search 🔍", switch_pm_param="start"
521
+ )
522
+ if query in _savn_cache:
523
+ return await event.answer(
524
+ _savn_cache[query],
525
+ switch_pm=f"Showing Results for {query}",
526
+ switch_pm_param="start",
527
+ )
528
+ results = await saavn_search(query)
529
+ swi = "🎵 Saavn Search" if results else "No Results Found!"
530
+ res = []
531
+ for song in results:
532
+ thumb = wb(song["image"], 0, "image/jpeg", [])
533
+ text = f"• **Title :** {song['title']}"
534
+ text += f"\n• **Year :** {song['year']}"
535
+ text += f"\n• **Lang :** {song['language']}"
536
+ text += f"\n• **Artist :** {song['artists']}"
537
+ text += f"\n• **Release Date :** {song['release_date']}"
538
+ res.append(
539
+ await event.builder.article(
540
+ title=song["title"],
541
+ description=song["artists"],
542
+ type="audio",
543
+ text=text,
544
+ include_media=True,
545
+ buttons=Button.switch_inline(
546
+ "Search Again 🔍", query="saavn", same_peer=True
547
+ ),
548
+ thumb=thumb,
549
+ content=wb(
550
+ song["url"],
551
+ 0,
552
+ "audio/mp4",
553
+ [
554
+ Audio(
555
+ title=song["title"],
556
+ duration=int(song["duration"]),
557
+ performer=song["artists"],
558
+ )
559
+ ],
560
+ ),
561
+ )
562
+ )
563
+ await event.answer(res, switch_pm=swi, switch_pm_param="start")
564
+ _savn_cache.update({query: res})
565
+
566
+
567
+ @in_pattern("tl", owner=True)
568
+ async def inline_tl(ult):
569
+ try:
570
+ match = ult.text.split(maxsplit=1)[1]
571
+ except IndexError:
572
+ text = f"**Telegram TlObjects Searcher.**\n__(Don't use if you don't know what it is!)__\n\n• Example Usage\n`@{asst.me.username} tl GetFullUserRequest`"
573
+ return await ult.answer(
574
+ [
575
+ await ult.builder.article(
576
+ title="How to Use?",
577
+ description="Tl Searcher by Ultroid",
578
+ url="https://t.me/TeamUltroid",
579
+ text=text,
580
+ )
581
+ ],
582
+ switch_pm="Tl Search 🔍",
583
+ switch_pm_param="start",
584
+ )
585
+ res = []
586
+ for key in tlobjects.values():
587
+ if match.lower() in key.__name__.lower():
588
+ tyyp = "Function" if "tl.functions." in str(key) else "Type"
589
+ text = f"**Name:** `{key.__name__}`\n"
590
+ text += f"**Category:** `{tyyp}`\n"
591
+ text += f"\n`from {key.__module__} import {key.__name__}`\n\n"
592
+ if args := str(inspect.signature(key))[1:][:-1]:
593
+ text += "**Parameter:**\n"
594
+ for para in args.split(","):
595
+ text += " " * 4 + "`" + para + "`\n"
596
+ text += f"\n**Layer:** `{LAYER}`"
597
+ res.append(
598
+ await ult.builder.article(
599
+ title=key.__name__,
600
+ description=tyyp,
601
+ url="https://t.me/TeamUltroid",
602
+ text=text[:4000],
603
+ )
604
+ )
605
+ mo = f"Showing {len(res)} results!" if res else f"No Results for {match}!"
606
+ await ult.answer(res[:50], switch_pm=mo, switch_pm_param="start")
607
+
608
+
609
+ InlinePlugin.update(
610
+ {
611
+ "Pʟᴀʏ Sᴛᴏʀᴇ Aᴘᴘs": "app telegram",
612
+ "Mᴏᴅᴅᴇᴅ Aᴘᴘs": "mods minecraft",
613
+ "Sᴇᴀʀᴄʜ Oɴ Gᴏᴏɢʟᴇ": "go TeamUltroid",
614
+ "WʜɪSᴘᴇʀ": "wspr @username Hello🎉",
615
+ "YᴏᴜTᴜʙᴇ Dᴏᴡɴʟᴏᴀᴅᴇʀ": "yt Ed Sheeran Perfect",
616
+ "Piston Eval": "run javascript console.log('Hello Ultroid')",
617
+ "OʀᴀɴɢᴇFᴏx🦊": "ofox beryllium",
618
+ "Tᴡɪᴛᴛᴇʀ Usᴇʀ": "twitter theultroid",
619
+ "Fᴅʀᴏɪᴅ Sᴇᴀʀᴄʜ": "fdroid telegram",
620
+ "Sᴀᴀᴠɴ sᴇᴀʀᴄʜ": "saavn",
621
+ "Tʟ Sᴇᴀʀᴄʜ": "tl",
622
+ }
623
+ )
assistant/localization.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ import re
9
+
10
+ from . import (
11
+ Button,
12
+ ULTConfig,
13
+ callback,
14
+ get_back_button,
15
+ get_languages,
16
+ get_string,
17
+ udB,
18
+ )
19
+
20
+
21
+ @callback("lang", owner=True)
22
+ async def setlang(event):
23
+ languages = get_languages()
24
+ tultd = [
25
+ Button.inline(
26
+ f"{languages[ult]['natively']} [{ult.lower()}]",
27
+ data=f"set_{ult}",
28
+ )
29
+ for ult in languages
30
+ ]
31
+ buttons = list(zip(tultd[::2], tultd[1::2]))
32
+ if len(tultd) % 2 == 1:
33
+ buttons.append((tultd[-1],))
34
+ buttons.append([Button.inline("« Back", data="mainmenu")])
35
+ await event.edit(get_string("ast_4"), buttons=buttons)
36
+
37
+
38
+ @callback(re.compile(b"set_(.*)"), owner=True)
39
+ async def settt(event):
40
+ lang = event.data_match.group(1).decode("UTF-8")
41
+ languages = get_languages()
42
+ ULTConfig.lang = lang
43
+ udB.del_key("language") if lang == "en" else udB.set_key("language", lang)
44
+ await event.edit(
45
+ f"Your language has been set to {languages[lang]['natively']} [{lang}].",
46
+ buttons=get_back_button("lang"),
47
+ )
assistant/manager/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from pyUltroid._misc._decorators import ultroid_cmd
9
+ from pyUltroid.fns.helper import inline_mention
10
+
11
+ from .. import *
assistant/manager/_help.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from . import *
9
+
10
+ START = """
11
+ 🪅 **Help Menu** 🪅
12
+
13
+ ✘ /start : Check I am Alive or not.
14
+ ✘ /help : Get This Message.
15
+ ✘ /repo : Get Bot's Repo..
16
+
17
+ 🧑‍💻 Join **@TeamUltroid**
18
+ """
19
+
20
+ ADMINTOOLS = """✘ **AdminTools** ✘
21
+
22
+ • /pin : Pins the Replied Message
23
+ • /pinned : Get Pinned message in chat.
24
+ • /unpin : Unpin the Replied message
25
+ • /unpin all : Unpin all Pinned Messages.
26
+
27
+ • /ban (username/id/reply) : Ban the User
28
+ • /unban (username/id/reply) : UnBan the User.
29
+
30
+ • /mute (username/id/reply) : Mute the User.
31
+ • /unmute (username/id/reply) : Unmute the User.
32
+
33
+ • /tban (username/id/reply) (time) : Temporary ban a user
34
+ • /tmute (username/id/reply) (time) : temporary Mutes a User.
35
+
36
+ • /purge (purge messages)
37
+
38
+ • /setgpic (reply photo) : keep Chat Photo of Group.
39
+ • /delgpic : remove current chat Photo."""
40
+
41
+ UTILITIES = """
42
+ ✘ ** Utilities ** ✘
43
+
44
+ • /info (reply/username/id) : get detailed info of user.
45
+ • /id : get chat/user id.
46
+ • /tr : Translate Languages..
47
+ • /q : Create Quotes.
48
+
49
+ • /paste (reply file/text) : paste content on Spaceb.in
50
+ • /meaning (text) : Get Meaning of that Word.
51
+ • /google (query) : Search Something on Google..
52
+
53
+ • /suggest (query/reply) : Creates a Yes / No Poll.
54
+ """
55
+
56
+ LOCKS = """
57
+ ✘ ** Locks ** ✘
58
+
59
+ • /lock (query) : lock particular content in chat.
60
+ • /unlock (query) : Unlock some content.
61
+
62
+ • All Queries
63
+ - `msgs` : for messages.
64
+ - `inlines` : for inline queries.
65
+ - `media` : for all medias.
66
+ - `games` : for games.
67
+ - `sticker` : for stickers.
68
+ - `polls` : for polls.
69
+ - `gif` : for gifs.
70
+ - `pin` : for pins.
71
+ - `changeinfo` : for change info right.
72
+ """
73
+
74
+ MISC = """
75
+ ✘ **Misc** ✘
76
+
77
+ • /joke : Get Random Jokes.
78
+ • /decide : Decide Something..
79
+
80
+ **✘ Stickertools ✘**
81
+ • /kang : add sticker to your pack.
82
+ • /listpack : get all of yours pack..
83
+ """
84
+
85
+ STRINGS = {"Admintools": ADMINTOOLS, "locks": LOCKS, "Utils": UTILITIES, "Misc": MISC}
86
+
87
+ MNGE = udB.get_key("MNGR_EMOJI") or "•"
88
+
89
+
90
+ def get_buttons():
91
+ BTTS = []
92
+ keys = STRINGS.copy()
93
+ while keys:
94
+ BT = []
95
+ for i in list(keys)[:2]:
96
+ text = f"{MNGE} {i} {MNGE}"
97
+ BT.append(Button.inline(text, f"hlp_{i}"))
98
+ del keys[i]
99
+ BTTS.append(BT)
100
+ url = f"https://t.me/{asst.me.username}?startgroup=true"
101
+ BTTS.append([Button.url("Add me to Group", url)])
102
+ return BTTS
103
+
104
+
105
+ @asst_cmd(pattern="help")
106
+ async def helpish(event):
107
+ if not event.is_private:
108
+ url = f"https://t.me/{asst.me.username}?start=start"
109
+ return await event.reply(
110
+ "Contact me in PM for help!", buttons=Button.url("Click me for Help", url)
111
+ )
112
+ if str(event.sender_id) in owner_and_sudos() and (
113
+ udB.get_key("DUAL_MODE") and (udB.get_key("DUAL_HNDLR") == "/")
114
+ ):
115
+ return
116
+ await event.reply(START, buttons=get_buttons())
117
+
118
+
119
+ @callback("mngbtn", owner=True)
120
+ async def ehwhshd(e):
121
+ buttons = get_buttons()
122
+ buttons.append([Button.inline("<< Back", "open")])
123
+ await e.edit(buttons=buttons)
124
+
125
+
126
+ @callback("mnghome")
127
+ async def home_aja(e):
128
+ await e.edit(START, buttons=get_buttons())
129
+
130
+
131
+ @callback(re.compile("hlp_(.*)"))
132
+ async def do_something(event):
133
+ match = event.pattern_match.group(1).strip().decode("utf-8")
134
+ await event.edit(STRINGS[match], buttons=Button.inline("<< Back", "mnghome"))
assistant/manager/_on_adds.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from telethon import events
9
+
10
+ from . import *
11
+
12
+
13
+ @asst.on(events.ChatAction(func=lambda x: x.user_added))
14
+ async def dueha(e):
15
+ user = await e.get_user()
16
+ if not user.is_self:
17
+ return
18
+ sm = udB.get_key("ON_MNGR_ADD")
19
+ if sm == "OFF":
20
+ return
21
+ if not sm:
22
+ sm = "Thanks for Adding me :)"
23
+ await e.reply(sm, link_preview=False)
assistant/manager/admins.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ import re
9
+
10
+ from telethon.errors.rpcerrorlist import UserNotParticipantError
11
+
12
+ from pyUltroid import _ult_cache
13
+
14
+ from . import *
15
+
16
+
17
+ @ultroid_cmd(pattern="d(kick|ban)", manager=True, require="ban_users")
18
+ async def dowj(e):
19
+ replied = await e.get_reply_message()
20
+ if replied:
21
+ user = replied.sender_id
22
+ else:
23
+ return await e.eor("Reply to a message...")
24
+ try:
25
+ await replied.delete()
26
+ if e.pattern_match.group(1).strip() == "kick":
27
+ await e.client.kick_participant(e.chat_id, user)
28
+ te = "Kicked"
29
+ else:
30
+ await e.client.edit_permissions(e.chat_id, user, view_messages=False)
31
+ te = "Banned"
32
+ await e.eor(f"{te} Successfully!")
33
+ except Exception as E:
34
+ await e.eor(str(E))
35
+
36
+
37
+ @callback(re.compile("cc_(.*)"), func=_ult_cache.get("admin_callback"))
38
+ async def callback_(event):
39
+ data = event.data_match.group(1).decode("utf-8")
40
+ if data not in _ult_cache.get("admin_callback", {}):
41
+ return
42
+ try:
43
+ perm = await event.client.get_permissions(event.chat_id, event.sender_id)
44
+ except UserNotParticipantError:
45
+ return await event.answer("Join the Group First!", alert=True)
46
+ if not perm.is_admin:
47
+ return await event.answer("You are not an Admin!", alert=True)
48
+ _ult_cache["admin_callback"].update({data: (event.sender, perm)})
49
+ await event.answer("Verification Done!")
50
+ await event.delete()
assistant/manager/afk.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from datetime import datetime as dt
9
+
10
+ from telethon.events import NewMessage
11
+ from telethon.tl.types import (
12
+ Message,
13
+ MessageEntityMention,
14
+ MessageEntityMentionName,
15
+ User,
16
+ )
17
+ from telethon.utils import get_display_name
18
+
19
+ from pyUltroid.fns.helper import inline_mention, time_formatter
20
+
21
+ from . import asst, asst_cmd
22
+
23
+ AFK = {}
24
+
25
+
26
+ @asst_cmd(pattern="afk", func=lambda x: not x.is_private)
27
+ async def go_afk(event):
28
+ sender = await event.get_sender()
29
+ if (not isinstance(sender, User)) or sender.bot:
30
+ return
31
+ try:
32
+ reason = event.text.split(" ", maxsplit=1)[1]
33
+ except IndexError:
34
+ reason = None
35
+ if event.is_reply and not reason:
36
+ replied = await event.get_reply_message()
37
+ if not reason and replied.text and not replied.media:
38
+ reason = replied.text
39
+ else:
40
+ reason = replied
41
+ time_ = dt.now()
42
+ if AFK.get(event.chat_id):
43
+ AFK[event.chat_id].update({event.sender_id: {"reason": reason, "time": time_}})
44
+ else:
45
+ AFK.update(
46
+ {event.chat_id: {event.sender_id: {"reason": reason, "time": time_}}}
47
+ )
48
+ mention = inline_mention(sender)
49
+ msg = f"**{mention} went AFK Now!**"
50
+ if reason and not isinstance(reason, str):
51
+ await event.reply(reason)
52
+ else:
53
+ msg += f"\n\n**Reason : ** `{reason}`"
54
+ await event.reply(msg)
55
+
56
+
57
+ @asst.on(NewMessage(func=lambda x: AFK.get(x.chat_id) and not x.is_private))
58
+ async def make_change(event):
59
+ if event.text.startswith("/afk"):
60
+ return
61
+ sender = await event.get_sender()
62
+ if (not isinstance(sender, User)) or sender.bot:
63
+ return
64
+ chat_ = AFK[event.chat_id]
65
+ if event.sender_id in chat_.keys():
66
+ name = get_display_name(event.sender)
67
+ cha_send = chat_[event.sender_id]
68
+ time_ = time_formatter((dt.now() - cha_send["time"]).seconds * 1000)
69
+ msg = f"**{name}** is No Longer AFK!\n**Was AFK for** {time_}"
70
+ await event.reply(msg)
71
+ del chat_[event.sender_id]
72
+ if not chat_:
73
+ del AFK[event.chat_id]
74
+ ST_SPAM = []
75
+ replied = await event.get_reply_message()
76
+ if replied:
77
+ name = get_display_name(replied.sender)
78
+ if replied.sender_id in chat_.keys():
79
+ s_der = chat_[replied.sender_id]
80
+ res_ = s_der["reason"]
81
+ time_ = time_formatter((dt.now() - s_der["time"]).seconds * 1000)
82
+ msg = f"**{name}** is AFK Currently!\n**From :** {time_}"
83
+ if res_ and isinstance(res_, str):
84
+ msg += f"\n**Reason :** {res_}"
85
+ elif res_ and isinstance(res_, Message):
86
+ await event.reply(res_)
87
+ await event.reply(msg)
88
+ ST_SPAM.append(replied.sender_id)
89
+ for ent, text in event.get_entities_text():
90
+ dont_send, entity = None, None
91
+ if isinstance(ent, MessageEntityMentionName):
92
+ c_id = ent.user_id
93
+ elif isinstance(ent, MessageEntityMention):
94
+ c_id = text
95
+ else:
96
+ c_id = None
97
+ if c_id:
98
+ entity = await event.client.get_entity(c_id)
99
+ if entity and entity.id in chat_.keys() and entity.id not in ST_SPAM:
100
+ ST_SPAM.append(entity.id)
101
+ s_der = chat_[entity.id]
102
+ name = get_display_name(entity)
103
+ res_ = s_der["reason"]
104
+ time_ = time_formatter((dt.now() - s_der["time"]).seconds * 1000)
105
+ msg = f"**{name}** is AFK Currently!\n**From :** {time_}"
106
+ if res_ and isinstance(res_, str):
107
+ msg += f"\n**Reason :** {res_}"
108
+ elif res_ and isinstance(res_, Message):
109
+ await event.reply(res_)
110
+ await event.reply(msg)
assistant/manager/misc.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+
9
+ import random
10
+
11
+ import aiohttp
12
+
13
+ from pyUltroid.dB import DEVLIST
14
+ from pyUltroid.fns.admins import admin_check
15
+
16
+ from . import *
17
+
18
+
19
+ @asst_cmd(pattern="decide")
20
+ async def dheh(e):
21
+ text = ["Yes", "NoU", "Maybe", "IDK"]
22
+ text = random.choice(text)
23
+ ri = e.reply_to_msg_id or e.id
24
+ await e.client.send_message(e.chat_id, text, reply_to=ri)
25
+
26
+
27
+ @asst_cmd(pattern="echo( (.*)|$)")
28
+ async def oqha(e):
29
+ if not await admin_check(e):
30
+ return
31
+ if match := e.pattern_match.group(1).strip():
32
+ text = match
33
+ reply_to = e
34
+ elif e.is_reply:
35
+ text = (await e.get_reply_message()).text
36
+ reply_to = e.reply_to_msg_id
37
+ else:
38
+ return await e.eor("What to Echo?", time=5)
39
+ try:
40
+ await e.delete()
41
+ except BaseException as ex:
42
+ LOGS.error(ex)
43
+ await e.client.send_message(e.chat_id, text, reply_to=reply_to)
44
+
45
+
46
+ @asst_cmd(pattern="kickme$")
47
+ async def doit(e):
48
+ if e.sender_id in DEVLIST:
49
+ return await eod(e, "`I will Not Kick You, my Developer..`")
50
+ try:
51
+ await e.client.kick_participant(e.chat_id, e.sender_id)
52
+ except Exception as Fe:
53
+ return await e.eor(str(Fe), time=5)
54
+ await e.eor("Yes, You are right, get out.", time=5)
55
+
56
+
57
+ @asst_cmd(pattern="joke$")
58
+ async def do_joke(e):
59
+ e = await e.get_reply_message() if e.is_reply else e
60
+ link = "https://v2.jokeapi.dev/joke/Any?blacklistFlags=nsfw,religious,political,racist,sexist,explicit&type=single"
61
+ async with aiohttp.ClientSession() as ses:
62
+ async with ses.get(link) as out:
63
+ out = await out.json()
64
+ await e.reply(out["joke"])
assistant/manager/stickermanager.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ import random
9
+
10
+ from telethon import errors
11
+ from telethon.errors.rpcerrorlist import StickersetInvalidError
12
+ from telethon.tl.functions.messages import GetStickerSetRequest as GetSticker
13
+ from telethon.tl.functions.messages import UploadMediaRequest
14
+ from telethon.tl.functions.stickers import AddStickerToSetRequest as AddSticker
15
+ from telethon.tl.functions.stickers import CreateStickerSetRequest
16
+ from telethon.tl.types import InputPeerSelf
17
+ from telethon.tl.types import InputStickerSetItem as SetItem
18
+ from telethon.tl.types import InputStickerSetShortName, User
19
+ from telethon.utils import get_display_name, get_input_document
20
+
21
+ from pyUltroid.fns.misc import Quotly
22
+ from pyUltroid.fns.tools import TgConverter
23
+
24
+ from . import LOGS, asst, asst_cmd, udB
25
+
26
+
27
+ @asst_cmd(
28
+ pattern="kang",
29
+ )
30
+ async def kang_cmd(ult):
31
+ sender = await ult.get_sender()
32
+ if not isinstance(sender, User):
33
+ return
34
+ if not ult.is_reply:
35
+ return await ult.eor("`Reply to a sticker/photo..`", time=5)
36
+ reply = await ult.get_reply_message()
37
+ if sender.username:
38
+ pre = sender.username[:4]
39
+ else:
40
+ pre = random.random_string(length=3)
41
+ animated, dl, video = None, None, None
42
+ try:
43
+ emoji = ult.text.split(maxsplit=1)[1]
44
+ except IndexError:
45
+ emoji = None
46
+ if reply.sticker:
47
+ file = get_input_document(reply.sticker)
48
+ emoji = emoji or reply.file.emoji
49
+ name = reply.file.name
50
+ if name.endswith(".tgs"):
51
+ animated = True
52
+ dl = await reply.download_media()
53
+ elif name.endswith(".webm"):
54
+ video = True
55
+ dl = await reply.download_media()
56
+ elif reply.photo:
57
+ dl = await reply.download_media()
58
+ name = "sticker.webp"
59
+ image = TgConverter.resize_photo_sticker(dl)
60
+ image.save(name, "WEBP")
61
+ elif reply.text:
62
+ dl = await Quotly().create_quotly(reply)
63
+ else:
64
+ return await ult.eor("`Reply to sticker or text to add it in your pack...`")
65
+ if not emoji:
66
+ emoji = "🏵"
67
+ if dl:
68
+ upl = await ult.client.upload_file(dl)
69
+ file = get_input_document(
70
+ await ult.client(UploadMediaRequest(InputPeerSelf(), upl))
71
+ )
72
+ get_ = udB.get_key("STICKERS") or {}
73
+ type_ = "anim" if animated else "static"
74
+ if not get_.get(ult.sender_id) or not get_.get(ult.sender_id, {}).get(type_):
75
+ sn = f"{pre}_{ult.sender_id}"
76
+ title = f"{get_display_name(sender)}'s Kang Pack"
77
+ if animated:
78
+ type_ = "anim"
79
+ sn += "_anim"
80
+ title += " (Animated)"
81
+ elif video:
82
+ type_ = "vid"
83
+ sn += "_vid"
84
+ title += " (Video)"
85
+ sn += f"_by_{asst.me.username}"
86
+ try:
87
+ await asst(GetSticker(InputStickerSetShortName(sn), hash=0))
88
+ sn = sn.replace(str(ult.sender_id), f"{ult.sender_id}_{ult.id}")
89
+ except StickersetInvalidError:
90
+ pass
91
+ try:
92
+ pack = await ult.client(
93
+ CreateStickerSetRequest(
94
+ user_id=sender.id,
95
+ title=title,
96
+ short_name=sn,
97
+ stickers=[SetItem(file, emoji=emoji)],
98
+ videos=video,
99
+ animated=animated,
100
+ software="@TeamUltroid",
101
+ )
102
+ )
103
+ except Exception as er:
104
+ return await ult.eor(str(er))
105
+ sn = pack.set.short_name
106
+ if not get_.get(ult.sender_id):
107
+ get_.update({ult.sender_id: {type_: [sn]}})
108
+ else:
109
+ get_[ult.sender_id].update({type_: [sn]})
110
+ udB.set_key("STICKERS", get_)
111
+ return await ult.reply(
112
+ f"**Kanged Successfully!\nEmoji :** {emoji}\n**Link :** [Click Here](https://t.me/addstickers/{sn})"
113
+ )
114
+ name = get_[ult.sender_id][type_][-1]
115
+ try:
116
+ await asst(GetSticker(InputStickerSetShortName(name), hash=0))
117
+ except StickersetInvalidError:
118
+ get_[ult.sender_id][type_].remove(name)
119
+ try:
120
+ await asst(
121
+ AddSticker(InputStickerSetShortName(name), SetItem(file, emoji=emoji))
122
+ )
123
+ except (errors.StickerpackStickersTooMuchError, errors.StickersTooMuchError):
124
+ sn = f"{pre}{ult.sender_id}_{ult.id}"
125
+ title = f"{get_display_name(sender)}'s Kang Pack"
126
+ if animated:
127
+ sn += "_anim"
128
+ title += " (Animated)"
129
+ elif video:
130
+ sn += "_vid"
131
+ title += "(Video)"
132
+ sn += f"_by_{asst.me.username}"
133
+ try:
134
+ pack = await ult.client(
135
+ CreateStickerSetRequest(
136
+ user_id=sender.id,
137
+ title=title,
138
+ short_name=sn,
139
+ stickers=[SetItem(file, emoji=emoji)],
140
+ animated=animated,
141
+ )
142
+ )
143
+ except Exception as er:
144
+ return await ult.eor(str(er))
145
+ get_[ult.sender_id][type_].append(pack.set.short_name)
146
+ udB.set_key("STICKERS", get_)
147
+ return await ult.reply(
148
+ f"**Created New Kang Pack!\nEmoji :** {emoji}\n**Link :** [Click Here](https://t.me/addstickers/{sn})"
149
+ )
150
+ except Exception as er:
151
+ LOGS.exception(er)
152
+ return await ult.reply(str(er))
153
+ await ult.reply(
154
+ f"Sticker Added to Pack Successfully\n**Link :** [Click Here](https://t.me/addstickers/{name})"
155
+ )
156
+
157
+
158
+ @asst_cmd(pattern="listpack")
159
+ async def do_magic(ult):
160
+ ko = udB.get_key("STICKERS") or {}
161
+ if not ko.get(ult.sender_id):
162
+ return await ult.reply("No Sticker Pack Found!")
163
+ al_ = []
164
+ ul = ko[ult.sender_id]
165
+ for _ in ul.keys():
166
+ al_.extend(ul[_])
167
+ msg = "• **Stickers Owned by You!**\n\n"
168
+ for _ in al_:
169
+ try:
170
+ pack = await ult.client(GetSticker(InputStickerSetShortName(_), hash=0))
171
+ msg += f"• [{pack.set.title}](https://t.me/addstickers/{_})\n"
172
+ except StickerSetInvalidError:
173
+ if ul.get("anim") and _ in ul["anim"]:
174
+ ul["anim"].remove(_)
175
+ elif ul.get("vid") and _ in ul["vid"]:
176
+ ul["vid"].remove(_)
177
+ else:
178
+ ul["static"].remove(_)
179
+ udB.set_key("STICKERS", ko)
180
+ await ult.reply(msg)
assistant/pmbot.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ # https://github.com/xditya/TeleBot/blob/master/telebot/plugins/mybot/pmbot/incoming.py
9
+
10
+ # --------------------------------------- Imports -------------------------------------------- #
11
+
12
+ import os
13
+
14
+ from telethon.errors.rpcerrorlist import UserNotParticipantError
15
+ from telethon.tl.custom import Button
16
+ from telethon.tl.functions.channels import GetFullChannelRequest
17
+ from telethon.tl.functions.messages import GetFullChatRequest
18
+ from telethon.tl.types import Channel, Chat
19
+ from telethon.utils import get_display_name
20
+
21
+ from pyUltroid.dB.base import KeyManager
22
+ from pyUltroid.dB.botchat_db import *
23
+ from pyUltroid.fns.helper import inline_mention
24
+
25
+ from . import *
26
+
27
+ botb = KeyManager("BOTBLS", cast=list)
28
+ FSUB = udB.get_key("PMBOT_FSUB")
29
+ CACHE = {}
30
+ # --------------------------------------- Incoming -------------------------------------------- #
31
+
32
+
33
+ @asst_cmd(
34
+ load=AST_PLUGINS,
35
+ incoming=True,
36
+ func=lambda e: e.is_private and not botb.contains(e.sender_id),
37
+ )
38
+ async def on_new_mssg(event):
39
+ who = event.sender_id
40
+ # doesn't reply to that user anymore
41
+ if event.text.startswith("/") or who == OWNER_ID:
42
+ return
43
+ if FSUB:
44
+ MSG = ""
45
+ BTTS = []
46
+ for chat in FSUB:
47
+ try:
48
+ await event.client.get_permissions(chat, event.sender_id)
49
+ except UserNotParticipantError:
50
+ if not MSG:
51
+ MSG += get_string("pmbot_1")
52
+ try:
53
+ uri = ""
54
+ TAHC_ = await event.client.get_entity(chat)
55
+ if hasattr(TAHC_, "username") and TAHC_.username:
56
+ uri = f"t.me/{TAHC_.username}"
57
+ elif CACHE.get(chat):
58
+ uri = CACHE[chat]
59
+ else:
60
+ if isinstance(TAHC_, Channel):
61
+ FUGB = await event.client(GetFullChannelRequest(chat))
62
+ elif isinstance(TAHC_, Chat):
63
+ FUGB = await event.client(GetFullChatRequest(chat))
64
+ else:
65
+ return
66
+ if FUGB.full_chat.exported_invite:
67
+ CACHE[chat] = FUGB.full_chat.exported_invite.link
68
+ uri = CACHE[chat]
69
+ BTTS.append(Button.url(get_display_name(TAHC_), uri))
70
+ except Exception as er:
71
+ LOGS.exception(f"Error On PmBot Force Sub!\n - {chat} \n{er}")
72
+ if MSG and BTTS:
73
+ return await event.reply(MSG, buttons=BTTS)
74
+ xx = await event.forward_to(OWNER_ID)
75
+ if event.fwd_from:
76
+ await xx.reply(f"From {inline_mention(event.sender)} [`{event.sender_id}`]")
77
+ add_stuff(xx.id, who)
78
+
79
+
80
+ # --------------------------------------- Outgoing -------------------------------------------- #
81
+
82
+
83
+ @asst_cmd(
84
+ load=AST_PLUGINS,
85
+ from_users=[OWNER_ID],
86
+ incoming=True,
87
+ func=lambda e: e.is_private and e.is_reply,
88
+ )
89
+ async def on_out_mssg(event):
90
+ x = event.reply_to_msg_id
91
+ to_user = get_who(x)
92
+ if event.text.startswith("/who"):
93
+ try:
94
+ k = await asst.get_entity(to_user)
95
+ photu = await event.client.download_profile_photo(k.id)
96
+ await event.reply(
97
+ f"• **Name :** {get_display_name(k)}\n• **ID :** `{k.id}`\n• **Link :** {inline_mention(k)}",
98
+ file=photu,
99
+ )
100
+ if photu:
101
+ os.remove(photu)
102
+ return
103
+ except BaseException as er:
104
+ return await event.reply(f"**ERROR : **{str(er)}")
105
+ elif event.text.startswith("/"):
106
+ return
107
+ if to_user:
108
+ await asst.send_message(to_user, event.message)
109
+
110
+
111
+ # --------------------------------------- Ban/Unban -------------------------------------------- #
112
+
113
+
114
+ @asst_cmd(
115
+ pattern="ban",
116
+ load=AST_PLUGINS,
117
+ from_users=[OWNER_ID],
118
+ func=lambda x: x.is_private,
119
+ )
120
+ async def banhammer(event):
121
+ if not event.is_reply:
122
+ return await event.reply(get_string("pmbot_2"))
123
+ target = get_who(event.reply_to_msg_id)
124
+ if botb.contains(target):
125
+ return await event.reply(get_string("pmbot_3"))
126
+
127
+ botb.add(target)
128
+ await event.reply(f"#BAN\nUser : {target}")
129
+ await asst.send_message(target, get_string("pmbot_4"))
130
+
131
+
132
+ @asst_cmd(
133
+ pattern="unban",
134
+ load=AST_PLUGINS,
135
+ from_users=[OWNER_ID],
136
+ func=lambda x: x.is_private,
137
+ )
138
+ async def unbanhammer(event):
139
+ if not event.is_reply:
140
+ return await event.reply(get_string("pmbot_5"))
141
+ target = get_who(event.reply_to_msg_id)
142
+ if not botb.contains(target):
143
+ return await event.reply(get_string("pmbot_6"))
144
+
145
+ botb.remove(target)
146
+ await event.reply(f"#UNBAN\nUser : {target}")
147
+ await asst.send_message(target, get_string("pmbot_7"))
148
+
149
+
150
+ # --------------------------------------- END -------------------------------------------- #
assistant/start.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from datetime import datetime
9
+
10
+ from pytz import timezone as tz
11
+ from telethon import Button, events
12
+ from telethon.errors.rpcerrorlist import MessageDeleteForbiddenError
13
+ from telethon.utils import get_display_name
14
+
15
+ from pyUltroid._misc import SUDO_M, owner_and_sudos
16
+ from pyUltroid.dB.base import KeyManager
17
+ from pyUltroid.fns.helper import inline_mention
18
+ from strings import get_string
19
+
20
+ from . import *
21
+
22
+ Owner_info_msg = udB.get_key("BOT_INFO_START")
23
+ custom_info = True
24
+ if Owner_info_msg is None:
25
+ custom_info = False
26
+ Owner_info_msg = f"""
27
+ **Owner** - {OWNER_NAME}
28
+ **OwnerID** - `{OWNER_ID}`
29
+
30
+ **Message Forwards** - {udB.get_key("PMBOT")}
31
+
32
+ **Ultroid [v{ultroid_version}](https://github.com/TeamUltroid/Ultroid), powered by @TeamUltroid**
33
+ """
34
+
35
+
36
+ _settings = [
37
+ [
38
+ Button.inline("API Kᴇʏs", data="cbs_apiset"),
39
+ Button.inline("Pᴍ Bᴏᴛ", data="cbs_chatbot"),
40
+ ],
41
+ [
42
+ Button.inline("Aʟɪᴠᴇ", data="cbs_alvcstm"),
43
+ Button.inline("PᴍPᴇʀᴍɪᴛ", data="cbs_ppmset"),
44
+ ],
45
+ [
46
+ Button.inline("Fᴇᴀᴛᴜʀᴇs", data="cbs_otvars"),
47
+ Button.inline("VC Sᴏɴɢ Bᴏᴛ", data="cbs_vcb"),
48
+ ],
49
+ [Button.inline("« Bᴀᴄᴋ", data="mainmenu")],
50
+ ]
51
+
52
+ _start = [
53
+ [
54
+ Button.inline("Lᴀɴɢᴜᴀɢᴇ 🌐", data="lang"),
55
+ Button.inline("Sᴇᴛᴛɪɴɢs ⚙️", data="setter"),
56
+ ],
57
+ [
58
+ Button.inline("Sᴛᴀᴛs ✨", data="stat"),
59
+ Button.inline("Bʀᴏᴀᴅᴄᴀsᴛ 📻", data="bcast"),
60
+ ],
61
+ [Button.inline("TɪᴍᴇZᴏɴᴇ 🌎", data="tz")],
62
+ ]
63
+
64
+
65
+ @callback("ownerinfo")
66
+ async def own(event):
67
+ msg = Owner_info_msg.format(
68
+ mention=event.sender.mention, me=inline_mention(ultroid_bot.me)
69
+ )
70
+ if custom_info:
71
+ msg += "\n\n• Powered by **@TeamUltroid**"
72
+ await event.edit(
73
+ msg,
74
+ buttons=[Button.inline("Close", data="closeit")],
75
+ link_preview=False,
76
+ )
77
+
78
+
79
+ @callback("closeit")
80
+ async def closet(lol):
81
+ try:
82
+ await lol.delete()
83
+ except MessageDeleteForbiddenError:
84
+ await lol.answer("MESSAGE_TOO_OLD", alert=True)
85
+
86
+
87
+ @asst_cmd(pattern="start( (.*)|$)", forwards=False, func=lambda x: not x.is_group)
88
+ async def ultroid(event):
89
+ args = event.pattern_match.group(1).strip()
90
+ keym = KeyManager("BOT_USERS", cast=list)
91
+ if not keym.contains(event.sender_id) and event.sender_id not in owner_and_sudos():
92
+ keym.add(event.sender_id)
93
+ kak_uiw = udB.get_key("OFF_START_LOG")
94
+ if not kak_uiw or kak_uiw != True:
95
+ msg = f"{inline_mention(event.sender)} `[{event.sender_id}]` started your [Assistant bot](@{asst.me.username})."
96
+ buttons = [[Button.inline("Info", "itkkstyo")]]
97
+ if event.sender.username:
98
+ buttons[0].append(
99
+ Button.mention(
100
+ "User", await event.client.get_input_entity(event.sender_id)
101
+ )
102
+ )
103
+ await event.client.send_message(
104
+ udB.get_key("LOG_CHANNEL"), msg, buttons=buttons
105
+ )
106
+ if event.sender_id not in SUDO_M.fullsudos:
107
+ ok = ""
108
+ me = inline_mention(ultroid_bot.me)
109
+ mention = inline_mention(event.sender)
110
+ if args and args != "set":
111
+ await get_stored_file(event, args)
112
+ if not udB.get_key("STARTMSG"):
113
+ if udB.get_key("PMBOT"):
114
+ ok = "You can contact my master using this bot!!\n\nSend your Message, I will Deliver it To Master."
115
+ await event.reply(
116
+ f"Hey there {mention}, this is Ultroid Assistant of {me}!\n\n{ok}",
117
+ file=udB.get_key("STARTMEDIA"),
118
+ buttons=[Button.inline("Info.", data="ownerinfo")]
119
+ if Owner_info_msg
120
+ else None,
121
+ )
122
+ else:
123
+ await event.reply(
124
+ udB.get_key("STARTMSG").format(me=me, mention=mention),
125
+ file=udB.get_key("STARTMEDIA"),
126
+ buttons=[Button.inline("Info.", data="ownerinfo")]
127
+ if Owner_info_msg
128
+ else None,
129
+ )
130
+ else:
131
+ name = get_display_name(event.sender)
132
+ if args == "set":
133
+ await event.reply(
134
+ "Choose from the below options -",
135
+ buttons=_settings,
136
+ )
137
+ elif args:
138
+ await get_stored_file(event, args)
139
+ else:
140
+ await event.reply(
141
+ get_string("ast_3").format(name),
142
+ buttons=_start,
143
+ )
144
+
145
+
146
+ @callback("itkkstyo", owner=True)
147
+ async def ekekdhdb(e):
148
+ text = f"When New Visitor will visit your Assistant Bot. You will get this log message!\n\nTo Disable : {HNDLR}setdb OFF_START_LOG True"
149
+ await e.answer(text, alert=True)
150
+
151
+
152
+ @callback("mainmenu", owner=True, func=lambda x: not x.is_group)
153
+ async def ultroid(event):
154
+ await event.edit(
155
+ get_string("ast_3").format(OWNER_NAME),
156
+ buttons=_start,
157
+ )
158
+
159
+
160
+ @callback("stat", owner=True)
161
+ async def botstat(event):
162
+ ok = len(udB.get_key("BOT_USERS") or [])
163
+ msg = """Ultroid Assistant - Stats
164
+ Total Users - {}""".format(
165
+ ok,
166
+ )
167
+ await event.answer(msg, cache_time=0, alert=True)
168
+
169
+
170
+ @callback("bcast", owner=True)
171
+ async def bdcast(event):
172
+ keym = KeyManager("BOT_USERS", cast=list)
173
+ total = keym.count()
174
+ await event.edit(f"• Broadcast to {total} users.")
175
+ async with event.client.conversation(OWNER_ID) as conv:
176
+ await conv.send_message(
177
+ "Enter your broadcast message.\nUse /cancel to stop the broadcast.",
178
+ )
179
+ response = await conv.get_response()
180
+ if response.message == "/cancel":
181
+ return await conv.send_message("Cancelled!!")
182
+ success = 0
183
+ fail = 0
184
+ await conv.send_message(f"Starting a broadcast to {total} users...")
185
+ start = datetime.now()
186
+ for i in keym.get():
187
+ try:
188
+ await asst.send_message(int(i), response)
189
+ success += 1
190
+ except BaseException:
191
+ fail += 1
192
+ end = datetime.now()
193
+ time_taken = (end - start).seconds
194
+ await conv.send_message(
195
+ f"""
196
+ **Broadcast completed in {time_taken} seconds.**
197
+ Total Users in Bot - {total}
198
+ **Sent to** : `{success} users.`
199
+ **Failed for** : `{fail} user(s).`""",
200
+ )
201
+
202
+
203
+ @callback("setter", owner=True)
204
+ async def setting(event):
205
+ await event.edit(
206
+ "Choose from the below options -",
207
+ buttons=_settings,
208
+ )
209
+
210
+
211
+ @callback("tz", owner=True)
212
+ async def timezone_(event):
213
+ await event.delete()
214
+ pru = event.sender_id
215
+ var = "TIMEZONE"
216
+ name = "Timezone"
217
+ async with event.client.conversation(pru) as conv:
218
+ await conv.send_message(
219
+ "Send Your TimeZone From This List [Check From Here](http://www.timezoneconverter.com/cgi-bin/findzone.tzc)"
220
+ )
221
+ response = conv.wait_event(events.NewMessage(chats=pru))
222
+ response = await response
223
+ themssg = response.message.message
224
+ if themssg == "/cancel":
225
+ return await conv.send_message(
226
+ "Cancelled!!",
227
+ buttons=get_back_button("mainmenu"),
228
+ )
229
+ try:
230
+ tz(themssg)
231
+ await setit(event, var, themssg)
232
+ await conv.send_message(
233
+ f"{name} changed to {themssg}\n",
234
+ buttons=get_back_button("mainmenu"),
235
+ )
236
+ except BaseException:
237
+ await conv.send_message(
238
+ "Wrong TimeZone, Try again",
239
+ buttons=get_back_button("mainmenu"),
240
+ )
assistant/ytdl.py ADDED
@@ -0,0 +1,307 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+
9
+ import os
10
+ import re
11
+
12
+ try:
13
+ from PIL import Image
14
+ except ImportError:
15
+ Image = None
16
+ from telethon import Button
17
+ from telethon.errors.rpcerrorlist import FilePartLengthInvalidError, MediaEmptyError
18
+ from telethon.tl.types import DocumentAttributeAudio, DocumentAttributeVideo
19
+ from telethon.tl.types import InputWebDocument as wb
20
+
21
+ from pyUltroid.fns.helper import (
22
+ bash,
23
+ fast_download,
24
+ humanbytes,
25
+ numerize,
26
+ time_formatter,
27
+ )
28
+ from pyUltroid.fns.ytdl import dler, get_buttons, get_formats
29
+
30
+ from . import LOGS, asst, callback, in_pattern, udB
31
+
32
+ try:
33
+ from youtubesearchpython import VideosSearch
34
+ except ImportError:
35
+ LOGS.info("'youtubesearchpython' not installed!")
36
+ VideosSearch = None
37
+
38
+
39
+ ytt = "https://graph.org/file/afd04510c13914a06dd03.jpg"
40
+ _yt_base_url = "https://www.youtube.com/watch?v="
41
+ BACK_BUTTON = {}
42
+
43
+
44
+ @in_pattern("yt", owner=True)
45
+ async def _(event):
46
+ try:
47
+ string = event.text.split(" ", maxsplit=1)[1]
48
+ except IndexError:
49
+ fuk = event.builder.article(
50
+ title="Search Something",
51
+ thumb=wb(ytt, 0, "image/jpeg", []),
52
+ text="**YᴏᴜTᴜʙᴇ Sᴇᴀʀᴄʜ**\n\nYou didn't search anything",
53
+ buttons=Button.switch_inline(
54
+ "Sᴇᴀʀᴄʜ Aɢᴀɪɴ",
55
+ query="yt ",
56
+ same_peer=True,
57
+ ),
58
+ )
59
+ await event.answer([fuk])
60
+ return
61
+ results = []
62
+ search = VideosSearch(string, limit=50)
63
+ nub = search.result()
64
+ nibba = nub["result"]
65
+ for v in nibba:
66
+ ids = v["id"]
67
+ link = _yt_base_url + ids
68
+ title = v["title"]
69
+ duration = v["duration"]
70
+ views = v["viewCount"]["short"]
71
+ publisher = v["channel"]["name"]
72
+ published_on = v["publishedTime"]
73
+ description = (
74
+ v["descriptionSnippet"][0]["text"]
75
+ if v.get("descriptionSnippet")
76
+ and len(v["descriptionSnippet"][0]["text"]) < 500
77
+ else "None"
78
+ )
79
+ thumb = f"https://i.ytimg.com/vi/{ids}/hqdefault.jpg"
80
+ text = f"**Title: [{title}]({link})**\n\n"
81
+ text += f"`Description: {description}\n\n"
82
+ text += f"「 Duration: {duration} 」\n"
83
+ text += f"「 Views: {views} 」\n"
84
+ text += f"「 Publisher: {publisher} 」\n"
85
+ text += f"「 Published on: {published_on} 」`"
86
+ desc = f"{title}\n{duration}"
87
+ file = wb(thumb, 0, "image/jpeg", [])
88
+ buttons = [
89
+ [
90
+ Button.inline("Audio", data=f"ytdl:audio:{ids}"),
91
+ Button.inline("Video", data=f"ytdl:video:{ids}"),
92
+ ],
93
+ [
94
+ Button.switch_inline(
95
+ "Sᴇᴀʀᴄʜ Aɢᴀɪɴ",
96
+ query="yt ",
97
+ same_peer=True,
98
+ ),
99
+ Button.switch_inline(
100
+ "Sʜᴀʀᴇ",
101
+ query=f"yt {string}",
102
+ same_peer=False,
103
+ ),
104
+ ],
105
+ ]
106
+ BACK_BUTTON.update({ids: {"text": text, "buttons": buttons}})
107
+ results.append(
108
+ await event.builder.article(
109
+ type="photo",
110
+ title=title,
111
+ description=desc,
112
+ thumb=file,
113
+ content=file,
114
+ text=text,
115
+ include_media=True,
116
+ buttons=buttons,
117
+ ),
118
+ )
119
+ await event.answer(results[:50])
120
+
121
+
122
+ @callback(
123
+ re.compile(
124
+ "ytdl:(.*)",
125
+ ),
126
+ owner=True,
127
+ )
128
+ async def _(e):
129
+ _e = e.pattern_match.group(1).strip().decode("UTF-8")
130
+ _lets_split = _e.split(":")
131
+ _ytdl_data = await dler(e, _yt_base_url + _lets_split[1])
132
+ _data = get_formats(_lets_split[0], _lets_split[1], _ytdl_data)
133
+ _buttons = get_buttons(_data)
134
+ _text = (
135
+ "`Select Your Format.`"
136
+ if _buttons
137
+ else "`Error downloading from YouTube.\nTry Restarting your bot.`"
138
+ )
139
+
140
+ await e.edit(_text, buttons=_buttons)
141
+
142
+
143
+ @callback(
144
+ re.compile(
145
+ "ytdownload:(.*)",
146
+ ),
147
+ owner=True,
148
+ )
149
+ async def _(event):
150
+ url = event.pattern_match.group(1).strip().decode("UTF-8")
151
+ lets_split = url.split(":")
152
+ vid_id = lets_split[2]
153
+ link = _yt_base_url + vid_id
154
+ format = lets_split[1]
155
+ try:
156
+ ext = lets_split[3]
157
+ except IndexError:
158
+ ext = "mp3"
159
+ if lets_split[0] == "audio":
160
+ opts = {
161
+ "format": "bestaudio",
162
+ "addmetadata": True,
163
+ "key": "FFmpegMetadata",
164
+ "prefer_ffmpeg": True,
165
+ "geo_bypass": True,
166
+ "outtmpl": f"%(id)s.{ext}",
167
+ "logtostderr": False,
168
+ "postprocessors": [
169
+ {
170
+ "key": "FFmpegExtractAudio",
171
+ "preferredcodec": ext,
172
+ "preferredquality": format,
173
+ },
174
+ {"key": "FFmpegMetadata"},
175
+ ],
176
+ }
177
+
178
+ ytdl_data = await dler(event, link, opts, True)
179
+ title = ytdl_data["title"]
180
+ if ytdl_data.get("artist"):
181
+ artist = ytdl_data["artist"]
182
+ elif ytdl_data.get("creator"):
183
+ artist = ytdl_data["creator"]
184
+ elif ytdl_data.get("channel"):
185
+ artist = ytdl_data["channel"]
186
+ views = numerize(ytdl_data.get("view_count")) or 0
187
+ thumb, _ = await fast_download(ytdl_data["thumbnail"], filename=f"{vid_id}.jpg")
188
+
189
+ likes = numerize(ytdl_data.get("like_count")) or 0
190
+ duration = ytdl_data.get("duration") or 0
191
+ description = (
192
+ ytdl_data["description"]
193
+ if len(ytdl_data["description"]) < 100
194
+ else ytdl_data["description"][:100]
195
+ )
196
+ description = description or "None"
197
+ filepath = f"{vid_id}.{ext}"
198
+ if not os.path.exists(filepath):
199
+ filepath = f"{filepath}.{ext}"
200
+ size = os.path.getsize(filepath)
201
+ file, _ = await event.client.fast_uploader(
202
+ filepath,
203
+ filename=f"{title}.{ext}",
204
+ show_progress=True,
205
+ event=event,
206
+ to_delete=True,
207
+ )
208
+
209
+ attributes = [
210
+ DocumentAttributeAudio(
211
+ duration=int(duration),
212
+ title=title,
213
+ performer=artist,
214
+ ),
215
+ ]
216
+ elif lets_split[0] == "video":
217
+ opts = {
218
+ "format": str(format),
219
+ "addmetadata": True,
220
+ "key": "FFmpegMetadata",
221
+ "prefer_ffmpeg": True,
222
+ "geo_bypass": True,
223
+ "outtmpl": f"%(id)s.{ext}",
224
+ "logtostderr": False,
225
+ "postprocessors": [{"key": "FFmpegMetadata"}],
226
+ }
227
+
228
+ ytdl_data = await dler(event, link, opts, True)
229
+ title = ytdl_data["title"]
230
+ if ytdl_data.get("artist"):
231
+ artist = ytdl_data["artist"]
232
+ elif ytdl_data.get("creator"):
233
+ artist = ytdl_data["creator"]
234
+ elif ytdl_data.get("channel"):
235
+ artist = ytdl_data["channel"]
236
+ views = numerize(ytdl_data.get("view_count")) or 0
237
+ thumb, _ = await fast_download(ytdl_data["thumbnail"], filename=f"{vid_id}.jpg")
238
+
239
+ try:
240
+ Image.open(thumb).save(thumb, "JPEG")
241
+ except Exception as er:
242
+ LOGS.exception(er)
243
+ thumb = None
244
+ description = (
245
+ ytdl_data["description"]
246
+ if len(ytdl_data["description"]) < 100
247
+ else ytdl_data["description"][:100]
248
+ )
249
+ likes = numerize(ytdl_data.get("like_count")) or 0
250
+ hi, wi = ytdl_data.get("height") or 720, ytdl_data.get("width") or 1280
251
+ duration = ytdl_data.get("duration") or 0
252
+ filepath = f"{vid_id}.mkv"
253
+ if not os.path.exists(filepath):
254
+ filepath = f"{filepath}.webm"
255
+ size = os.path.getsize(filepath)
256
+ file, _ = await event.client.fast_uploader(
257
+ filepath,
258
+ filename=f"{title}.mkv",
259
+ show_progress=True,
260
+ event=event,
261
+ to_delete=True,
262
+ )
263
+
264
+ attributes = [
265
+ DocumentAttributeVideo(
266
+ duration=int(duration),
267
+ w=wi,
268
+ h=hi,
269
+ supports_streaming=True,
270
+ ),
271
+ ]
272
+ description = description if description != "" else "None"
273
+ text = f"**Title: [{title}]({_yt_base_url}{vid_id})**\n\n"
274
+ text += f"`📝 Description: {description}\n\n"
275
+ text += f"「 Duration: {time_formatter(int(duration)*1000)} 」\n"
276
+ text += f"「 Artist: {artist} 」\n"
277
+ text += f"「 Views: {views} 」\n"
278
+ text += f"「 Likes: {likes} 」\n"
279
+ text += f"「 Size: {humanbytes(size)} 」`"
280
+ button = Button.switch_inline("Search More", query="yt ", same_peer=True)
281
+ try:
282
+ await event.edit(
283
+ text,
284
+ file=file,
285
+ buttons=button,
286
+ attributes=attributes,
287
+ thumb=thumb,
288
+ )
289
+ except (FilePartLengthInvalidError, MediaEmptyError):
290
+ file = await asst.send_message(
291
+ udB.get_key("LOG_CHANNEL"),
292
+ text,
293
+ file=file,
294
+ buttons=button,
295
+ attributes=attributes,
296
+ thumb=thumb,
297
+ )
298
+ await event.edit(text, file=file.media, buttons=button)
299
+ await bash(f"rm {vid_id}.jpg")
300
+
301
+
302
+ @callback(re.compile("ytdl_back:(.*)"), owner=True)
303
+ async def ytdl_back(event):
304
+ id_ = event.data_match.group(1).decode("utf-8")
305
+ if not BACK_BUTTON.get(id_):
306
+ return await event.answer("Query Expired! Search again 🔍")
307
+ await event.edit(**BACK_BUTTON[id_])
docker-compose.yml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ services:
2
+ worker:
3
+ build: .
4
+ environment:
5
+ REDIS_URI: $REDIS_URI
6
+ REDIS_PASSWORD: $REDIS_PASSWORD
7
+ SESSION: $SESSION
8
+ API_ID: $API_ID # defaults to None
9
+ API_HASH: $API_HASH # defaults to None
10
+ MONGO_URI: $MONGO_URI # defaults to None
11
+ BOT_TOKEN: $BOT_TOKEN # Not mandatory
12
+ LOG_CHANNEL: $LOG_CHANNEL # Not mandatory
13
+ DATABASE_URL: $DATABASE_URL # defaults to None
14
+ OKTETO_TOKEN: $OKTETO_TOKEN
heroku.yml ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ build:
2
+ docker:
3
+ ultroid: Dockerfile
install-termux ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Ultroid - UserBot
3
+ # Copyright (C) 2021-2025 TeamUltroid
4
+ #
5
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
6
+ # PLease read the GNU Affero General Public License in <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ if [ -d "resources" ]
9
+ then
10
+ echo "Current directory Identified.."
11
+ else
12
+ apt install git -y
13
+ git clone https://github.com/TeamUltroid/Ultroid
14
+ cd Ultroid
15
+ fi
16
+ bash resources/startup/termux.sh
installer.sh ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ REPO="https://github.com/TeamUltroid/Ultroid.git"
4
+ CURRENT_DIR="$(pwd)"
5
+ ENV_FILE_PATH=".env"
6
+ DIR="/root/TeamUltroid"
7
+
8
+ while [ $# -gt 0 ]; do
9
+ case "$1" in
10
+ --dir=*)
11
+ DIR="${1#*=}" || DIR="/root/TeamUltroid"
12
+ ;;
13
+ --branch=*)
14
+ BRANCH="${1#*=}" || BRANCH="main"
15
+ ;;
16
+ --env-file=*)
17
+ ENV_FILE_PATH="${1#*=}" || ENV_FILE_PATH=".env"
18
+ ;;
19
+ --no-root)
20
+ NO_ROOT=true
21
+ ;;
22
+ *)
23
+ echo "Unknown parameter passed: $1"
24
+ exit 1
25
+ ;;
26
+ esac
27
+ shift
28
+ done
29
+
30
+ check_dependencies() {
31
+ # check if debian
32
+ echo "Checking dependencies..."
33
+ # read file with root access
34
+ if ! [[ $(ls -l "/etc/sudoers" | cut -d " " -f1) =~ "r" ]]; then
35
+ # check dependencies if installed
36
+ echo -e "Root access not found. Checking if dependencies are installed." >&2
37
+ if ! [ -x "$(command -v python3)" ] || ! [ -x "$(command -v python)" ]; then
38
+ echo -e "Python3 isn't installed. Please install python3.8 or higher to run this bot." >&2
39
+ exit 1
40
+ fi
41
+ if [ $(python3 -c "import sys; print(sys.version_info[1])") -lt 8 ] || [ $(python -c "import sys; print(sys.version_info[1])") -lt 8 ]; then
42
+ echo -e "Python 3.8 or higher is required to run this bot." >&2
43
+ exit 1
44
+ fi
45
+ # check if any of ffmpeg, mediainfo, neofetch, git is not installed
46
+ if ! command -v ffmpeg &>/dev/null || ! command -v mediainfo &>/dev/null || ! command -v neofetch &>/dev/null || ! command -v git &>/dev/null; then
47
+ echo -e "Some dependencies aren't installed. Please install ffmpeg, mediainfo, neofetch and git to run this bot." >&2
48
+ exit 1
49
+ fi
50
+ fi
51
+ if [ -x "$(command -v apt-get)" ]; then
52
+ echo -e "Installing dependencies..."
53
+ # check if any of ffmpeg, mediainfo, neofetch, git is not installed via dpkg
54
+ if dpkg -l | grep -q ffmpeg || dpkg -l | grep -q mediainfo || dpkg -l | grep -q neofetch || dpkg -l | grep -q git; then
55
+ sudo apt-get -qq -o=Dpkg::Use-Pty=0 update
56
+ sudo apt-get install -qq -o=Dpkg::Use-Pty=0 python3 python3-pip ffmpeg mediainfo neofetch git -y
57
+ fi
58
+ elif [ -x "$(command -v pacman)" ]; then
59
+ echo -e "Installing dependencies..."
60
+ if pacman -Q | grep -q ffmpeg || pacman -Q | grep -q mediainfo || pacman -Q | grep -q neofetch || pacman -Q | grep -q git; then
61
+ sudo pacman -Sy python python-pip git ffmpeg mediainfo neofetch --noconfirm
62
+ fi
63
+ else
64
+ echo -e "Unknown OS. Checking if dependecies are installed" >&2
65
+ if ! [ -x "$(command -v python3)" ] || ! [ -x "$(command -v python)" ]; then
66
+ echo -e "Python3 isn't installed. Please install python3.8 or higher to run this bot." >&2
67
+ exit 1
68
+ fi
69
+ if [ $(python3 -c "import sys; print(sys.version_info[1])") -lt 8 ] || [ $(python -c "import sys; print(sys.version_info[1])") -lt 8 ]; then
70
+ echo -e "Python 3.8 or higher is required to run this bot." >&2
71
+ exit 1
72
+ fi
73
+ if ! command -v ffmpeg &>/dev/null || ! command -v mediainfo &>/dev/null || ! command -v neofetch &>/dev/null || ! command -v git &>/dev/null; then
74
+ echo -e "Some dependencies aren't installed. Please install ffmpeg, mediainfo, neofetch and git to run this bot." >&2
75
+ exit 1
76
+ fi
77
+ fi
78
+ }
79
+
80
+ check_python() {
81
+ # check if python is installed
82
+ if ! command -v python3 &>/dev/null; then
83
+ echo -e "Python3 isn't installed. Please install python3.8 or higher to run this bot."
84
+ exit 1
85
+ elif ! command -v python &>/dev/null; then
86
+ echo -e "Python3 isn't installed. Please install python3.8 or higher to run this bot."
87
+ exit 1
88
+ fi
89
+ if [ $(python3 -c "import sys; print(sys.version_info[1])") -lt 8 ]; then
90
+ echo -e "Python 3.8 or higher is required to run this bot."
91
+ exit 1
92
+ elif [ $(python -c "import sys; print(sys.version_info[1])") -lt 3 ]; then
93
+ if [ $(python -c "import sys; print(sys.version_info[1])") -lt 8 ]; then
94
+ echo -e "Python 3.8 or higher is required to run this bot."
95
+ exit 1
96
+ fi
97
+ fi
98
+ }
99
+
100
+ clone_repo() {
101
+ # check if pyultroid, startup, plugins folders exist
102
+ cd $DIR
103
+ if [ -d $DIR ]; then
104
+ if [ -d $DIR/.git ]; then
105
+ echo -e "Updating Ultroid ${BRANCH}... "
106
+ cd $DIR
107
+ git pull
108
+ currentbranch="$(git rev-parse --abbrev-ref HEAD)"
109
+ if [ ! $BRANCH ]; then
110
+ export BRANCH=$currentbranch
111
+ fi
112
+ case $currentbranch in
113
+ $BRANCH)
114
+ # do nothing
115
+ ;;
116
+ *)
117
+ echo -e "Switching to branch ${BRANCH}... "
118
+ echo -e $currentbranch
119
+ git checkout $BRANCH
120
+ ;;
121
+ esac
122
+ else
123
+ rm -rf $DIR
124
+ exit 1
125
+ fi
126
+ if [ -d "addons" ]; then
127
+ cd addons
128
+ git pull
129
+ fi
130
+ return
131
+ else
132
+ if [ ! $BRANCH ]; then
133
+ export BRANCH="main"
134
+ fi
135
+ mkdir -p $DIR
136
+ echo -e "Cloning Ultroid ${BRANCH}... "
137
+ git clone -b $BRANCH $REPO $DIR
138
+ fi
139
+ }
140
+
141
+ install_requirements() {
142
+ pip3 install -q --upgrade pip
143
+ echo -e "\n\nInstalling requirements... "
144
+ pip3 install -q --no-cache-dir -r $DIR/requirements.txt
145
+ pip3 install -q -r $DIR/resources/startup/optional-requirements.txt
146
+ }
147
+
148
+ railways_dep() {
149
+ if [ $RAILWAY_STATIC_URL ]; then
150
+ echo -e "Installing YouTube dependency... "
151
+ pip3 install -q yt-dlp
152
+ fi
153
+ }
154
+
155
+ misc_install() {
156
+ if [ $SETUP_PLAYWRIGHT ]
157
+ then
158
+ echo -e "Installing playwright."
159
+ pip3 install playwright
160
+ playwright install
161
+ fi
162
+ if [ $OKTETO_TOKEN ]; then
163
+ echo -e "Installing Okteto-CLI... "
164
+ curl https://get.okteto.com -sSfL | sh
165
+ elif [ $VCBOT ]; then
166
+ if [ -d $DIR/vcbot ]; then
167
+ cd $DIR/vcbot
168
+ git pull
169
+ else
170
+ echo -e "Cloning VCBOT.."
171
+ git clone https://github.com/TeamUltroid/VcBot $DIR/vcbot
172
+ fi
173
+ pip3 install pytgcalls==3.0.0.dev23 && pip3 install av -q --no-binary av
174
+ fi
175
+ }
176
+
177
+ dep_install() {
178
+ echo -e "\n\nInstalling DB Requirement..."
179
+ if [ $MONGO_URI ]; then
180
+ echo -e " Installing MongoDB Requirements..."
181
+ pip3 install -q pymongo[srv]
182
+ elif [ $DATABASE_URL ]; then
183
+ echo -e " Installing PostgreSQL Requirements..."
184
+ pip3 install -q psycopg2-binary
185
+ elif [ $REDIS_URI ]; then
186
+ echo -e " Installing Redis Requirements..."
187
+ pip3 install -q redis hiredis
188
+ fi
189
+ }
190
+
191
+ main() {
192
+ echo -e "Starting Ultroid Setup..."
193
+ if [ -d "pyUltroid" ] && [ -d "resources" ] && [ -d "plugins" ]; then
194
+ DIR=$CURRENT_DIR
195
+ fi
196
+ if [ -f $ENV_FILE_PATH ]
197
+ then
198
+ set -a
199
+ source <(cat $ENV_FILE_PATH | sed -e '/^#/d;/^\s*$/d' -e "s/'/'\\\''/g" -e "s/=\(.*\)/='\1'/g")
200
+ set +a
201
+ cp $ENV_FILE_PATH .env
202
+ fi
203
+ (check_dependencies)
204
+ (check_python)
205
+ (clone_repo)
206
+ (install_requirements)
207
+ (railways_dep)
208
+ (dep_install)
209
+ (misc_install)
210
+ echo -e "\n\nSetup Completed."
211
+ }
212
+
213
+ if [ $NO_ROOT ]; then
214
+ echo -e "Running with non root"
215
+ main
216
+ return 0
217
+ elif [ -t 0 ]; then
218
+ unameOut="$(uname -s)"
219
+ case "${unameOut}" in
220
+ Linux*) machine=Linux;;
221
+ Darwin*) machine=Mac;;
222
+ CYGWIN*) machine=Cygwin;;
223
+ MINGW*) machine=MinGw;;
224
+ *) machine="UNKNOWN:${unameOut}"
225
+ esac
226
+ if machine != "Linux"; then
227
+ echo -e "This script is only for Linux. Please use the Windows installer."
228
+ exit 1
229
+ fi
230
+ # check if sudo is installed
231
+ if ! command -v sudo &>/dev/null; then
232
+ echo -e "Sudo isn't installed. Please install sudo to run this bot."
233
+ exit 1
234
+ fi
235
+ sudo echo "Sudo permission granted."
236
+ main
237
+ else
238
+ echo "Not an interactive terminal, skipping sudo."
239
+ # run main function
240
+ main
241
+ fi
multi_client.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import os
3
+ import subprocess
4
+ import sys
5
+
6
+ vars = ["API_ID", "API_HASH", "SESSION"]
7
+
8
+ def _check(z):
9
+ new = []
10
+ for var in vars:
11
+ ent = os.environ.get(var + z)
12
+ if not ent:
13
+ return False, new
14
+ new.append(ent)
15
+ return True, new
16
+
17
+ for z in range(5):
18
+ n = str(z + 1)
19
+ if z == 0:
20
+ z = ""
21
+ fine, out = _check(str(z))
22
+ if fine:
23
+ subprocess.Popen(
24
+ [sys.executable, "-m", "pyUltroid", out[0], out[1], out[2], out[3], out[4], n],
25
+ stdin=None,
26
+ stderr=None,
27
+ stdout=None,
28
+ cwd=None,
29
+ )
30
+
31
+ loop = asyncio.get_event_loop()
32
+
33
+ try:
34
+ loop.run_forever()
35
+ except Exception as er:
36
+ print(er)
37
+ finally:
38
+ loop.close()
39
+
okteto-pipeline.yml ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ icon: https://raw.githubusercontent.com/TeamUltroid/Ultroid/main/resources/extras/logo_readme.jpg
2
+ deploy:
3
+ - okteto deploy --build -f docker-compose.yml
plugins/__init__.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ import asyncio
9
+ import os
10
+ import time
11
+ from random import choice
12
+
13
+ import requests
14
+ from telethon import Button, events
15
+ from telethon.tl import functions, types # pylint:ignore
16
+
17
+ from pyUltroid import *
18
+ from pyUltroid._misc._assistant import asst_cmd, callback, in_pattern
19
+ from pyUltroid._misc._decorators import ultroid_cmd
20
+ from pyUltroid._misc._wrappers import eod, eor
21
+ from pyUltroid.dB import DEVLIST, ULTROID_IMAGES
22
+ from pyUltroid.fns.helper import *
23
+ from pyUltroid.fns.misc import *
24
+ from pyUltroid.fns.tools import *
25
+ from pyUltroid.startup._database import _BaseDatabase as Database
26
+ from pyUltroid.version import __version__, ultroid_version
27
+ from strings import get_help, get_string
28
+ from catbox import CatboxUploader
29
+
30
+ udB: Database
31
+
32
+ Redis = udB.get_key
33
+ con = TgConverter
34
+ quotly = Quotly()
35
+ OWNER_NAME = ultroid_bot.full_name
36
+ OWNER_ID = ultroid_bot.uid
37
+
38
+ ultroid_bot: UltroidClient
39
+ asst: UltroidClient
40
+
41
+ LOG_CHANNEL = udB.get_key("LOG_CHANNEL")
42
+
43
+
44
+ def inline_pic():
45
+ INLINE_PIC = udB.get_key("INLINE_PIC")
46
+ if INLINE_PIC is None:
47
+ INLINE_PIC = choice(ULTROID_IMAGES)
48
+ elif INLINE_PIC == False:
49
+ INLINE_PIC = None
50
+ return INLINE_PIC
51
+
52
+
53
+ Telegraph = telegraph_client()
54
+ cat_uploader = CatboxUploader()
55
+
56
+ upload_file = cat_uploader.upload_file
57
+
58
+ List = []
59
+ Dict = {}
60
+ InlinePlugin = {}
61
+ N = 0
62
+ cmd = ultroid_cmd
63
+ STUFF = {}
64
+
65
+ # Chats, which needs to be ignore for some cases
66
+ # Considerably, there can be many
67
+ # Feel Free to Add Any other...
68
+
69
+ NOSPAM_CHAT = [
70
+ -1001361294038, # UltroidSupportChat
71
+ -1001387666944, # PyrogramChat
72
+ -1001109500936, # TelethonChat
73
+ -1001050982793, # Python
74
+ -1001256902287, # DurovsChat
75
+ -1001473548283, # SharingUserbot
76
+ ]
77
+
78
+ KANGING_STR = [
79
+ "Using Witchery to kang this sticker...",
80
+ "Plagiarising hehe...",
81
+ "Inviting this sticker over to my pack...",
82
+ "Kanging this sticker...",
83
+ "Hey that's a nice sticker!\nMind if I kang?!..",
84
+ "Hehe me stel ur stiker...",
85
+ "Ay look over there (☉。☉)!→\nWhile I kang this...",
86
+ "Roses are red violets are blue, kanging this sticker so my pack looks cool",
87
+ "Imprisoning this sticker...",
88
+ "Mr.Steal-Your-Sticker is stealing this sticker... ",
89
+ ]
90
+
91
+
92
+ ATRA_COL = [
93
+ "DarkCyan",
94
+ "DeepSkyBlue",
95
+ "DarkTurquoise",
96
+ "Cyan",
97
+ "LightSkyBlue",
98
+ "Turquoise",
99
+ "MediumVioletRed",
100
+ "Aquamarine",
101
+ "Lightcyan",
102
+ "Azure",
103
+ "Moccasin",
104
+ "PowderBlue",
105
+ ]
plugins/_chatactions.py ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ import asyncio
9
+
10
+ from telethon import events
11
+ from telethon.errors.rpcerrorlist import UserNotParticipantError
12
+ from telethon.tl.functions.channels import GetParticipantRequest
13
+ from telethon.utils import get_display_name
14
+
15
+ from pyUltroid.dB import stickers
16
+ from pyUltroid.dB.echo_db import check_echo
17
+ from pyUltroid.dB.forcesub_db import get_forcesetting
18
+ from pyUltroid.dB.gban_mute_db import is_gbanned
19
+ from pyUltroid.dB.greetings_db import get_goodbye, get_welcome, must_thank
20
+ from pyUltroid.dB.nsfw_db import is_profan
21
+ from pyUltroid.fns.helper import inline_mention
22
+ from pyUltroid.fns.tools import async_searcher, create_tl_btn, get_chatbot_reply
23
+
24
+ try:
25
+ from ProfanityDetector import detector
26
+ except ImportError:
27
+ detector = None
28
+ from . import LOG_CHANNEL, LOGS, asst, get_string, types, udB, ultroid_bot
29
+ from ._inline import something
30
+
31
+
32
+ @ultroid_bot.on(events.ChatAction())
33
+ async def Function(event):
34
+ try:
35
+ await DummyHandler(event)
36
+ except Exception as er:
37
+ LOGS.exception(er)
38
+
39
+
40
+ async def DummyHandler(ult):
41
+ # clean chat actions
42
+ key = udB.get_key("CLEANCHAT") or []
43
+ if ult.chat_id in key:
44
+ try:
45
+ await ult.delete()
46
+ except BaseException:
47
+ pass
48
+
49
+ # thank members
50
+ if must_thank(ult.chat_id):
51
+ chat_count = (await ult.client.get_participants(ult.chat_id, limit=0)).total
52
+ if chat_count % 100 == 0:
53
+ stik_id = chat_count / 100 - 1
54
+ sticker = stickers[stik_id]
55
+ await ult.respond(file=sticker)
56
+ # force subscribe
57
+ if (
58
+ udB.get_key("FORCESUB")
59
+ and ((ult.user_joined or ult.user_added))
60
+ and get_forcesetting(ult.chat_id)
61
+ ):
62
+ user = await ult.get_user()
63
+ if not user.bot:
64
+ joinchat = get_forcesetting(ult.chat_id)
65
+ try:
66
+ await ultroid_bot(GetParticipantRequest(int(joinchat), user.id))
67
+ except UserNotParticipantError:
68
+ await ultroid_bot.edit_permissions(
69
+ ult.chat_id, user.id, send_messages=False
70
+ )
71
+ res = await ultroid_bot.inline_query(
72
+ asst.me.username, f"fsub {user.id}_{joinchat}"
73
+ )
74
+ await res[0].click(ult.chat_id, reply_to=ult.action_message.id)
75
+
76
+ if ult.user_joined or ult.added_by:
77
+ user = await ult.get_user()
78
+ chat = await ult.get_chat()
79
+ # gbans and @UltroidBans checks
80
+ if udB.get_key("ULTROID_BANS"):
81
+ try:
82
+ is_banned = await async_searcher(
83
+ "https://bans.ultroid.tech/api/status",
84
+ json={"userId": user.id},
85
+ post=True,
86
+ re_json=True,
87
+ )
88
+ if is_banned["is_banned"]:
89
+ await ult.client.edit_permissions(
90
+ chat.id,
91
+ user.id,
92
+ view_messages=False,
93
+ )
94
+ await ult.respond(
95
+ f'**@UltroidBans:** Banned user detected and banned!\n`{str(is_banned)}`.\nBan reason: {is_banned["reason"]}',
96
+ )
97
+
98
+ except BaseException:
99
+ pass
100
+ reason = is_gbanned(user.id)
101
+ if reason and chat.admin_rights:
102
+ try:
103
+ await ult.client.edit_permissions(
104
+ chat.id,
105
+ user.id,
106
+ view_messages=False,
107
+ )
108
+ gban_watch = get_string("can_1").format(inline_mention(user), reason)
109
+ await ult.reply(gban_watch)
110
+ except Exception as er:
111
+ LOGS.exception(er)
112
+
113
+ # greetings
114
+ elif get_welcome(ult.chat_id):
115
+ user = await ult.get_user()
116
+ chat = await ult.get_chat()
117
+ title = chat.title or "this chat"
118
+ count = (
119
+ chat.participants_count
120
+ or (await ult.client.get_participants(chat, limit=0)).total
121
+ )
122
+ mention = inline_mention(user)
123
+ name = user.first_name
124
+ fullname = get_display_name(user)
125
+ uu = user.username
126
+ username = f"@{uu}" if uu else mention
127
+ wel = get_welcome(ult.chat_id)
128
+ msgg = wel["welcome"]
129
+ med = wel["media"] or None
130
+ userid = user.id
131
+ msg = None
132
+ if msgg:
133
+ msg = msgg.format(
134
+ mention=mention,
135
+ group=title,
136
+ count=count,
137
+ name=name,
138
+ fullname=fullname,
139
+ username=username,
140
+ userid=userid,
141
+ )
142
+ if wel.get("button"):
143
+ btn = create_tl_btn(wel["button"])
144
+ await something(ult, msg, med, btn)
145
+ elif msg:
146
+ send = await ult.reply(
147
+ msg,
148
+ file=med,
149
+ )
150
+ await asyncio.sleep(150)
151
+ await send.delete()
152
+ else:
153
+ await ult.reply(file=med)
154
+ elif (ult.user_left or ult.user_kicked) and get_goodbye(ult.chat_id):
155
+ user = await ult.get_user()
156
+ chat = await ult.get_chat()
157
+ title = chat.title or "this chat"
158
+ count = (
159
+ chat.participants_count
160
+ or (await ult.client.get_participants(chat, limit=0)).total
161
+ )
162
+ mention = inline_mention(user)
163
+ name = user.first_name
164
+ fullname = get_display_name(user)
165
+ uu = user.username
166
+ username = f"@{uu}" if uu else mention
167
+ wel = get_goodbye(ult.chat_id)
168
+ msgg = wel["goodbye"]
169
+ med = wel["media"]
170
+ userid = user.id
171
+ msg = None
172
+ if msgg:
173
+ msg = msgg.format(
174
+ mention=mention,
175
+ group=title,
176
+ count=count,
177
+ name=name,
178
+ fullname=fullname,
179
+ username=username,
180
+ userid=userid,
181
+ )
182
+ if wel.get("button"):
183
+ btn = create_tl_btn(wel["button"])
184
+ await something(ult, msg, med, btn)
185
+ elif msg:
186
+ send = await ult.reply(
187
+ msg,
188
+ file=med,
189
+ )
190
+ await asyncio.sleep(150)
191
+ await send.delete()
192
+ else:
193
+ await ult.reply(file=med)
194
+
195
+
196
+ @ultroid_bot.on(events.NewMessage(incoming=True))
197
+ async def chatBot_replies(e):
198
+ sender = await e.get_sender()
199
+ if not isinstance(sender, types.User) or sender.bot:
200
+ return
201
+ if check_echo(e.chat_id, e.sender_id):
202
+ try:
203
+ await e.respond(e.message)
204
+ except Exception as er:
205
+ LOGS.exception(er)
206
+ key = udB.get_key("CHATBOT_USERS") or {}
207
+ if e.text and key.get(e.chat_id) and sender.id in key[e.chat_id]:
208
+ msg = await get_chatbot_reply(e.message.message)
209
+ if msg:
210
+ sleep = udB.get_key("CHATBOT_SLEEP") or 1.5
211
+ await asyncio.sleep(sleep)
212
+ await e.reply(msg)
213
+ chat = await e.get_chat()
214
+ if e.is_group and sender.username:
215
+ await uname_stuff(e.sender_id, sender.username, sender.first_name)
216
+ elif e.is_private and chat.username:
217
+ await uname_stuff(e.sender_id, chat.username, chat.first_name)
218
+ if detector and is_profan(e.chat_id) and e.text:
219
+ x, y = detector(e.text)
220
+ if y:
221
+ await e.delete()
222
+
223
+
224
+ @ultroid_bot.on(events.Raw(types.UpdateUserName))
225
+ async def uname_change(e):
226
+ await uname_stuff(e.user_id, e.usernames[0] if e.usernames else None, e.first_name)
227
+
228
+
229
+ async def uname_stuff(id, uname, name):
230
+ if udB.get_key("USERNAME_LOG"):
231
+ old_ = udB.get_key("USERNAME_DB") or {}
232
+ old = old_.get(id)
233
+ # Ignore Name Logs
234
+ if old and old == uname:
235
+ return
236
+ if old and uname:
237
+ await asst.send_message(
238
+ LOG_CHANNEL,
239
+ get_string("can_2").format(old, uname),
240
+ )
241
+ elif old:
242
+ await asst.send_message(
243
+ LOG_CHANNEL,
244
+ get_string("can_3").format(f"[{name}](tg://user?id={id})", old),
245
+ )
246
+ elif uname:
247
+ await asst.send_message(
248
+ LOG_CHANNEL,
249
+ get_string("can_4").format(f"[{name}](tg://user?id={id})", uname),
250
+ )
251
+
252
+ old_[id] = uname
253
+ udB.set_key("USERNAME_DB", old_)
plugins/_help.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from telethon.errors.rpcerrorlist import (
9
+ BotInlineDisabledError,
10
+ BotMethodInvalidError,
11
+ BotResponseTimeoutError,
12
+ )
13
+ from telethon.tl.custom import Button
14
+
15
+ from pyUltroid.dB._core import HELP, LIST
16
+ from pyUltroid.fns.tools import cmd_regex_replace
17
+
18
+ from . import HNDLR, LOGS, OWNER_NAME, asst, get_string, inline_pic, udB, ultroid_cmd
19
+
20
+ _main_help_menu = [
21
+ [
22
+ Button.inline(get_string("help_4"), data="uh_Official_"),
23
+ Button.inline(get_string("help_5"), data="uh_Addons_"),
24
+ ],
25
+ [
26
+ Button.inline(get_string("help_6"), data="uh_VCBot_"),
27
+ Button.inline(get_string("help_7"), data="inlone"),
28
+ ],
29
+ [
30
+ Button.inline(get_string("help_8"), data="ownr"),
31
+ Button.url(
32
+ get_string("help_9"), url=f"https://t.me/{asst.me.username}?start=set"
33
+ ),
34
+ ],
35
+ [Button.inline(get_string("help_10"), data="close")],
36
+ ]
37
+
38
+
39
+ @ultroid_cmd(pattern="help( (.*)|$)")
40
+ async def _help(ult):
41
+ plug = ult.pattern_match.group(1).strip()
42
+ chat = await ult.get_chat()
43
+ if plug:
44
+ try:
45
+ if plug in HELP["Official"]:
46
+ output = f"**Plugin** - `{plug}`\n"
47
+ for i in HELP["Official"][plug]:
48
+ output += i
49
+ output += "\n© @TeamUltroid"
50
+ await ult.eor(output)
51
+ elif HELP.get("Addons") and plug in HELP["Addons"]:
52
+ output = f"**Plugin** - `{plug}`\n"
53
+ for i in HELP["Addons"][plug]:
54
+ output += i
55
+ output += "\n© @TeamUltroid"
56
+ await ult.eor(output)
57
+ elif HELP.get("VCBot") and plug in HELP["VCBot"]:
58
+ output = f"**Plugin** - `{plug}`\n"
59
+ for i in HELP["VCBot"][plug]:
60
+ output += i
61
+ output += "\n© @TeamUltroid"
62
+ await ult.eor(output)
63
+ else:
64
+ try:
65
+ x = get_string("help_11").format(plug)
66
+ for d in LIST[plug]:
67
+ x += HNDLR + d
68
+ x += "\n"
69
+ x += "\n© @TeamUltroid"
70
+ await ult.eor(x)
71
+ except BaseException:
72
+ file = None
73
+ compare_strings = []
74
+ for file_name in LIST:
75
+ compare_strings.append(file_name)
76
+ value = LIST[file_name]
77
+ for j in value:
78
+ j = cmd_regex_replace(j)
79
+ compare_strings.append(j)
80
+ if j.strip() == plug:
81
+ file = file_name
82
+ break
83
+ if not file:
84
+ # the enter command/plugin name is not found
85
+ text = f"`{plug}` is not a valid plugin!"
86
+ best_match = None
87
+ for _ in compare_strings:
88
+ if plug in _ and not _.startswith("_"):
89
+ best_match = _
90
+ break
91
+ if best_match:
92
+ text += f"\nDid you mean `{best_match}`?"
93
+ return await ult.eor(text)
94
+ output = f"**Command** `{plug}` **found in plugin** - `{file}`\n"
95
+ if file in HELP["Official"]:
96
+ for i in HELP["Official"][file]:
97
+ output += i
98
+ elif HELP.get("Addons") and file in HELP["Addons"]:
99
+ for i in HELP["Addons"][file]:
100
+ output += i
101
+ elif HELP.get("VCBot") and file in HELP["VCBot"]:
102
+ for i in HELP["VCBot"][file]:
103
+ output += i
104
+ output += "\n© @TeamUltroid"
105
+ await ult.eor(output)
106
+ except BaseException as er:
107
+ LOGS.exception(er)
108
+ await ult.eor("Error 🤔 occured.")
109
+ else:
110
+ try:
111
+ results = await ult.client.inline_query(asst.me.username, "ultd")
112
+ except BotMethodInvalidError:
113
+ z = []
114
+ for x in LIST.values():
115
+ z.extend(x)
116
+ cmd = len(z) + 10
117
+ if udB.get_key("MANAGER") and udB.get_key("DUAL_HNDLR") == "/":
118
+ _main_help_menu[2:3] = [[Button.inline("• Manager Help •", "mngbtn")]]
119
+ return await ult.reply(
120
+ get_string("inline_4").format(
121
+ OWNER_NAME,
122
+ len(HELP["Official"]),
123
+ len(HELP["Addons"] if "Addons" in HELP else []),
124
+ cmd,
125
+ ),
126
+ file=inline_pic(),
127
+ buttons=_main_help_menu,
128
+ )
129
+ except BotResponseTimeoutError:
130
+ return await ult.eor(
131
+ get_string("help_2").format(HNDLR),
132
+ )
133
+ except BotInlineDisabledError:
134
+ return await ult.eor(get_string("help_3"))
135
+ await results[0].click(chat.id, reply_to=ult.reply_to_msg_id, hide_via=True)
136
+ await ult.delete()
plugins/_inline.py ADDED
@@ -0,0 +1,455 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ import re
9
+ import time
10
+ from datetime import datetime
11
+ from os import remove
12
+
13
+ from git import Repo
14
+ from telethon import Button
15
+ from telethon.tl.types import InputWebDocument, Message
16
+ from telethon.utils import resolve_bot_file_id
17
+
18
+ from pyUltroid._misc._assistant import callback, in_pattern
19
+ from pyUltroid.dB._core import HELP, LIST
20
+ from pyUltroid.fns.helper import gen_chlog, time_formatter, updater
21
+ from pyUltroid.fns.misc import split_list
22
+
23
+ from . import (
24
+ HNDLR,
25
+ LOGS,
26
+ OWNER_NAME,
27
+ InlinePlugin,
28
+ asst,
29
+ get_string,
30
+ inline_pic,
31
+ split_list,
32
+ start_time,
33
+ udB,
34
+ )
35
+ from ._help import _main_help_menu
36
+
37
+ # ================================================#
38
+
39
+ helps = get_string("inline_1")
40
+
41
+ add_ons = udB.get_key("ADDONS")
42
+
43
+ zhelps = get_string("inline_3") if add_ons is False else get_string("inline_2")
44
+ PLUGINS = HELP.get("Official", [])
45
+ ADDONS = HELP.get("Addons", [])
46
+ upage = 0
47
+ # ============================================#
48
+
49
+ # --------------------BUTTONS--------------------#
50
+
51
+ SUP_BUTTONS = [
52
+ [
53
+ Button.url("• Repo •", url="https://github.com/TeamUltroid/Ultroid"),
54
+ Button.url("• Support •", url="t.me/UltroidSupportChat"),
55
+ ],
56
+ ]
57
+
58
+ # --------------------BUTTONS--------------------#
59
+
60
+
61
+ @in_pattern(owner=True, func=lambda x: not x.text)
62
+ async def inline_alive(o):
63
+ TLINK = inline_pic() or "https://graph.org/file/74d6259983e0642923fdb.jpg"
64
+ MSG = "• **Ultroid Userbot •**"
65
+ WEB0 = InputWebDocument(
66
+ "https://graph.org/file/acd4f5d61369f74c5e7a7.jpg", 0, "image/jpg", []
67
+ )
68
+ RES = [
69
+ await o.builder.article(
70
+ type="photo",
71
+ text=MSG,
72
+ include_media=True,
73
+ buttons=SUP_BUTTONS,
74
+ title="Ultroid Userbot",
75
+ description="Userbot | Telethon",
76
+ url=TLINK,
77
+ thumb=WEB0,
78
+ content=InputWebDocument(TLINK, 0, "image/jpg", []),
79
+ )
80
+ ]
81
+ await o.answer(
82
+ RES,
83
+ private=True,
84
+ cache_time=300,
85
+ switch_pm="👥 ULTROID PORTAL",
86
+ switch_pm_param="start",
87
+ )
88
+
89
+
90
+ @in_pattern("ultd", owner=True)
91
+ async def inline_handler(event):
92
+ z = []
93
+ for x in LIST.values():
94
+ z.extend(x)
95
+ text = get_string("inline_4").format(
96
+ OWNER_NAME,
97
+ len(HELP.get("Official", [])),
98
+ len(HELP.get("Addons", [])),
99
+ len(z),
100
+ )
101
+ if inline_pic():
102
+ result = await event.builder.photo(
103
+ file=inline_pic(),
104
+ link_preview=False,
105
+ text=text,
106
+ buttons=_main_help_menu,
107
+ )
108
+ else:
109
+ result = await event.builder.article(
110
+ title="Ultroid Help Menu", text=text, buttons=_main_help_menu
111
+ )
112
+ await event.answer([result], private=True, cache_time=300, gallery=True)
113
+
114
+
115
+ @in_pattern("pasta", owner=True)
116
+ async def _(event):
117
+ ok = event.text.split("-")[1]
118
+ if not ok.startswith("http"):
119
+ link = f"https://spaceb.in/{ok}"
120
+ raw = f"https://spaceb.in/api/v1/documents/{ok}/raw"
121
+ else:
122
+ link = ok
123
+ raw = f"{ok}/raw"
124
+ result = await event.builder.article(
125
+ title="Paste",
126
+ text="Pasted to Spacebin 🌌",
127
+ buttons=[
128
+ [
129
+ Button.url("SpaceBin", url=link),
130
+ Button.url("Raw", url=raw),
131
+ ],
132
+ ],
133
+ )
134
+ await event.answer([result])
135
+
136
+
137
+ @callback("ownr", owner=True)
138
+ async def setting(event):
139
+ z = []
140
+ for x in LIST.values():
141
+ z.extend(x)
142
+ await event.edit(
143
+ get_string("inline_4").format(
144
+ OWNER_NAME,
145
+ len(HELP.get("Official", [])),
146
+ len(HELP.get("Addons", [])),
147
+ len(z),
148
+ ),
149
+ file=inline_pic(),
150
+ link_preview=False,
151
+ buttons=[
152
+ [
153
+ Button.inline("•Pɪɴɢ•", data="pkng"),
154
+ Button.inline("•Uᴘᴛɪᴍᴇ•", data="upp"),
155
+ ],
156
+ [
157
+ Button.inline("•Stats•", data="alive"),
158
+ Button.inline("•Uᴘᴅᴀᴛᴇ•", data="doupdate"),
159
+ ],
160
+ [Button.inline("« Bᴀᴄᴋ", data="open")],
161
+ ],
162
+ )
163
+
164
+
165
+ _strings = {"Official": helps, "Addons": zhelps, "VCBot": get_string("inline_6")}
166
+
167
+
168
+ @callback(re.compile("uh_(.*)"), owner=True)
169
+ async def help_func(ult):
170
+ key, count = ult.data_match.group(1).decode("utf-8").split("_")
171
+ if key == "VCBot" and HELP.get("VCBot") is None:
172
+ return await ult.answer(get_string("help_12"), alert=True)
173
+ elif key == "Addons" and HELP.get("Addons") is None:
174
+ return await ult.answer(get_string("help_13").format(HNDLR), alert=True)
175
+ if "|" in count:
176
+ _, count = count.split("|")
177
+ count = int(count) if count else 0
178
+ text = _strings.get(key, "").format(OWNER_NAME, len(HELP.get(key)))
179
+ await ult.edit(text, buttons=page_num(count, key), link_preview=False)
180
+
181
+
182
+ @callback(re.compile("uplugin_(.*)"), owner=True)
183
+ async def uptd_plugin(event):
184
+ key, file = event.data_match.group(1).decode("utf-8").split("_")
185
+ index = None
186
+ if "|" in file:
187
+ file, index = file.split("|")
188
+ key_ = HELP.get(key, [])
189
+ hel_p = f"Plugin Name - `{file}`\n"
190
+ help_ = ""
191
+ try:
192
+ for i in key_[file]:
193
+ help_ += i
194
+ except BaseException:
195
+ if file in LIST:
196
+ help_ = get_string("help_11").format(file)
197
+ for d in LIST[file]:
198
+ help_ += HNDLR + d
199
+ help_ += "\n"
200
+ if not help_:
201
+ help_ = f"{file} has no Detailed Help!"
202
+ help_ += "\n© @TeamUltroid"
203
+ buttons = []
204
+ if inline_pic():
205
+ data = f"sndplug_{key}_{file}"
206
+ if index is not None:
207
+ data += f"|{index}"
208
+ buttons.append(
209
+ [
210
+ Button.inline(
211
+ "« Sᴇɴᴅ Pʟᴜɢɪɴ »",
212
+ data=data,
213
+ )
214
+ ]
215
+ )
216
+ data = f"uh_{key}_"
217
+ if index is not None:
218
+ data += f"|{index}"
219
+ buttons.append(
220
+ [
221
+ Button.inline("« Bᴀᴄᴋ", data=data),
222
+ ]
223
+ )
224
+ try:
225
+ await event.edit(help_, buttons=buttons)
226
+ except Exception as er:
227
+ LOGS.exception(er)
228
+ help = f"Do `{HNDLR}help {key}` to get list of commands."
229
+ await event.edit(help, buttons=buttons)
230
+
231
+
232
+ @callback(data="doupdate", owner=True)
233
+ async def _(event):
234
+ if not await updater():
235
+ return await event.answer(get_string("inline_9"), cache_time=0, alert=True)
236
+ if not inline_pic():
237
+ return await event.answer(f"Do '{HNDLR}update' to update..")
238
+ repo = Repo.init()
239
+ changelog, tl_chnglog = await gen_chlog(
240
+ repo, f"HEAD..upstream/{repo.active_branch}"
241
+ )
242
+ changelog_str = changelog + "\n\n" + get_string("inline_8")
243
+ if len(changelog_str) > 1024:
244
+ await event.edit(get_string("upd_4"))
245
+ with open("ultroid_updates.txt", "w+") as file:
246
+ file.write(tl_chnglog)
247
+ await event.edit(
248
+ get_string("upd_5"),
249
+ file="ultroid_updates.txt",
250
+ buttons=[
251
+ [Button.inline("• Uᴘᴅᴀᴛᴇ Nᴏᴡ •", data="updatenow")],
252
+ [Button.inline("« Bᴀᴄᴋ", data="ownr")],
253
+ ],
254
+ )
255
+ remove("ultroid_updates.txt")
256
+ else:
257
+ await event.edit(
258
+ changelog_str,
259
+ buttons=[
260
+ [Button.inline("Update Now", data="updatenow")],
261
+ [Button.inline("« Bᴀᴄᴋ", data="ownr")],
262
+ ],
263
+ parse_mode="html",
264
+ )
265
+
266
+
267
+ @callback(data="pkng", owner=True)
268
+ async def _(event):
269
+ start = datetime.now()
270
+ end = datetime.now()
271
+ ms = (end - start).microseconds
272
+ pin = f"🌋Pɪɴɢ = {ms} microseconds"
273
+ await event.answer(pin, cache_time=0, alert=True)
274
+
275
+
276
+ @callback(data="upp", owner=True)
277
+ async def _(event):
278
+ uptime = time_formatter((time.time() - start_time) * 1000)
279
+ pin = f"🙋Uᴘᴛɪᴍᴇ = {uptime}"
280
+ await event.answer(pin, cache_time=0, alert=True)
281
+
282
+
283
+ @callback(data="inlone", owner=True)
284
+ async def _(e):
285
+ _InButtons = [
286
+ Button.switch_inline(_, query=InlinePlugin[_], same_peer=True)
287
+ for _ in list(InlinePlugin.keys())
288
+ ]
289
+ InButtons = split_list(_InButtons, 2)
290
+
291
+ button = InButtons.copy()
292
+ button.append(
293
+ [
294
+ Button.inline("« Bᴀᴄᴋ", data="open"),
295
+ ],
296
+ )
297
+ await e.edit(buttons=button, link_preview=False)
298
+
299
+
300
+ @callback(data="open", owner=True)
301
+ async def opner(event):
302
+ z = []
303
+ for x in LIST.values():
304
+ z.extend(x)
305
+ await event.edit(
306
+ get_string("inline_4").format(
307
+ OWNER_NAME,
308
+ len(HELP.get("Official", [])),
309
+ len(HELP.get("Addons", [])),
310
+ len(z),
311
+ ),
312
+ buttons=_main_help_menu,
313
+ link_preview=False,
314
+ )
315
+
316
+
317
+ @callback(data="close", owner=True)
318
+ async def on_plug_in_callback_query_handler(event):
319
+ await event.edit(
320
+ get_string("inline_5"),
321
+ buttons=Button.inline("Oᴘᴇɴ Aɢᴀɪɴ", data="open"),
322
+ )
323
+
324
+
325
+ def page_num(index, key):
326
+ rows = udB.get_key("HELP_ROWS") or 5
327
+ cols = udB.get_key("HELP_COLUMNS") or 2
328
+ loaded = HELP.get(key, [])
329
+ emoji = udB.get_key("EMOJI_IN_HELP") or "✘"
330
+ List = [
331
+ Button.inline(f"{emoji} {x} {emoji}", data=f"uplugin_{key}_{x}|{index}")
332
+ for x in sorted(loaded)
333
+ ]
334
+ all_ = split_list(List, cols)
335
+ fl_ = split_list(all_, rows)
336
+ try:
337
+ new_ = fl_[index]
338
+ except IndexError:
339
+ new_ = fl_[0] if fl_ else []
340
+ index = 0
341
+ if index == 0 and len(fl_) == 1:
342
+ new_.append([Button.inline("« Bᴀᴄᴋ »", data="open")])
343
+ else:
344
+ new_.append(
345
+ [
346
+ Button.inline(
347
+ "« Pʀᴇᴠɪᴏᴜs",
348
+ data=f"uh_{key}_{index-1}",
349
+ ),
350
+ Button.inline("« Bᴀᴄᴋ »", data="open"),
351
+ Button.inline(
352
+ "Nᴇxᴛ »",
353
+ data=f"uh_{key}_{index+1}",
354
+ ),
355
+ ]
356
+ )
357
+ return new_
358
+
359
+
360
+ # --------------------------------------------------------------------------------- #
361
+
362
+
363
+ STUFF = {}
364
+
365
+
366
+ @in_pattern("stf(.*)", owner=True)
367
+ async def ibuild(e):
368
+ n = e.pattern_match.group(1).strip()
369
+ builder = e.builder
370
+ if not (n and n.isdigit()):
371
+ return
372
+ ok = STUFF.get(int(n))
373
+ txt = ok.get("msg")
374
+ pic = ok.get("media")
375
+ btn = ok.get("button")
376
+ if not (pic or txt):
377
+ txt = "Hey!"
378
+ if pic:
379
+ try:
380
+ include_media = True
381
+ mime_type, _pic = None, None
382
+ cont, results = None, None
383
+ try:
384
+ ext = str(pic).split(".")[-1].lower()
385
+ except BaseException:
386
+ ext = None
387
+ if ext in ["img", "jpg", "png"]:
388
+ _type = "photo"
389
+ mime_type = "image/jpg"
390
+ elif ext in ["mp4", "mkv", "gif"]:
391
+ mime_type = "video/mp4"
392
+ _type = "gif"
393
+ else:
394
+ try:
395
+ if "telethon.tl.types" in str(type(pic)):
396
+ _pic = pic
397
+ else:
398
+ _pic = resolve_bot_file_id(pic)
399
+ except BaseException:
400
+ pass
401
+ if _pic:
402
+ results = [
403
+ await builder.document(
404
+ _pic,
405
+ title="Ultroid Op",
406
+ text=txt,
407
+ description="@TeamUltroid",
408
+ buttons=btn,
409
+ link_preview=False,
410
+ )
411
+ ]
412
+ else:
413
+ _type = "article"
414
+ include_media = False
415
+ if not results:
416
+ if include_media:
417
+ cont = InputWebDocument(pic, 0, mime_type, [])
418
+ results = [
419
+ await builder.article(
420
+ title="Ultroid Op",
421
+ type=_type,
422
+ text=txt,
423
+ description="@TeamUltroid",
424
+ include_media=include_media,
425
+ buttons=btn,
426
+ thumb=cont,
427
+ content=cont,
428
+ link_preview=False,
429
+ )
430
+ ]
431
+ return await e.answer(results)
432
+ except Exception as er:
433
+ LOGS.exception(er)
434
+ result = [
435
+ await builder.article("Ultroid Op", text=txt, link_preview=False, buttons=btn)
436
+ ]
437
+ await e.answer(result)
438
+
439
+
440
+ async def something(e, msg, media, button, reply=True, chat=None):
441
+ if e.client._bot:
442
+ return await e.reply(msg, file=media, buttons=button)
443
+ num = len(STUFF) + 1
444
+ STUFF.update({num: {"msg": msg, "media": media, "button": button}})
445
+ try:
446
+ res = await e.client.inline_query(asst.me.username, f"stf{num}")
447
+ return await res[0].click(
448
+ chat or e.chat_id,
449
+ reply_to=bool(isinstance(e, Message) and reply),
450
+ hide_via=True,
451
+ silent=True,
452
+ )
453
+
454
+ except Exception as er:
455
+ LOGS.exception(er)
plugins/_ultroid.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from telethon.errors import (
9
+ BotMethodInvalidError,
10
+ ChatSendInlineForbiddenError,
11
+ ChatSendMediaForbiddenError,
12
+ )
13
+
14
+ from . import LOG_CHANNEL, LOGS, Button, asst, eor, get_string, ultroid_cmd
15
+
16
+ REPOMSG = """
17
+ • **ULTROID USERBOT** •\n
18
+ • Repo - [Click Here](https://github.com/TeamUltroid/Ultroid)
19
+ • Addons - [Click Here](https://github.com/TeamUltroid/UltroidAddons)
20
+ • Support - @UltroidSupportChat
21
+ """
22
+
23
+ RP_BUTTONS = [
24
+ [
25
+ Button.url(get_string("bot_3"), "https://github.com/TeamUltroid/Ultroid"),
26
+ Button.url("Addons", "https://github.com/TeamUltroid/UltroidAddons"),
27
+ ],
28
+ [Button.url("Support Group", "t.me/UltroidSupportChat")],
29
+ ]
30
+
31
+ ULTSTRING = """🎇 **Thanks for Deploying Ultroid Userbot!**
32
+
33
+ • Here, are the Some Basic stuff from, where you can Know, about its Usage."""
34
+
35
+
36
+ @ultroid_cmd(
37
+ pattern="repo$",
38
+ manager=True,
39
+ )
40
+ async def repify(e):
41
+ try:
42
+ q = await e.client.inline_query(asst.me.username, "")
43
+ await q[0].click(e.chat_id)
44
+ return await e.delete()
45
+ except (
46
+ ChatSendInlineForbiddenError,
47
+ ChatSendMediaForbiddenError,
48
+ BotMethodInvalidError,
49
+ ):
50
+ pass
51
+ except Exception as er:
52
+ LOGS.info(f"Error while repo command : {str(er)}")
53
+ await e.eor(REPOMSG)
54
+
55
+
56
+ @ultroid_cmd(pattern="ultroid$")
57
+ async def useUltroid(rs):
58
+ button = Button.inline("Start >>", "initft_2")
59
+ msg = await asst.send_message(
60
+ LOG_CHANNEL,
61
+ ULTSTRING,
62
+ file="https://graph.org/file/54a917cc9dbb94733ea5f.jpg",
63
+ buttons=button,
64
+ )
65
+ if not (rs.chat_id == LOG_CHANNEL and rs.client._bot):
66
+ await eor(rs, f"**[Click Here]({msg.message_link})**")
plugins/_userlogs.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ import os
9
+ import re
10
+
11
+ from telethon.errors.rpcerrorlist import (
12
+ ChannelPrivateError,
13
+ ChatWriteForbiddenError,
14
+ MediaCaptionTooLongError,
15
+ MediaEmptyError,
16
+ MessageTooLongError,
17
+ PeerIdInvalidError,
18
+ UserNotParticipantError,
19
+ )
20
+ from telethon.tl.types import MessageEntityMention, MessageEntityMentionName, User
21
+ from telethon.utils import get_display_name
22
+
23
+ from pyUltroid.dB.botchat_db import tag_add, who_tag
24
+
25
+ from . import (
26
+ LOG_CHANNEL,
27
+ LOGS,
28
+ Button,
29
+ asst,
30
+ callback,
31
+ events,
32
+ get_string,
33
+ inline_mention,
34
+ udB,
35
+ ultroid_bot,
36
+ )
37
+
38
+ CACHE_SPAM = {}
39
+ TAG_EDITS = {}
40
+
41
+
42
+ @ultroid_bot.on(
43
+ events.NewMessage(
44
+ incoming=True,
45
+ func=lambda e: (e.mentioned),
46
+ ),
47
+ )
48
+ async def all_messages_catcher(e):
49
+ x = await e.get_sender()
50
+ if isinstance(x, User) and (x.bot or x.verified):
51
+ return
52
+ if not udB.get_key("TAG_LOG"):
53
+ return
54
+ NEEDTOLOG = udB.get_key("TAG_LOG")
55
+ if e.chat_id == NEEDTOLOG:
56
+ return
57
+ buttons = await parse_buttons(e)
58
+ try:
59
+ sent = await asst.send_message(NEEDTOLOG, e.message, buttons=buttons)
60
+ if TAG_EDITS.get(e.chat_id):
61
+ TAG_EDITS[e.chat_id].update({e.id: {"id": sent.id, "msg": e}})
62
+ else:
63
+ TAG_EDITS.update({e.chat_id: {e.id: {"id": sent.id, "msg": e}}})
64
+ tag_add(sent.id, e.chat_id, e.id)
65
+ except MediaEmptyError as er:
66
+ LOGS.debug(f"handling {er}.")
67
+ try:
68
+ msg = await asst.get_messages(e.chat_id, ids=e.id)
69
+ sent = await asst.send_message(NEEDTOLOG, msg, buttons=buttons)
70
+ if TAG_EDITS.get(e.chat_id):
71
+ TAG_EDITS[e.chat_id].update({e.id: {"id": sent.id, "msg": e}})
72
+ else:
73
+ TAG_EDITS.update({e.chat_id: {e.id: {"id": sent.id, "msg": e}}})
74
+ tag_add(sent.id, e.chat_id, e.id)
75
+ except Exception as me:
76
+ if not isinstance(me, (PeerIdInvalidError, ValueError)):
77
+ LOGS.exception(me)
78
+ if e.photo or e.sticker or e.gif:
79
+ try:
80
+ media = await e.download_media()
81
+ sent = await asst.send_message(
82
+ NEEDTOLOG, e.message.text, file=media, buttons=buttons
83
+ )
84
+ if TAG_EDITS.get(e.chat_id):
85
+ TAG_EDITS[e.chat_id].update({e.id: {"id": sent.id, "msg": e}})
86
+ else:
87
+ TAG_EDITS.update({e.chat_id: {e.id: {"id": sent.id, "msg": e}}})
88
+ return os.remove(media)
89
+ except Exception as er:
90
+ LOGS.exception(er)
91
+ await asst.send_message(NEEDTOLOG, get_string("com_4"), buttons=buttons)
92
+ except (PeerIdInvalidError, ValueError) as er:
93
+ LOGS.exception(er)
94
+ try:
95
+ CACHE_SPAM[NEEDTOLOG]
96
+ except KeyError:
97
+ await asst.send_message(
98
+ udB.get_key("LOG_CHANNEL"), get_string("userlogs_1")
99
+ )
100
+ CACHE_SPAM.update({NEEDTOLOG: True})
101
+ except ChatWriteForbiddenError:
102
+ try:
103
+ await asst.get_permissions(NEEDTOLOG, "me")
104
+ MSG = get_string("userlogs_4")
105
+ except UserNotParticipantError:
106
+ MSG = get_string("userlogs_2")
107
+ try:
108
+ CACHE_SPAM[NEEDTOLOG]
109
+ except KeyError:
110
+ await asst.send_message(LOG_CHANNEL, MSG)
111
+ CACHE_SPAM.update({NEEDTOLOG: True})
112
+ except Exception as er:
113
+ LOGS.exception(er)
114
+
115
+
116
+ if udB.get_key("TAG_LOG"):
117
+
118
+ @ultroid_bot.on(events.MessageEdited(func=lambda x: not x.out))
119
+ async def upd_edits(event):
120
+ x = event.sender
121
+ if isinstance(x, User) and (x.bot or x.verified):
122
+ return
123
+ if event.chat_id not in TAG_EDITS:
124
+ if event.sender_id == udB.get_key("TAG_LOG"):
125
+ return
126
+ if event.is_private:
127
+ return
128
+ if entities := event.get_entities_text():
129
+ is_self = False
130
+ username = event.client.me.username
131
+ if username:
132
+ username = username.lower()
133
+ for ent, text in entities:
134
+ if isinstance(ent, MessageEntityMention):
135
+ is_self = text[1:].lower() == username
136
+ elif isinstance(ent, MessageEntityMentionName):
137
+ is_self = ent.user_id == event.client.me.id
138
+ if is_self:
139
+ text = f"**#Edited & #Mentioned**\n\n{event.text}"
140
+ try:
141
+ sent = await asst.send_message(
142
+ udB.get_key("TAG_LOG"),
143
+ text,
144
+ buttons=await parse_buttons(event),
145
+ )
146
+ except Exception as er:
147
+ return LOGS.exception(er)
148
+ if TAG_EDITS.get(event.chat_id):
149
+ TAG_EDITS[event.chat_id].update({event.id: {"id": sent.id}})
150
+ else:
151
+ TAG_EDITS.update({event.chat_id: {event.id: {"id": sent.id}}})
152
+ return
153
+ d_ = TAG_EDITS[event.chat_id]
154
+ if not d_.get(event.id):
155
+ return
156
+ d_ = d_[event.id]
157
+ if d_["msg"].text == event.text:
158
+ return
159
+ msg = None
160
+ if d_.get("count"):
161
+ d_["count"] += 1
162
+ else:
163
+ msg = True
164
+ d_.update({"count": 1})
165
+ if d_["count"] > 10:
166
+ return # some limit to take edits
167
+ try:
168
+ MSG = await asst.get_messages(udB.get_key("TAG_LOG"), ids=d_["id"])
169
+ except Exception as er:
170
+ return LOGS.exception(er)
171
+ TEXT = MSG.text
172
+ if msg:
173
+ TEXT += "\n\n🖋 **Later Edited to !**"
174
+ strf = event.edit_date.strftime("%H:%M:%S")
175
+ if "\n" not in event.text:
176
+ TEXT += f"\n• `{strf}` : {event.text}"
177
+ else:
178
+ TEXT += f"\n• `{strf}` :\n-> {event.text}"
179
+ if d_["count"] == 10:
180
+ TEXT += "\n\n• __Only the first 10 Edits are shown.__"
181
+ try:
182
+ msg = await MSG.edit(TEXT, buttons=await parse_buttons(event))
183
+ d_["msg"] = msg
184
+ except (MessageTooLongError, MediaCaptionTooLongError):
185
+ del TAG_EDITS[event.chat_id][event.id]
186
+ except Exception as er:
187
+ LOGS.exception(er)
188
+
189
+ @ultroid_bot.on(
190
+ events.NewMessage(
191
+ outgoing=True,
192
+ chats=[udB.get_key("TAG_LOG")],
193
+ func=lambda e: e.reply_to,
194
+ )
195
+ )
196
+ async def idk(e):
197
+ id = e.reply_to_msg_id
198
+ chat, msg = who_tag(id)
199
+ if chat and msg:
200
+ try:
201
+ await ultroid_bot.send_message(chat, e.message, reply_to=msg)
202
+ except BaseException as er:
203
+ LOGS.exception(er)
204
+
205
+
206
+ # log for assistant/user joins/add
207
+
208
+
209
+ async def when_added_or_joined(event):
210
+ user = await event.get_user()
211
+ chat = await event.get_chat()
212
+ if not (user and user.is_self):
213
+ return
214
+ if getattr(chat, "username", None):
215
+ chat = f"[{chat.title}](https://t.me/{chat.username}/{event.action_message.id})"
216
+ else:
217
+ chat = f"[{chat.title}](https://t.me/c/{chat.id}/{event.action_message.id})"
218
+ key = "bot" if event.client._bot else "user"
219
+ buttons = Button.inline(
220
+ get_string("userlogs_3"), data=f"leave_ch_{event.chat_id}|{key}"
221
+ )
222
+ if event.user_added:
223
+ tmp = event.added_by
224
+ text = f"#ADD_LOG\n\n{inline_mention(tmp)} just added {inline_mention(user)} to {chat}."
225
+ elif event.from_request:
226
+ text = f"#APPROVAL_LOG\n\n{inline_mention(user)} just got Chat Join Approval to {chat}."
227
+ else:
228
+ text = f"#JOIN_LOG\n\n{inline_mention(user)} just joined {chat}."
229
+ await asst.send_message(udB.get_key("LOG_CHANNEL"), text, buttons=buttons)
230
+
231
+
232
+ asst.add_event_handler(
233
+ when_added_or_joined, events.ChatAction(func=lambda x: x.user_added)
234
+ )
235
+ ultroid_bot.add_event_handler(
236
+ when_added_or_joined,
237
+ events.ChatAction(func=lambda x: x.user_added or x.user_joined),
238
+ )
239
+ _client = {"bot": asst, "user": ultroid_bot}
240
+
241
+
242
+ @callback(
243
+ re.compile(
244
+ "leave_ch_(.*)",
245
+ ),
246
+ from_users=[ultroid_bot.uid],
247
+ )
248
+ async def leave_ch_at(event):
249
+ cht = event.data_match.group(1).decode("UTF-8")
250
+ ch_id, client = cht.split("|")
251
+ try:
252
+ client = _client[client]
253
+ except KeyError:
254
+ return
255
+ try:
256
+ name = (await client.get_entity(int(ch_id))).title
257
+ await client.delete_dialog(int(ch_id))
258
+ except UserNotParticipantError:
259
+ pass
260
+ except ChannelPrivateError:
261
+ return await event.edit(
262
+ "`[CANT_ACCESS_CHAT]` `Maybe already left or got banned.`"
263
+ )
264
+ except Exception as er:
265
+ LOGS.exception(er)
266
+ return await event.answer(str(er))
267
+ await event.edit(get_string("userlogs_5").format(name))
268
+
269
+
270
+ @callback("do_nothing")
271
+ async def _(event):
272
+ await event.answer()
273
+
274
+
275
+ async def parse_buttons(event):
276
+ y, x = event.chat, event.sender
277
+ where_n, who_n = get_display_name(y), get_display_name(x)
278
+ where_l = event.message_link
279
+ buttons = [[Button.url(where_n, where_l)]]
280
+ if isinstance(x, User) and x.username:
281
+ try:
282
+ buttons.append(
283
+ [Button.mention(who_n, await asst.get_input_entity(x.username))]
284
+ )
285
+ except Exception as er:
286
+ LOGS.exception(er)
287
+ buttons.append([Button.url(who_n, f"t.me/{x.username}")])
288
+ elif getattr(x, "username"):
289
+ buttons.append([Button.url(who_n, f"t.me/{x.username}")])
290
+ else:
291
+ buttons.append([Button.url(who_n, where_l)])
292
+ replied = await event.get_reply_message()
293
+ if replied and replied.out:
294
+ button = Button.url("Replied to", replied.message_link)
295
+ if len(who_n) > 7:
296
+ buttons.append([button])
297
+ else:
298
+ buttons[-1].append(button)
299
+ return buttons
plugins/_wspr.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ import re
9
+
10
+ from telethon import Button
11
+ from telethon.errors.rpcerrorlist import (
12
+ BotInlineDisabledError,
13
+ BotResponseTimeoutError,
14
+ MessageNotModifiedError,
15
+ )
16
+ from telethon.tl import types
17
+ from telethon.tl.functions.users import GetFullUserRequest as gu
18
+
19
+ from . import (
20
+ HNDLR,
21
+ LOGS,
22
+ asst,
23
+ callback,
24
+ get_string,
25
+ in_pattern,
26
+ inline_mention,
27
+ ultroid_bot,
28
+ ultroid_cmd,
29
+ )
30
+
31
+ buddhhu = {}
32
+
33
+
34
+ @ultroid_cmd(
35
+ pattern="wspr( (.*)|$)",
36
+ )
37
+ async def _(e):
38
+ if e.reply_to_msg_id:
39
+ okk = await e.get_reply_message()
40
+ put = f"@{okk.sender.username}" if okk.sender.username else okk.sender_id
41
+ else:
42
+ put = e.pattern_match.group(1).strip()
43
+ if put:
44
+ try:
45
+ results = await e.client.inline_query(asst.me.username, f"msg {put}")
46
+ except BotResponseTimeoutError:
47
+ return await e.eor(
48
+ get_string("help_2").format(HNDLR),
49
+ )
50
+ except BotInlineDisabledError:
51
+ return await e.eor(get_string("help_3"))
52
+ await results[0].click(e.chat_id, reply_to=e.reply_to_msg_id, hide_via=True)
53
+ return await e.delete()
54
+ await e.eor(get_string("wspr_3"))
55
+
56
+
57
+ @in_pattern("wspr", owner=True)
58
+ async def _(e):
59
+ iuser = e.query.user_id
60
+ zzz = e.text.split(maxsplit=2)
61
+ try:
62
+ query = zzz[1]
63
+ if query.isdigit():
64
+ query = int(query)
65
+ logi = await ultroid_bot.get_entity(query)
66
+ if not isinstance(logi, types.User):
67
+ raise ValueError("Invalid Username.")
68
+ except IndexError:
69
+ sur = await e.builder.article(
70
+ title="Give Username",
71
+ description="You Didn't Type Username or id.",
72
+ text="You Didn't Type Username or id.",
73
+ )
74
+ return await e.answer([sur])
75
+ except ValueError as er:
76
+ LOGS.exception(er)
77
+ sur = await e.builder.article(
78
+ title="User Not Found",
79
+ description="Make sure username or id is correct.",
80
+ text="Make sure username or id is correct.",
81
+ )
82
+ return await e.answer([sur])
83
+ try:
84
+ desc = zzz[2]
85
+ except IndexError:
86
+ sur = await e.builder.article(
87
+ title="Type ur msg", text="You Didn't Type Your Msg"
88
+ )
89
+ return await e.answer([sur])
90
+ button = [
91
+ [
92
+ Button.inline("Secret Msg", data=f"dd_{e.id}"),
93
+ Button.inline("Delete Msg", data=f"del_{e.id}"),
94
+ ],
95
+ [
96
+ Button.switch_inline(
97
+ "New", query=f"wspr {logi.username or logi.id}", same_peer=True
98
+ )
99
+ ],
100
+ ]
101
+ us = logi.username or logi.first_name
102
+ sur = await e.builder.article(
103
+ title=logi.first_name,
104
+ description=desc,
105
+ text=get_string("wspr_1").format(us),
106
+ buttons=button,
107
+ )
108
+ buddhhu.update({e.id: [logi.id, iuser, desc]})
109
+ await e.answer([sur])
110
+
111
+
112
+ @in_pattern("msg", owner=True)
113
+ async def _(e):
114
+ zzz = e.text.split(maxsplit=1)
115
+ desc = "Touch me"
116
+ try:
117
+ query = zzz[1]
118
+ if query.isdigit():
119
+ query = int(query)
120
+ logi = await ultroid_bot(gu(id=query))
121
+ user = logi.users[0]
122
+ mention = inline_mention(user)
123
+ x = user.status
124
+ if isinstance(x, types.UserStatusOnline):
125
+ status = "Online"
126
+ elif isinstance(x, types.UserStatusOffline):
127
+ status = "Offline"
128
+ elif isinstance(x, types.UserStatusRecently):
129
+ status = "Last Seen Recently"
130
+ elif isinstance(x, types.UserStatusLastMonth):
131
+ status = "Last seen months ago"
132
+ elif isinstance(x, types.UserStatusLastWeek):
133
+ status = "Last seen weeks ago"
134
+ else:
135
+ status = "Can't Tell"
136
+ text = f"**Name:** `{user.first_name}`\n"
137
+ text += f"**Id:** `{user.id}`\n"
138
+ if user.username:
139
+ text += f"**Username:** `{user.username}`\n"
140
+ url = f"https://t.me/{user.username}"
141
+ else:
142
+ text += f"**Mention:** `{mention}`\n"
143
+ url = f"tg://user?id={user.id}"
144
+ text += f"**Status:** `{status}`\n"
145
+ text += f"**About:** `{logi.full_user.about}`"
146
+ button = [
147
+ Button.url("Private", url=url),
148
+ Button.switch_inline(
149
+ "Secret msg",
150
+ query=f"wspr {query} Hello 👋",
151
+ same_peer=True,
152
+ ),
153
+ ]
154
+ sur = e.builder.article(
155
+ title=user.first_name,
156
+ description=desc,
157
+ text=text,
158
+ buttons=button,
159
+ )
160
+ except IndexError:
161
+ sur = e.builder.article(
162
+ title="Give Username",
163
+ description="You Didn't Type Username or id.",
164
+ text="You Didn't Type Username or id.",
165
+ )
166
+ except BaseException as er:
167
+ LOGS.exception(er)
168
+ name = get_string("wspr_4").format(query)
169
+ sur = e.builder.article(
170
+ title=name,
171
+ text=name,
172
+ )
173
+
174
+ await e.answer([sur])
175
+
176
+
177
+ @callback(
178
+ re.compile(
179
+ "dd_(.*)",
180
+ ),
181
+ )
182
+ async def _(e):
183
+ ids = int(e.pattern_match.group(1).strip().decode("UTF-8"))
184
+ if buddhhu.get(ids):
185
+ if e.sender_id in buddhhu[ids]:
186
+ await e.answer(buddhhu[ids][-1], alert=True)
187
+ else:
188
+ await e.answer("Not For You", alert=True)
189
+ else:
190
+ await e.answer(get_string("wspr_2"), alert=True)
191
+
192
+
193
+ @callback(re.compile("del_(.*)"))
194
+ async def _(e):
195
+ ids = int(e.pattern_match.group(1).strip().decode("UTF-8"))
196
+ if buddhhu.get(ids):
197
+ if e.sender_id in buddhhu[ids]:
198
+ buddhhu.pop(ids)
199
+ try:
200
+ await e.edit(get_string("wspr_2"))
201
+ except MessageNotModifiedError:
202
+ pass
203
+ else:
204
+ await e.answer(get_string("wspr_5"), alert=True)
plugins/admintools.py ADDED
@@ -0,0 +1,471 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from . import get_help
9
+
10
+ __doc__ = get_help("help_admintools")
11
+
12
+ import asyncio
13
+
14
+ from telethon.errors import BadRequestError
15
+ from telethon.errors.rpcerrorlist import ChatNotModifiedError, UserIdInvalidError
16
+ from telethon.tl.functions.channels import EditAdminRequest, GetFullChannelRequest
17
+ from telethon.tl.functions.messages import GetFullChatRequest, SetHistoryTTLRequest
18
+ from telethon.tl.types import InputMessagesFilterPinned
19
+ from telethon.utils import get_display_name
20
+
21
+ from pyUltroid.dB import DEVLIST
22
+ from pyUltroid.fns.admins import ban_time
23
+ from pyUltroid.fns.info import get_uinfo
24
+
25
+ from . import HNDLR, LOGS, eod, eor, get_string, inline_mention, types, ultroid_cmd
26
+
27
+
28
+ @ultroid_cmd(
29
+ pattern="promote( (.*)|$)",
30
+ admins_only=True,
31
+ manager=True,
32
+ require="add_admins",
33
+ fullsudo=True,
34
+ )
35
+ async def prmte(ult):
36
+ xx = await ult.eor(get_string("com_1"))
37
+ user, rank = await get_uinfo(ult)
38
+ rank = rank or "Admin"
39
+ FullRight = False
40
+ if not user:
41
+ return await xx.edit(get_string("pro_1"))
42
+ if rank.split()[0] == "-f":
43
+ try:
44
+ rank = rank.split(maxsplit=1)[1]
45
+ except IndexError:
46
+ rank = "Admin"
47
+ FullRight = True
48
+ try:
49
+ if FullRight:
50
+ await ult.client(
51
+ EditAdminRequest(ult.chat_id, user.id, ult.chat.admin_rights, rank)
52
+ )
53
+ else:
54
+ await ult.client.edit_admin(
55
+ ult.chat_id,
56
+ user.id,
57
+ invite_users=True,
58
+ ban_users=True,
59
+ delete_messages=True,
60
+ pin_messages=True,
61
+ manage_call=True,
62
+ title=rank,
63
+ )
64
+ await eod(
65
+ xx, get_string("pro_2").format(inline_mention(user), ult.chat.title, rank)
66
+ )
67
+ except Exception as ex:
68
+ return await xx.edit(f"`{ex}`")
69
+
70
+
71
+ @ultroid_cmd(
72
+ pattern="demote( (.*)|$)",
73
+ admins_only=True,
74
+ manager=True,
75
+ require="add_admins",
76
+ fullsudo=True,
77
+ )
78
+ async def dmote(ult):
79
+ xx = await ult.eor(get_string("com_1"))
80
+ user, rank = await get_uinfo(ult)
81
+ if not rank:
82
+ rank = "Not Admin"
83
+ if not user:
84
+ return await xx.edit(get_string("de_1"))
85
+ try:
86
+ await ult.client.edit_admin(
87
+ ult.chat_id,
88
+ user.id,
89
+ invite_users=None,
90
+ ban_users=None,
91
+ delete_messages=None,
92
+ pin_messages=None,
93
+ manage_call=None,
94
+ title=rank,
95
+ )
96
+ await eod(xx, get_string("de_2").format(inline_mention(user), ult.chat.title))
97
+ except Exception as ex:
98
+ return await xx.edit(f"`{ex}`")
99
+
100
+
101
+ @ultroid_cmd(
102
+ pattern="ban( (.*)|$)",
103
+ admins_only=True,
104
+ manager=True,
105
+ require="ban_users",
106
+ fullsudo=True,
107
+ )
108
+ async def bban(ult):
109
+ something = await get_uinfo(ult)
110
+ if not something:
111
+ return
112
+ user, reason = something
113
+ if not user:
114
+ return await eod(ult, get_string("ban_1"))
115
+ if user.id in DEVLIST:
116
+ return await eod(ult, get_string("ban_2"))
117
+ try:
118
+ await ult.client.edit_permissions(ult.chat_id, user.id, view_messages=False)
119
+ except UserIdInvalidError:
120
+ return await eod(ult, get_string("adm_1"))
121
+ except BadRequestError:
122
+ return await eod(ult, get_string("ban_3"))
123
+ senderme = inline_mention(await ult.get_sender())
124
+ userme = inline_mention(user)
125
+ text = get_string("ban_4").format(userme, senderme, ult.chat.title)
126
+ if reason:
127
+ text += get_string("ban_5").format(reason)
128
+ await eod(ult, text)
129
+
130
+
131
+ @ultroid_cmd(
132
+ pattern="unban( (.*)|$)",
133
+ admins_only=True,
134
+ manager=True,
135
+ require="ban_users",
136
+ fullsudo=True,
137
+ )
138
+ async def uunban(ult):
139
+ xx = await ult.eor(get_string("com_1"))
140
+ if ult.text[1:].startswith("unbanall"):
141
+ return
142
+ something = await get_uinfo(ult)
143
+ if not something:
144
+ return
145
+ user, reason = something
146
+ if not user:
147
+ return await xx.edit(get_string("unban_1"))
148
+ try:
149
+ await ult.client.edit_permissions(ult.chat_id, user.id, view_messages=True)
150
+ except UserIdInvalidError:
151
+ return await eod(ult, get_string("adm_1"))
152
+ except BadRequestError:
153
+ return await xx.edit(get_string("adm_2"))
154
+ sender = inline_mention(await ult.get_sender())
155
+ text = get_string("unban_3").format(inline_mention(user), sender, ult.chat.title)
156
+ if reason:
157
+ text += get_string("ban_5").format(reason)
158
+ await xx.edit(text)
159
+
160
+
161
+ @ultroid_cmd(
162
+ pattern="kick( (.*)|$)",
163
+ manager=True,
164
+ require="ban_users",
165
+ fullsudo=True,
166
+ )
167
+ async def kck(ult):
168
+ if "kickme" in ult.text:
169
+ return
170
+ if ult.is_private:
171
+ return await ult.eor("`Use this in Group/Channel.`", time=5)
172
+ ml = ult.text.split(" ", maxsplit=1)[0]
173
+ xx = await ult.eor(get_string("com_1"))
174
+ something = await get_uinfo(ult)
175
+ if not something:
176
+ return
177
+ user, reason = something
178
+ if not user:
179
+ return await xx.edit(get_string("adm_1"))
180
+ if user.id in DEVLIST:
181
+ return await xx.edit(get_string("kick_2"))
182
+ if getattr(user, "is_self", False):
183
+ return await xx.edit(get_string("kick_3"))
184
+ try:
185
+ await ult.client.kick_participant(ult.chat_id, user.id)
186
+ except BadRequestError as er:
187
+ LOGS.info(er)
188
+ return await xx.edit(get_string("kick_1"))
189
+ except Exception as e:
190
+ LOGS.exception(e)
191
+ return
192
+ text = get_string("kick_4").format(
193
+ inline_mention(user), inline_mention(await ult.get_sender()), ult.chat.title
194
+ )
195
+ if reason:
196
+ text += get_string("ban_5").format(reason)
197
+ await xx.edit(text)
198
+
199
+
200
+ @ultroid_cmd(
201
+ pattern="tban( (.*)|$)",
202
+ admins_only=True,
203
+ manager=True,
204
+ require="ban_users",
205
+ fullsudo=True,
206
+ )
207
+ async def tkicki(e):
208
+ huh = e.text.split()
209
+ inputt = None
210
+ try:
211
+ tme = huh[1]
212
+ except IndexError:
213
+ return await e.eor(get_string("adm_3"), time=15)
214
+ try:
215
+ inputt = huh[2]
216
+ except IndexError:
217
+ if e.reply_to_msg_id:
218
+ inputt = (await e.get_reply_message()).sender_id
219
+ if not inputt:
220
+ return await e.eor(get_string("tban_1"))
221
+ userid = await e.client.parse_id(inputt)
222
+ try:
223
+ user = await e.client.get_entity(userid)
224
+ except Exception as ex:
225
+ return await eor(e, f"`{ex}`")
226
+ try:
227
+ bun = ban_time(tme)
228
+ await e.client.edit_permissions(
229
+ e.chat_id, user.id, until_date=bun, view_messages=False
230
+ )
231
+ await eod(
232
+ e,
233
+ get_string("tban_2").format(inline_mention(user), e.chat.title, tme),
234
+ time=15,
235
+ )
236
+ except Exception as m:
237
+ return await e.eor(str(m))
238
+
239
+
240
+ @ultroid_cmd(pattern="pin$", manager=True, require="pin_messages", fullsudo=True)
241
+ async def pin(msg):
242
+ if not msg.is_reply:
243
+ return await eor(msg, get_string("pin_1"))
244
+ me = await msg.get_reply_message()
245
+ if me.is_private:
246
+ text = "`Pinned.`"
247
+ else:
248
+ text = f"Pinned [This Message]({me.message_link}) !"
249
+ try:
250
+ await msg.client.pin_message(msg.chat_id, me.id, notify=False)
251
+ except BadRequestError:
252
+ return await eor(msg, get_string("adm_2"))
253
+ except Exception as e:
254
+ return await eor(msg, f"**ERROR:**`{e}`")
255
+ await eor(msg, text)
256
+
257
+
258
+ @ultroid_cmd(
259
+ pattern="unpin($| (.*))",
260
+ manager=True,
261
+ require="pin_messages",
262
+ fullsudo=True,
263
+ )
264
+ async def unp(ult):
265
+ xx = await ult.eor(get_string("com_1"))
266
+ ch = (ult.pattern_match.group(1).strip()).strip()
267
+ msg = None
268
+ if ult.is_reply:
269
+ msg = ult.reply_to_msg_id
270
+ elif ch != "all":
271
+ return await xx.edit(get_string("unpin_1").format(HNDLR))
272
+ try:
273
+ await ult.client.unpin_message(ult.chat_id, msg)
274
+ except BadRequestError:
275
+ return await xx.edit(get_string("adm_2"))
276
+ except Exception as e:
277
+ return await xx.edit(f"**ERROR:**`{e}`")
278
+ await xx.edit("`Unpinned!`")
279
+
280
+
281
+ @ultroid_cmd(
282
+ pattern="tpin( (.*)|$)",
283
+ admins_only=True,
284
+ manager=True,
285
+ require="pin_messages",
286
+ fullsudo=True,
287
+ )
288
+ async def pin_message(ult):
289
+ match = ult.pattern_match.group(1).strip()
290
+ if not ult.is_reply:
291
+ return await ult.eor("`Reply to message..`", time=6)
292
+ if not match:
293
+ return await ult.eor("`Please provide time..`", time=8)
294
+ msg = await ult.eor(get_string("com_1"))
295
+ msg_id = ult.reply_to_msg_id
296
+ try:
297
+ time = ban_time(match)
298
+ await ult.client.pin_message(ult.chat_id, msg_id)
299
+ await msg.eor(f"`pinned for time` `{time}`", time=8)
300
+ except Exception as er:
301
+ return await msg.edit(str(er))
302
+ await asyncio.sleep(time)
303
+ try:
304
+ await ult.client.unpin_message(ult.chat_id, msg_id)
305
+ except Exception as er:
306
+ LOGS.exception(er)
307
+
308
+
309
+ @ultroid_cmd(pattern="purge( (.*)|$)", manager=True, require="delete_messages")
310
+ async def fastpurger(purg):
311
+ match = purg.pattern_match.group(1).strip()
312
+ try:
313
+ ABC = purg.text[6]
314
+ except IndexError:
315
+ ABC = None
316
+ if ABC and purg.text[6] in ["m", "a"]:
317
+ return
318
+ if not purg._client._bot and (
319
+ (match)
320
+ or (purg.is_reply and (purg.is_private or isinstance(purg.chat, types.Chat)))
321
+ ):
322
+ p = 0
323
+ async for msg in purg.client.iter_messages(
324
+ purg.chat_id,
325
+ limit=int(match) if match else None,
326
+ min_id=purg.reply_to_msg_id if purg.is_reply else None,
327
+ ):
328
+ await msg.delete()
329
+ p += 0
330
+ return await eor(purg, f"Purged {p} Messages! ", time=5)
331
+ if not purg.reply_to_msg_id:
332
+ return await eor(purg, get_string("purge_1"), time=10)
333
+ try:
334
+ await purg.client.delete_messages(
335
+ purg.chat_id, list(range(purg.reply_to_msg_id, purg.id))
336
+ )
337
+
338
+ except Exception as er:
339
+ LOGS.info(er)
340
+ await purg.eor("__Fast purge complete!__", time=5)
341
+
342
+
343
+ @ultroid_cmd(
344
+ pattern="purgeme( (.*)|$)",
345
+ )
346
+ async def fastpurgerme(purg):
347
+ if num := purg.pattern_match.group(1).strip():
348
+ try:
349
+ nnt = int(num)
350
+ except BaseException:
351
+ await eor(purg, get_string("com_3"), time=5)
352
+ return
353
+ mp = 0
354
+ async for mm in purg.client.iter_messages(
355
+ purg.chat_id, limit=nnt, from_user="me"
356
+ ):
357
+ await mm.delete()
358
+ mp += 1
359
+ await eor(purg, f"Purged {mp} Messages!", time=5)
360
+ return
361
+ elif not purg.reply_to_msg_id:
362
+ return await eod(
363
+ purg,
364
+ "`Reply to a message to purge from or use it like ``purgeme <num>`",
365
+ time=10,
366
+ )
367
+ chat = await purg.get_input_chat()
368
+ msgs = []
369
+ async for msg in purg.client.iter_messages(
370
+ chat,
371
+ from_user="me",
372
+ min_id=purg.reply_to_msg_id,
373
+ ):
374
+ msgs.append(msg)
375
+ if msgs:
376
+ await purg.client.delete_messages(chat, msgs)
377
+ await purg.eor(
378
+ "__Fast purge complete!__\n**Purged** `" + str(len(msgs)) + "` **messages.**",
379
+ time=5,
380
+ )
381
+
382
+
383
+ @ultroid_cmd(
384
+ pattern="purgeall$",
385
+ )
386
+ async def _(e):
387
+ if not e.is_reply:
388
+ return await eod(
389
+ e,
390
+ get_string("purgeall_1"),
391
+ )
392
+
393
+ msg = await e.get_reply_message()
394
+ name = msg.sender
395
+ try:
396
+ await e.client.delete_messages(e.chat_id, from_user=msg.sender_id)
397
+ await e.eor(get_string("purgeall_2").format(name.first_name), time=5)
398
+ except Exception as er:
399
+ return await e.eor(str(er), time=5)
400
+
401
+ @ultroid_cmd(pattern="pinned", manager=True, groups_only=True)
402
+ async def djshsh(event):
403
+ chat = await event.get_chat()
404
+ if isinstance(chat, types.Chat):
405
+ FChat = await event.client(GetFullChatRequest(chat.id))
406
+ elif isinstance(chat, types.Channel):
407
+ FChat = await event.client(GetFullChannelRequest(chat.id))
408
+ else:
409
+ return
410
+ msg_id = FChat.full_chat.pinned_msg_id
411
+ if not msg_id:
412
+ return await event.eor(get_string("pinned_1"))
413
+ msg = await event.client.get_messages(chat.id, ids=msg_id)
414
+ if msg:
415
+ await event.eor(get_string("pinned_2").format(msg.message_link))
416
+
417
+
418
+ @ultroid_cmd(
419
+ pattern="listpinned$",
420
+ )
421
+ async def get_all_pinned(event):
422
+ x = await event.eor(get_string("com_1"))
423
+ chat_id = (str(event.chat_id)).replace("-100", "")
424
+ chat_name = get_display_name(event.chat)
425
+ a = ""
426
+ c = 1
427
+ async for i in event.client.iter_messages(
428
+ event.chat_id, filter=InputMessagesFilterPinned
429
+ ):
430
+ if i.message:
431
+ t = " ".join(i.message.split()[:4])
432
+ txt = f"{t}...."
433
+ else:
434
+ txt = "Go to message."
435
+ a += f"{c}. <a href=https://t.me/c/{chat_id}/{i.id}>{txt}</a>\n"
436
+ c += 1
437
+
438
+ if c == 1:
439
+ m = f"<b>The pinned message in {chat_name}:</b>\n\n"
440
+ else:
441
+ m = f"<b>List of pinned message(s) in {chat_name}:</b>\n\n"
442
+
443
+ if not a:
444
+ return await eor(x, get_string("listpin_1"), time=5)
445
+
446
+ await x.edit(m + a, parse_mode="html")
447
+
448
+
449
+ @ultroid_cmd(
450
+ pattern="autodelete( (.*)|$)",
451
+ admins_only=True,
452
+ )
453
+ async def autodelte(ult):
454
+ match = ult.pattern_match.group(1).strip()
455
+ if not match or match not in ["24h", "7d", "1m", "off"]:
456
+ return await ult.eor("`Please Use in Proper Format..`", time=5)
457
+ if match == "24h":
458
+ tt = 3600 * 24
459
+ elif match == "7d":
460
+ tt = 3600 * 24 * 7
461
+ elif match == "1m":
462
+ tt = 3600 * 24 * 31
463
+ else:
464
+ tt = 0
465
+ try:
466
+ await ult.client(SetHistoryTTLRequest(ult.chat_id, period=tt))
467
+ except ChatNotModifiedError:
468
+ return await ult.eor(
469
+ f"Auto Delete Setting is Already same to `{match}`", time=5
470
+ )
471
+ await ult.eor(f"Auto Delete Status Changed to `{match}` !")
plugins/afk.py ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from . import get_help
9
+
10
+ __doc__ = get_help("help_afk")
11
+
12
+
13
+ import asyncio
14
+
15
+ from telethon import events
16
+
17
+ from pyUltroid.dB.afk_db import add_afk, del_afk, is_afk
18
+ from pyUltroid.dB.base import KeyManager
19
+
20
+ from . import (
21
+ LOG_CHANNEL,
22
+ NOSPAM_CHAT,
23
+ Redis,
24
+ asst,
25
+ get_string,
26
+ mediainfo,
27
+ udB,
28
+ ultroid_bot,
29
+ ultroid_cmd,
30
+ upload_file
31
+ )
32
+
33
+ old_afk_msg = []
34
+
35
+ is_approved = KeyManager("PMPERMIT", cast=list).contains
36
+
37
+
38
+ @ultroid_cmd(pattern="afk( (.*)|$)", owner_only=True)
39
+ async def set_afk(event):
40
+ if event.client._bot or is_afk():
41
+ return
42
+ text, media, media_type = None, None, None
43
+ if event.pattern_match.group(1).strip():
44
+ text = event.text.split(maxsplit=1)[1]
45
+ reply = await event.get_reply_message()
46
+ if reply:
47
+ if reply.text and not text:
48
+ text = reply.text
49
+ if reply.media:
50
+ media_type = mediainfo(reply.media)
51
+ if media_type.startswith(("pic", "gif")):
52
+ file = await event.client.download_media(reply.media)
53
+ media = upload_file(file)
54
+ else:
55
+ media = reply.file.id
56
+ await event.eor("`Done`", time=2)
57
+ add_afk(text, media_type, media)
58
+ ultroid_bot.add_handler(remove_afk, events.NewMessage(outgoing=True))
59
+ ultroid_bot.add_handler(
60
+ on_afk,
61
+ events.NewMessage(
62
+ incoming=True, func=lambda e: bool(e.mentioned or e.is_private)
63
+ ),
64
+ )
65
+ msg1, msg2 = None, None
66
+ if text and media:
67
+ if "sticker" in media_type:
68
+ msg1 = await ultroid_bot.send_file(event.chat_id, file=media)
69
+ msg2 = await ultroid_bot.send_message(
70
+ event.chat_id, get_string("afk_5").format(text)
71
+ )
72
+ else:
73
+ msg1 = await ultroid_bot.send_message(
74
+ event.chat_id, get_string("afk_5").format(text), file=media
75
+ )
76
+ elif media:
77
+ if "sticker" in media_type:
78
+ msg1 = await ultroid_bot.send_file(event.chat_id, file=media)
79
+ msg2 = await ultroid_bot.send_message(event.chat_id, get_string("afk_6"))
80
+ else:
81
+ msg1 = await ultroid_bot.send_message(
82
+ event.chat_id, get_string("afk_6"), file=media
83
+ )
84
+ elif text:
85
+ msg1 = await event.respond(get_string("afk_5").format(text))
86
+ else:
87
+ msg1 = await event.respond(get_string("afk_6"))
88
+ old_afk_msg.append(msg1)
89
+ if msg2:
90
+ old_afk_msg.append(msg2)
91
+ return await asst.send_message(LOG_CHANNEL, msg2.text)
92
+ await asst.send_message(LOG_CHANNEL, msg1.text)
93
+
94
+
95
+ async def remove_afk(event):
96
+ if event.is_private and udB.get_key("PMSETTING") and not is_approved(event.chat_id):
97
+ return
98
+ elif "afk" in event.text.lower():
99
+ return
100
+ elif event.chat_id in NOSPAM_CHAT:
101
+ return
102
+ if is_afk():
103
+ _, _, _, afk_time = is_afk()
104
+ del_afk()
105
+ off = await event.reply(get_string("afk_1").format(afk_time))
106
+ await asst.send_message(LOG_CHANNEL, get_string("afk_2").format(afk_time))
107
+ for x in old_afk_msg:
108
+ try:
109
+ await x.delete()
110
+ except BaseException:
111
+ pass
112
+ await asyncio.sleep(10)
113
+ await off.delete()
114
+
115
+
116
+ async def on_afk(event):
117
+ if event.is_private and Redis("PMSETTING") and not is_approved(event.chat_id):
118
+ return
119
+ elif "afk" in event.text.lower():
120
+ return
121
+ elif not is_afk():
122
+ return
123
+ if event.chat_id in NOSPAM_CHAT:
124
+ return
125
+ sender = await event.get_sender()
126
+ if sender.bot or sender.verified:
127
+ return
128
+ text, media_type, media, afk_time = is_afk()
129
+ msg1, msg2 = None, None
130
+ if text and media:
131
+ if "sticker" in media_type:
132
+ msg1 = await event.reply(file=media)
133
+ msg2 = await event.reply(get_string("afk_3").format(afk_time, text))
134
+ else:
135
+ msg1 = await event.reply(
136
+ get_string("afk_3").format(afk_time, text), file=media
137
+ )
138
+ elif media:
139
+ if "sticker" in media_type:
140
+ msg1 = await event.reply(file=media)
141
+ msg2 = await event.reply(get_string("afk_4").format(afk_time))
142
+ else:
143
+ msg1 = await event.reply(get_string("afk_4").format(afk_time), file=media)
144
+ elif text:
145
+ msg1 = await event.reply(get_string("afk_3").format(afk_time, text))
146
+ else:
147
+ msg1 = await event.reply(get_string("afk_4").format(afk_time))
148
+ for x in old_afk_msg:
149
+ try:
150
+ await x.delete()
151
+ except BaseException:
152
+ pass
153
+ old_afk_msg.append(msg1)
154
+ if msg2:
155
+ old_afk_msg.append(msg2)
156
+
157
+
158
+ if udB.get_key("AFK_DB"):
159
+ ultroid_bot.add_handler(remove_afk, events.NewMessage(outgoing=True))
160
+ ultroid_bot.add_handler(
161
+ on_afk,
162
+ events.NewMessage(
163
+ incoming=True, func=lambda e: bool(e.mentioned or e.is_private)
164
+ ),
165
+ )
plugins/aiwrapper.py ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ """
9
+ ✘ Commands Available -
10
+
11
+ • `{i}gemini <prompt>`
12
+ Get response from Google Gemini.
13
+
14
+ • `{i}antr <prompt>`
15
+ Get response from Anthropic Claude.
16
+
17
+ • `{i}gpt <prompt>`
18
+ Get response from OpenAI GPT.
19
+
20
+ • `{i}deepseek <prompt>`
21
+ Get response from DeepSeek AI.
22
+
23
+ Set custom models using:
24
+ • OPENAI_MODEL: default: gpt-4o-mini
25
+ • ANTHROPIC_MODEL: claude-3-opus-20240229
26
+ • GEMINI_MODEL: gemini-1.5-flash
27
+ • DEEPSEEK_MODEL: deepseek-chat
28
+ """
29
+
30
+ import json
31
+ from . import LOGS, eor, get_string, udB, ultroid_cmd, async_searcher
32
+ import aiohttp
33
+ import asyncio
34
+
35
+
36
+ ENDPOINTS = {
37
+ "gpt": "https://api.openai.com/v1/chat/completions",
38
+ "antr": "https://api.anthropic.com/v1/messages",
39
+ "gemini": "https://generativelanguage.googleapis.com/v1beta/chat/completions",
40
+ "deepseek": "https://api.deepseek.com/chat/completions"
41
+ }
42
+
43
+ DEFAULT_MODELS = {
44
+ "gpt": "gpt-4o-mini",
45
+ "antr": "claude-3-opus-20240229",
46
+ "gemini": "gemini-1.5-flash",
47
+ "deepseek": "deepseek-chat"
48
+ }
49
+
50
+
51
+ def get_model(provider):
52
+ """Get model name from database or use default"""
53
+ model_keys = {
54
+ "gpt": "OPENAI_MODEL",
55
+ "antr": "ANTHROPIC_MODEL",
56
+ "gemini": "GEMINI_MODEL",
57
+ "deepseek": "DEEPSEEK_MODEL"
58
+ }
59
+ return udB.get_key(model_keys[provider]) or DEFAULT_MODELS[provider]
60
+
61
+
62
+ async def stream_response(msg, text):
63
+ """Stream response by editing message"""
64
+ current = ""
65
+ # Split into chunks of ~100 characters at word boundaries
66
+ words = text.split()
67
+ chunks = []
68
+ current_chunk = []
69
+
70
+ for word in words:
71
+ current_chunk.append(word)
72
+ if len(" ".join(current_chunk)) > 100:
73
+ chunks.append(" ".join(current_chunk[:-1]))
74
+ current_chunk = [word]
75
+ if current_chunk:
76
+ chunks.append(" ".join(current_chunk))
77
+
78
+ for chunk in chunks:
79
+ current += chunk + " "
80
+ try:
81
+ await msg.edit(current)
82
+ except Exception:
83
+ pass
84
+ await asyncio.sleep(0.5)
85
+ return current
86
+
87
+
88
+ async def get_ai_response(provider, prompt, api_key, stream=False):
89
+ """Get response from AI provider"""
90
+ try:
91
+ headers = {"Content-Type": "application/json"}
92
+ model = get_model(provider)
93
+
94
+ if provider == "gpt":
95
+ headers["Authorization"] = f"Bearer {api_key}"
96
+ data = {
97
+ "model": model,
98
+ "messages": [{"role": "user", "content": prompt}],
99
+ "stream": stream
100
+ }
101
+ if not stream:
102
+ response = await async_searcher(
103
+ ENDPOINTS[provider],
104
+ headers=headers,
105
+ post=True,
106
+ json=data,
107
+ re_json=True
108
+ )
109
+ yield response["choices"][0]["message"]["content"]
110
+ return
111
+
112
+ async with aiohttp.ClientSession() as session:
113
+ async with session.post(
114
+ ENDPOINTS[provider],
115
+ headers=headers,
116
+ json=data
117
+ ) as resp:
118
+ async for line in resp.content:
119
+ if line:
120
+ try:
121
+ json_line = json.loads(line.decode('utf-8').strip().strip('data:').strip())
122
+ if 'choices' in json_line and json_line['choices']:
123
+ content = json_line['choices'][0].get('delta', {}).get('content', '')
124
+ if content:
125
+ yield content
126
+ except Exception:
127
+ continue
128
+
129
+ elif provider == "antr":
130
+ headers["x-api-key"] = api_key
131
+ headers["anthropic-version"] = "2023-06-01"
132
+ data = {
133
+ "model": model,
134
+ "messages": [{"role": "user", "content": prompt}],
135
+ "stream": stream
136
+ }
137
+ if not stream:
138
+ response = await async_searcher(
139
+ ENDPOINTS[provider],
140
+ headers=headers,
141
+ post=True,
142
+ json=data,
143
+ re_json=True
144
+ )
145
+ yield response["content"][0]["text"]
146
+ return
147
+
148
+ async with aiohttp.ClientSession() as session:
149
+ async with session.post(
150
+ ENDPOINTS[provider],
151
+ headers=headers,
152
+ json=data
153
+ ) as resp:
154
+ async for line in resp.content:
155
+ if line:
156
+ try:
157
+ json_line = json.loads(line.decode('utf-8').strip())
158
+ if 'content' in json_line:
159
+ content = json_line['content'][0]['text']
160
+ if content:
161
+ yield content
162
+ except Exception:
163
+ continue
164
+
165
+ elif provider == "gemini":
166
+ headers["Authorization"] = f"Bearer {api_key}"
167
+ data = {
168
+ "model": model,
169
+ "messages": [
170
+ {"role": "system", "content": "You are a helpful assistant."},
171
+ {"role": "user", "content": prompt}
172
+ ],
173
+ "stream": stream
174
+ }
175
+
176
+ if not stream:
177
+ try:
178
+ response = await async_searcher(
179
+ ENDPOINTS[provider],
180
+ headers=headers,
181
+ post=True,
182
+ json=data,
183
+ re_json=True
184
+ )
185
+ if "error" in response:
186
+ error = response["error"]
187
+ if error.get("code") == 429:
188
+ retry_delay = None
189
+ for detail in error.get("details", []):
190
+ if detail.get("@type") == "type.googleapis.com/google.rpc.RetryInfo":
191
+ retry_delay = detail.get("retryDelay", "60s").rstrip("s")
192
+ error_msg = f"⚠️ Rate limit exceeded. Please try again in {retry_delay} seconds."
193
+ if "free_tier" in str(error):
194
+ error_msg += "\nConsider upgrading to a paid tier for higher quotas."
195
+ yield error_msg
196
+ return
197
+ yield f"Error: {error.get('message', 'Unknown error occurred')}"
198
+ return
199
+ yield response["choices"][0]["message"]["content"]
200
+ except Exception as e:
201
+ LOGS.exception(e)
202
+ yield f"Error: {str(e)}"
203
+ return
204
+
205
+ async with aiohttp.ClientSession() as session:
206
+ try:
207
+ async with session.post(
208
+ ENDPOINTS[provider],
209
+ headers=headers,
210
+ json=data
211
+ ) as resp:
212
+ if resp.status == 429:
213
+ error_data = await resp.json()
214
+ retry_delay = "60"
215
+ for detail in error_data.get("error", {}).get("details", []):
216
+ if detail.get("@type") == "type.googleapis.com/google.rpc.RetryInfo":
217
+ retry_delay = detail.get("retryDelay", "60s").rstrip("s")
218
+ yield f"⚠️ Rate limit exceeded. Please try again in {retry_delay} seconds."
219
+ return
220
+
221
+ if resp.status != 200:
222
+ error_data = await resp.json()
223
+ yield f"Error: {error_data.get('error', {}).get('message', 'Unknown error occurred')}"
224
+ return
225
+
226
+ async for line in resp.content:
227
+ if line:
228
+ text = line.decode('utf-8').strip()
229
+ if text.startswith('data: '):
230
+ data = text[6:] # Remove 'data: ' prefix
231
+ if data == '[DONE]':
232
+ break
233
+ try:
234
+ json_data = json.loads(data)
235
+ if 'choices' in json_data and json_data['choices']:
236
+ content = json_data['choices'][0].get('delta', {}).get('content', '')
237
+ if content:
238
+ yield content
239
+ except json.JSONDecodeError:
240
+ continue
241
+ except Exception as e:
242
+ LOGS.exception(e)
243
+ yield f"Error: {str(e)}"
244
+
245
+ elif provider == "deepseek":
246
+ headers["Authorization"] = f"Bearer {api_key}"
247
+ data = {
248
+ "model": model,
249
+ "messages": [{"role": "user", "content": prompt}],
250
+ "stream": stream
251
+ }
252
+ if not stream:
253
+ response = await async_searcher(
254
+ ENDPOINTS[provider],
255
+ headers=headers,
256
+ post=True,
257
+ json=data,
258
+ re_json=True
259
+ )
260
+ yield response["choices"][0]["message"]["content"]
261
+ return
262
+
263
+ async with aiohttp.ClientSession() as session:
264
+ async with session.post(
265
+ ENDPOINTS[provider],
266
+ headers=headers,
267
+ json=data
268
+ ) as resp:
269
+ async for line in resp.content:
270
+ if line:
271
+ try:
272
+ json_line = json.loads(line.decode('utf-8').strip())
273
+ if 'choices' in json_line and json_line['choices']:
274
+ content = json_line['choices'][0].get('delta', {}).get('content', '')
275
+ if content:
276
+ yield content
277
+ except Exception:
278
+ continue
279
+
280
+ except Exception as e:
281
+ LOGS.exception(e)
282
+ yield f"Error: {str(e)}"
283
+
284
+
285
+ @ultroid_cmd(pattern="gemini( (.*)|$)")
286
+ async def gemini_ai(event):
287
+ """Use Google Gemini"""
288
+ prompt = event.pattern_match.group(1).strip()
289
+ if not prompt:
290
+ return await event.eor("❌ Please provide a prompt!")
291
+
292
+ api_key = udB.get_key("GEMINI_API_KEY")
293
+ if not api_key:
294
+ return await event.eor("⚠️ Please set Gemini API key using `setdb GEMINI_API_KEY your_api_key`")
295
+
296
+ msg = await event.eor("🤔 Thinking...")
297
+ model = get_model("gemini")
298
+
299
+ header = (
300
+ "🤖 **Google Gemini**\n"
301
+ f"**Model:** `{model}`\n"
302
+ "➖➖➖➖➖➖➖➖➖➖\n\n"
303
+ f"**🔍 Prompt:**\n{prompt}\n\n"
304
+ "**💡 Response:**\n"
305
+ )
306
+
307
+ if event.client.me.bot:
308
+ await msg.edit(header)
309
+ response = ""
310
+ async for chunk in get_ai_response("gemini", prompt, api_key, stream=True):
311
+ response += chunk
312
+ try:
313
+ await msg.edit(header + response)
314
+ except Exception:
315
+ pass
316
+ else:
317
+ response = ""
318
+ async for chunk in get_ai_response("gemini", prompt, api_key, stream=True):
319
+ response += chunk
320
+ try:
321
+ await msg.edit(header + response)
322
+ except Exception:
323
+ pass
324
+
325
+ @ultroid_cmd(pattern="antr( (.*)|$)")
326
+ async def anthropic_ai(event):
327
+ """Use Anthropic Claude"""
328
+ prompt = event.pattern_match.group(1).strip()
329
+ if not prompt:
330
+ return await event.eor("❌ Please provide a prompt!")
331
+
332
+ api_key = udB.get_key("ANTHROPIC_KEY")
333
+ if not api_key:
334
+ return await event.eor("⚠️ Please set Anthropic API key using `setdb ANTHROPIC_KEY your_api_key`")
335
+
336
+ msg = await event.eor("🤔 Thinking...")
337
+ model = get_model("antr")
338
+
339
+ formatted_response = (
340
+ "🧠 **Anthropic Claude**\n"
341
+ f"**Model:** `{model}`\n"
342
+ "➖➖➖➖➖➖➖➖➖➖\n\n"
343
+ f"**🔍 Prompt:**\n{prompt}\n\n"
344
+ f"**💡 Response:**\n"
345
+ )
346
+
347
+ if event.client.me.bot:
348
+ await msg.edit(formatted_response)
349
+ response = ""
350
+ async for chunk in get_ai_response("antr", prompt, api_key, stream=True):
351
+ response += chunk
352
+ try:
353
+ await msg.edit(formatted_response + response)
354
+ except Exception:
355
+ pass
356
+ else:
357
+ response = ""
358
+ async for chunk in get_ai_response("antr", prompt, api_key, stream=True):
359
+ response += chunk
360
+ try:
361
+ await msg.edit(formatted_response + response)
362
+ except Exception:
363
+ pass
364
+
365
+ @ultroid_cmd(pattern="gpt( (.*)|$)")
366
+ async def openai_ai(event):
367
+ """Use OpenAI GPT"""
368
+ prompt = event.pattern_match.group(1).strip()
369
+ if not prompt:
370
+ return await event.eor("❌ Please provide a prompt!")
371
+
372
+ api_key = udB.get_key("OPENAI_API_KEY")
373
+ if not api_key:
374
+ return await event.eor("⚠️ Please set GPT API key using `setdb OPENAI_API_KEY your_api_key`")
375
+
376
+ msg = await event.eor("🤔 Thinking...")
377
+ model = get_model("gpt")
378
+
379
+ header = (
380
+ "🌟 **OpenAI GPT**\n"
381
+ f"**Model:** `{model}`\n"
382
+ "➖➖➖➖➖➖➖➖➖➖\n\n"
383
+ f"**🔍 Prompt:**\n{prompt}\n\n"
384
+ "**💡 Response:**\n"
385
+ )
386
+
387
+ if event.client.me.bot:
388
+ await msg.edit(header)
389
+ response = ""
390
+ async for chunk in get_ai_response("gpt", prompt, api_key, stream=True):
391
+ response += chunk
392
+ try:
393
+ await msg.edit(header + response)
394
+ except Exception:
395
+ pass
396
+ else:
397
+ response =""
398
+ async for chunk in get_ai_response("gpt", prompt, api_key, stream=True):
399
+ response += chunk
400
+ try:
401
+ await msg.edit(header + response)
402
+ except Exception:
403
+ pass
404
+
405
+ @ultroid_cmd(pattern="deepseek( (.*)|$)")
406
+ async def deepseek_ai(event):
407
+ """Use DeepSeek AI"""
408
+ prompt = event.pattern_match.group(1).strip()
409
+ if not prompt:
410
+ return await event.eor("❌ Please provide a prompt!")
411
+
412
+ api_key = udB.get_key("DEEPSEEK_API_KEY")
413
+ if not api_key:
414
+ return await event.eor("⚠️ Please set DeepSeek API key using `setdb DEEPSEEK_API_KEY your_api_key`")
415
+
416
+ msg = await event.eor("🤔 Thinking...")
417
+ model = get_model("deepseek")
418
+
419
+ formatted_response = (
420
+ "🤖 **DeepSeek AI**\n"
421
+ f"**Model:** `{model}`\n"
422
+ "➖➖➖➖➖➖➖➖➖➖\n\n"
423
+ f"**🔍 Prompt:**\n{prompt}\n\n"
424
+ f"**💡 Response:**\n"
425
+ )
426
+
427
+ if event.client.me.bot:
428
+ await msg.edit(formatted_response)
429
+ response = ""
430
+ async for chunk in get_ai_response("deepseek", prompt, api_key, stream=True):
431
+ response += chunk
432
+ try:
433
+ await msg.edit(formatted_response + response)
434
+ except Exception:
435
+ pass
436
+ else:
437
+ response = ""
438
+ async for chunk in get_ai_response("deepseek", prompt, api_key, stream=True):
439
+ response += chunk
440
+
441
+ try:
442
+ await msg.edit(formatted_response + response)
443
+ except Exception:
444
+ pass
445
+
plugins/antiflood.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from . import get_help
9
+
10
+ __doc__ = get_help("help_antiflood")
11
+
12
+
13
+ import re
14
+
15
+ from telethon.events import NewMessage as NewMsg
16
+
17
+ from pyUltroid.dB import DEVLIST
18
+ from pyUltroid.dB.antiflood_db import get_flood, get_flood_limit, rem_flood, set_flood
19
+ from pyUltroid.fns.admins import admin_check
20
+
21
+ from . import Button, Redis, asst, callback, eod, get_string, ultroid_bot, ultroid_cmd
22
+
23
+ _check_flood = {}
24
+
25
+ if Redis("ANTIFLOOD"):
26
+
27
+ @ultroid_bot.on(
28
+ NewMsg(
29
+ chats=list(get_flood().keys()),
30
+ ),
31
+ )
32
+ async def flood_checm(event):
33
+ count = 1
34
+ chat = (await event.get_chat()).title
35
+ if event.chat_id in _check_flood.keys():
36
+ if event.sender_id == list(_check_flood[event.chat_id].keys())[0]:
37
+ count = _check_flood[event.chat_id][event.sender_id]
38
+ _check_flood[event.chat_id] = {event.sender_id: count + 1}
39
+ else:
40
+ _check_flood[event.chat_id] = {event.sender_id: count}
41
+ else:
42
+ _check_flood[event.chat_id] = {event.sender_id: count}
43
+ if await admin_check(event, silent=True) or getattr(event.sender, "bot", None):
44
+ return
45
+ if event.sender_id in DEVLIST:
46
+ return
47
+ if _check_flood[event.chat_id][event.sender_id] >= int(
48
+ get_flood_limit(event.chat_id)
49
+ ):
50
+ try:
51
+ name = event.sender.first_name
52
+ await event.client.edit_permissions(
53
+ event.chat_id, event.sender_id, send_messages=False
54
+ )
55
+ del _check_flood[event.chat_id]
56
+ await event.reply(f"#AntiFlood\n\n{get_string('antiflood_3')}")
57
+ await asst.send_message(
58
+ int(Redis("LOG_CHANNEL")),
59
+ f"#Antiflood\n\n`Muted `[{name}](tg://user?id={event.sender_id})` in {chat}`",
60
+ buttons=Button.inline(
61
+ "Unmute", data=f"anti_{event.sender_id}_{event.chat_id}"
62
+ ),
63
+ )
64
+ except BaseException:
65
+ pass
66
+
67
+
68
+ @callback(
69
+ re.compile(
70
+ "anti_(.*)",
71
+ ),
72
+ )
73
+ async def unmuting(e):
74
+ ino = (e.data_match.group(1)).decode("UTF-8").split("_")
75
+ user = int(ino[0])
76
+ chat = int(ino[1])
77
+ user_name = (await ultroid_bot.get_entity(user)).first_name
78
+ chat_title = (await ultroid_bot.get_entity(chat)).title
79
+ await ultroid_bot.edit_permissions(chat, user, send_messages=True)
80
+ await e.edit(
81
+ f"#Antiflood\n\n`Unmuted `[{user_name}](tg://user?id={user})` in {chat_title}`"
82
+ )
83
+
84
+
85
+ @ultroid_cmd(
86
+ pattern="setflood ?(\\d+)",
87
+ admins_only=True,
88
+ )
89
+ async def setflood(e):
90
+ input_ = e.pattern_match.group(1).strip()
91
+ if not input_:
92
+ return await e.eor("`What?`", time=5)
93
+ if not input_.isdigit():
94
+ return await e.eor(get_string("com_3"), time=5)
95
+ if m := set_flood(e.chat_id, input_):
96
+ return await eod(e, get_string("antiflood_4").format(input_))
97
+
98
+
99
+ @ultroid_cmd(
100
+ pattern="remflood$",
101
+ admins_only=True,
102
+ )
103
+ async def remove_flood(e):
104
+ hmm = rem_flood(e.chat_id)
105
+ try:
106
+ del _check_flood[e.chat_id]
107
+ except BaseException:
108
+ pass
109
+ if hmm:
110
+ return await e.eor(get_string("antiflood_1"), time=5)
111
+ await e.eor(get_string("antiflood_2"), time=5)
112
+
113
+
114
+ @ultroid_cmd(
115
+ pattern="getflood$",
116
+ admins_only=True,
117
+ )
118
+ async def getflood(e):
119
+ if ok := get_flood_limit(e.chat_id):
120
+ return await e.eor(get_string("antiflood_5").format(ok), time=5)
121
+ await e.eor(get_string("antiflood_2"), time=5)
plugins/asstcmd.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+ from . import get_help
9
+
10
+ __doc__ = get_help("help_asstcmd")
11
+
12
+ import os
13
+
14
+ from pyUltroid.dB.asstcmd_db import add_cmd, cmd_reply, list_cmds, rem_cmd
15
+ from pyUltroid.fns.tools import create_tl_btn, format_btn, get_msg_button
16
+ from telethon import events, utils
17
+
18
+ from . import asst, get_string, mediainfo, udB, ultroid_cmd, upload_file
19
+
20
+
21
+ @ultroid_cmd(pattern="addcmd( (.*)|$)")
22
+ async def ac(e):
23
+ wrd = (e.pattern_match.group(1).strip()).lower()
24
+ wt = await e.get_reply_message()
25
+ if not (wt and wrd):
26
+ return await e.eor(get_string("asstcmd_1"), time=5)
27
+ if "/" in wrd:
28
+ wrd = wrd.replace("/", "")
29
+ btn = format_btn(wt.buttons) if wt.buttons else None
30
+ if wt and wt.media:
31
+ wut = mediainfo(wt.media)
32
+ if wut.startswith(("pic", "gif")):
33
+ dl = await e.client.download_media(wt.media)
34
+ m = upload_file(dl)
35
+ os.remove(dl)
36
+ elif wut == "video":
37
+ if wt.media.document.size > 8 * 1000 * 1000:
38
+ return await e.eor(get_string("com_4"), time=5)
39
+ dl = await e.client.download_media(wt.media)
40
+ m = upload_file(dl)
41
+ os.remove(dl)
42
+ else:
43
+ m = utils.pack_bot_file_id(wt.media)
44
+ if wt.text:
45
+ txt = wt.text
46
+ if not btn:
47
+ txt, btn = get_msg_button(wt.text)
48
+ add_cmd(wrd, txt, m, btn)
49
+ else:
50
+ add_cmd(wrd, None, m, btn)
51
+ else:
52
+ txt = wt.text
53
+ if not btn:
54
+ txt, btn = get_msg_button(wt.text)
55
+ add_cmd(wrd, txt, None, btn)
56
+ asst.add_handler(
57
+ astcmds,
58
+ events.NewMessage(
59
+ func=lambda x: x.text.startswith("/") and x.text[1:] in list(list_cmds())
60
+ ),
61
+ )
62
+ await e.eor(get_string("asstcmd_4").format(wrd))
63
+
64
+
65
+ @ultroid_cmd(pattern="remcmd( (.*)|$)")
66
+ async def rc(e):
67
+ wrd = (e.pattern_match.group(1).strip()).lower()
68
+ if not wrd:
69
+ return await e.eor(get_string("asstcmd_2"), time=5)
70
+ wrd = wrd.replace("/", "")
71
+ rem_cmd(wrd)
72
+ await e.eor(get_string("asstcmd_3").format(wrd))
73
+
74
+
75
+ @ultroid_cmd(pattern="listcmd$")
76
+ async def lscmd(e):
77
+ if list_cmds():
78
+ ok = get_string("asstcmd_6")
79
+ for x in list_cmds():
80
+ ok += f"/{x}" + "\n"
81
+ return await e.eor(ok)
82
+ return await e.eor(get_string("asstcmd_5"))
83
+
84
+
85
+ async def astcmds(e):
86
+ xx = (e.text.replace("/", "")).lower().split()[0]
87
+ if cmd_reply(xx):
88
+ msg, media, bt = cmd_reply(xx)
89
+ if bt:
90
+ bt = create_tl_btn(bt)
91
+ await e.reply(msg, file=media, buttons=bt)
92
+
93
+
94
+ if udB.get_key("ASST_CMDS"):
95
+ asst.add_handler(
96
+ astcmds,
97
+ events.NewMessage(
98
+ func=lambda x: x.text.startswith("/") and x.text[1:] in list(list_cmds())
99
+ ),
100
+ )
plugins/audiotools.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultroid - UserBot
2
+ # Copyright (C) 2021-2025 TeamUltroid
3
+ #
4
+ # This file is a part of < https://github.com/TeamUltroid/Ultroid/ >
5
+ # PLease read the GNU Affero General Public License in
6
+ # <https://www.github.com/TeamUltroid/Ultroid/blob/main/LICENSE/>.
7
+
8
+
9
+ import os
10
+ import time
11
+ from datetime import datetime as dt
12
+
13
+ from pyUltroid.fns.tools import set_attributes
14
+
15
+ from . import (
16
+ LOGS,
17
+ ULTConfig,
18
+ bash,
19
+ downloader,
20
+ eod,
21
+ eor,
22
+ genss,
23
+ get_help,
24
+ get_string,
25
+ humanbytes,
26
+ mediainfo,
27
+ stdr,
28
+ time_formatter,
29
+ ultroid_cmd
30
+ )
31
+
32
+ __doc__ = get_help("help_audiotools")
33
+
34
+
35
+ @ultroid_cmd(pattern="makevoice$")
36
+ async def vnc(e):
37
+ if not e.reply_to:
38
+ return await eod(e, get_string("audiotools_1"))
39
+ r = await e.get_reply_message()
40
+ if not mediainfo(r.media).startswith(("audio", "video")):
41
+ return await eod(e, get_string("spcltool_1"))
42
+ xxx = await e.eor(get_string("com_1"))
43
+ file, _ = await e.client.fast_downloader(
44
+ r.document,
45
+ )
46
+ await xxx.edit(get_string("audiotools_2"))
47
+ await bash(
48
+ f"ffmpeg -i '{file.name}' -map 0:a -codec:a libopus -b:a 100k -vbr on out.opus"
49
+ )
50
+ try:
51
+ await e.client.send_message(
52
+ e.chat_id, file="out.opus", force_document=False, reply_to=r
53
+ )
54
+ except Exception as er:
55
+ LOGS.exception(er)
56
+ return await xxx.edit("`Failed to convert in Voice...`")
57
+ await xxx.delete()
58
+ os.remove(file.name)
59
+ os.remove("out.opus")
60
+
61
+
62
+ @ultroid_cmd(pattern="atrim( (.*)|$)")
63
+ async def trim_aud(e):
64
+ sec = e.pattern_match.group(1).strip()
65
+ if not sec or "-" not in sec:
66
+ return await eod(e, get_string("audiotools_3"))
67
+ a, b = sec.split("-")
68
+ if int(a) >= int(b):
69
+ return await eod(e, get_string("audiotools_4"))
70
+ vido = await e.get_reply_message()
71
+ if vido and vido.media and mediainfo(vido.media).startswith(("video", "audio")):
72
+ if hasattr(vido.media, "document"):
73
+ vfile = vido.media.document
74
+ name = vido.file.name
75
+ else:
76
+ vfile = vido.media
77
+ name = ""
78
+ if not name:
79
+ name = dt.now().isoformat("_", "seconds") + ".mp4"
80
+ xxx = await e.eor(get_string("audiotools_5"))
81
+ c_time = time.time()
82
+ file = await downloader(
83
+ f"resources/downloads/{name}",
84
+ vfile,
85
+ xxx,
86
+ c_time,
87
+ f"Downloading {name}...",
88
+ )
89
+
90
+ o_size = os.path.getsize(file.name)
91
+ d_time = time.time()
92
+ diff = time_formatter((d_time - c_time) * 1000)
93
+ file_name = (file.name).split("/")[-1]
94
+ out = file_name.replace(file_name.split(".")[-1], "_trimmed.aac")
95
+ if int(b) > int(await genss(file.name)):
96
+ os.remove(file.name)
97
+ return await eod(xxx, get_string("audiotools_6"))
98
+ ss, dd = stdr(int(a)), stdr(int(b))
99
+ xxx = await xxx.edit(
100
+ f"Downloaded `{file.name}` of `{humanbytes(o_size)}` in `{diff}`.\n\nNow Trimming Audio from `{ss}` to `{dd}`..."
101
+ )
102
+ cmd = f'ffmpeg -i "{file.name}" -preset ultrafast -ss {ss} -to {dd} -vn -acodec copy "{out}" -y'
103
+ await bash(cmd)
104
+ os.remove(file.name)
105
+ f_time = time.time()
106
+ n_file, _ = await e.client.fast_uploader(
107
+ out, show_progress=True, event=e, message="Uploading...", to_delete=True
108
+ )
109
+ attributes = await set_attributes(out)
110
+
111
+ caption = get_string("audiotools_7").format(ss, dd)
112
+ await e.client.send_file(
113
+ e.chat_id,
114
+ n_file,
115
+ thumb=ULTConfig.thumb,
116
+ caption=caption,
117
+ attributes=attributes,
118
+ force_document=False,
119
+ reply_to=e.reply_to_msg_id,
120
+ )
121
+ await xxx.delete()
122
+ else:
123
+ await e.eor(get_string("audiotools_1"), time=5)
124
+
125
+
126
+ @ultroid_cmd(pattern="extractaudio$")
127
+ async def ex_aud(e):
128
+ reply = await e.get_reply_message()
129
+ if not (reply and reply.media and mediainfo(reply.media).startswith("video")):
130
+ return await e.eor(get_string("audiotools_8"))
131
+ name = reply.file.name or "video.mp4"
132
+ vfile = reply.media.document
133
+ msg = await e.eor(get_string("com_1"))
134
+ c_time = time.time()
135
+ file = await downloader(
136
+ f"resources/downloads/{name}",
137
+ vfile,
138
+ msg,
139
+ c_time,
140
+ f"Downloading {name}...",
141
+ )
142
+
143
+ out_file = f"{file.name}.aac"
144
+ cmd = f"ffmpeg -i {file.name} -vn -acodec copy {out_file}"
145
+ o, err = await bash(cmd)
146
+ os.remove(file.name)
147
+ attributes = await set_attributes(out_file)
148
+
149
+ f_time = time.time()
150
+ try:
151
+ n_file, _ = await e.client.fast_uploader(
152
+ out_file, show_progress=True, event=e, message="Uploading...", to_delete=True
153
+ )
154
+
155
+ except FileNotFoundError:
156
+ return await eor(msg, get_string("audiotools_9"))
157
+ await e.reply(
158
+ get_string("audiotools_10"),
159
+ file=n_file,
160
+ thumb=ULTConfig.thumb,
161
+ attributes=attributes,
162
+ )
163
+ await msg.delete()