mirror of
https://github.com/yasirarism/MissKatyPyro.git
synced 2025-12-29 09:44:50 +00:00
Compare commits
No commits in common. "stable" and "master" have entirely different histories.
153 changed files with 10741 additions and 6760 deletions
|
|
@ -6,8 +6,11 @@ name = "python"
|
|||
[analyzers.meta]
|
||||
runtime_version = "3.x.x"
|
||||
|
||||
[[transformers]]
|
||||
name = "yapf"
|
||||
|
||||
[[transformers]]
|
||||
name = "isort"
|
||||
|
||||
[[transformers]]
|
||||
name = "black"
|
||||
name = "ruff"
|
||||
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
|
|
@ -1,5 +1,4 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: [yasirarism] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
paypal: ['']# Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
QRIS: ['https://yasirpedia.eu.org/images/my-qris.jpg']
|
||||
other: ['https://yasirpedia.eu.org/images/my-qris.jpg', 'https://paypal.me/yasirarism'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
33
.github/workflows/deploy.yml
vendored
33
.github/workflows/deploy.yml
vendored
|
|
@ -1,33 +0,0 @@
|
|||
name: Deploying to heroku
|
||||
|
||||
on: workflow_dispatch
|
||||
|
||||
env:
|
||||
IMAGE_NAME: worker
|
||||
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
|
||||
CONFIG_FILE_URL: ${{ secrets.CONFIG_FILE_URL }}
|
||||
|
||||
jobs:
|
||||
build_and_push:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Build the image
|
||||
run: docker build . --file Dockerfile -t "${IMAGE_NAME}"
|
||||
|
||||
- name: Login into heroku container registry
|
||||
run: heroku container:login
|
||||
|
||||
- name: Create heroku app name
|
||||
run: heroku create --region eu ${{ secrets.HEROKU_APP_NAME }}
|
||||
|
||||
- name: Push the image container to heroku
|
||||
run: heroku container:push "${IMAGE_NAME}" -a ${{ secrets.HEROKU_APP_NAME }}
|
||||
|
||||
- name: Release image to heroku
|
||||
run: heroku container:release "${IMAGE_NAME}" -a ${{ secrets.HEROKU_APP_NAME }}
|
||||
|
||||
- name: Setting up config env
|
||||
run: heroku config:set CONFIG_FILE_URL="${CONFIG_FILE_URL}" HEROKU_API_KEY="${HEROKU_API_KEY}" HEROKU_APP_NAME="${{ secrets.HEROKU_APP_NAME }}" -a ${{ secrets.HEROKU_APP_NAME }}
|
||||
36
.github/workflows/h3r0ku.yml
vendored
Normal file
36
.github/workflows/h3r0ku.yml
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
name: H3r0ku Deployer
|
||||
|
||||
on: workflow_dispatch
|
||||
|
||||
env:
|
||||
IMAGE_NAME: misskatybotnew
|
||||
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
|
||||
HEROKU_APP: ${{ secrets.HEROKU_APP }}
|
||||
|
||||
jobs:
|
||||
build_and_push:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Build the image
|
||||
run: docker build . --file Dockerfile --tag "${IMAGE_NAME}"
|
||||
|
||||
- name: Login into Heroku Container registry
|
||||
run: heroku container:login
|
||||
|
||||
- name: Push the image to Heroku
|
||||
run: heroku container:push "${IMAGE_NAME}" -a "${HEROKU_APP}"
|
||||
|
||||
- name: Release image to Heroku
|
||||
run: heroku container:release "${IMAGE_NAME}" -a "${HEROKU_APP}"
|
||||
|
||||
- name: Check ENV
|
||||
run: |
|
||||
API_KEY="$(heroku config:get HEROKU_API_KEY -a ${HEROKU_APP})"
|
||||
APP_NAME="$(heroku config:get HEROKU_APP -a ${HEROKU_APP})"
|
||||
if [[ -z "${API_KEY}" && -z "${APP_NAME}" ]]; then
|
||||
heroku config:set HEROKU_API_KEY="${HEROKU_API_KEY}" \
|
||||
HEROKU_APP="${HEROKU_APP}"
|
||||
fi
|
||||
8
.github/workflows/notify.yml
vendored
8
.github/workflows/notify.yml
vendored
|
|
@ -19,8 +19,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Notify the commit on Telegram.
|
||||
- name: Notify the commit on Telegram YasirPediaFeed.
|
||||
uses: EverythingSuckz/github-telegram-notify@main
|
||||
with:
|
||||
bot_token: '${{ secrets.BOT_TOKEN }}'
|
||||
chat_id: '${{ secrets.CHAT_ID }}'
|
||||
- name: Notify the commit on Telegram SecretGrup.
|
||||
uses: EverythingSuckz/github-telegram-notify@main
|
||||
with:
|
||||
bot_token: '${{ secrets.BOT_TOKEN }}'
|
||||
chat_id: '-1001777794636'
|
||||
topic_id: '436599'
|
||||
|
|
|
|||
38
Dockerfile
38
Dockerfile
|
|
@ -1,19 +1,19 @@
|
|||
# * @author Yasir Aris M <yasiramunandar@gmail.com>
|
||||
# * @date 2022-12-01 09:12:27
|
||||
# * @projectName MissKatyPyro
|
||||
# * Copyright ©YasirPedia All rights reserved
|
||||
|
||||
# Base Docker Using Alpine 3.18, Python 3.11.4 and Built In Pip
|
||||
## With Built in Pip Package
|
||||
FROM yasirarism/misskaty-docker:alpine
|
||||
## Without Built in Pip Package
|
||||
# FROM yasirarism/misskaty-docker:free
|
||||
|
||||
# Set Hostname
|
||||
ENV HOSTNAME yasir-server
|
||||
# Copy Files
|
||||
COPY . .
|
||||
# Instal pip package
|
||||
# RUN pip3 install --no-cache-dir -r requirements.txt --break-system-packages
|
||||
# Set CMD Bot
|
||||
CMD ["bash", "start.sh"]
|
||||
# * @author Yasir Aris M <yasiramunandar@gmail.com>
|
||||
# * @date 2022-12-01 09:12:27
|
||||
# * @projectName MissKatyPyro
|
||||
# * Copyright ©YasirPedia All rights reserved
|
||||
|
||||
# Base Docker Using Ubuntu 24.04, Python 3.12 and Built In Pip
|
||||
## With Built in Pip Package
|
||||
FROM yasirarism/misskaty-docker:py3.13
|
||||
## Without Built in Pip Package
|
||||
# FROM yasirarism/misskaty-docker:free
|
||||
|
||||
# Set Hostname
|
||||
ENV HOSTNAME=yasir-server
|
||||
# Copy Files
|
||||
COPY . .
|
||||
# Instal pip package if you use free depedencies
|
||||
# RUN pip3 install --no-cache-dir -r requirements.txt
|
||||
# Set CMD Bot
|
||||
CMD ["bash", "start.sh"]
|
||||
|
|
|
|||
1
Procfile
Normal file
1
Procfile
Normal file
|
|
@ -0,0 +1 @@
|
|||
worker: bash start.sh
|
||||
400
README.id.md
400
README.id.md
|
|
@ -1,194 +1,206 @@
|
|||
# MissKatyPyro
|
||||
|
||||
<!--Badges-->
|
||||
![MIT License][license-shield] ![Repository Size][repository-size-shield] ![Issue Closed][issue-closed-shield]
|
||||
|
||||
<!--Project Title Image-->
|
||||
<p align="center">
|
||||
<img src="https://repository-images.githubusercontent.com/433350689/26cb713b-43c3-4dec-94cb-6c80599547e8" width="200" height="200"/>
|
||||
</p>
|
||||
|
||||
<!--Project Buttons-->
|
||||
[![Readme in Indonesian][readme-ko-shield]][readme-ko-url] [![View Demo][view-demo-shield]][view-demo-url] [![Report bug][report-bug-shield]][report-bug-url] [![Request feature][request-feature-shield]][request-feature-url]
|
||||
|
||||
<!--Table of Contents-->
|
||||
# Table of Contents
|
||||
- [[1] About MissKaty](#1-about-misskaty)
|
||||
- [[2] Framework Tools And Server That Used To Build This Bot](#2-framework-tools-and-server-that-used-to-build-this-bot)
|
||||
- [[3] Donation](#3-donation)
|
||||
- [[4] Notes](#4-notes)
|
||||
- [[5] Features](#5-features)
|
||||
- [[6] Variables](#6-variables)
|
||||
- [[7] Deploying Tutorial](#7-deploy-recommended-using-dockerdocker-compose)
|
||||
- [Build And Run Using Legacy Method](#build-and-run-using-legacy-method)
|
||||
- [Build And Run Using Docker](#build-and-run-using-docker)
|
||||
- [Build And Run The Docker Image Using docker-compose](#build-and-run-the-docker-image-using-docker-compose)
|
||||
- [[8] Credits](#8-thanks-to)
|
||||
- [[9] Disclaimer](#8-disclaimer)
|
||||
|
||||
# [1] About MissKaty
|
||||
*MissKaty* adalah Bot Telegram yang dibuat menggunakan Python dan library Pyrogram. Banyak fitur yang berguna untuk kita gunakan. Saya berharap suatu saat jika project ini dihentikan, ada yang melanjutkan atau mengembangkannya lagi. Saya memberi nama MissKaty karena saya suka kucing, hewan lucu yang suka bermain dan bersahabat dengan manusia.
|
||||
|
||||
## [2] Framework Tools And Server That Used To Build This Bot
|
||||
🌱 PyroFork v2.x.x (Fork Pyrogram dengan Dukungan Topik dan Beberapa Patch)<br>
|
||||
🌱 Dukungan Python 3.11<br>
|
||||
🌱 MongoDB sebagai Database<br>
|
||||
🌱 PyKeyboard for Building Pagination<br>
|
||||
🌱 VS Code<br>
|
||||
🌱 VPS/Server With Docker Support (Recommended)<br>
|
||||
|
||||
## [3] Donation
|
||||
*Khusus Indonesia Saja:*<br>
|
||||
🌱 [QRIS][qris-url]<br>
|
||||
|
||||
*Untuk Semua Negara:*<br>
|
||||
🌱 [Paypal][paypal-url]<br>
|
||||
|
||||
## [4] Notes
|
||||
Jika Anda ingin membantu saya memperbaiki beberapa kesalahan di bot saya, Anda dapat membuat PR ke repo ini. Saya sangat senang jika Anda dapat membantu saya. Anda juga dapat memberikan dukungan kepada saya untuk membeli server.
|
||||
|
||||
## [5] Features
|
||||
|
||||
| FEATURE MY BOT |🌱|
|
||||
| ------------- | ------------- |
|
||||
| Basic Admin Feature |✔️|
|
||||
| AFK Feature |✔️|
|
||||
| Downloader FB, TikTok and YT-DLP Support |✔️|
|
||||
| MultiLanguage Support (Still Beta) |⚠️|
|
||||
| NightMode |✔️|
|
||||
| ChatBot based on OpenAI |✔️|
|
||||
| MissKaty Mata |✔️|
|
||||
| Inline Search |✔️|
|
||||
| Sticker Tools |✔️|
|
||||
| PasteBin Tools |✔️|
|
||||
| WebScraper (Pahe, MelongMovie, LK21, Terbit21, Kusonime, etc) |✔️|
|
||||
| IMDB Search With Multi Language Per User |✔️|
|
||||
| GenSS From Media and MediaInfo Generator |✔️|
|
||||
| And Many More.. |✔️|
|
||||
|
||||
## [6] Variables
|
||||
|
||||
### Variabel yang Diperlukan
|
||||
* `BOT_TOKEN`: Buat bot menggunakan [@BotFather](https://t.me/BotFather), dan dapatkan token Telegram API.
|
||||
* `API_ID`: Dapatkan value ini dari [telegram.org](https://my.telegram.org/apps)
|
||||
* `API_HASH`: Dapatkan value ini dari [telegram.org](https://my.telegram.org/apps)
|
||||
* `DATABASE_URI`: [mongoDB](https://www.mongodb.com) URI. Dapatkan value ini dari [mongoDB](https://www.mongodb.com). Untuk bantuan lebih lanjut, tonton [video] ini(https://youtu.be/1G1XwEOnxxo)
|
||||
* `LOG_CHANNEL` : Channel untuk mencatat aktivitas bot. Pastikan bot adalah admin di channel.
|
||||
|
||||
### Variabel Opsional
|
||||
* `USER_SESSION` : String session untuk Userbot.
|
||||
* `DATABASE_NAME`: Nama database di MongoDB
|
||||
* `COMMAND_HANDLER`: Daftar perintah handler bot dipisahkan dengan spasi. Contoh: `. !` > jadi bot akan merespon dengan `.cmd` atau `!cmd`
|
||||
* `SUDO`: User ID yang memiliki akses ke bot, dipisahkan dengan spasi
|
||||
* `OPENAI_API`: Dapatkan dari Web OpenAI
|
||||
* `CURRENCY_API`: Dapatkan API Key di https://app.exchangerate-api.com/sign-up
|
||||
|
||||
## [7] Tutorial Deploy (Recommended using Docker/Docker Compose)
|
||||
|
||||
#### Bangun Dan Jalankan Menggunakan Metode Lama
|
||||
- Pastikan versi python minimum adalah 3.8 untuk mencegah beberapa error. Periksa dengan perintah ini:
|
||||
```
|
||||
python3 --version
|
||||
```
|
||||
- Instal semua dependensi yang membutuhkan bot untuk dijalankan. *(memerlukan akses root, Anda dapat melewati ini jika server Anda tidak memiliki akses root tetapi beberapa plugin tidak berfungsi)*
|
||||
```
|
||||
apt update -y & apt install libjpeg-dev zlib1g-dev libwebp-dev python3-pip python3-lxml git wget curl lokal ffmpeg tzdata neofetch mediainfo speedtest-cli -y
|
||||
```
|
||||
- Instal requirements.txt, jika menggunakan python 3.11, Anda harus menggunakan opsi venv saat menginstal.<br/>
|
||||
*Python < 3.10*
|
||||
```
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
*Python 3.11*
|
||||
```
|
||||
Install venv dari terminal server kamu
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
- Atur config environment saat menjalankan bot dan jangan lupa isi semua value yang wajib di isi.
|
||||
- Jalankan Bot
|
||||
```
|
||||
bash start.sh
|
||||
```
|
||||
|
||||
#### Build And Run Using Docker
|
||||
|
||||
- Mulai daemon Docker (Lewati jika sudah berjalan):
|
||||
```
|
||||
sudo dockerd
|
||||
```
|
||||
- Build Docker image:
|
||||
```
|
||||
sudo docker build . -t misskaty
|
||||
```
|
||||
- Jalankan Docker image:
|
||||
```
|
||||
sudo docker run misskaty
|
||||
```
|
||||
- Untuk Menghentikan image:
|
||||
```
|
||||
sudo docker ps
|
||||
sudo docker stop <pid>
|
||||
```
|
||||
|
||||
#### Build And Run The Docker Image Using docker-compose
|
||||
|
||||
- Install docker-compose
|
||||
```
|
||||
sudo apt install docker-compose
|
||||
```
|
||||
- Build and run Docker image or to view current running image:
|
||||
```
|
||||
sudo docker-compose up
|
||||
```
|
||||
- After editing files with nano for example (nano start.sh):
|
||||
```
|
||||
sudo docker-compose up --build
|
||||
```
|
||||
- To stop the running image:
|
||||
```
|
||||
sudo docker ps
|
||||
```
|
||||
```
|
||||
sudo docker-compose stop <pid>
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
|
||||
## [8] Thanks to
|
||||
- Terima kasih Kepada Allah Swt.
|
||||
- Terima kasih Kepada Dan [Pyrogram Library](https://github.com/pyrogram/pyrogram).
|
||||
- Terima kasih Kepada [The Hamker Cat](https://github.com/TheHamkerCat) Untuk Kode WilliamButcher.
|
||||
- Terima kasih Kepada [Team Yukki](https://github.com/TeamYukki) Untuk Kode AFK Bot.
|
||||
- Terima kasih Kepada [Wrench](https://github.com/EverythingSuckz) Untuk Beberapa Kode.
|
||||
- Terima kasih Kepada [AmanoTeam](https://github.com/AmanoTeam) Untuk Template MultiBahasa.
|
||||
- Dan Semua Orang Yang Membantuku Dalam Hidupku...
|
||||
Jika kode Anda digunakan dalam repo ini dan ingin memberikan kredit, silakan buka masalah..
|
||||
|
||||
## [9] Disclaimer
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.en.html#header)
|
||||
Dilisensikan di bawah [GNU AGPL 2.0.](https://github.com/yasirarism/MissKatyPyro/blob/master/LICENSE)
|
||||
PERINGATAN: Menjual Kode Kepada Orang Lain Demi Uang *Dilarang Keras*. Tuhan selalu melihatmu dimanapun kamu berada.
|
||||
|
||||
<!--Url for Badges-->
|
||||
[license-shield]: https://img.shields.io/github/license/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=04B4AE
|
||||
[repository-size-shield]: https://img.shields.io/github/repo-size/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=BE81F7
|
||||
[issue-closed-shield]: https://img.shields.io/github/issues-closed/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=FE9A2E
|
||||
|
||||
<!--Url for Buttons-->
|
||||
[readme-ko-shield]: https://img.shields.io/badge/-readme%20in%20Indonesian-2E2E2E?style=for-the-badge
|
||||
[view-demo-shield]: https://img.shields.io/badge/-%F0%9F%98%8E%20view%20demo-F3F781?style=for-the-badge
|
||||
[view-demo-url]: https://t.me/MissKatyPyro
|
||||
[report-bug-shield]: https://img.shields.io/badge/-%F0%9F%90%9E%20report%20bug-F5A9A9?style=for-the-badge
|
||||
[report-bug-url]: https://github.com/yasirarism/MissKatyPyro/issues
|
||||
[request-feature-shield]: https://img.shields.io/badge/-%E2%9C%A8%20request%20feature-A9D0F5?style=for-the-badge
|
||||
[request-feature-url]: https://github.com/yasirarism/MissKatyPyro/issues
|
||||
|
||||
<!--URLS-->
|
||||
[readme-ko-url]: README.md
|
||||
[kofi-url]: https://ko-fi.com/yasirarism
|
||||
[paypal-url]: https://paypal.me/yasirarism
|
||||
[qris-url]: https://telegra.ph/file/2acf7698f300ef3d9138f.jpg
|
||||
[sociabuzz-url]: https://sociabuzz.com/yasirarism/tribe
|
||||
[saweria-url]: https://saweria.co/yasirarism
|
||||
[trakteer-url]: https://trakteer.id/yasir-aris-sp7cn
|
||||
# MissKatyPyro
|
||||
```diff
|
||||
- Saya tidak akan memberikan dukungan apapun terhadap fork repo ini, jadi apapun yang terjadi adalah tanggungjawabmu. Jangan menghubungi saya karena kesalahanmu sendiri. Saya berhenti melalukan update repo ini, hanya beberapa perbaikan kecil yang akan saya lakukan.
|
||||
```
|
||||
|
||||
<!--Badges-->
|
||||
![MIT License][license-shield] ![Repository Size][repository-size-shield] ![Issue Closed][issue-closed-shield]
|
||||
|
||||
<!--Project Title Image-->
|
||||
<p align="center">
|
||||
<img src="https://repository-images.githubusercontent.com/433350689/26cb713b-43c3-4dec-94cb-6c80599547e8" width="200" height="200"/>
|
||||
</p>
|
||||
|
||||
<!--Project Buttons-->
|
||||
[![Readme in Indonesian][readme-ko-shield]][readme-ko-url] [![View Demo][view-demo-shield]][view-demo-url] [![Report bug][report-bug-shield]][report-bug-url] <!-- [![Request feature][request-feature-shield]][request-feature-url] -->
|
||||
|
||||
<!--Table of Contents-->
|
||||
# Table of Contents
|
||||
- [[1] Tentang MissKaty](#1-about-misskaty)
|
||||
- [[2] Alat Kerangka Dan Server Yang Digunakan Untuk Membangun Bot Ini](#2-framework-tools-and-server-that-used-to-build-this-bot)
|
||||
- [[3] Donation](#3-donation)
|
||||
- [[4] Notes](#4-notes)
|
||||
- [[5] Features](#5-features)
|
||||
- [[6] Variables](#6-variables)
|
||||
- [[7] Deploying Tutorial](#7-deploy-recommended-using-dockerdocker-compose)
|
||||
- [Build And Run Using Legacy Method](#build-and-run-using-legacy-method)
|
||||
- [Build And Run Using Docker](#build-and-run-using-docker)
|
||||
- [Build And Run The Docker Image Using docker-compose](#build-and-run-the-docker-image-using-docker-compose)
|
||||
- [[8] Credits](#8-thanks-to)
|
||||
- [[9] Disclaimer](#8-disclaimer)
|
||||
|
||||
# [1] Tentang MissKaty
|
||||
*MissKaty* adalah Bot Telegram yang dibuat menggunakan Python dan library Pyrogram. Banyak fitur yang berguna untuk kita gunakan. Saya berharap suatu saat jika project ini dihentikan, ada yang melanjutkan atau mengembangkannya lagi. Saya memberi nama MissKaty karena saya suka kucing, hewan lucu yang suka bermain dan bersahabat dengan manusia.
|
||||
|
||||
## [2] Alat Kerangka Dan Server Yang Digunakan Untuk Membangun Bot Ini
|
||||
🌱 PyroFork v2.x.x (Fork Pyrogram dengan Dukungan Topik, Stories dan Beberapa Patch)<br>
|
||||
🌱 Dukungan Python 3.11<br>
|
||||
🌱 MongoDB sebagai Database<br>
|
||||
🌱 PyKeyboard for Building Pagination<br>
|
||||
🌱 VS Code<br>
|
||||
🌱 VPS/Server dengan Root dan Docker Support (Recommended)<br>
|
||||
|
||||
## [3] Donation
|
||||
*Khusus Indonesia Saja:*<br>
|
||||
🌱 [QRIS][qris-url]<br>
|
||||
🌱 [Mayar ID][mayar]<br>
|
||||
|
||||
*Untuk Semua Negara:*<br>
|
||||
🌱 [Paypal][paypal-url]<br>
|
||||
|
||||
## [4] Notes
|
||||
Jika Anda ingin membantu saya memperbaiki beberapa kesalahan di bot saya, Anda dapat membuat PR ke repo ini. Saya sangat senang jika Anda dapat membantu saya. Anda juga dapat memberikan dukungan kepada saya untuk membeli server.
|
||||
|
||||
## [5] Features
|
||||
|
||||
| FEATURE MY BOT |🌱|
|
||||
| ------------- | ------------- |
|
||||
| Basic Admin Feature |✔️|
|
||||
| AFK Feature |✔️|
|
||||
| Downloader FB, TikTok and YT-DLP Support |✔️|
|
||||
| MultiLanguage Support (Still Beta) |⚠️|
|
||||
| NightMode |✔️|
|
||||
| ChatBot based on OpenAI, and Google Bard |✔️|
|
||||
| MissKaty Mata |✔️|
|
||||
| Inline Search |✔️|
|
||||
| Sticker Tools |✔️|
|
||||
| PasteBin Tools |✔️|
|
||||
| WebScraper (Pahe, MelongMovie, LK21, Terbit21, Kusonime, etc) |✔️|
|
||||
| IMDB Search With Multi Language Per User |✔️|
|
||||
| GenSS From Media and MediaInfo Generator |✔️|
|
||||
| And Many More.. |✔️|
|
||||
|
||||
## [6] Variables
|
||||
|
||||
### Variabel yang Diperlukan
|
||||
* `BOT_TOKEN`: Buat bot menggunakan [@BotFather](https://t.me/BotFather), dan dapatkan token Telegram API.
|
||||
* `API_ID`: Dapatkan value ini dari [telegram.org](https://my.telegram.org/apps)
|
||||
* `API_HASH`: Dapatkan value ini dari [telegram.org](https://my.telegram.org/apps)
|
||||
* `DATABASE_URI`: [mongoDB](https://www.mongodb.com) URI. Dapatkan value ini dari [mongoDB](https://www.mongodb.com).
|
||||
* `LOG_CHANNEL` : Channel untuk mencatat aktivitas bot. Pastikan bot adalah admin di channel.
|
||||
|
||||
### Variabel Opsional
|
||||
* `YT_COOKIES` : Dapatkan cookies Youtube menggunakan https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc?pli=1 dan simpan isi file di github gist. Salin raw url dan isi di vars ini.
|
||||
* `USER_SESSION` : String session untuk Userbot.
|
||||
* `DATABASE_NAME`: Nama database di MongoDB
|
||||
* `PAYDISINI_KEY`: Api Key PayDisini
|
||||
* `PAYDISINI_CHANNEL_ID`: Channel ID QRIS paydisini
|
||||
* `COMMAND_HANDLER`: Daftar perintah handler bot dipisahkan dengan spasi. Contoh: `. !` > jadi bot akan merespon dengan `.cmd` atau `!cmd`
|
||||
* `SUDO`: User ID yang memiliki akses ke bot, dipisahkan dengan spasi
|
||||
* `OPENAI_API`: Dapatkan dari Web OpenAI (Deprecated Temporary)
|
||||
* `GOOGLE_API`: Pelajari dari ini https://github.com/dsdanielpark/Bard-API untuk mendapatkan cookies dan set sebagai api key.
|
||||
* `CURRENCY_API`: Dapatkan API Key di https://app.exchangerate-api.com/sign-up
|
||||
|
||||
## [7] Tutorial Deploy (Recommended using Docker/Docker Compose)
|
||||
|
||||
#### Bangun Dan Jalankan Menggunakan Metode Lama
|
||||
- Pastikan versi python minimum adalah 3.8 dan maksimal python 3.11 untuk mencegah beberapa error. Periksa dengan perintah ini:
|
||||
```
|
||||
python3 --version
|
||||
```
|
||||
- Instal semua dependensi yang membutuhkan bot untuk dijalankan. *(memerlukan akses root, Anda dapat melewati ini jika server Anda tidak memiliki akses root tetapi beberapa plugin tidak berfungsi)*
|
||||
```
|
||||
apt update -y & apt install libjpeg-dev zlib1g-dev libwebp-dev python3-pip python3-lxml git wget curl lokal ffmpeg tzdata neofetch mediainfo speedtest-cli -y
|
||||
```
|
||||
- Instal requirements.txt, jika menggunakan python 3.11, Anda harus menggunakan opsi venv saat menginstal.<br/>
|
||||
*Python < 3.10*
|
||||
```
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
*Python 3.11*
|
||||
```
|
||||
python3 -m venv nama_venv
|
||||
source nama_venv/bin/activate
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
- Atur config environment saat menjalankan bot dan jangan lupa isi semua value yang wajib di isi.
|
||||
- Jalankan Bot
|
||||
```
|
||||
bash start.sh
|
||||
```
|
||||
|
||||
#### Build And Run Using Docker
|
||||
|
||||
- Mulai daemon Docker (Lewati jika sudah berjalan):
|
||||
```
|
||||
sudo dockerd
|
||||
```
|
||||
- Build Docker image:
|
||||
```
|
||||
sudo docker build . -t misskaty
|
||||
```
|
||||
- Jalankan Docker image:
|
||||
```
|
||||
sudo docker run misskaty
|
||||
```
|
||||
- Untuk Menghentikan image:
|
||||
```
|
||||
sudo docker ps
|
||||
sudo docker stop <pid>
|
||||
```
|
||||
|
||||
#### Build And Run The Docker Image Using docker-compose
|
||||
|
||||
- Install docker-compose
|
||||
```
|
||||
sudo apt install docker-compose
|
||||
```
|
||||
- Build and run Docker image or to view current running image:
|
||||
```
|
||||
sudo docker-compose up
|
||||
```
|
||||
- After editing files with nano for example (nano start.sh):
|
||||
```
|
||||
sudo docker-compose up --build
|
||||
```
|
||||
- To stop the running image:
|
||||
```
|
||||
sudo docker ps
|
||||
```
|
||||
```
|
||||
sudo docker-compose stop <pid>
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
|
||||
## [8] Thanks to
|
||||
- Terimakasih Kepada Allah Swt.
|
||||
- Terimakasih Kepada Dan [Pyrogram Library](https://github.com/pyrogram/pyrogram) sebagai base pyrofork.
|
||||
- Terimakasih kepada Mayuri [Mayuri-Chan](https://github.com/Mayuri-Chan) sebagai pemilik library Pyrofork.
|
||||
- Terimakasih kepada TeamDriveCok dan Secret Group TBK di Telegram.
|
||||
- Terimakasih Kepada [The Hamker Cat](https://github.com/TheHamkerCat) Untuk Kode WilliamButcher.
|
||||
- Terimakasih Kepada [Team Yukki](https://github.com/TeamYukki) Untuk Kode AFK Bot.
|
||||
- Terimakasih Kepada [Wrench](https://github.com/EverythingSuckz) Untuk Beberapa Kode.
|
||||
- Terimakasih Kepada [AmanoTeam](https://github.com/AmanoTeam) Untuk Template MultiBahasa.
|
||||
- Dan Semua Orang Yang Membantuku Dalam Hidupku...
|
||||
Jika kode Anda digunakan dalam repo ini dan ingin memberikan kredit, silakan buka masalah..
|
||||
|
||||
## [9] Disclaimer
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.en.html#header)
|
||||
Dilisensikan di bawah [GNU AGPL 2.0.](https://github.com/yasirarism/MissKatyPyro/blob/master/LICENSE)
|
||||
PERINGATAN: *Dilarang Keras* Menjual Kode Kepada Orang Lain Demi Uang Tanpa Seijin Saya. Atau saya akan menghentikan project ini selamanyaa....
|
||||
|
||||
<!--Url for Badges-->
|
||||
[license-shield]: https://img.shields.io/github/license/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=04B4AE
|
||||
[repository-size-shield]: https://img.shields.io/github/repo-size/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=BE81F7
|
||||
[issue-closed-shield]: https://img.shields.io/github/issues-closed/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=FE9A2E
|
||||
|
||||
<!--Url for Buttons-->
|
||||
[readme-ko-shield]: https://img.shields.io/badge/-readme%20in%20Indonesian-2E2E2E?style=for-the-badge
|
||||
[view-demo-shield]: https://img.shields.io/badge/-%F0%9F%98%8E%20view%20demo-F3F781?style=for-the-badge
|
||||
[view-demo-url]: https://t.me/MissKatyPyro
|
||||
[report-bug-shield]: https://img.shields.io/badge/-%F0%9F%90%9E%20report%20bug-F5A9A9?style=for-the-badge
|
||||
[report-bug-url]: https://github.com/yasirarism/MissKatyPyro/issues
|
||||
[request-feature-shield]: https://img.shields.io/badge/-%E2%9C%A8%20request%20feature-A9D0F5?style=for-the-badge
|
||||
[request-feature-url]: https://github.com/yasirarism/MissKatyPyro/issues
|
||||
|
||||
<!--URLS-->
|
||||
[readme-ko-url]: README.md
|
||||
[kofi-url]: https://ko-fi.com/yasirarism
|
||||
[paypal-url]: https://paypal.me/yasirarism
|
||||
[qris-url]: https://img.yasirweb.eu.org/file/2acf7698f300ef3d9138f.jpg
|
||||
[mayar]: https://yasirarism.mayar.link/payme
|
||||
[sociabuzz-url]: https://sociabuzz.com/yasirarism/tribe
|
||||
[saweria-url]: https://saweria.co/yasirarism
|
||||
[trakteer-url]: https://trakteer.id/yasir-aris-sp7cn
|
||||
|
|
|
|||
399
README.md
399
README.md
|
|
@ -1,194 +1,205 @@
|
|||
# MissKatyPyro
|
||||
|
||||
<!--Badges-->
|
||||
![MIT License][license-shield] ![Repository Size][repository-size-shield] ![Issue Closed][issue-closed-shield]
|
||||
|
||||
<!--Project Title Image-->
|
||||
<p align="center">
|
||||
<img src="https://repository-images.githubusercontent.com/433350689/26cb713b-43c3-4dec-94cb-6c80599547e8" width="200" height="200"/>
|
||||
</p>
|
||||
|
||||
<!--Project Buttons-->
|
||||
[![Readme in Indonesian][readme-ko-shield]][readme-ko-url] [![View Demo][view-demo-shield]][view-demo-url] [![Report bug][report-bug-shield]][report-bug-url] [![Request feature][request-feature-shield]][request-feature-url]
|
||||
|
||||
<!--Table of Contents-->
|
||||
# Table of Contents
|
||||
- [[1] About MissKaty](#1-about-misskaty)
|
||||
- [[2] Framework Tools And Server That Used To Build This Bot](#2-framework-tools-and-server-that-used-to-build-this-bot)
|
||||
- [[3] Donation](#3-donation)
|
||||
- [[4] Notes](#4-notes)
|
||||
- [[5] Features](#5-features)
|
||||
- [[6] Variables](#6-variables)
|
||||
- [[7] Deploying Tutorial](#7-deploy-recommended-using-dockerdocker-compose)
|
||||
- [Build And Run Using Legacy Method](#build-and-run-using-legacy-method)
|
||||
- [Build And Run Using Docker](#build-and-run-using-docker)
|
||||
- [Build And Run The Docker Image Using docker-compose](#build-and-run-the-docker-image-using-docker-compose)
|
||||
- [[8] Credits](#8-thanks-to)
|
||||
- [[9] Disclaimer](#8-disclaimer)
|
||||
|
||||
# [1] About MissKaty
|
||||
*MissKaty* is a Telegram Bot built using Python and the Pyrogram library. Many useful features for us to use. I hope that one day this project will be discontinued, someone will continue or develop it again. I gave the name MissKaty because I like cats, a cute animal that likes to be played with and friendly with humans.
|
||||
|
||||
## [2] Framework Tools And Server That Used To Build This Bot
|
||||
🌱 PyroFork v2.x.x (Fork of Pyrogram with Topics Support and Some Patch)<br>
|
||||
🌱 Python 3.11 Support<br>
|
||||
🌱 MongoDB as Database<br>
|
||||
🌱 PyKeyboard for Building Pagination<br>
|
||||
🌱 VS Code<br>
|
||||
🌱 VPS/Server With Docker Support (Recommended)<br>
|
||||
|
||||
## [3] Donation and Support
|
||||
*For Indonesian Only and some supported country:*<br>
|
||||
🌱 [QRIS][qris-url]<br>
|
||||
|
||||
*For International Payment:*<br>
|
||||
🌱 [Paypal][paypal-url]<br>
|
||||
|
||||
## [4] Notes
|
||||
If you want help me fixing some error in my bot, you can make pull request to this repo. I'm very glad if you can help me. You can also give support to me for buying server.
|
||||
|
||||
## [5] Features
|
||||
|
||||
| FEATURE MY BOT |🌱|
|
||||
| ------------- | ------------- |
|
||||
| Basic Admin Feature |✔️|
|
||||
| AFK Feature |✔️|
|
||||
| Downloader FB, TikTok and YT-DLP Support |✔️|
|
||||
| MultiLanguage Support (Unfinished) |⚠️|
|
||||
| NightMode |✔️|
|
||||
| ChatBot based on OpenAI |✔️|
|
||||
| MissKaty Mata |✔️|
|
||||
| Inline Search |✔️|
|
||||
| Sticker Tools |✔️|
|
||||
| PasteBin Tools |✔️|
|
||||
| WebScraper (Pahe, MelongMovie, LK21, Terbit21, Kusonime, etc) |✔️|
|
||||
| IMDB Search With Multi Language Per User |✔️|
|
||||
| GenSS From Media and MediaInfo Generator |✔️|
|
||||
| And Many More.. |✔️|
|
||||
|
||||
## [6] Variables
|
||||
|
||||
### Required Variables
|
||||
* `BOT_TOKEN`: Create a bot using [@BotFather](https://t.me/BotFather), and get the Telegram API token.
|
||||
* `API_ID`: Get this value from [telegram.org](https://my.telegram.org/apps)
|
||||
* `API_HASH`: Get this value from [telegram.org](https://my.telegram.org/apps)
|
||||
* `DATABASE_URI`: [mongoDB](https://www.mongodb.com) URI. Get this value from [mongoDB](https://www.mongodb.com). For more help watch this [video](https://youtu.be/1G1XwEOnxxo)
|
||||
* `LOG_CHANNEL` : A channel to log the activities of bot. Make sure bot is an admin in the channel.
|
||||
|
||||
### Optional Variables
|
||||
* `USER_SESSION` : Session string for Userbot.
|
||||
* `DATABASE_NAME`: Name of the database in MongoDB
|
||||
* `COMMAND_HANDLER`: List of handler bot command splitted by space. Ex: `. !` > so bot will respond with `.cmd` or `!cmd`
|
||||
* `SUDO`: User ID that have access to bot, split by space
|
||||
* `OPENAI_API`: Get it from OpenAI Web
|
||||
* `CURRENCY_API`: Get API Key from https://app.exchangerate-api.com/sign-up
|
||||
|
||||
## [7] Tutorial Deploy (Recommended using Docker/Docker Compose)
|
||||
|
||||
#### Build And Run Using Legacy Method
|
||||
- Make sure minimum python version is 3.8 to prevent some errors. Check it with this command:
|
||||
```
|
||||
python3 --version
|
||||
```
|
||||
- Install all dependency that needed bot to run. *(need root access, you can skip this if your server didn't have root access but some plugins will not work)*
|
||||
```
|
||||
apt update -y & apt install libjpeg-dev zlib1g-dev libwebp-dev python3-pip python3-lxml git wget curl ffmpeg locales tzdata neofetch mediainfo speedtest-cli -y
|
||||
```
|
||||
- Install requirements.txt, if using python 3.11, you need use venv when install pip package.<br/>
|
||||
*Python < 3.10*
|
||||
```
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
*Python 3.11*
|
||||
```
|
||||
Install venv from your terminal and activate it
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
- Setting your config.env or via environment and dont forget fill all required value.
|
||||
- Run Bot
|
||||
```
|
||||
bash start.sh
|
||||
```
|
||||
|
||||
#### Build And Run Using Docker
|
||||
|
||||
- Start Docker daemon (Skip if already running):
|
||||
```
|
||||
sudo dockerd
|
||||
```
|
||||
- Build Docker image:
|
||||
```
|
||||
sudo docker build . -t misskaty
|
||||
```
|
||||
- Run the image:
|
||||
```
|
||||
sudo docker run misskaty
|
||||
```
|
||||
- To stop the image:
|
||||
```
|
||||
sudo docker ps
|
||||
sudo docker stop <pid>
|
||||
```
|
||||
|
||||
#### Build And Run The Docker Image Using docker-compose
|
||||
|
||||
- Install docker-compose
|
||||
```
|
||||
sudo apt install docker-compose
|
||||
```
|
||||
- Build and run Docker image or to view current running image:
|
||||
```
|
||||
sudo docker-compose up
|
||||
```
|
||||
- After editing files with nano for example (nano start.sh):
|
||||
```
|
||||
sudo docker-compose up --build
|
||||
```
|
||||
- To stop the running image:
|
||||
```
|
||||
sudo docker ps
|
||||
```
|
||||
```
|
||||
sudo docker-compose stop <pid>
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
|
||||
## [8] Thanks to
|
||||
- Thanks To Allah Swt.
|
||||
- Thanks To Dan For His Awesome [Library](https://github.com/pyrogram/pyrogram).
|
||||
- Thanks To [The Hamker Cat](https://github.com/TheHamkerCat) For WilliamButcher Code.
|
||||
- Thanks To [Team Yukki](https://github.com/TeamYukki) For AFK Bot Code.
|
||||
- Thanks To [Wrench](https://github.com/EverythingSuckz) For Some Code.
|
||||
- Thanks To [AmanoTeam](https://github.com/AmanoTeam) For MultiLanguage Template.
|
||||
- And All People Who Help Me In My Life...
|
||||
If your code used in this repo and want to give credit please open issue..
|
||||
|
||||
## [9] Disclaimer
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.en.html#header)
|
||||
Licensed under [GNU AGPL 2.0.](https://github.com/yasirarism/MissKatyPyro/blob/master/LICENSE)
|
||||
WARNING: Selling The Codes To Other People For Money Is *Strictly Prohibited*. God always sees you.
|
||||
|
||||
<!--Url for Badges-->
|
||||
[license-shield]: https://img.shields.io/github/license/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=04B4AE
|
||||
[repository-size-shield]: https://img.shields.io/github/repo-size/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=BE81F7
|
||||
[issue-closed-shield]: https://img.shields.io/github/issues-closed/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=FE9A2E
|
||||
|
||||
<!--Url for Buttons-->
|
||||
[readme-ko-shield]: https://img.shields.io/badge/-readme%20in%20Indonesian-2E2E2E?style=for-the-badge
|
||||
[view-demo-shield]: https://img.shields.io/badge/-%F0%9F%98%8E%20view%20demo-F3F781?style=for-the-badge
|
||||
[view-demo-url]: https://t.me/MissKatyPyro
|
||||
[report-bug-shield]: https://img.shields.io/badge/-%F0%9F%90%9E%20report%20bug-F5A9A9?style=for-the-badge
|
||||
[report-bug-url]: https://github.com/yasirarism/MissKatyPyro/issues
|
||||
[request-feature-shield]: https://img.shields.io/badge/-%E2%9C%A8%20request%20feature-A9D0F5?style=for-the-badge
|
||||
[request-feature-url]: https://github.com/yasirarism/MissKatyPyro/issues
|
||||
|
||||
<!--URLS-->
|
||||
[readme-ko-url]: README.id.md
|
||||
[kofi-url]: https://ko-fi.com/yasirarism
|
||||
[paypal-url]: https://paypal.me/yasirarism
|
||||
[qris-url]: https://telegra.ph/file/9427d61d6968b8ee4fb2f.jpg
|
||||
[sociabuzz-url]: https://sociabuzz.com/yasirarism/tribe
|
||||
[saweria-url]: https://saweria.co/yasirarism
|
||||
[trakteer-url]: https://trakteer.id/yasir-aris-sp7cn
|
||||
# MissKatyPyro
|
||||
```diff
|
||||
- I will not give any support to your fork, so try learn by yourself!! Don't contact me because of your fault. I will stop update to this repo, and i will only give minor bugfix to this repo.
|
||||
```
|
||||
|
||||
<!--Badges-->
|
||||
![MIT License][license-shield] ![Repository Size][repository-size-shield] ![Issue Closed][issue-closed-shield]
|
||||
|
||||
<!--Project Title Image-->
|
||||
<p align="center">
|
||||
<img src="https://repository-images.githubusercontent.com/433350689/26cb713b-43c3-4dec-94cb-6c80599547e8" width="200" height="200"/>
|
||||
</p>
|
||||
|
||||
<!--Project Buttons-->
|
||||
[![Readme in Indonesian][readme-ko-shield]][readme-ko-url] [![View Demo][view-demo-shield]][view-demo-url] [![Report bug][report-bug-shield]][report-bug-url] <!-- [![Request feature][request-feature-shield]][request-feature-url] -->
|
||||
|
||||
<!--Table of Contents-->
|
||||
# Table of Contents
|
||||
- [[1] About MissKaty](#1-about-misskaty)
|
||||
- [[2] Framework Tools And Server That Used To Build This Bot](#2-framework-tools-and-server-that-used-to-build-this-bot)
|
||||
- [[3] Support Creator](#3-donation)
|
||||
- [[4] Notes](#4-notes)
|
||||
- [[5] Features](#5-features)
|
||||
- [[6] Variables](#6-variables)
|
||||
- [[7] Deploying Tutorial](#7-deploy-recommended-using-dockerdocker-compose)
|
||||
- [Build And Run Using Legacy Method](#build-and-run-using-legacy-method)
|
||||
- [Build And Run Using Docker](#build-and-run-using-docker)
|
||||
- [Build And Run The Docker Image Using docker-compose](#build-and-run-the-docker-image-using-docker-compose)
|
||||
- [[8] Credits](#8-thanks-to)
|
||||
- [[9] Disclaimer](#8-disclaimer)
|
||||
|
||||
# [1] About MissKaty
|
||||
*MissKaty* is a Telegram Bot built using Python and the Pyrogram library. Many useful features for us to use. I hope that one day this project will be discontinued, someone will continue or develop it again. I gave the name MissKaty because I like cats, a cute animal that likes to be played with and friendly with humans.
|
||||
|
||||
## [2] Framework Tools And Server That Used To Build This Bot
|
||||
🌱 PyroFork v2.x.x (Fork of Pyrogram with Topics, Stories Support and Some Patch)<br>
|
||||
🌱 Python 3.12 Support<br>
|
||||
🌱 MongoDB as Database<br>
|
||||
🌱 PyKeyboard for Building Pagination<br>
|
||||
🌱 VS Code<br>
|
||||
🌱 VPS/Server With Root and Docker Support (Recommended)<br>
|
||||
|
||||
## [3] Donation and Support
|
||||
*For Indonesian Only and some supported country:*<br>
|
||||
🌱 [QRIS][qris-url]<br>
|
||||
|
||||
*For International Payment:*<br>
|
||||
🌱 [Paypal][paypal-url]<br>
|
||||
|
||||
## [4] Notes
|
||||
If you want help me fixing some error in my bot, you can make pull request to this repo. I'm very glad if you can help me. You can also give support to me for buying server.
|
||||
|
||||
## [5] Features
|
||||
|
||||
| FEATURE MY BOT |🌱|
|
||||
| ------------- | ------------- |
|
||||
| Basic Admin Feature |✔️|
|
||||
| AFK Feature |✔️|
|
||||
| Downloader FB, TikTok and YT-DLP Support |✔️|
|
||||
| MultiLanguage Support (Unfinished) |⚠️|
|
||||
| NightMode |✔️|
|
||||
| ChatBot based on OpenAI and Google Bard |✔️|
|
||||
| MissKaty Mata |✔️|
|
||||
| Inline Search |✔️|
|
||||
| Sticker Tools |✔️|
|
||||
| PasteBin Tools |✔️|
|
||||
| WebScraper (Pahe, MelongMovie, LK21, Terbit21, Kusonime, etc) |✔️|
|
||||
| IMDB Search With Multi Language Per User |✔️|
|
||||
| GenSS From Media and MediaInfo Generator |✔️|
|
||||
| And Many More.. |✔️|
|
||||
|
||||
## [6] Variables
|
||||
|
||||
### Required Variables
|
||||
* `BOT_TOKEN`: Create a bot using [@BotFather](https://t.me/BotFather), and get the Telegram API token.
|
||||
* `API_ID`: Get this value from [telegram.org](https://my.telegram.org/apps)
|
||||
* `API_HASH`: Get this value from [telegram.org](https://my.telegram.org/apps)
|
||||
* `DATABASE_URI`: [mongoDB](https://www.mongodb.com) URI. Get this value from [mongoDB](https://www.mongodb.com).
|
||||
* `LOG_CHANNEL` : A channel to log the activities of bot. Make sure bot is an admin in the channel.
|
||||
|
||||
### Optional Variables
|
||||
* `YT_COOKIES` : Get YT cookies using https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc?pli=1 and save cookies value on github gist. Copy raw url and fill in this vars.
|
||||
* `USER_SESSION` : Session string for Userbot.
|
||||
* `DATABASE_NAME`: Name of the database in MongoDB
|
||||
* `PAYDISINI_KEY`: Api Key PayDisini
|
||||
* `PAYDISINI_CHANNEL_ID`: Channel ID QRIS paydisini
|
||||
* `COMMAND_HANDLER`: List of handler bot command splitted by space. Ex: `. !` > so bot will respond with `.cmd` or `!cmd`
|
||||
* `SUDO`: User ID that have access to bot, split by space
|
||||
* `OPENAI_API`: Create personal access token from github, and set as this env. Make sure you have access to Github Model.
|
||||
* `GOOGLEAI_KEY`: Learn how to get api key from this https://ai.google.dev/tutorials/python_quickstart?hl=en.
|
||||
* `CURRENCY_API`: Get API Key from https://app.exchangerate-api.com/sign-up
|
||||
|
||||
## [7] Tutorial Deploy (Recommended using Docker/Docker Compose)
|
||||
|
||||
#### Build And Run Using Legacy Method
|
||||
- Make sure minimum python version is 3.8 and max python 3.12 to prevent some errors. Check it with this command:
|
||||
```
|
||||
python3 --version
|
||||
```
|
||||
- Install all dependency that needed bot to run. *(need root access, you can skip this if your server didn't have root access but some plugins will not work)*
|
||||
```
|
||||
apt update -y & apt install libjpeg-dev zlib1g-dev libwebp-dev python3-pip python3-lxml git wget curl ffmpeg locales tzdata neofetch mediainfo speedtest-cli -y
|
||||
```
|
||||
- Install requirements.txt, if using python => 3.11, you need use venv when install pip package.<br/>
|
||||
*Python < 3.10*
|
||||
```
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
*Python => 3.11*
|
||||
```
|
||||
python3 -m venv nama_venv
|
||||
source nama_venv/bin/activate
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
- Setting your config.env or via environment and dont forget fill all required value.
|
||||
- Run Bot
|
||||
```
|
||||
bash start.sh
|
||||
```
|
||||
|
||||
#### Build And Run Using Docker
|
||||
|
||||
- Start Docker daemon (Skip if already running):
|
||||
```
|
||||
sudo dockerd
|
||||
```
|
||||
- Build Docker image:
|
||||
```
|
||||
sudo docker build . -t misskaty
|
||||
```
|
||||
- Run the image:
|
||||
```
|
||||
sudo docker run misskaty
|
||||
```
|
||||
- To stop the image:
|
||||
```
|
||||
sudo docker ps
|
||||
sudo docker stop <pid>
|
||||
```
|
||||
|
||||
#### Build And Run The Docker Image Using docker-compose
|
||||
|
||||
- Install docker-compose
|
||||
```
|
||||
sudo apt install docker-compose
|
||||
```
|
||||
- Build and run Docker image or to view current running image:
|
||||
```
|
||||
sudo docker-compose up
|
||||
```
|
||||
- After editing files with nano for example (nano start.sh):
|
||||
```
|
||||
sudo docker-compose up --build
|
||||
```
|
||||
- To stop the running image:
|
||||
```
|
||||
sudo docker ps
|
||||
```
|
||||
```
|
||||
sudo docker-compose stop <pid>
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
|
||||
## [8] Thanks to
|
||||
- Thanks To Allah Swt.
|
||||
- Thanks To Dan For [Pyrogram Library](https://github.com/pyrogram/pyrogram) as founder of pyrogram.
|
||||
- Thanks To Mayuri For [Pyrofork Library](https://github.com/Mayuri-Chan) as owner of pyrofork library.
|
||||
- Thanks To TeamDrivecok and SecretGroup TBK in Telegram.
|
||||
- Thanks To [The Hamker Cat](https://github.com/TheHamkerCat) For WilliamButcher Code.
|
||||
- Thanks To [Team Yukki](https://github.com/TeamYukki) For AFK Bot Code.
|
||||
- Thanks To [Wrench](https://github.com/EverythingSuckz) For Some Code.
|
||||
- Thanks To [AmanoTeam](https://github.com/AmanoTeam) For MultiLanguage Template.
|
||||
- And All People Who Help Me In My Life...
|
||||
If your code used in this repo and want to give credit please open issue..
|
||||
|
||||
## [9] Disclaimer
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.en.html#header)
|
||||
Licensed under [GNU AGPL 2.0.](https://github.com/yasirarism/MissKatyPyro/blob/master/LICENSE)
|
||||
WARNING: Selling The Codes To Other People For Money Is *Strictly Prohibited*. Or i will stop this project forever.
|
||||
|
||||
<!--Url for Badges-->
|
||||
[license-shield]: https://img.shields.io/github/license/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=04B4AE
|
||||
[repository-size-shield]: https://img.shields.io/github/repo-size/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=BE81F7
|
||||
[issue-closed-shield]: https://img.shields.io/github/issues-closed/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=FE9A2E
|
||||
|
||||
<!--Url for Buttons-->
|
||||
[readme-ko-shield]: https://img.shields.io/badge/-readme%20in%20Indonesian-2E2E2E?style=for-the-badge
|
||||
[view-demo-shield]: https://img.shields.io/badge/-%F0%9F%98%8E%20view%20demo-F3F781?style=for-the-badge
|
||||
[view-demo-url]: https://t.me/MissKatyBot
|
||||
[report-bug-shield]: https://img.shields.io/badge/-%F0%9F%90%9E%20report%20bug-F5A9A9?style=for-the-badge
|
||||
[report-bug-url]: https://github.com/yasirarism/MissKatyPyro/issues
|
||||
[request-feature-shield]: https://img.shields.io/badge/-%E2%9C%A8%20request%20feature-A9D0F5?style=for-the-badge
|
||||
[request-feature-url]: https://github.com/yasirarism/MissKatyPyro/issues
|
||||
|
||||
<!--URLS-->
|
||||
[readme-ko-url]: README.id.md
|
||||
[kofi-url]: https://ko-fi.com/yasirarism
|
||||
[paypal-url]: https://paypal.me/yasirarism
|
||||
[qris-url]: https://img.yasirweb.eu.org/file/ee74ce527fb8264b54691.jpg
|
||||
[mayar]: https://yasirarism.mayar.link/payme
|
||||
[sociabuzz-url]: https://sociabuzz.com/yasirarism/tribe
|
||||
[saweria-url]: https://saweria.co/yasirarism
|
||||
[trakteer-url]: https://trakteer.id/yasir-aris-sp7cn
|
||||
|
|
|
|||
61
app.json
Normal file
61
app.json
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"name": "MissKaty RoBot",
|
||||
"description": "Multi Function Telegram Bot, made in python using Pyrofork.",
|
||||
"logo": "https://img.yasirweb.eu.org/file/2136400c0c5505d1b41e4.jpg",
|
||||
"keywords": [
|
||||
"pyrogram",
|
||||
"pyrofork",
|
||||
"telegram",
|
||||
"userbot",
|
||||
"python",
|
||||
"group-management"
|
||||
],
|
||||
"repository": "https://git.yasirweb.eu.org/yasirarism/MissKatyPyro",
|
||||
"website": "https://yasirpedia.eu.orh",
|
||||
"success_url": "https://t.me/YasirPediaChannel",
|
||||
"stack": "container",
|
||||
"env": {
|
||||
"API_ID": {
|
||||
"description": "Your api id, from my.telegram.org or @ScrapperRoBot.",
|
||||
"value": "",
|
||||
"required": false
|
||||
},
|
||||
"API_HASH": {
|
||||
"description": "Your api hash, from my.telegram.org or @ScrapperRoBot.",
|
||||
"value": "",
|
||||
"required": false
|
||||
},
|
||||
"USER_SESSION": {
|
||||
"description": "Session String (pyrogram) for your telegram user account. The userbot will NOT work without a session string!!",
|
||||
"value": ""
|
||||
},
|
||||
"DATABASE_URI": {
|
||||
"description": "Mongodb URL",
|
||||
"value": ""
|
||||
},
|
||||
"SUDO": {
|
||||
"description": "Allowed user in to use sudo command.",
|
||||
"value": ""
|
||||
},
|
||||
"OWNER_ID": {
|
||||
"description": "OWNER OF THIS BOT.",
|
||||
"value": ""
|
||||
},
|
||||
"GOOGLE_AI_KEY": {
|
||||
"description": "Your Gemini AI Key",
|
||||
"value": "",
|
||||
"required": false
|
||||
},
|
||||
"DATABASE_NAME": {
|
||||
"description": "Your database name.",
|
||||
"value": "",
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
"formation": {
|
||||
"misskaty": {
|
||||
"quantity": 1,
|
||||
"size": "basic"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
API_HASH=
|
||||
API_ID=
|
||||
BOT_TOKEN=
|
||||
DATABASE_URI=mongodb+srv://
|
||||
DATABASE_URI=
|
||||
LOG_CHANNEL=
|
||||
|
||||
# Optional Vars
|
||||
|
|
@ -12,4 +12,5 @@ SUPPORT_CHAT=YasirPediaChannel
|
|||
COMMAND_HANDLER=
|
||||
USER_SESSION=
|
||||
OPENAI_API=
|
||||
GOOGLEAI_API=
|
||||
CURRENCY_API=
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
"""
|
||||
* @author yasir <yasiramunandar@gmail.com>
|
||||
* @date 2022-09-06 10:12:09
|
||||
* @projectName MissKatyPyro
|
||||
* Copyright @YasirPedia All rights reserved
|
||||
* @author yasir <yasiramunandar@gmail.com>
|
||||
* @date 2022-09-06 10:12:09
|
||||
* @projectName MissKatyPyro
|
||||
* Copyright @YasirPedia All rights reserved
|
||||
"""
|
||||
|
||||
from async_pymongo import AsyncClient
|
||||
|
||||
from misskaty.vars import DATABASE_NAME, DATABASE_URI
|
||||
|
|
|
|||
|
|
@ -1,33 +1,35 @@
|
|||
from database import dbname
|
||||
from typing import List
|
||||
|
||||
blacklist_filtersdb = dbname["blacklistFilters"]
|
||||
|
||||
async def get_blacklisted_words(chat_id: int) -> List[str]:
|
||||
_filters = await blacklist_filtersdb.find_one({"chat_id": chat_id})
|
||||
if not _filters:
|
||||
return []
|
||||
return _filters["filters"]
|
||||
|
||||
async def save_blacklist_filter(chat_id: int, word: str):
|
||||
word = word.lower().strip()
|
||||
_filters = await get_blacklisted_words(chat_id)
|
||||
_filters.append(word)
|
||||
await blacklist_filtersdb.update_one(
|
||||
{"chat_id": chat_id},
|
||||
{"$set": {"filters": _filters}},
|
||||
upsert=True,
|
||||
)
|
||||
|
||||
async def delete_blacklist_filter(chat_id: int, word: str) -> bool:
|
||||
filtersd = await get_blacklisted_words(chat_id)
|
||||
word = word.lower().strip()
|
||||
if word in filtersd:
|
||||
filtersd.remove(word)
|
||||
await blacklist_filtersdb.update_one(
|
||||
{"chat_id": chat_id},
|
||||
{"$set": {"filters": filtersd}},
|
||||
upsert=True,
|
||||
)
|
||||
return True
|
||||
return False
|
||||
from typing import List
|
||||
|
||||
from database import dbname
|
||||
|
||||
blacklist_filtersdb = dbname["blacklistFilters"]
|
||||
|
||||
|
||||
async def get_blacklisted_words(chat_id: int) -> List[str]:
|
||||
_filters = await blacklist_filtersdb.find_one({"chat_id": chat_id})
|
||||
return [] if not _filters else _filters["filters"]
|
||||
|
||||
|
||||
async def save_blacklist_filter(chat_id: int, word: str):
|
||||
word = word.lower().strip()
|
||||
_filters = await get_blacklisted_words(chat_id)
|
||||
_filters.append(word)
|
||||
await blacklist_filtersdb.update_one(
|
||||
{"chat_id": chat_id},
|
||||
{"$set": {"filters": _filters}},
|
||||
upsert=True,
|
||||
)
|
||||
|
||||
|
||||
async def delete_blacklist_filter(chat_id: int, word: str) -> bool:
|
||||
filtersd = await get_blacklisted_words(chat_id)
|
||||
word = word.lower().strip()
|
||||
if word in filtersd:
|
||||
filtersd.remove(word)
|
||||
await blacklist_filtersdb.update_one(
|
||||
{"chat_id": chat_id},
|
||||
{"$set": {"filters": filtersd}},
|
||||
upsert=True,
|
||||
)
|
||||
return True
|
||||
return False
|
||||
|
|
|
|||
153
database/feds_db.py
Normal file
153
database/feds_db.py
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
from datetime import datetime
|
||||
|
||||
import pytz
|
||||
|
||||
from database import dbname
|
||||
from misskaty.vars import SUDO, OWNER_ID
|
||||
|
||||
fedsdb = dbname["federation"]
|
||||
|
||||
|
||||
def get_fed_info(fed_id):
|
||||
get = fedsdb.find_one({"fed_id": str(fed_id)})
|
||||
return False if get is None else get
|
||||
|
||||
|
||||
async def get_fed_id(chat_id):
|
||||
get = await fedsdb.find_one({"chat_ids.chat_id": int(chat_id)})
|
||||
|
||||
if get is None:
|
||||
return False
|
||||
return next(
|
||||
(
|
||||
get["fed_id"]
|
||||
for chat_info in get.get("chat_ids", [])
|
||||
if chat_info["chat_id"] == int(chat_id)
|
||||
),
|
||||
False,
|
||||
)
|
||||
|
||||
|
||||
async def get_feds_by_owner(owner_id):
|
||||
cursor = fedsdb.find({"owner_id": owner_id})
|
||||
feds = await cursor.to_list(length=None)
|
||||
if not feds:
|
||||
return False
|
||||
return [{"fed_id": fed["fed_id"], "fed_name": fed["fed_name"]} for fed in feds]
|
||||
|
||||
|
||||
async def transfer_owner(fed_id, current_owner_id, new_owner_id):
|
||||
if await is_user_fed_owner(fed_id, current_owner_id):
|
||||
await fedsdb.update_one(
|
||||
{"fed_id": fed_id, "owner_id": current_owner_id},
|
||||
{"$set": {"owner_id": new_owner_id}},
|
||||
)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
async def set_log_chat(fed_id, log_group_id: int):
|
||||
await fedsdb.update_one(
|
||||
{"fed_id": fed_id}, {"$set": {"log_group_id": log_group_id}}
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
async def get_fed_name(chat_id):
|
||||
get = await fedsdb.find_one(int(chat_id))
|
||||
return False if get is None else get["fed_name"]
|
||||
|
||||
|
||||
async def is_user_fed_owner(fed_id, user_id: int):
|
||||
getfed = await get_fed_info(fed_id)
|
||||
if not getfed:
|
||||
return False
|
||||
owner_id = getfed["owner_id"]
|
||||
return user_id == owner_id or user_id not in SUDO or user_id != OWNER_ID
|
||||
|
||||
|
||||
async def search_fed_by_id(fed_id):
|
||||
get = await fedsdb.find_one({"fed_id": str(fed_id)})
|
||||
|
||||
return get if get is not None else False
|
||||
|
||||
|
||||
def chat_join_fed(fed_id, chat_name, chat_id):
|
||||
return fedsdb.update_one(
|
||||
{"fed_id": fed_id},
|
||||
{"$push": {"chat_ids": {"chat_id": int(chat_id), "chat_name": chat_name}}},
|
||||
)
|
||||
|
||||
|
||||
async def chat_leave_fed(chat_id):
|
||||
result = await fedsdb.update_one(
|
||||
{"chat_ids.chat_id": int(chat_id)},
|
||||
{"$pull": {"chat_ids": {"chat_id": int(chat_id)}}},
|
||||
)
|
||||
return result.modified_count > 0
|
||||
|
||||
|
||||
async def user_join_fed(fed_id, user_id):
|
||||
result = await fedsdb.update_one(
|
||||
{"fed_id": fed_id}, {"$addToSet": {"fadmins": int(user_id)}}, upsert=True
|
||||
)
|
||||
return result.modified_count > 0
|
||||
|
||||
|
||||
async def user_demote_fed(fed_id, user_id):
|
||||
result = await fedsdb.update_one(
|
||||
{"fed_id": fed_id}, {"$pull": {"fadmins": int(user_id)}}
|
||||
)
|
||||
return result.modified_count > 0
|
||||
|
||||
|
||||
async def search_user_in_fed(fed_id, user_id):
|
||||
getfed = await search_fed_by_id(fed_id)
|
||||
return False if getfed is None else user_id in getfed["fadmins"]
|
||||
|
||||
|
||||
async def chat_id_and_names_in_fed(fed_id):
|
||||
getfed = await search_fed_by_id(fed_id)
|
||||
|
||||
if getfed is None or "chat_ids" not in getfed:
|
||||
return [], []
|
||||
|
||||
chat_ids = [chat["chat_id"] for chat in getfed["chat_ids"]]
|
||||
chat_names = [chat["chat_name"] for chat in getfed["chat_ids"]]
|
||||
return chat_ids, chat_names
|
||||
|
||||
|
||||
async def add_fban_user(fed_id, user_id, reason):
|
||||
current_date = datetime.now(pytz.timezone("Asia/Jakarta")).strftime(
|
||||
"%Y-%m-%d %H:%M"
|
||||
)
|
||||
await fedsdb.update_one(
|
||||
{"fed_id": fed_id},
|
||||
{
|
||||
"$push": {
|
||||
"banned_users": {
|
||||
"user_id": int(user_id),
|
||||
"reason": reason,
|
||||
"date": current_date,
|
||||
}
|
||||
}
|
||||
},
|
||||
upsert=True,
|
||||
)
|
||||
|
||||
|
||||
async def remove_fban_user(fed_id, user_id):
|
||||
await fedsdb.update_one(
|
||||
{"fed_id": fed_id}, {"$pull": {"banned_users": {"user_id": int(user_id)}}}
|
||||
)
|
||||
|
||||
|
||||
async def check_banned_user(fed_id, user_id):
|
||||
result = await fedsdb.find_one({"fed_id": fed_id, "banned_users.user_id": user_id})
|
||||
if result and "banned_users" in result:
|
||||
for user in result["banned_users"]:
|
||||
if user.get("user_id") == user_id:
|
||||
return {"reason": user.get("reason"), "date": user.get("date")}
|
||||
|
||||
return False
|
||||
|
|
@ -24,6 +24,10 @@ async def delete_filter(chat_id: int, name: str) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
async def deleteall_filters(chat_id: int):
|
||||
return await filtersdb.delete_one({"chat_id": chat_id})
|
||||
|
||||
|
||||
async def get_filter(chat_id: int, name: str) -> Union[bool, dict]:
|
||||
name = name.lower().strip()
|
||||
_filters = await _get_filters(chat_id)
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
from database import dbname
|
||||
|
||||
gbansdb = dbname["gban"]
|
||||
|
||||
|
||||
async def is_gbanned_user(user_id: int) -> bool:
|
||||
user = await gbansdb.find_one({"user_id": user_id})
|
||||
return bool(user)
|
||||
|
||||
|
||||
async def add_gban_user(user_id: int):
|
||||
is_gbanned = await is_gbanned_user(user_id)
|
||||
if is_gbanned:
|
||||
return
|
||||
return await gbansdb.insert_one({"user_id": user_id})
|
||||
|
||||
|
||||
async def remove_gban_user(user_id: int):
|
||||
is_gbanned = await is_gbanned_user(user_id)
|
||||
if not is_gbanned:
|
||||
return
|
||||
return await gbansdb.delete_one({"user_id": user_id})
|
||||
from database import dbname
|
||||
|
||||
gbansdb = dbname["gban"]
|
||||
|
||||
|
||||
async def is_gbanned_user(user_id: int) -> bool:
|
||||
user = await gbansdb.find_one({"user_id": user_id})
|
||||
return bool(user)
|
||||
|
||||
|
||||
async def add_gban_user(user_id: int):
|
||||
is_gbanned = await is_gbanned_user(user_id)
|
||||
if is_gbanned:
|
||||
return
|
||||
return await gbansdb.insert_one({"user_id": user_id})
|
||||
|
||||
|
||||
async def remove_gban_user(user_id: int):
|
||||
is_gbanned = await is_gbanned_user(user_id)
|
||||
if not is_gbanned:
|
||||
return
|
||||
return await gbansdb.delete_one({"user_id": user_id})
|
||||
|
|
|
|||
19
database/greetings_db.py
Normal file
19
database/greetings_db.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from database import dbname
|
||||
|
||||
greetingdb = dbname["greetings"]
|
||||
|
||||
|
||||
async def is_welcome(chat_id: int) -> bool:
|
||||
return bool(await greetingdb.find_one({"chat_id": chat_id}))
|
||||
|
||||
|
||||
async def toggle_welcome(chat_id: int):
|
||||
if await is_welcome(chat_id):
|
||||
await greetingdb.delete_one({"chat_id": chat_id})
|
||||
return False
|
||||
else:
|
||||
await greetingdb.insert_one({"chat_id": chat_id})
|
||||
return True
|
||||
|
||||
|
||||
# todo other features for custom welcome here
|
||||
|
|
@ -42,3 +42,7 @@ async def save_note(chat_id: int, name: str, note: dict):
|
|||
await notesdb.update_one(
|
||||
{"chat_id": chat_id}, {"$set": {"notes": _notes}}, upsert=True
|
||||
)
|
||||
|
||||
|
||||
async def deleteall_notes(chat_id: int):
|
||||
return await notesdb.delete_one({"chat_id": chat_id})
|
||||
|
|
|
|||
16
database/payment_db.py
Normal file
16
database/payment_db.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from database import dbname
|
||||
from typing import Optional
|
||||
|
||||
autopay = dbname["autpay"]
|
||||
|
||||
|
||||
async def delete_autopay(uniqueCode: str):
|
||||
await autopay.delete_one({"_id": uniqueCode})
|
||||
|
||||
async def get_autopay(uniqueCode: str):
|
||||
exists = await autopay.find_one({"_id": uniqueCode})
|
||||
return exists
|
||||
|
||||
async def autopay_update(msg_id: Optional[int] = "", note: Optional[str] = "", user_id: Optional[int] = "", amount: Optional[int] = "", status: Optional[str] = "", uniqueCode: Optional[str] = "", createdAt: Optional[str] = ""):
|
||||
data = {"msg_id": msg_id, "note": note, "user_id": user_id, "amount": amount, "status": status, "createdAt": createdAt}
|
||||
await autopay.update_one({"_id": uniqueCode}, {"$set": data}, upsert=True)
|
||||
|
|
@ -7,7 +7,7 @@ class UsersData:
|
|||
def __init__(self, uri, database_name):
|
||||
self._client = AsyncClient(uri)
|
||||
self.db = self._client[database_name]
|
||||
self.col = self.db["users"]
|
||||
self.col = self.db["userlist"]
|
||||
self.grp = self.db["groups"]
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -44,17 +44,14 @@ class UsersData:
|
|||
return await self.col.count_documents({})
|
||||
|
||||
async def remove_ban(self, id):
|
||||
ban_status = dict(is_banned=False, ban_reason="")
|
||||
await self.col.update_one({"id": id}, {"$set": {"ban_status": ban_status}})
|
||||
return await self.col.delete_one({"_id": id})
|
||||
|
||||
async def ban_user(self, user_id, ban_reason="No Reason"):
|
||||
ban_status = dict(is_banned=True, ban_reason=ban_reason)
|
||||
await self.col.update_one({"id": user_id}, {"$set": {"ban_status": ban_status}})
|
||||
return await self.col.insert_one({"_id": user_id, "reason": ban_reason})
|
||||
|
||||
async def get_ban_status(self, id):
|
||||
default = dict(is_banned=False, ban_reason="")
|
||||
user = await self.col.find_one({"id": int(id)})
|
||||
return user.get("ban_status", default) if user else default
|
||||
user = await self.col.find_one({"_id": int(id)})
|
||||
return user if user else False
|
||||
|
||||
async def get_all_users(self):
|
||||
return self.col.find({})
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
version: "3.3"
|
||||
|
||||
services:
|
||||
app:
|
||||
misskaty:
|
||||
image: misskaty
|
||||
container_name: misskaty
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
command: bash start.sh
|
||||
restart: on-failure
|
||||
|
||||
# restarter:
|
||||
# image: docker:cli
|
||||
# volumes: ["/var/run/docker.sock:/var/run/docker.sock"]
|
||||
# command: ["/bin/sh", "-c", "while true; do sleep 259200; docker restart misskaty; done"]
|
||||
# restart: on-failure
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
build:
|
||||
docker:
|
||||
worker: Dockerfile
|
||||
misskaty: Dockerfile
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@
|
|||
"ban_admin_err": "Lol, it's crazy if i can banned an admin.",
|
||||
"mute_admin_err": "Lol, it's crazy if i can mute an admin.",
|
||||
"warn_admin_err": "Lol, it's crazy if i can warn an admin.",
|
||||
"kick_msg": "**Kicked User:** {mention} [{id}]\n**Kicked By:** {kicker}\n**Reason:** {reasonmsg}",
|
||||
"ban_msg": "**Banned User:** {mention} [{id}]\n**Banned By:** {banner}\n",
|
||||
"kick_msg": "**Kicked User:** {mention} [`{id}`]\n**Kicked By:** {kicker}\n**Reason:** {reasonmsg}",
|
||||
"ban_msg": "**Banned User:** {mention} [`{id}`]\n**Banned By:** {banner}\n",
|
||||
"unban_msg": "__Banned removed by {mention}__",
|
||||
"no_ban_permission": "Please give me ban permission to ban user in this group.",
|
||||
"no_more_99": "You can't use more than 99",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"no_question": "Please use command <code>/{cmd} [question]</code> to ask your question.",
|
||||
"no_question": "Please use command <code>/{cmd} [question]</code> to ask your question with AI.",
|
||||
"find_answers_str": "Wait a moment looking for your answer..",
|
||||
"dont_spam": "Don't spam please, please wait {tm} second or i will ban you from this bot.",
|
||||
"answers_too_long": "Question for your answer has exceeded TG text limit, check this link to view.\n\n{answerlink}"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
{
|
||||
"already_up": "Its already up-to date!",
|
||||
"already_up": "It's already up-to-date!",
|
||||
"up_and_rest": "<b>Updated with default branch, restarting now.</b>",
|
||||
"cl_btn": "❌ Close",
|
||||
"no_eval": "__No evaluate message!__",
|
||||
"privacy_policy": "<b>Privacy Policy For {botname}</b>\n\n<b>Effective Date:</b> 02 August 2024\n\nThis Privacy Policy explains how we collect, use, and protect your information when you interact with our Telegram bot designed for group management.\n\n<b>1. Information We Collect</b>\n\nWhen you use our Telegram bot, we may collect the following types of information:\n- <b>User Information</b>: Basic information such as your Telegram ID, username, and any other data you choose to provide.\n- <b>Group Information</b>: Data related to the groups you manage, including group ID, group name, and member details.\n\n<b>2. How We Use Your Information</b>\n\nWe use the collected information to:\n- Manage and maintain group functionalities.\n- Provide support and improve our services.\n- Communicate important updates or changes related to the bot.\n\n<b>3. How We Protect Your Information</b>\n\nWe implement various security measures to protect your information from unauthorized access, alteration, disclosure, or destruction. However, please be aware that no method of transmission over the internet or electronic storage is 100% secure.\n\n<b>4. Sharing Your Information</b>\n\nWe do not share your information with third parties, except as required by law or as necessary to provide the services described.\n\n<b>5. Your Rights</b>\n\nYou have the right to access, correct, or delete your personal information. If you wish to exercise any of these rights, please contact us directly through the bot.\n\n<b>6. Changes to This Privacy Policy</b>\n\nWe may update this Privacy Policy from time to time. We will notify you of any significant changes by updating the policy in the bot. It is your responsibility to review this policy periodically.\n\n<b>7. Contact Us</b>\n\nIf you have any questions or concerns about this Privacy Policy or our practices, please contact us my owner.",
|
||||
"run_eval": "<i>Processing eval pyrogram..</i>",
|
||||
"run_exec": "<i>Processing exec pyrogram..</i>",
|
||||
"no_cmd": "No command to execute was given.",
|
||||
"success": "Success",
|
||||
"no_reply": "No Reply"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"back_btn": "« Go back",
|
||||
"no_results": "No Results.",
|
||||
"unknown_id": "Sorry I can't recognize this user. Maybe I've never met him.",
|
||||
"exp_task": "😶🌫️ Timeout. Task has been cancelled!"
|
||||
}
|
||||
|
|
@ -9,5 +9,6 @@
|
|||
"err_parse": "Failed parse URL, check logs..",
|
||||
"wait": "Please wait..",
|
||||
"unauth": "Not Your Task..",
|
||||
"endlist": "That's the end of list"
|
||||
"endlist": "That's the end of list",
|
||||
"vip-btn": "Because some user abuse and my server cannot handle it, best video now only for bot owner."
|
||||
}
|
||||
|
|
@ -25,8 +25,8 @@
|
|||
"ban_admin_err": "Hah, sungguh gila jika saya bisa melarang seorang admin.",
|
||||
"mute_admin_err": "Hah, sungguh gila jika saya bisa membisukan admin.",
|
||||
"warn_admin_err": "Hah, sungguh gila jika saya bisa memperingatkan seorang admin.",
|
||||
"kick_msg": "**Pengguna yang Ditendang:** {mention} [{id}]\n**Ditendang Oleh:** {kicker}\n**Alasan:** {reasonmsg}",
|
||||
"ban_msg": "**Pengguna yang Dilarang:** {mention} [{id}]\n**Dilarang Oleh:** {banner}\n",
|
||||
"kick_msg": "**Pengguna yang Ditendang:** {mention} [`{id}`]\n**Ditendang Oleh:** {kicker}\n**Alasan:** {reasonmsg}",
|
||||
"ban_msg": "**Pengguna yang Dibanned:** {mention} [`{id}`]\n**Dibanned Oleh:** {banner}\n",
|
||||
"unban_msg": "__Banned dihapus oleh {mention}__",
|
||||
"no_ban_permission": "Tolong beri saya izin banned untuk membanned pengguna di grup ini.",
|
||||
"no_more_99": "Anda tidak dapat menggunakan lebih dari 99",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"no_question": "Harap gunakan perintah <code>/{cmd} [question]</code> untuk mengajukan pertanyaan Anda menggunakan OpenAI.",
|
||||
"no_question": "Harap gunakan perintah <code>/{cmd} [question]</code> untuk mengajukan pertanyaan Anda menggunakan AI.",
|
||||
"find_answers_str": "Sedang mencari jawaban terbaik buat Anda..",
|
||||
"dont_spam": "Tolong jangan melakukan spam, harap tunggu {tm} detik atau saya akan membanned Anda dari bot ini.",
|
||||
"answers_too_long": "Pertanyaan untuk jawaban Anda telah melampaui batas teks TG, periksa tautan ini untuk melihatnya.\n\n{answerlink}"
|
||||
}
|
||||
"answers_too_long": "Jawaban untuk pertanyaan Anda telah melampaui limit batas teks Telegram, periksa tautan ini untuk melihatnya.\n\n{answerlink}"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
"up_and_rest": "<b>Diperbarui dengan branch default, dimulai ulang sekarang.</b>",
|
||||
"cl_btn": "❌ Tutup",
|
||||
"no_eval": "__Tidak ada pesan eval!__",
|
||||
"privacy_policy": "<b>Kebijakan Privasi Untuk {botname}</b>\n\n<b>Tanggal Efektif:</b> 02 Agustus 2024\n\nKebijakan Privasi ini menjelaskan bagaimana kami mengumpulkan, menggunakan, dan melindungi informasi Anda saat Anda berinteraksi dengan bot Telegram kami yang dirancang untuk manajemen grup.\n\n<b>1. Informasi yang Kami Kumpulkan</b>\n\nSaat Anda menggunakan bot Telegram kami, kami dapat mengumpulkan jenis informasi berikut:\n- <b>Informasi Pengguna</b>: Informasi dasar seperti ID Telegram Anda, nama pengguna, dan data lain yang Anda pilih untuk diberikan.\n- <b>Informasi Grup</b>: Data yang terkait dengan grup yang Anda kelola, termasuk ID grup, nama grup, dan detail anggota.\n\n<b>2. Bagaimana Kami Menggunakan Informasi Anda</b>\n\nKami menggunakan informasi yang dikumpulkan untuk:\n- Mengelola dan memelihara fungsionalitas grup.\n- Memberikan dukungan dan meningkatkan layanan kami.\n- Mengomunikasikan pembaruan atau perubahan penting yang terkait dengan bot.\n\n<b>3. Cara Kami Melindungi Informasi Anda</b>\n\nKami menerapkan berbagai langkah keamanan untuk melindungi informasi Anda dari akses, perubahan, pengungkapan, atau penghancuran yang tidak sah. Namun, perlu diketahui bahwa tidak ada metode transmisi melalui internet atau penyimpanan elektronik yang 100% aman.\n\n<b>4. Berbagi Informasi Anda</b>\n\nKami tidak membagikan informasi Anda dengan pihak ketiga, kecuali sebagaimana diwajibkan oleh hukum atau sebagaimana diperlukan untuk menyediakan layanan yang dijelaskan.\n\n<b>5. Hak Anda</b>\n\nAnda memiliki hak untuk mengakses, memperbaiki, atau menghapus informasi pribadi Anda. Jika Anda ingin menggunakan salah satu hak ini, silakan hubungi kami langsung melalui bot.\n\n<b>6. Perubahan pada Kebijakan Privasi Ini</b>\n\nKami dapat memperbarui Kebijakan Privasi ini dari waktu ke waktu. Kami akan memberi tahu Anda tentang setiap perubahan signifikan dengan memperbarui kebijakan di bot. Merupakan tanggung jawab Anda untuk meninjau kebijakan ini secara berkala.\n\n<b>7. Hubungi Kami</b>\n\nJika Anda memiliki pertanyaan atau masalah tentang Kebijakan Privasi ini atau praktik kami, silakan hubungi kami sebagai pemilik saya.",
|
||||
"run_eval": "<i>Memproses pyrogram eval..</i>",
|
||||
"run_exec": "<i>Memproses pyrogram eksekutif..</i>",
|
||||
"no_cmd": "Tidak ada perintah untuk dieksekusi.",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"back_btn": "« Kembali",
|
||||
"unknown_id": "Maaf saya tidak bisa mengenali pengguna ini. Mungkin saya belum pernah bertemu dengannya.",
|
||||
"no_results": "Tidak ada hasil yang ditemukan.",
|
||||
"exp_task": "😶🌫️ Waktu Habis. Tugas Telah Dibatalkan!"
|
||||
}
|
||||
|
|
@ -9,5 +9,6 @@
|
|||
"err_parse": "Gagal menguraikan URL, periksa log..",
|
||||
"tunggu": "Harap tunggu..",
|
||||
"unauth": "Bukan Tugas Anda..",
|
||||
"endlist": "Itu adalah akhir dari daftar"
|
||||
"endlist": "Itu adalah akhir dari daftar",
|
||||
"vip-btn": "Karena beberapa penyalahgunaan pengguna, video terbaik sekarang hanya untuk pemilik bot."
|
||||
}
|
||||
|
|
@ -25,8 +25,8 @@
|
|||
"ban_admin_err": "Lol, edan yen aku bisa nglarang admin.",
|
||||
"mute_admin_err": "Lol, edan yen aku bisa bisu admin.",
|
||||
"warn_admin_err": "Lol, edan yen aku bisa ngelingake admin.",
|
||||
"kick_msg": "**Panganggo Ditendhang:** {mention} [{id}]\n**Ditendhang dening:** {kicker}\n**Alasan:** {reasonmsg}",
|
||||
"ban_msg": "**Panganggo sing Dicekal:** {mention} [{id}]\n**Dicekal Dening:** {banner}\n",
|
||||
"kick_msg": "**Panganggo Ditendhang:** {mention} [`{id}`]\n**Ditendhang dening:** {kicker}\n**Alasan:** {reasonmsg}",
|
||||
"ban_msg": "**Panganggo sing Dicekal:** {mention} [`{id}`]\n**Dicekal Dening:** {banner}\n",
|
||||
"unban_msg": "__Dicekal dibusak kanthi {mention}__",
|
||||
"no_ban_permission": "Mangga kula nyuwun idin nglarang panganggo ing grup punika.",
|
||||
"no_more_99": "Sampeyan ora bisa nggunakake luwih saka 99",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"no_question": "Tulung gunakake printah <code>/{cmd} [pitakon]</code> kanggo takon nganggo fitur OpenAI.",
|
||||
"no_question": "Tulung gunakake printah <code>/{cmd} [pitakon]</code> kanggo takon nganggo fitur AI.",
|
||||
"find_answers_str": "Lagi goleki jawaban paling apik kanggo sampeyan..",
|
||||
"dont_spam": "Aja spam, mangga ngenteni {tm} detik utawa aku bakal nglarang sampeyan saka bot iki.",
|
||||
"answers_too_long": "Pitakonan kanggo jawaban sampeyan wis ngluwihi wates teks TG, priksa pranala iki kanggo ndeleng.\n\n{answerlink}"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
"up_and_rest": "<b>Dianyari nganggo branch standar, diwiwiti maneh saiki.</b>",
|
||||
"cl_btn": "❌ Tutup",
|
||||
"no_eval": "__Ora ana pesen eval!__",
|
||||
"privacy_policy": "<b>Kabijakan Privasi Kanggo {botname}</b>\n\n<b>Tanggal Efektif:</b> 26 Muharram 1446 H\n\nKebijakan Privasi iki nerangake carane kita ngumpulake, nggunakake, lan nglindhungi informasi nalika sampeyan sesambungan karo bot Telegram sing dirancang kanggo manajemen grup.\n\n<b>1. Informasi sing Diklumpukake</b>\n\nYen sampeyan nggunakake bot Telegram, kita bisa ngumpulake jinis informasi ing ngisor iki:\n- <b>Informasi Pangguna</b>: Informasi dhasar kayata ID Telegram, jeneng pangguna, lan data liyane sing sampeyan pilih kanggo diwenehake.\n- <b>Informasi Grup</b>: Data sing ana hubungane karo grup sing sampeyan atur, kalebu ID grup, jeneng grup, lan rincian anggota.\n\n<b>2. Carane Kita Gunakake Informasi Panjenengan</b>\n\nKita nggunakake informasi sing diklumpukake kanggo:\n- Ngatur lan njaga fungsi grup.\n- Nyedhiyani dhukungan lan ningkatake layanan kita.\n- Nyedhiyakake nganyari penting utawa owah-owahan sing ana gandhengane karo bot.\n\n<b>3. Carane Kita Nglindhungi Informasi Panjenengan</b>\n\nKita nindakake macem-macem langkah keamanan kanggo nglindhungi informasi saka akses ora sah, owah-owahan, pambocoran, utawa karusakan. Nanging, elinga yen ora ana cara transmisi liwat Internet utawa panyimpenan elektronik sing 100% aman.\n\n<b>4. Nuduhake Informasi Panjenengan</b>\n\nKita ora nuduhake informasi sampeyan karo pihak katelu, kajaba sing dibutuhake dening hukum utawa minangka perlu kanggo nyedhiyani layanan diterangake.\n\n<b>5. Hak Panjenengan</b>\n\nSampeyan duwe hak kanggo ngakses, mbenerake, utawa mbusak informasi pribadhi. Yen sampeyan pengin nggunakake hak kasebut, hubungi kita langsung liwat bot.\n\n<b>6. Owah-owahan ing Kebijakan Privasi Iki</b>\n\nKita bisa nganyari Kebijakan Privasi iki saka wektu kanggo wektu. Kita bakal menehi kabar babagan owah-owahan sing signifikan kanthi nganyari kabijakan ing bot. Sampeyan tanggung jawab kanggo mriksa kabijakan iki sacara periodik.\n\n<b>7. Hubungi Kita</b>\n\nYen sampeyan duwe pitakonan utawa uneg-uneg babagan Kebijakan Privasi iki utawa praktik kita, hubungi pemilikku.",
|
||||
"run_eval": "<i>Ngolah pyrogram eval..</i>",
|
||||
"run_exec": "<i>Ngolah pyrogram exec..</i>",
|
||||
"no_cmd": "Ora ana prentah kanggo nglakokaké.",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"back_btn": "« Balik Maneh",
|
||||
"unknown_id": "Nganpunten, aku ora ngerti pengguna iki. Mungkin aku durung pernah ketemu mbi dek'e.",
|
||||
"no_results": "Ora ana asil sing ditemokake.",
|
||||
"exp_task": "😶🌫️ Wektu wis entek. Tugas wis dibatalake!"
|
||||
}
|
||||
|
|
@ -9,5 +9,6 @@
|
|||
"err_parse": "Gagal ngurai URL, mriksa log..",
|
||||
"ngenteni": "Tulung ngenteni..",
|
||||
"unauth": "Ora Tugasmu..",
|
||||
"endlist": "Iku pungkasan dhaptar"
|
||||
"endlist": "Iku pungkasan dhaptar",
|
||||
"vip-btn": "Amarga sawetara pangguna penyalahgunaan, video paling apik saiki mung sing nduwe bot iki."
|
||||
}
|
||||
68
locales/ru-RU/admin.json
Normal file
68
locales/ru-RU/admin.json
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"no_admin_error": "Вы должны быть администратором, чтобы воспользоваться этой командой.",
|
||||
"no_permission_error": "К сожалению у Вас нет необходимых прав для выполнения команды. Отсутствующие права: {permissions}",
|
||||
"private_not_allowed": "Данную команду невозможно использовать в приватном чате. Если Вам нужна помощь, пожалуйста, воспользуйтесь командой /help.",
|
||||
"purge_no_reply": "Ответьте На Сообщение, Чтобы Очистить Его.",
|
||||
"delete_no_reply": "Ответьте На Сообщение, Чтобы Удалить Его.",
|
||||
"pin_no_reply": "Ответьте На Сообщение, Чтобы Закрепить/Открепить Его.",
|
||||
"report_no_reply": "Ответьте На Сообщение, Для Жалобы на Пользователя.",
|
||||
"no_delete_perm": "Пожалуйста, предоставьте мне доступ на удаление сообщений.",
|
||||
"purge_success": "Успешно удалено {del_total} сообщений..",
|
||||
"user_not_found": "Я не могу найти такого пользователя.",
|
||||
"invalid_id_uname": "⚠️ Неправильный userid/username",
|
||||
"kick_self_err": "Я не могу выгнать себя, я могу уйти если вы хотите.",
|
||||
"ban_self_err": "Я не могу заблокировать себя, я могу уйти если вы хотите.",
|
||||
"report_self_err": "Зачем вы жалуетесь на себя?",
|
||||
"demote_self_err": "Я не могу понизить себя.",
|
||||
"warn_self_err": "Я не могу дать предупреждение себе.",
|
||||
"mute_self_err": "Я не могу дать мут себе.",
|
||||
"kick_sudo_err": "Ого, ты хочешь выгнать моего хозяина?",
|
||||
"ban_sudo_err": "Ого, ты хочешь заблокировать моего хозяина?",
|
||||
"demote_sudo_err": "Ого, ты хочешь понизить моего хозяина?",
|
||||
"warn_sudo_err": "Ого, ты хочешь дать предупреждение моему хозяину?",
|
||||
"mute_sudo_err": "Ого, ты хочешь дать мут моему хозяину?",
|
||||
"kick_admin_err": "Лол, это безумие если я выгоню администратора.",
|
||||
"ban_admin_err": "Лол, это безумие если я заблокирую администратора.",
|
||||
"mute_admin_err": "Лол, это безумие если я дам мут администратору.",
|
||||
"warn_admin_err": "Лол, это безумие если я дам предупреждение администратору.",
|
||||
"kick_msg": "**Выгнан Пользователь:** {mention} [`{id}`]\n**Выгнал:** {kicker}\n**Причина:** {reasonmsg}",
|
||||
"ban_msg": "**Заблокирован Пользователь:** {mention} [`{id}`]\n**Выгнал:** {banner}\n",
|
||||
"unban_msg": "__Блокировка убрана с {mention}__",
|
||||
"no_ban_permission": "Пожалуйста, предоставьте мне доступ на блокировку пользователей.",
|
||||
"no_more_99": "Вы не можете использовать более 99.",
|
||||
"banned_time": "**Заблокирован на:** {val}\n",
|
||||
"muted_time": "**Дан мут на:** {val}\n",
|
||||
"banned_reason": "**Причина:** {reas}",
|
||||
"unban_channel_err": "Вы не можете разблокировать канал.",
|
||||
"give_unban_user": "Укажите имя пользователя или ответьте на сообщение, чтобы разблокировать пользователя.",
|
||||
"unban_success": "Успешно разблокирован {umention}!",
|
||||
"give_idban_with_msg_link": "Укажите userid/username вместе с ссылкой на сообщением и причиной для внесения в список блокировки.",
|
||||
"give_idunban_with_msg_link": "Укажите userid/username вместе с ссылкой на сообщением и причиной для внесения в список разблокировки.",
|
||||
"give_reason_list_ban": "Вы должны указать причину для внесения в список блокировки",
|
||||
"Invalid_tg_link": "Введена неверная ссылка на сообщение.",
|
||||
"multiple_ban_progress": "`Блокировка пользователя к нескольким группам. Это может занять некоторое время.`",
|
||||
"multiple_unban_progress": "`Разблокировка пользователя к нескольким группам. Это может занять некоторое время.`",
|
||||
"failed_get_uname": "Не удалось получить username пользователей групп.",
|
||||
"listban_msg": "**Блокировка пользователя по списку:** {mention}\n**ID заблокированного пользователя:** `{uid}`\n**Администратор:** {frus}\n**Затронутые чаты:** `{ct}`\n**Причина:** {reas}",
|
||||
"listunban_msg": "**Разблокировка пользователя по списку:** {mention}\n**ID разблокированного пользователя:** `{uid}`\n**Администратор:** {frus}\n**Затронутые чаты:** `{ct}`\n**Причина:** {reas}",
|
||||
"promote_self_err": "Я не могу понизить себя.",
|
||||
"no_promote_perm": "К сожалению, у меня нет прав для повышения прав пользователей.",
|
||||
"full_promote": "Полностью повышен {umention}!",
|
||||
"normal_promote": "Повышен {umention}!",
|
||||
"pin_success": "**Закреплено [Это]({link}) сообщение.**",
|
||||
"unpin_success": "**Откреплено [Это]({link}) сообщение.**",
|
||||
"pin_no_perm": "Пожалуйста, дайте мне права на закрепления сообщения, чтобы воспользоваться командой!.",
|
||||
"report_msg": "Сообщил {user_mention} администраторам!",
|
||||
"reported_is_admin": "Вы же знаете, что этот пользователь Администратор?",
|
||||
"user_no_warn": "У пользователя {mention} нет предупреждений.",
|
||||
"ch_warn_msg": "У пользователя {mention} есть {warns}/3 предупреждений.",
|
||||
"warn_msg": "**Предуждение Пользователю:** {mention}\n**Дал предупреждение:** {warner}\n**Причина:** {reas}\n**Предупреждений:** {twarn}/3",
|
||||
"rmwarn_msg": "Предупрждения сняты с {mention}.",
|
||||
"unwarn_msg": "Предупрждения сняты {mention}.",
|
||||
"rmmute_msg": "__Мут снят с {mention}__",
|
||||
"unmute_msg": "Размучен! {umention}",
|
||||
"reply_to_rm_warn": "Ответьте на сообщение, чтобы убрать с пользователя предупрждения.",
|
||||
"exceed_warn_msg": "Лимит предупреждений у {mention} достигнуто, ЗАБЛОКИРОВАН!",
|
||||
"mute_msg": "**Замучен пользователь:** {mention}\n**Дал мут:** {muter}\n",
|
||||
"rm_warn_btn": "🚨 Предупреждение удалено 🚨"
|
||||
}
|
||||
13
locales/ru-RU/afk.json
Normal file
13
locales/ru-RU/afk.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"no_channel": "Эта функция недоступна каналам.",
|
||||
"on_afk_msg_no_r": "**{usr}** [<code>{id}</code>] вернулся в онлайн и отсуствовал в течении {tm}\n\n",
|
||||
"on_afk_msg_with_r": "**{usr}** [<code>{id}</code>] вернулся в онлайн и отсуствовал в течении {tm}\n\n**Причина:** `{reas}`\n\n",
|
||||
"is_afk_msg_no_r": "**{usr}** [<code>{id}</code>] в АФК на протяжении {tm}.\n\n",
|
||||
"is_afk_msg_with_r": "**{usr}** [<code>{id}</code>] В АФК на протяжении {tm}.\n\n**Причина:** {reas}\n\n",
|
||||
"is_online": "**{usr}** [<code>{id}</code>] вернулся в онлайн",
|
||||
"now_afk": "{usr} [<code>{id}</code>] ушёл в АФК!.",
|
||||
"afkdel_help": "**Использование:**\n/{cmd} [ENABLE|DISABLE] чтобы Включить/Выключить автоудаление сообщений.",
|
||||
"afkdel_disable": "Автоудаление АФК сообщений сообщение Отключено.",
|
||||
"afkdel_enable": "Автоудаление АФК сообщений сообщение в этом чате Включено.",
|
||||
"is_afk": "{usr} [<code>{id}</code>] в АФК!"
|
||||
}
|
||||
6
locales/ru-RU/chatbot_ai.json
Normal file
6
locales/ru-RU/chatbot_ai.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"no_question": "Пожалуйста, используйте команду <code>/{cmd} [Вопрос]</code> чтобы задать Ваш вопрос ИИ.",
|
||||
"find_answers_str": "Секундочку, ожидайте ответа..",
|
||||
"dont_spam": "Не спамьте, пожалуйста, подождите {tm} секунд или я заблокирую Вам доступ к боту.",
|
||||
"answers_too_long": "Ответ на Ваш вопрос слишком длинный, откройте ссылку, чтобы посмотреть.\n\n{answerlink}"
|
||||
}
|
||||
12
locales/ru-RU/dev.json
Normal file
12
locales/ru-RU/dev.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"already_up": "Уже обновлен!",
|
||||
"up_and_rest": "<b>Обновлен с веткой по умолчанию, идёт перезапуск.</b>",
|
||||
"cl_btn": "❌ Закрыть",
|
||||
"no_eval": "__Нет eval сообщения!__",
|
||||
"privacy_policy": "<b>Политика Конфидециальности Для {botname}</b>\n\n<b>Дата Вступления в Силу:</b> 02 Августа 2024\nЭта Политика Конфидециальности объясняет Как мы собираем, используем, и защищаем Вашу информацию, когда Вы взаимодействуете с Нашим Телеграм ботом, предназначенного для управления группами.\n\n<b>1. Информация что мы собираем</b>\n\nКогда Вы используете Нашего Телеграм бота, мы можем собирать следующие типы информации:\n- <b>Информация о Пользователе</b>: Основная информация, такая как: Телеграм Айди, Ваш юзернейм, и любая другая информация, что Вы решите предоставить.\n- <b>Информация о Группе</b>: Информация относящаяся к группам, которыми Вы управляете, включая Айди группы, имя группы, и информацию по участникам.\n\n<b>2. Как Мы Используем Вашу Информацию</b>\n\nМы используем собранную информацию ждя:\n- Управления и поддержки функциональных возможностей группы.\n- Оказание поддержки и улучшения Наших услуг.\n- Сообщение о Важных Обновления или Изменениях, связанных с ботом.\n\n<b>3. Как Мы Защищаем Ваши Данные</b>\n\nМы применяем разоичные меры безопасности для защиты Вашей информации от несакционированного доступа, изменения, раскрытия или уничтожения. Однако, пожалуйста, имейте в виду, что ни один способ передачи через интернет или электронное хранилище не предоставляет 100% безопасность.\n\n<b>4. Обмен Вашей Информацией</b>\n\nМы не производим обмен Вашей информации с третьими лицами, за исключением случаев, предусмотренных законом, или необходимых для предоставления описанных услуг.\n\n<b>5. Ваши Права</b>\n\nУ Вас есть право на доступ, корректирование, или удаления Ваших персональных данных. Если вы хотите воспользоваться любым из этих прав, свяжитесь с нами напрямую через бот.\n\n<b>6. Изменения в Политике конфиденциальности</b>\n\nМы можем время от времени обновлять настоящую Политику конфиденциальности. Мы будем уведомлять вас о любых существенных изменениях путем обновления политики в боте. Вы обязаны периодически просматривать эту политику.\n\n<b>7. Связь с Нами</b>\n\nЕсли у вас есть вопросы или сомнения по поводу данной Политики конфиденциальности или наших действий, пожалуйста, свяжитесь с моим владельцем.",
|
||||
"run_eval": "<i>Производим eval в pyrogram..</i>",
|
||||
"run_exec": "<i>Производим exec в pyrogram..</i>",
|
||||
"no_cmd": "Не было дано ни одной команды для выполнения.",
|
||||
"success": "Успешно",
|
||||
"no_reply": "Без ответа"
|
||||
}
|
||||
3
locales/ru-RU/fun.json
Normal file
3
locales/ru-RU/fun.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"result": "🎲 Кубик остановился на номере: {number}"
|
||||
}
|
||||
6
locales/ru-RU/general.json
Normal file
6
locales/ru-RU/general.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"back_btn": "« Вернуться",
|
||||
"no_results": "Нет Результатов.",
|
||||
"unknown_id": "К сожалению, я не могу распознать этого Пользователя. Возможно я никогда не виделся с ним.",
|
||||
"exp_task": "😶🌫️ Время истекло. Задача была остановлена!"
|
||||
}
|
||||
13
locales/ru-RU/genss.json
Normal file
13
locales/ru-RU/genss.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"wait_msg": "Дайте мне немного времени, чтобы обработать Ваш запрос!! 😴",
|
||||
"wait_dl": "<code>Обработка, пожалуйста, подождите..</code>",
|
||||
"dl_progress": "Пытаюсь скачать, пожалуйста, подождите..",
|
||||
"up_progress": "Пытаюсь загрузить...",
|
||||
"success_dl_msg": "Файл был скачан в <code>{path}</code>.",
|
||||
"fail_open": "😟 Простите! Я не могу открыть этот файл.",
|
||||
"limit_dl": "К сожалению, Загрузка ограничена до 2 ГБ, чтобы уменьшить количество флуда. Вы можете конвертировать Ваши файлы в ссылки.",
|
||||
"err_ssgen": "Не удалось сгенерировать скриншот.\n\n{exc}",
|
||||
"up_msg": "☑️ Успешно сгенерирован скриншот.\n\n{namma} (<code>{id}</code>)\n#️⃣ #ssgen #айди{id}\n\nСкрин сгенерирован с помощью @{bot_uname}",
|
||||
"no_reply": "Ответьте на видео или документ в Телеграм, или используйте прямую ссылку после команды, чтобы создать скриншот из медиафайла!",
|
||||
"choose_no_ss": "Выберите количество результатов для скриншота? 🥳.\n\nОбщая продолжительность: `{td}` (`{dur} секунд`)"
|
||||
}
|
||||
13
locales/ru-RU/grup_tools.json
Normal file
13
locales/ru-RU/grup_tools.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"sudo_join_msg": "Ого, мой крутой владелец добавил меня в группу!",
|
||||
"log_bot_added": "#Новая Группа\nГруппа = {ttl}(<code>{cid}</code>)\nКол-во Участников = <code>{tot}</code>\nДобавил - {r_j}",
|
||||
"support_btn": "Поддержка",
|
||||
"help_btn": "ℹ️ Помощь",
|
||||
"update_btn": "📢 Обновления",
|
||||
"chat_not_allowed": "<b>ЧАТ НЕДОСТУПЕН 🐞\n\nМой владелец заблокировал меня от работы здесь! Вы можете связаться с владельцем бота..</b>",
|
||||
"welcome_thanks": "<b>Спасибочки, что добавили меня в {ttl} ❣️\n\nЕсли у Вас будут какие-нибудь проблемы или предложения, Вы можете написать мне.</b>",
|
||||
"capt_welc": "Приветик {umention} [<code>{uid}</code>], Добро пожаловать в группу {ttl}.",
|
||||
"combot_msg": "<b>#CAS Federation Ban</b>\nПользователь {umention} [<code>{uid}</code>] был замечен за спамботом и быль заблокирован. Работает на основе <a href='https://api.cas.chat/check?user_id={u.id}'>Combot AntiSpam.</a>",
|
||||
"spamwatch_msg": "<b>#SpamWatch Federation Ban</b>\nПользователь {umention} [<code>{uid}</code>] был заблокирован за <code>{reas}</code>.\n" ,
|
||||
"welcpic_msg": "Добро пожаловать {userr} [{id}]"
|
||||
}
|
||||
1
locales/ru-RU/help_menu.json
Normal file
1
locales/ru-RU/help_menu.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
5
locales/ru-RU/lang_setting.json
Normal file
5
locales/ru-RU/lang_setting.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"language_changed_successfully": "Язык был успешно изменен.",
|
||||
"language_changer_chat": "Здесь Вы можете изменить язык, используемый ботов в чате.\nЕсли Вашего языка нет в этом списке, и Вы хотите внести свой вклад, Вы можете открыть вопрос в моем репозитории в гитхабе.",
|
||||
"language_changer_private": "Здесь Вы можете изменить язык, используемый ботов в приватном чате.\n\nЕсли Вы хотите поменять язык, пожалуйста, воспользуйтесь командой <code>/setchatlang</code> on it.\nЕсли Вашего языка нет в этом списке, и Вы хотите внести свой вклад, Вы можете открыть вопрос в моем репозитории в гитхабе."
|
||||
}
|
||||
4
locales/ru-RU/main.json
Normal file
4
locales/ru-RU/main.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"language_name": "Russian",
|
||||
"language_flag": "🇷🇺"
|
||||
}
|
||||
15
locales/ru-RU/media_extractor.json
Normal file
15
locales/ru-RU/media_extractor.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"sub_extr_help": "Пожалуйста, воспользуйтесь командой /{cmd} [Ссылка] чтобы посмотреть субтитры или аудио в видео-файле.",
|
||||
"conv_sub_help": "Используйте команду /{cmd} ответом на .ass или .vtt файл, чтобы конвертировать файлы .ass или .vtt в srt.",
|
||||
"progress_str": "⏳ Обработка запроса..",
|
||||
"convert_str": "⏳ Конвертация...",
|
||||
"unauth_cb": "⚠️ Отказано в доступе!",
|
||||
"cancel_btn": "❌ Отмена",
|
||||
"invalid_cb": "⚠️ НЕ УДАЛЯЙТЕ ВАШЕ СООБЩЕНИЕ!",
|
||||
"up_str": "Загрузка файлов..",
|
||||
"press_btn_msg": "Нажмите на кнопку, чтобы конвертировать субтитры/аудио. В данный момент поддерживаются только ссылки.\nОбработано за {timelog}",
|
||||
"fail_extr_media": "Не удалось извлечь носитель, убедитесь, что ваша ссылка не защищена WAF или может быть недоступна для бота.",
|
||||
"fail_extr_sub": "Не удалось извлечь суб-файл, возможно, это неподдерживаемый формат.\n\nСсылка: {link}\nОшибка: {e}",
|
||||
"capt_extr_sub": "<b>Файл:</b> <code>{nf}</code>\n\nИзвлечено с помощью @{bot} за {timelog}",
|
||||
"capt_conv_sub": "<code>{nf}.srt</code>\n\nКонвертирован с помощью @{bot}"
|
||||
}
|
||||
11
locales/ru-RU/mediainfo.json
Normal file
11
locales/ru-RU/mediainfo.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"processing_text": "`Обработка. Общее время зависит от размера Ваших файлов...`",
|
||||
"wait_msg": "`Пожалуйста, подождите немного...`",
|
||||
"err_link": "Похоже, что отправленная Вами ссылка недействительна, убедитесь, что это прямая ссылка и ее можно скачать.",
|
||||
"media_invalid": "Пожалуйста, ответьте на действительный медиа-файл.",
|
||||
"dl_limit_exceeded": "К сожалению, загрузка ограничена до 2GB для уменьшения флуда. Вы можете конвертировать Ваши файлы в ссылку.",
|
||||
"dl_args_text": "Пытаюсь скачать..",
|
||||
"mediainfo_help": "Используйте команду /{cmd} [ссылка], или ответьте на сообщение с медиа командой /{cmd}.",
|
||||
"capt_media": "ℹ️ Ваши результаты медаинфо..\n\n**Запрос от:** {ment}",
|
||||
"viweb": "💬 Открыть ссылку"
|
||||
}
|
||||
16
locales/ru-RU/nightmodev2.json
Normal file
16
locales/ru-RU/nightmodev2.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"nmd_disabled": "Ночной режим выключен.",
|
||||
"nmd_not_enabled": "Ночной режим не включен в этом чате.",
|
||||
"invalid_time_format": "Неверный формат времени. Используйте формат HH:MM.",
|
||||
"invalid_lockdur": "Неверная продолжительность. Используйте правильный формат.\nНапример: 6h (для 6 часов), 10m для 10 минут.",
|
||||
"schedule_already_on": "Задание уже запущено в этом чате. Для отключения используйте аргумент `-d`.",
|
||||
"nmd_enable_success": "Успешно запущен Ночной режим в этом чате.\nГруппа будет закрыта на {st} и будет открываться в {lockdur} каждый день.",
|
||||
"nmd_off_not_admin": "#Ночной_режим_ОШИБКА\nНе удалось выключить Ночной режим aв `{chat_id}`, поскольку {bname} не администратор в чате `{chat_id}`",
|
||||
"nmd_off_not_present": "#Ночной_режим_ОШИБКА\nНе удалось выключить Ночной режим в `{chat_id}`, поскольку {bname} не присутствует в чате `{chat_id}`. Группа удалена из списка.",
|
||||
"nmd_off_err": "#Ночной_режим_ОШИБКА\nНе удалось выключить Ночной режим в `{chat_id}`\nОШИБКА: `{e}`",
|
||||
"nmd_off_success": "#Ночной_режим_ОБРАБОТЧИК\n📆 {dt}\n\n☀️ Группа открыта.\nЗакроется в {close_at}",
|
||||
"nmd_on_not_admin": "#Ночной_режим_ОШИБКА\nНе удалось включить Ночной режим в `{chat_id}`, поскольку {bname} не администратор в чате `{chat_id}`",
|
||||
"nmd_on_not_present": "#Ночной_режим_ОШИБКА\nНе удалось включить Ночной режим в `{chat_id}`, поскольку {bname} не присутствует в чате `{chat_id}`. Группа удалена из списка.",
|
||||
"nmd_on_err": "#Ночной_режим_ОШИБКА\nНе удалось включить Ночной режим в `{chat_id}`\nОШИБКА: `{e}`",
|
||||
"nmd_on_success": "#Ночной_режим_ОБРАБОТЧИК\n📆 {dt}\n\n🌗 Группа закрывается.\nБудет открыта в {open_at}"
|
||||
}
|
||||
6
locales/ru-RU/ocr.json
Normal file
6
locales/ru-RU/ocr.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"no_photo": "Ответьте на Изображение командой /{cmd} чтобы прочитать текст.",
|
||||
"read_ocr": "Читаю изображение..",
|
||||
"result_ocr": "Распознано:\n<code>{result}</code>",
|
||||
"ocr_helper": "/ocr [Ответом на Изображение] - Прочесть Текст с Изображения"
|
||||
}
|
||||
13
locales/ru-RU/sangmata.json
Normal file
13
locales/ru-RU/sangmata.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"no_uname": "<code>Нет Юзернейма</code>",
|
||||
"no_last_name": "<code>Нет Фамилии</code>",
|
||||
"uname_change_msg": "✨ Изменил Юзернейм: {bef} ➡️ {aft}.\n",
|
||||
"lastname_change_msg": "✨ Изменил Фамилию: {bef} ➡️ {aft}.\n",
|
||||
"firstname_change_msg": "✨ Изменил Имя: {bef} ➡️ {aft}.\n",
|
||||
"set_sangmata_help": "Используйте <code>/{cmd} on</code>, чтобы включить СангМату. Если Вы хотите выключить, используйте параметр off.",
|
||||
"sangmata_already_on": "СангМата уже включена в Ваших группах.",
|
||||
"sangmata_enabled": "СангМата включена в Ваших группах.",
|
||||
"sangmata_already_off": "СангМата уже выключена в Ваших группах.",
|
||||
"sangmata_disabled": "СангМата выключена в Ваших группах.",
|
||||
"wrong_param": "Неизвестный параметр, используйте только параметры on/off."
|
||||
}
|
||||
11
locales/ru-RU/start_help.json
Normal file
11
locales/ru-RU/start_help.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"newgroup_log": "#НоваяГруппа\nГруппа = {jdl}(<code>{id}</code>)\nКол-во Участников = <code>{c}</code>",
|
||||
"newuser_log": "#НовыйПользователь\nАйди - <code>{id}</code>\nИмя - {nm}",
|
||||
"help_name": "Здесь помощь по **{mod}**:\n",
|
||||
"help_txt": "Привет {kamuh}, Меня зовут {bot}.\nЯ бот, разработанный на pyrogram с некоторыми полезными функциями.\nТы можешь ознакомиться, кликнув по кнопке ниже.\n\nОсновные команды:\n - /start: Запустить бота\n - /help: получить это сообщение\n - /setlang: Изменить язык бота [БЕТА]",
|
||||
"click_me": "Кликни",
|
||||
"back_btn": "Назад",
|
||||
"click_btn": "Кликни на кнопку ниже, чтобы получить помощь по {nm}",
|
||||
"pm_detail": "Напиши мне в ЛС для получения Подробной Информации.",
|
||||
"start_msg": "Привет {kamuh}, Напиши мне в ЛС, чтобы получить информацио о моих функциях. Ты можешь изменить язык бота, используя команду /setlang, но она всё ещё на стадии бета."
|
||||
}
|
||||
19
locales/ru-RU/stickers.json
Normal file
19
locales/ru-RU/stickers.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"no_anim_stick": "Анимационные стикеры не поддерживаются!",
|
||||
"not_sticker": "Это не стикер!",
|
||||
"unkang_msg": "Попытка убрать с пака..",
|
||||
"unkang_success": "Стикер был убран с Вашего пака",
|
||||
"unkang_error": "Не удалось убрать стикер из пака.\n\nОШИБКА: {e}",
|
||||
"unkang_help": "Пожалуйста, ответьте на стикерпак, созданный {c}, чтобы удалить стикер из пака.",
|
||||
"anon_warn": "Вы анон-админ, создайте стикеры в ЛС со мной.",
|
||||
"kang_msg": "Пытается украсть Ваш стикер...",
|
||||
"stick_no_name": "У стикера нет названия.",
|
||||
"kang_help": "Хотите чтобы я угадал стикер? Пожалуйста, отметьте стикер.",
|
||||
"exist_pack": "<code>Используем существующий стикерпак...</code>",
|
||||
"new_packs": "<b>Создаем новый стикерпак...</b>",
|
||||
"please_start_msg": "Похоже, Вы никогда не общались со мной в личном чате, Вам нужно сделать это первым...",
|
||||
"click_me": "Кликни",
|
||||
"pack_full": "Ваш набор стикеров полон, если Ваш набор не находится в v1 Напишите /kang 1\nЕсли он не находится в v2 Напиши /kang 2\nи так далее..",
|
||||
"viewpack": "👀 Посмотреть Ваш пак",
|
||||
"kang_success": "<b>Стикеры успешно украдены!</b>\n<b>Эмодзи:</b> {emot}"
|
||||
}
|
||||
20
locales/ru-RU/web_scraper.json
Normal file
20
locales/ru-RU/web_scraper.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"no_result": "К сожалению, не удалось найти никаких результатов!",
|
||||
"no_result_w_query": "К сожалению, нет результатов по запросу: {kueri}",
|
||||
"get_data": "⏳ Пожалуйста, подождите, получаю данные..",
|
||||
"cl_btn": "❌ Закрыть",
|
||||
"back_btn": "↩️ Вернуться",
|
||||
"dl_text": "⬇️ Скачать",
|
||||
"cat_text": "Категория",
|
||||
"quality": "Качество",
|
||||
"ex_data": "👇 Вытащить данные ",
|
||||
"unauth": "Эта кнопка не для Вас..",
|
||||
"invalid_cb": "Неверные данные обратного вызова, отправьте команду еще раз..",
|
||||
"res_scrape": "<b>Собранная информация с</b> <code>{link}</code>:\n\n{kl}",
|
||||
"header_with_query": "<b>#{web} Результаты Для:</b> <code>{kueri}</code>\n\n",
|
||||
"header_no_query": "<b>#{web} Последнее:</b>\n🌀 Используйте /{cmd} [заголовок] чтобы начать поиск по заголовку.\n\n",
|
||||
"invalid_cmd_scrape": "Используйте команду /{cmd} <b>[link]</b> для поиска ссылки на скачивание.",
|
||||
"err_getweb": "ОШИБКА: Не удалось получить данные с интернета из-за {err}.",
|
||||
"err_getapi": "ОШИБКА: Не удалось получить данные с АПИ",
|
||||
"unsupport_dl_btn": "Некоторые результаты не отображаются в кнопке извлечения из-за неподдерживаемой ссылки."
|
||||
}
|
||||
6
locales/ru-RU/webss.json
Normal file
6
locales/ru-RU/webss.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"no_url": "Отправьте ссылку, чтобы сделать скриншот.",
|
||||
"wait_str": "Делаю скрин...",
|
||||
"str_credit": "🌞 Скриншот сгенерирован при помощи Puppeteer",
|
||||
"ss_failed_str": "Не удалось сделать скриншот.\nОшибка: {err}"
|
||||
}
|
||||
14
locales/ru-RU/ytdl_plugins.json
Normal file
14
locales/ru-RU/ytdl_plugins.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"no_channel": "Эта команда недоступна для группы или анонимного пользователя.",
|
||||
"no_query": "Пожалуйста, введите запрос..!",
|
||||
"no_res": "Нет результатов по `{kweri}`",
|
||||
"dl_btn": "Скачать",
|
||||
"back": "Вернуться",
|
||||
"yts_msg": "Опубликовано {pub}\n\n<b>❯ Длительность:</b> {dur}\n<b>❯ Просмотров:</b> {vi}\n<b>❯ Загрузил:</b> <a href='{clink}'>{cname}</a>\n\n",
|
||||
"invalid_link": "Пожалуйста, введите корректную ссылку для YT-DLP",
|
||||
"err_parse": "Не удалось обработать ссылку, смотрите логи..",
|
||||
"wait": "Пожалуйста, подождите..",
|
||||
"unauth": "Это не Ваша задача..",
|
||||
"endlist": "Она в конце списка",
|
||||
"vip-btn": "Поскольку некоторые пользователи злоупотребляют функцией, а мой сервер не может справиться с этим, лучшее видео теперь доступно только для владельцу бота."
|
||||
}
|
||||
|
|
@ -2,45 +2,57 @@
|
|||
# * @date 2023-06-21 22:12:27
|
||||
# * @projectName MissKatyPyro
|
||||
# * Copyright ©YasirPedia All rights reserved
|
||||
import os
|
||||
import time
|
||||
from asyncio import get_event_loop
|
||||
from faulthandler import enable as faulthandler_enable
|
||||
from logging import ERROR, INFO, StreamHandler, basicConfig, getLogger, handlers
|
||||
|
||||
import uvloop, uvicorn
|
||||
from apscheduler.jobstores.mongodb import MongoDBJobStore
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from async_pymongo import AsyncClient
|
||||
from pymongo import MongoClient
|
||||
from pyrogram import Client
|
||||
from web.webserver import api
|
||||
|
||||
from misskaty.core import misskaty_patch
|
||||
from misskaty.vars import (
|
||||
API_HASH,
|
||||
API_ID,
|
||||
BOT_TOKEN,
|
||||
DATABASE_NAME,
|
||||
DATABASE_URI,
|
||||
PORT,
|
||||
TZ,
|
||||
USER_SESSION,
|
||||
)
|
||||
|
||||
basicConfig(
|
||||
level=INFO,
|
||||
format="[%(asctime)s - %(levelname)s] - %(name)s.%(funcName)s - %(message)s",
|
||||
format="[%(levelname)s] - [%(asctime)s - %(name)s - %(message)s] -> [%(module)s:%(lineno)d]",
|
||||
datefmt="%d-%b-%y %H:%M:%S",
|
||||
handlers=[
|
||||
handlers.RotatingFileHandler("MissKatyLogs.txt", mode="w+", maxBytes=1000000),
|
||||
handlers.RotatingFileHandler(
|
||||
"MissKatyLogs.txt", mode="w+", maxBytes=5242880, backupCount=1
|
||||
),
|
||||
StreamHandler(),
|
||||
],
|
||||
)
|
||||
getLogger("pyrogram").setLevel(ERROR)
|
||||
getLogger("openai").setLevel(ERROR)
|
||||
getLogger("httpx").setLevel(ERROR)
|
||||
getLogger("iytdl").setLevel(ERROR)
|
||||
|
||||
MOD_LOAD = []
|
||||
MOD_NOLOAD = ["subscene_dl"]
|
||||
HELPABLE = {}
|
||||
cleanmode = {}
|
||||
botStartTime = time.time()
|
||||
misskaty_version = "v2.10.2 - Stable"
|
||||
misskaty_version = "v2.16.1"
|
||||
|
||||
uvloop.install()
|
||||
faulthandler_enable()
|
||||
from misskaty.core import misskaty_patch
|
||||
|
||||
# Pyrogram Bot Client
|
||||
app = Client(
|
||||
|
|
@ -48,13 +60,22 @@ app = Client(
|
|||
api_id=API_ID,
|
||||
api_hash=API_HASH,
|
||||
bot_token=BOT_TOKEN,
|
||||
mongodb=dict(connection=AsyncClient(DATABASE_URI), remove_peers=False),
|
||||
mongodb=dict(connection=AsyncClient(DATABASE_URI), remove_peers=True),
|
||||
sleep_threshold=180,
|
||||
app_version="MissKatyPyro Stable",
|
||||
workers=50,
|
||||
max_concurrent_transmissions=4,
|
||||
)
|
||||
app.db = AsyncClient(DATABASE_URI)
|
||||
app.log = getLogger("MissKaty")
|
||||
|
||||
# Pyrogram UserBot Client
|
||||
user = Client(
|
||||
"YasirUBot",
|
||||
session_string=USER_SESSION,
|
||||
mongodb=dict(connection=AsyncClient(DATABASE_URI), remove_peers=False),
|
||||
sleep_threshold=180,
|
||||
app_version="MissKaty Ubot",
|
||||
)
|
||||
|
||||
jobstores = {
|
||||
|
|
@ -64,15 +85,26 @@ jobstores = {
|
|||
}
|
||||
scheduler = AsyncIOScheduler(jobstores=jobstores, timezone=TZ)
|
||||
|
||||
async def run_wsgi():
|
||||
config = uvicorn.Config(api, host="0.0.0.0", port=int(PORT))
|
||||
server = uvicorn.Server(config)
|
||||
await server.serve()
|
||||
|
||||
app.start()
|
||||
BOT_ID = app.me.id
|
||||
BOT_NAME = app.me.first_name
|
||||
BOT_USERNAME = app.me.username
|
||||
if USER_SESSION:
|
||||
user.start()
|
||||
UBOT_ID = user.me.id
|
||||
UBOT_NAME = user.me.first_name
|
||||
UBOT_USERNAME = user.me.username
|
||||
try:
|
||||
user.start()
|
||||
UBOT_ID = user.me.id
|
||||
UBOT_NAME = user.me.first_name
|
||||
UBOT_USERNAME = user.me.username
|
||||
except Exception as e:
|
||||
app.log.error(f"Error while starting UBot: {e}")
|
||||
UBOT_ID = None
|
||||
UBOT_NAME = None
|
||||
UBOT_USERNAME = None
|
||||
else:
|
||||
UBOT_ID = None
|
||||
UBOT_NAME = None
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
"""
|
||||
* @author yasir <yasiramunandar@gmail.com>
|
||||
* @date 2022-12-01 09:12:27
|
||||
* @projectName MissKatyPyro
|
||||
* Copyright @YasirPedia All rights reserved
|
||||
"""
|
||||
* @author yasir <yasiramunandar@gmail.com>
|
||||
* @date 2022-12-01 09:12:27
|
||||
* @projectName MissKatyPyro
|
||||
* Copyright @YasirPedia All rights reserved
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import importlib
|
||||
import os
|
||||
|
|
@ -15,14 +16,22 @@ from pyrogram import __version__, idle
|
|||
from pyrogram.raw.all import layer
|
||||
|
||||
from database import dbname
|
||||
from misskaty import BOT_NAME, BOT_USERNAME, HELPABLE, UBOT_NAME, app, scheduler
|
||||
from misskaty import (
|
||||
BOT_NAME,
|
||||
BOT_USERNAME,
|
||||
HELPABLE,
|
||||
UBOT_NAME,
|
||||
app,
|
||||
get_event_loop,
|
||||
scheduler,
|
||||
run_wsgi
|
||||
)
|
||||
from misskaty.plugins import ALL_MODULES
|
||||
from misskaty.plugins.web_scraper import web
|
||||
from misskaty.vars import SUDO, USER_SESSION
|
||||
from misskaty.vars import OWNER_ID, USER_SESSION
|
||||
from utils import auto_clean
|
||||
|
||||
LOGGER = getLogger(__name__)
|
||||
loop = asyncio.get_event_loop()
|
||||
LOGGER = getLogger("MissKaty")
|
||||
|
||||
|
||||
# Run Bot
|
||||
|
|
@ -48,25 +57,24 @@ async def start_bot():
|
|||
LOGGER.info(bot_modules)
|
||||
LOGGER.info("+===============+===============+===============+===============+")
|
||||
LOGGER.info("[INFO]: BOT STARTED AS @%s!", BOT_USERNAME)
|
||||
|
||||
try:
|
||||
LOGGER.info("[INFO]: SENDING ONLINE STATUS")
|
||||
for i in SUDO:
|
||||
if USER_SESSION:
|
||||
await app.send_message(
|
||||
i,
|
||||
f"USERBOT AND BOT STARTED with Pyrogram v{__version__}..\nUserBot: {UBOT_NAME}\nBot: {BOT_NAME}\n\nwith Pyrogram v{__version__} (Layer {layer}) started on @{BOT_USERNAME}.\n\n<code>{bot_modules}</code>",
|
||||
)
|
||||
else:
|
||||
await app.send_message(
|
||||
i,
|
||||
f"BOT STARTED with Pyrogram v{__version__} as {BOT_NAME}\n\nwith Pyrogram v{__version__} (Layer {layer}) started on @{BOT_USERNAME}.\n\n<code>{bot_modules}</code>",
|
||||
)
|
||||
if USER_SESSION:
|
||||
await app.send_message(
|
||||
OWNER_ID,
|
||||
f"USERBOT AND BOT STARTED with Pyrogram v{__version__}..\nUserBot: {UBOT_NAME}\nBot: {BOT_NAME}\n\nwith Pyrogram v{__version__} (Layer {layer}) started on @{BOT_USERNAME}.\n\n<code>{bot_modules}</code>",
|
||||
)
|
||||
else:
|
||||
await app.send_message(
|
||||
OWNER_ID,
|
||||
f"BOT STARTED with Pyrogram v{__version__}..\nBot: {BOT_NAME}\n\nwith Pyrogram v{__version__} (Layer {layer}) started on @{BOT_USERNAME}.\n\n<code>{bot_modules}</code>",
|
||||
)
|
||||
except Exception as e:
|
||||
LOGGER.error(str(e))
|
||||
scheduler.start()
|
||||
asyncio.create_task(run_wsgi())
|
||||
if "web" not in await dbname.list_collection_names():
|
||||
webdb = dbname.web
|
||||
webdb = dbname["web"]
|
||||
for key, value in web.items():
|
||||
await webdb.insert_one({key: value})
|
||||
if os.path.exists("restart.pickle"):
|
||||
|
|
@ -84,14 +92,15 @@ async def start_bot():
|
|||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
loop.run_until_complete(start_bot())
|
||||
get_event_loop().run_until_complete(start_bot())
|
||||
app.loop.run_forever()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except Exception:
|
||||
err = traceback.format_exc()
|
||||
LOGGER.info(err)
|
||||
finally:
|
||||
loop.stop()
|
||||
app.loop.stop()
|
||||
LOGGER.info(
|
||||
"------------------------ Stopped Services ------------------------"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
# skipcq
|
||||
from .errors import *
|
||||
from .misc import *
|
||||
from .permissions import *
|
||||
from .ratelimiter import *
|
||||
from .errors import capture_err
|
||||
from .misc import asyncify, new_task
|
||||
from .permissions import adminsOnly, require_admin
|
||||
|
||||
__all__ = [
|
||||
"capture_err",
|
||||
"asyncify",
|
||||
"new_task",
|
||||
"adminsOnly",
|
||||
"require_admin",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ from functools import wraps
|
|||
from pyrogram.errors.exceptions.forbidden_403 import ChatWriteForbidden
|
||||
from pyrogram.types import CallbackQuery
|
||||
|
||||
from misskaty import app
|
||||
from misskaty.vars import LOG_CHANNEL
|
||||
|
||||
|
||||
|
|
@ -24,7 +23,7 @@ def capture_err(func):
|
|||
try:
|
||||
return await func(client, message, *args, **kwargs)
|
||||
except ChatWriteForbidden:
|
||||
return await app.leave_chat(message.chat.id)
|
||||
return await client.leave_chat(message.chat.id)
|
||||
except Exception as err:
|
||||
exc = traceback.format_exc()
|
||||
error_feedback = "ERROR | {} | {}\n\n{}\n\n{}\n".format(
|
||||
|
|
@ -45,7 +44,7 @@ def capture_err(func):
|
|||
) as log:
|
||||
log.write(error_feedback)
|
||||
log.close()
|
||||
await app.send_document(
|
||||
await client.send_document(
|
||||
LOG_CHANNEL,
|
||||
f"crash_{tgl_now.strftime('%d %B %Y')}.txt",
|
||||
caption=f"Crash Report of this Bot\n{cap_day}",
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
import asyncio
|
||||
import logging
|
||||
from functools import wraps
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def asyncify(func):
|
||||
async def inner(*args, **kwargs):
|
||||
loop = asyncio.get_running_loop()
|
||||
func_out = await loop.run_in_executor(None, func, *args, **kwargs)
|
||||
return func_out
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
def new_task(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
return loop.create_task(func(*args, **kwargs))
|
||||
except Exception as e:
|
||||
LOGGER.error(
|
||||
f"Failed to create task for {func.__name__} : {e}"
|
||||
) # skipcq: PYL-E0602
|
||||
|
||||
return wrapper
|
||||
import asyncio
|
||||
import logging
|
||||
from functools import wraps
|
||||
|
||||
LOGGER = logging.getLogger("MissKaty")
|
||||
|
||||
|
||||
def asyncify(func):
|
||||
async def inner(*args, **kwargs):
|
||||
loop = asyncio.get_running_loop()
|
||||
func_out = await loop.run_in_executor(None, func, *args, **kwargs)
|
||||
return func_out
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
def new_task(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
return loop.create_task(func(*args, **kwargs))
|
||||
except Exception as e:
|
||||
LOGGER.error(
|
||||
f"Failed to create task for {func.__name__} : {e}"
|
||||
) # skipcq: PYL-E0602
|
||||
|
||||
return wrapper
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ from time import time
|
|||
from traceback import format_exc as err
|
||||
from typing import Optional, Union
|
||||
|
||||
from cachetools import TTLCache
|
||||
from pyrogram import Client, enums
|
||||
from pyrogram.errors import ChannelPrivate, ChatAdminRequired, ChatWriteForbidden
|
||||
from pyrogram.types import CallbackQuery, Message
|
||||
|
||||
from misskaty import app
|
||||
from misskaty.vars import SUDO
|
||||
from misskaty.helper.sqlite_helper import Cache
|
||||
from misskaty.vars import SUDO, OWNER_ID
|
||||
|
||||
from ...helper.localization import (
|
||||
default_language,
|
||||
|
|
@ -93,7 +93,7 @@ async def check_perms(
|
|||
return False
|
||||
|
||||
|
||||
admins_in_chat = TTLCache(maxsize=1000, ttl=6 * 60 * 60)
|
||||
admins_in_chat = Cache(filename="admin_cache.db", path="cache", in_memory=False)
|
||||
|
||||
|
||||
async def list_admins(chat_id: int):
|
||||
|
|
@ -103,26 +103,29 @@ async def list_admins(chat_id: int):
|
|||
return admins_in_chat[chat_id]["data"]
|
||||
|
||||
try:
|
||||
admins_in_chat[chat_id] = {
|
||||
"last_updated_at": time(),
|
||||
"data": [
|
||||
member.user.id
|
||||
async for member in app.get_chat_members(
|
||||
chat_id, filter=enums.ChatMembersFilter.ADMINISTRATORS
|
||||
)
|
||||
],
|
||||
}
|
||||
admins_in_chat.add(
|
||||
chat_id,
|
||||
{
|
||||
"last_updated_at": time(),
|
||||
"data": [
|
||||
member.user.id
|
||||
async for member in app.get_chat_members(
|
||||
chat_id, filter=enums.ChatMembersFilter.ADMINISTRATORS
|
||||
)
|
||||
],
|
||||
},
|
||||
timeout=6 * 60 * 60,
|
||||
)
|
||||
return admins_in_chat[chat_id]["data"]
|
||||
except ChannelPrivate:
|
||||
return
|
||||
|
||||
|
||||
async def authorised(func, subFunc2, client, message, *args, **kwargs):
|
||||
chatID = message.chat.id
|
||||
try:
|
||||
await func(client, message, *args, **kwargs)
|
||||
except ChatWriteForbidden:
|
||||
await app.leave_chat(chatID)
|
||||
await message.chat.leave()
|
||||
except Exception as e:
|
||||
try:
|
||||
await message.reply_text(str(e.MESSAGE))
|
||||
|
|
@ -135,11 +138,10 @@ async def authorised(func, subFunc2, client, message, *args, **kwargs):
|
|||
|
||||
async def unauthorised(message: Message, permission, subFunc2):
|
||||
text = f"You don't have the required permission to perform this action.\n**Permission:** __{permission}__"
|
||||
chatID = message.chat.id
|
||||
try:
|
||||
await message.reply_text(text)
|
||||
except ChatWriteForbidden:
|
||||
await app.leave_chat(chatID)
|
||||
await message.chat.leave()
|
||||
return subFunc2
|
||||
|
||||
|
||||
|
|
@ -163,7 +165,7 @@ def adminsOnly(permission):
|
|||
# For admins and sudo users
|
||||
userID = message.from_user.id
|
||||
permissions = await member_permissions(chatID, userID)
|
||||
if userID not in SUDO and permission not in permissions:
|
||||
if userID not in SUDO or userID != OWNER_ID and permission not in permissions:
|
||||
return await unauthorised(message, permission, subFunc2)
|
||||
return await authorised(func, subFunc2, client, message, *args, **kwargs)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
from functools import wraps
|
||||
from typing import Callable, Union
|
||||
|
||||
from cachetools import TTLCache
|
||||
from pyrogram import Client
|
||||
from pyrogram.errors import QueryIdInvalid
|
||||
from pyrogram.types import CallbackQuery, Message
|
||||
|
||||
from ..ratelimiter_func import RateLimiter
|
||||
|
||||
ratelimit = RateLimiter()
|
||||
# storing spammy user in cache for 1minute before allowing them to use commands again.
|
||||
warned_users = TTLCache(maxsize=128, ttl=60)
|
||||
warning_message = "Spam detected! ignoring your all requests for few minutes."
|
||||
|
||||
|
||||
def ratelimiter(func: Callable) -> Callable:
|
||||
"""
|
||||
Restricts user's from spamming commands or pressing buttons multiple times
|
||||
using leaky bucket algorithm and pyrate_limiter.
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
async def decorator(client: Client, update: Union[Message, CallbackQuery]):
|
||||
userid = update.from_user.id if update.from_user else update.sender_chat.id
|
||||
is_limited = await ratelimit.acquire(userid)
|
||||
|
||||
if is_limited and userid not in warned_users:
|
||||
if isinstance(update, Message):
|
||||
await update.reply_text(warning_message)
|
||||
warned_users[userid] = 1
|
||||
return
|
||||
|
||||
elif isinstance(update, CallbackQuery):
|
||||
try:
|
||||
await update.answer(warning_message, show_alert=True)
|
||||
except QueryIdInvalid:
|
||||
warned_users[userid] = 1
|
||||
return
|
||||
warned_users[userid] = 1
|
||||
return
|
||||
|
||||
elif is_limited and userid in warned_users:
|
||||
pass
|
||||
else:
|
||||
return await func(client, update)
|
||||
|
||||
return decorator
|
||||
|
|
@ -1 +1 @@
|
|||
from . import bound, listen, methods, decorators
|
||||
from . import bound, decorators, methods
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ from logging import getLogger
|
|||
from typing import Union
|
||||
|
||||
from pyrogram.errors import (
|
||||
ChannelPrivate,
|
||||
ChatAdminRequired,
|
||||
ChatSendPlainForbidden,
|
||||
ChatWriteForbidden,
|
||||
FloodWait,
|
||||
MessageAuthorRequired,
|
||||
|
|
@ -14,16 +16,16 @@ from pyrogram.errors import (
|
|||
MessageIdInvalid,
|
||||
MessageNotModified,
|
||||
MessageTooLong,
|
||||
TopicClosed,
|
||||
)
|
||||
from pyrogram.types import Message
|
||||
|
||||
LOGGER = getLogger(__name__)
|
||||
LOGGER = getLogger("MissKaty")
|
||||
|
||||
Message.input = property(
|
||||
lambda m: m.text[m.text.find(m.command[0]) + len(m.command[0]) + 1 :]
|
||||
if len(m.command) > 1
|
||||
else None
|
||||
)
|
||||
|
||||
@property
|
||||
def parse_cmd(msg):
|
||||
return msg.text.split(None, 1)[1] if len(msg.command) > 1 else None
|
||||
|
||||
|
||||
async def reply_text(
|
||||
|
|
@ -86,11 +88,14 @@ async def reply_text(
|
|||
await asleep(del_in)
|
||||
return bool(await msg.delete_msg())
|
||||
except FloodWait as e:
|
||||
LOGGER.warning(f"Got floodwait in {self.chat.id} for {e.value}'s.")
|
||||
await asleep(e.value)
|
||||
return await reply_text(self, text, *args, **kwargs)
|
||||
except (ChatWriteForbidden, ChatAdminRequired):
|
||||
except (TopicClosed, ChannelPrivate):
|
||||
return
|
||||
except (ChatWriteForbidden, ChatAdminRequired, ChatSendPlainForbidden):
|
||||
LOGGER.info(
|
||||
f"Leaving from {self.chat.title} [{self.chat.id}] because doesn't have admin permission."
|
||||
f"Leaving from {self.chat.title} [{self.chat.id}] because doesn't have enough permission."
|
||||
)
|
||||
return await self.chat.leave()
|
||||
|
||||
|
|
@ -130,10 +135,10 @@ async def edit_text(
|
|||
await asleep(del_in)
|
||||
return bool(await msg.delete_msg())
|
||||
except FloodWait as e:
|
||||
LOGGER.warning(str(e))
|
||||
LOGGER.warning(f"Got floodwait in {self.chat.id} for {e.value}'s.")
|
||||
await asleep(e.value)
|
||||
return await edit_text(self, text, *args, **kwargs)
|
||||
except MessageNotModified:
|
||||
except (MessageNotModified, ChannelPrivate):
|
||||
return False
|
||||
except (ChatWriteForbidden, ChatAdminRequired):
|
||||
LOGGER.info(
|
||||
|
|
@ -318,3 +323,4 @@ Message.edit_or_send_as_file = edit_or_send_as_file
|
|||
Message.reply_or_send_as_file = reply_or_send_as_file
|
||||
Message.reply_as_file = reply_as_file
|
||||
Message.delete_msg = delete
|
||||
Message.input = parse_cmd
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from .callback import callback
|
||||
from .command import command
|
||||
|
||||
__all__ = ["callback", "command"]
|
||||
from .adminsOnly import adminsOnly
|
||||
from .callback import callback
|
||||
from .command import command
|
||||
|
||||
__all__ = ["callback", "command", "adminsOnly"]
|
||||
|
|
|
|||
208
misskaty/core/misskaty_patch/decorators/adminsOnly.py
Normal file
208
misskaty/core/misskaty_patch/decorators/adminsOnly.py
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram
|
||||
# Copyright (C) 2021 - 2022 Jayant Hegde Kageri <https://github.com/jayantkageri>
|
||||
|
||||
# This file is part of tgEasy.
|
||||
|
||||
# tgEasy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# tgEasy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import contextlib
|
||||
import typing
|
||||
|
||||
import pyrogram
|
||||
from cachetools import TTLCache
|
||||
from pyrogram.methods import Decorators
|
||||
|
||||
from ..utils import check_rights, handle_error, is_admin
|
||||
|
||||
ANON = TTLCache(maxsize=250, ttl=30)
|
||||
|
||||
|
||||
async def anonymous_admin(m: pyrogram.types.Message):
|
||||
"""
|
||||
Helper function for Anonymous Admin Verification
|
||||
"""
|
||||
keyboard = pyrogram.types.InlineKeyboardMarkup(
|
||||
[
|
||||
[
|
||||
pyrogram.types.InlineKeyboardButton(
|
||||
text="Verify!",
|
||||
callback_data=f"anon.{m.id}",
|
||||
),
|
||||
]
|
||||
]
|
||||
)
|
||||
return await m.reply_text(
|
||||
"Click here to prove you are admin with the required rights to perform this action!",
|
||||
reply_markup=keyboard,
|
||||
)
|
||||
|
||||
|
||||
async def anonymous_admin_verification(
|
||||
self, CallbackQuery: pyrogram.types.CallbackQuery
|
||||
):
|
||||
if int(
|
||||
f"{CallbackQuery.message.chat.id}{CallbackQuery.data.split('.')[1]}"
|
||||
) not in set(ANON.keys()):
|
||||
try:
|
||||
await CallbackQuery.message.edit_text("Button has been Expired.")
|
||||
except pyrogram.errors.RPCError:
|
||||
with contextlib.suppress(pyrogram.errors.RPCError):
|
||||
await CallbackQuery.message.delete()
|
||||
return
|
||||
cb = ANON.pop(
|
||||
int(f"{CallbackQuery.message.chat.id}{CallbackQuery.data.split('.')[1]}")
|
||||
)
|
||||
try:
|
||||
member = await CallbackQuery.message.chat.get_member(CallbackQuery.from_user.id)
|
||||
except pyrogram.errors.exceptions.bad_request_400.UserNotParticipant:
|
||||
return await CallbackQuery.answer(
|
||||
"You're not member of this group.", show_alert=True
|
||||
)
|
||||
except pyrogram.errors.exceptions.forbidden_403.ChatAdminRequired:
|
||||
return await CallbackQuery.message.edit_text(
|
||||
"I must be admin to execute this task, or i will leave from this group.",
|
||||
)
|
||||
if member.status not in (
|
||||
pyrogram.enums.ChatMemberStatus.OWNER,
|
||||
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,
|
||||
):
|
||||
return await CallbackQuery.answer("You need to be an admin to do this.")
|
||||
permission = cb[2]
|
||||
|
||||
if isinstance(permission, str) and not await check_rights(
|
||||
CallbackQuery.message.chat.id,
|
||||
CallbackQuery.from_user.id,
|
||||
permission,
|
||||
client=self,
|
||||
):
|
||||
return await CallbackQuery.message.edit_text(
|
||||
f"You are Missing the following Rights to use this Command:\n{permission}",
|
||||
)
|
||||
if isinstance(permission, list):
|
||||
permissions = ""
|
||||
for perm in permission:
|
||||
if not await check_rights(
|
||||
CallbackQuery.message.chat.id,
|
||||
CallbackQuery.from_user.id,
|
||||
perm,
|
||||
client=self,
|
||||
):
|
||||
permissions += f"\n{perm}"
|
||||
if permissions != "":
|
||||
return await CallbackQuery.message.edit_text(
|
||||
f"You are Missing the following Rights to use this Command:{permissions}",
|
||||
)
|
||||
try:
|
||||
await CallbackQuery.message.delete()
|
||||
await cb[1](self, cb[0])
|
||||
except pyrogram.errors.exceptions.forbidden_403.ChatAdminRequired:
|
||||
return await CallbackQuery.message.edit_text(
|
||||
"I must be admin to execute this task, or i will leave from this group.",
|
||||
)
|
||||
except BaseException as e:
|
||||
return await handle_error(e, CallbackQuery)
|
||||
|
||||
|
||||
def adminsOnly(
|
||||
self,
|
||||
permission: typing.Union[str, list],
|
||||
TRUST_ANON_ADMINS: typing.Union[bool, bool] = False,
|
||||
):
|
||||
"""
|
||||
# `tgEasy.tgClient.adminsOnly`
|
||||
- A decorater for running the function only if the admin have the specified Rights.
|
||||
- If the admin is Anonymous Admin, it also checks his rights by making a Callback.
|
||||
- Parameters:
|
||||
- permission (str):
|
||||
- Permission which the User must have to use the Functions
|
||||
|
||||
- TRUST_ANON_ADMIN (bool) **optional**:
|
||||
- If the user is an Anonymous Admin, then it bypasses his right check.
|
||||
|
||||
# Example
|
||||
.. code-block:: python
|
||||
from tgEasy import tgClient
|
||||
import pyrogram
|
||||
|
||||
app = tgClient(pyrogram.Client())
|
||||
|
||||
@app.command("start")
|
||||
@app.adminsOnly("can_change_info")
|
||||
async def start(client, message):
|
||||
await message.reply_text(f"Hello Admin {message.from_user.mention}")
|
||||
"""
|
||||
|
||||
def wrapper(func):
|
||||
async def decorator(client, message):
|
||||
permissions = ""
|
||||
if message.chat.type != pyrogram.enums.ChatType.SUPERGROUP:
|
||||
return await message.reply_text(
|
||||
"This command can be used in supergroups only.",
|
||||
)
|
||||
if message.sender_chat and not TRUST_ANON_ADMINS:
|
||||
ANON[int(f"{message.chat.id}{message.id}")] = (
|
||||
message,
|
||||
func,
|
||||
permission,
|
||||
)
|
||||
return await anonymous_admin(message)
|
||||
if not await is_admin(
|
||||
message.chat.id,
|
||||
message.from_user.id,
|
||||
client=client,
|
||||
):
|
||||
return await message.reply_text(
|
||||
"Only admins can execute this Command!",
|
||||
)
|
||||
if isinstance(permission, str) and not await check_rights(
|
||||
message.chat.id,
|
||||
message.from_user.id,
|
||||
permission,
|
||||
client=client,
|
||||
):
|
||||
return await message.reply_text(
|
||||
f"You are Missing the following Rights to use this Command:\n{permission}",
|
||||
)
|
||||
if isinstance(permission, list):
|
||||
for perm in permission:
|
||||
if not await check_rights(
|
||||
message.chat.id,
|
||||
message.from_user.id,
|
||||
perm,
|
||||
client=client,
|
||||
):
|
||||
permissions += f"\n{perm}"
|
||||
if permissions != "":
|
||||
return await message.reply_text(
|
||||
f"You are Missing the following Rights to use this Command:{permissions}",
|
||||
)
|
||||
try:
|
||||
await func(client, message)
|
||||
except pyrogram.errors.exceptions.forbidden_403.ChatWriteForbidden:
|
||||
await client.leave_chat(message.chat.id)
|
||||
except BaseException as exception:
|
||||
await handle_error(exception, message)
|
||||
|
||||
self.add_handler(
|
||||
pyrogram.handlers.CallbackQueryHandler(
|
||||
anonymous_admin_verification,
|
||||
pyrogram.filters.regex("^anon."),
|
||||
),
|
||||
)
|
||||
return decorator
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
Decorators.adminsOnly = adminsOnly
|
||||
|
|
@ -1,100 +1,102 @@
|
|||
# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram
|
||||
# Copyright (C) 2021 - 2022 Jayant Hegde Kageri <https://github.com/jayantkageri>
|
||||
|
||||
# This file is part of tgEasy.
|
||||
|
||||
# tgEasy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# tgEasy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import typing
|
||||
import pyrogram
|
||||
from pyrogram.methods import Decorators
|
||||
from ..utils import handle_error
|
||||
|
||||
def callback(
|
||||
self,
|
||||
data: typing.Union[str, list],
|
||||
self_admin: typing.Union[bool, bool] = False,
|
||||
filter: typing.Union[pyrogram.filters.Filter, pyrogram.filters.Filter] = None,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
### `Client.callback`
|
||||
|
||||
- A decorater to Register Callback Quiries in simple way and manage errors in that Function itself, alternative for `@pyrogram.Client.on_callback_query(pyrogram.filters.regex('^data.*'))`
|
||||
- Parameters:
|
||||
- data (str || list):
|
||||
- The callback query to be handled for a function
|
||||
|
||||
- self_admin (bool) **optional**:
|
||||
- If True, the command will only executeed if the Bot is Admin in the Chat, By Default False
|
||||
|
||||
- filter (`~pyrogram.filters`) **optional**:
|
||||
- Pyrogram Filters, hope you know about this, for Advaced usage. Use `and` for seaperating filters.
|
||||
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
import pyrogram
|
||||
|
||||
app = pyrogram.Client()
|
||||
|
||||
@app.command("start")
|
||||
async def start(client, message):
|
||||
await message.reply_text(
|
||||
f"Hello {message.from_user.mention}",
|
||||
reply_markup=pyrogram.types.InlineKeyboardMarkup([[
|
||||
pyrogram.types.InlineKeyboardButton(
|
||||
"Click Here",
|
||||
"data"
|
||||
)
|
||||
]])
|
||||
)
|
||||
|
||||
@app.callback("data")
|
||||
async def data(client, CallbackQuery):
|
||||
await CallbackQuery.answer("Hello :)", show_alert=True)
|
||||
"""
|
||||
if filter:
|
||||
filter = pyrogram.filters.regex(f"^{data}.*") & args["filter"]
|
||||
else:
|
||||
filter = pyrogram.filters.regex(f"^{data}.*")
|
||||
|
||||
def wrapper(func):
|
||||
async def decorator(client, CallbackQuery: pyrogram.types.CallbackQuery):
|
||||
if self_admin:
|
||||
me = await client.get_chat_member(
|
||||
CallbackQuery.message.chat.id, (await client.get_me()).id
|
||||
)
|
||||
if me.status not in (
|
||||
pyrogram.enums.ChatMemberStatus.OWNER,
|
||||
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,
|
||||
):
|
||||
return await CallbackQuery.message.edit_text(
|
||||
"I must be admin to execute this Command"
|
||||
)
|
||||
try:
|
||||
await func(client, CallbackQuery)
|
||||
except pyrogram.errors.exceptions.forbidden_403.ChatAdminRequired:
|
||||
pass
|
||||
except BaseException as e:
|
||||
return await handle_error(e, CallbackQuery)
|
||||
|
||||
self.add_handler(
|
||||
pyrogram.handlers.CallbackQueryHandler(decorator, filter)
|
||||
)
|
||||
return decorator
|
||||
|
||||
return wrapper
|
||||
|
||||
Decorators.on_cb = callback
|
||||
# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram
|
||||
# Copyright (C) 2021 - 2022 Jayant Hegde Kageri <https://github.com/jayantkageri>
|
||||
|
||||
# This file is part of tgEasy.
|
||||
|
||||
# tgEasy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# tgEasy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import typing
|
||||
|
||||
import pyrogram
|
||||
from pyrogram.methods import Decorators
|
||||
|
||||
from ..utils import handle_error
|
||||
|
||||
|
||||
def callback(
|
||||
self,
|
||||
data: typing.Union[str, list],
|
||||
self_admin: typing.Union[bool, bool] = False,
|
||||
filtercb: typing.Union[pyrogram.filters.Filter] = None,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
### `Client.callback`
|
||||
|
||||
- A decorater to Register Callback Quiries in simple way and manage errors in that Function itself, alternative for `@pyrogram.Client.on_callback_query(pyrogram.filters.regex('^data.*'))`
|
||||
- Parameters:
|
||||
- data (str || list):
|
||||
- The callback query to be handled for a function
|
||||
|
||||
- self_admin (bool) **optional**:
|
||||
- If True, the command will only executeed if the Bot is Admin in the Chat, By Default False
|
||||
|
||||
- filter (`~pyrogram.filters`) **optional**:
|
||||
- Pyrogram Filters, hope you know about this, for Advaced usage. Use `and` for seaperating filters.
|
||||
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
import pyrogram
|
||||
|
||||
app = pyrogram.Client()
|
||||
|
||||
@app.command("start")
|
||||
async def start(client, message):
|
||||
await message.reply_text(
|
||||
f"Hello {message.from_user.mention}",
|
||||
reply_markup=pyrogram.types.InlineKeyboardMarkup([[
|
||||
pyrogram.types.InlineKeyboardButton(
|
||||
"Click Here",
|
||||
"data"
|
||||
)
|
||||
]])
|
||||
)
|
||||
|
||||
@app.callback("data")
|
||||
async def data(client, CallbackQuery):
|
||||
await CallbackQuery.answer("Hello :)", show_alert=True)
|
||||
"""
|
||||
if filtercb:
|
||||
filtercb = pyrogram.filters.regex(f"^{data}.*") & args["filter"]
|
||||
else:
|
||||
filtercb = pyrogram.filters.regex(f"^{data}.*")
|
||||
|
||||
def wrapper(func):
|
||||
async def decorator(client, CallbackQuery: pyrogram.types.CallbackQuery):
|
||||
if self_admin:
|
||||
me = await client.get_chat_member(
|
||||
CallbackQuery.message.chat.id, (await client.get_me()).id
|
||||
)
|
||||
if me.status not in (
|
||||
pyrogram.enums.ChatMemberStatus.OWNER,
|
||||
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,
|
||||
):
|
||||
return await CallbackQuery.message.edit_text(
|
||||
"I must be admin to execute this Command"
|
||||
)
|
||||
try:
|
||||
await func(client, CallbackQuery)
|
||||
except pyrogram.errors.exceptions.forbidden_403.ChatAdminRequired:
|
||||
pass
|
||||
except BaseException as e:
|
||||
return await handle_error(e, CallbackQuery)
|
||||
|
||||
self.add_handler(pyrogram.handlers.CallbackQueryHandler(decorator, filtercb))
|
||||
return decorator
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
Decorators.on_cb = callback
|
||||
|
|
|
|||
|
|
@ -1,129 +1,133 @@
|
|||
import typing
|
||||
import pyrogram
|
||||
from ..utils import handle_error
|
||||
from pyrogram.methods import Decorators
|
||||
from misskaty.vars import COMMAND_HANDLER
|
||||
|
||||
def command(
|
||||
self,
|
||||
command: typing.Union[str, list],
|
||||
is_disabled: typing.Union[bool, bool] = False,
|
||||
pm_only: typing.Union[bool, bool] = False,
|
||||
group_only: typing.Union[bool, bool] = False,
|
||||
self_admin: typing.Union[bool, bool] = False,
|
||||
self_only: typing.Union[bool, bool] = False,
|
||||
no_channel: typing.Union[bool, bool] = False,
|
||||
handler: typing.Optional[list] = None,
|
||||
filter: typing.Union[pyrogram.filters.Filter, pyrogram.filters.Filter] = None,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
### `tgClient.command`
|
||||
- A decorater to Register Commands in simple way and manage errors in that Function itself, alternative for `@pyrogram.Client.on_message(pyrogram.filters.command('command'))`
|
||||
- Parameters:
|
||||
- command (str || list):
|
||||
- The command to be handled for a function
|
||||
|
||||
- group_only (bool) **optional**:
|
||||
- If True, the command will only executed in Groups only, By Default False.
|
||||
|
||||
- pm_only (bool) **optional**:
|
||||
- If True, the command will only executed in Private Messages only, By Default False.
|
||||
|
||||
- self_only (bool) **optional**:
|
||||
- If True, the command will only excute if used by Self only, By Default False.
|
||||
|
||||
- handler (list) **optional**:
|
||||
- If set, the command will be handled by the specified Handler, By Default `Config.HANDLERS`.
|
||||
|
||||
- self_admin (bool) **optional**:
|
||||
- If True, the command will only executeed if the Bot is Admin in the Chat, By Default False
|
||||
|
||||
- filter (`~pyrogram.filters`) **optional**:
|
||||
- Pyrogram Filters, hope you know about this, for Advaced usage. Use `and` for seaperating filters.
|
||||
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
import pyrogram
|
||||
|
||||
app = pyrogram.Client()
|
||||
|
||||
@app.on_cmd("start", is_disabled=False, group_only=False, pm_only=False, self_admin=False, self_only=False, pyrogram.filters.chat("777000") and pyrogram.filters.text)
|
||||
async def start(client, message):
|
||||
await message.reply_text(f"Hello {message.from_user.mention}")
|
||||
"""
|
||||
if handler is None:
|
||||
handler = COMMAND_HANDLER
|
||||
if filter:
|
||||
if self_only:
|
||||
filter = (
|
||||
pyrogram.filters.command(command, prefixes=handler)
|
||||
& filter
|
||||
& pyrogram.filters.me
|
||||
)
|
||||
else:
|
||||
filter = (
|
||||
pyrogram.filters.command(command, prefixes=handler)
|
||||
& filter
|
||||
& pyrogram.filters.me
|
||||
)
|
||||
else:
|
||||
if self_only:
|
||||
filter = (
|
||||
pyrogram.filters.command(command, prefixes=handler)
|
||||
& pyrogram.filters.me
|
||||
)
|
||||
else:
|
||||
filter = pyrogram.filters.command(command, prefixes=handler)
|
||||
|
||||
def wrapper(func):
|
||||
async def decorator(client, message: pyrogram.types.Message):
|
||||
if is_disabled:
|
||||
return await message.reply_text("Sorry, this command has been disabled by owner.")
|
||||
if not message.from_user:
|
||||
return await message.reply_text("I'm cannot identify user. Use my command in private chat.")
|
||||
if (
|
||||
self_admin
|
||||
and message.chat.type != pyrogram.enums.ChatType.SUPERGROUP
|
||||
):
|
||||
return await message.reply_text(
|
||||
"This command can be used in supergroups only."
|
||||
)
|
||||
if self_admin:
|
||||
me = await client.get_chat_member(
|
||||
message.chat.id, (await client.get_me()).id
|
||||
)
|
||||
if me.status not in (
|
||||
pyrogram.enums.ChatMemberStatus.OWNER,
|
||||
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,
|
||||
):
|
||||
return await message.reply_text(
|
||||
"I must be admin to execute this Command"
|
||||
)
|
||||
if (
|
||||
group_only
|
||||
and message.chat.type != pyrogram.enums.ChatType.SUPERGROUP
|
||||
):
|
||||
return await message.reply_text(
|
||||
"This command can be used in supergroups only."
|
||||
)
|
||||
if pm_only and message.chat.type != pyrogram.enums.ChatType.PRIVATE:
|
||||
return await message.reply_text(
|
||||
"This command can be used in PMs only."
|
||||
)
|
||||
try:
|
||||
await func(client, message)
|
||||
except pyrogram.errors.exceptions.forbidden_403.ChatWriteForbidden:
|
||||
await client.leave_chat(message.chat.id)
|
||||
except BaseException as exception:
|
||||
return await handle_error(exception, message)
|
||||
|
||||
self.add_handler(
|
||||
pyrogram.handlers.MessageHandler(callback=decorator, filters=filter)
|
||||
)
|
||||
return decorator
|
||||
|
||||
return wrapper
|
||||
|
||||
Decorators.on_cmd = command
|
||||
import typing
|
||||
|
||||
import pyrogram
|
||||
from pyrogram.methods import Decorators
|
||||
|
||||
from misskaty.core import pyro_cooldown
|
||||
from misskaty.vars import COMMAND_HANDLER
|
||||
|
||||
from ..utils import handle_error
|
||||
|
||||
|
||||
def command(
|
||||
self,
|
||||
cmd: typing.Union[str, list],
|
||||
is_disabled: typing.Union[bool, bool] = False,
|
||||
pm_only: typing.Union[bool, bool] = False,
|
||||
group_only: typing.Union[bool, bool] = False,
|
||||
self_admin: typing.Union[bool, bool] = False,
|
||||
self_only: typing.Union[bool, bool] = False,
|
||||
no_channel: typing.Union[bool, bool] = False,
|
||||
handler: typing.Optional[list] = None,
|
||||
filtercmd: typing.Union[pyrogram.filters.Filter] = None,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
### `tgClient.command`
|
||||
- A decorater to Register Commands in simple way and manage errors in that Function itself, alternative for `@pyrogram.Client.on_message(pyrogram.filters.command('command'))`
|
||||
- Parameters:
|
||||
- cmd (str || list):
|
||||
- The command to be handled for a function
|
||||
|
||||
- group_only (bool) **optional**:
|
||||
- If True, the command will only executed in Groups only, By Default False.
|
||||
|
||||
- pm_only (bool) **optional**:
|
||||
- If True, the command will only executed in Private Messages only, By Default False.
|
||||
|
||||
- self_only (bool) **optional**:
|
||||
- If True, the command will only excute if used by Self only, By Default False.
|
||||
|
||||
- handler (list) **optional**:
|
||||
- If set, the command will be handled by the specified Handler, By Default `Config.HANDLERS`.
|
||||
|
||||
- self_admin (bool) **optional**:
|
||||
- If True, the command will only executeed if the Bot is Admin in the Chat, By Default False
|
||||
|
||||
- filtercmd (`~pyrogram.filters`) **optional**:
|
||||
- Pyrogram Filters, hope you know about this, for Advaced usage. Use `and` for seaperating filters.
|
||||
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
import pyrogram
|
||||
|
||||
app = pyrogram.Client()
|
||||
|
||||
@app.on_cmd("start", is_disabled=False, group_only=False, pm_only=False, self_admin=False, self_only=False, pyrogram.filters.chat("777000") and pyrogram.filters.text)
|
||||
async def start(client, message):
|
||||
await message.reply_text(f"Hello {message.from_user.mention}")
|
||||
"""
|
||||
if handler is None:
|
||||
handler = COMMAND_HANDLER
|
||||
if filtercmd:
|
||||
if self_only:
|
||||
filtercmd = (
|
||||
pyrogram.filters.command(cmd, prefixes=handler)
|
||||
& filtercmd
|
||||
& pyrogram.filters.me
|
||||
)
|
||||
else:
|
||||
filtercmd = (
|
||||
pyrogram.filters.command(cmd, prefixes=handler)
|
||||
& filtercmd
|
||||
& pyrogram.filters.me
|
||||
& pyro_cooldown.wait(7)
|
||||
)
|
||||
else:
|
||||
if self_only:
|
||||
filtercmd = (
|
||||
pyrogram.filters.command(cmd, prefixes=handler) & pyrogram.filters.me
|
||||
)
|
||||
else:
|
||||
filtercmd = pyrogram.filters.command(
|
||||
cmd, prefixes=handler
|
||||
) & pyro_cooldown.wait(7)
|
||||
|
||||
def wrapper(func):
|
||||
async def decorator(client, message: pyrogram.types.Message):
|
||||
if is_disabled:
|
||||
return await message.reply_text(
|
||||
"Sorry, this command has been disabled by owner."
|
||||
)
|
||||
if not message.from_user and no_channel:
|
||||
return await message.reply_text(
|
||||
"I'm cannot identify user. Use my command in private chat."
|
||||
)
|
||||
if self_admin and message.chat.type != pyrogram.enums.ChatType.SUPERGROUP:
|
||||
return await message.reply_text(
|
||||
"This command can be used in supergroups only."
|
||||
)
|
||||
if self_admin:
|
||||
me = await client.get_chat_member(
|
||||
message.chat.id, (await client.get_me()).id
|
||||
)
|
||||
if me.status not in (
|
||||
pyrogram.enums.ChatMemberStatus.OWNER,
|
||||
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,
|
||||
):
|
||||
return await message.reply_text(
|
||||
"I must be admin to execute this Command"
|
||||
)
|
||||
if group_only and message.chat.type != pyrogram.enums.ChatType.SUPERGROUP:
|
||||
return await message.reply_text(
|
||||
"This command can be used in supergroups only."
|
||||
)
|
||||
if pm_only and message.chat.type != pyrogram.enums.ChatType.PRIVATE:
|
||||
return await message.reply_text("This command can be used in PMs only.")
|
||||
try:
|
||||
await func(client, message)
|
||||
except pyrogram.errors.exceptions.forbidden_403.ChatWriteForbidden:
|
||||
await client.leave_chat(message.chat.id)
|
||||
except BaseException as exception:
|
||||
return await handle_error(exception, message)
|
||||
|
||||
self.add_handler(
|
||||
pyrogram.handlers.MessageHandler(callback=decorator, filters=filtercmd)
|
||||
)
|
||||
return decorator
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
Decorators.on_cmd = command
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
# skipcq
|
||||
from .listen import Chat, Client, MessageHandler, User
|
||||
|
|
@ -1,386 +0,0 @@
|
|||
"""
|
||||
pyromod - A monkeypatcher add-on for Pyrogram
|
||||
Copyright (C) 2020 Cezar H. <https://github.com/usernein>
|
||||
|
||||
This file is part of pyromod.
|
||||
|
||||
pyromod is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
pyromod is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with pyromod. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from enum import Enum
|
||||
from inspect import iscoroutinefunction
|
||||
from typing import Callable, Optional, Union
|
||||
|
||||
import pyrogram
|
||||
|
||||
from ..utils import PyromodConfig, patch, patchable
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ListenerStopped(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ListenerTimeout(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ListenerTypes(Enum):
|
||||
MESSAGE = "message"
|
||||
CALLBACK_QUERY = "callback_query"
|
||||
|
||||
|
||||
@patch(pyrogram.client.Client)
|
||||
class Client:
|
||||
@patchable()
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.listeners = {listener_type: {} for listener_type in ListenerTypes}
|
||||
self.old__init__(*args, **kwargs)
|
||||
|
||||
@patchable()
|
||||
async def listen(
|
||||
self,
|
||||
identifier: tuple,
|
||||
filters=None,
|
||||
listener_type=ListenerTypes.MESSAGE,
|
||||
timeout=None,
|
||||
unallowed_click_alert=True,
|
||||
):
|
||||
if type(listener_type) != ListenerTypes:
|
||||
raise TypeError(
|
||||
"Parameter listener_type should be a"
|
||||
" value from pyromod.listen.ListenerTypes"
|
||||
)
|
||||
|
||||
future = self.loop.create_future()
|
||||
future.add_done_callback(
|
||||
lambda f: self.stop_listening(identifier, listener_type)
|
||||
)
|
||||
|
||||
listener_data = {
|
||||
"future": future,
|
||||
"filters": filters,
|
||||
"unallowed_click_alert": unallowed_click_alert,
|
||||
}
|
||||
|
||||
self.listeners[listener_type].update({identifier: listener_data})
|
||||
|
||||
try:
|
||||
return await asyncio.wait_for(future, timeout)
|
||||
except asyncio.exceptions.TimeoutError:
|
||||
if callable(PyromodConfig.timeout_handler):
|
||||
PyromodConfig.timeout_handler(identifier, listener_data, timeout)
|
||||
elif PyromodConfig.throw_exceptions:
|
||||
raise ListenerTimeout(timeout)
|
||||
|
||||
@patchable()
|
||||
async def ask(
|
||||
self,
|
||||
text,
|
||||
identifier: tuple,
|
||||
filters=None,
|
||||
listener_type=ListenerTypes.MESSAGE,
|
||||
timeout=None,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
request = await self.send_message(identifier[0], text, *args, **kwargs)
|
||||
response = await self.listen(identifier, filters, listener_type, timeout)
|
||||
if response:
|
||||
response.request = request
|
||||
|
||||
return response
|
||||
|
||||
"""
|
||||
needed for matching when message_id or
|
||||
user_id is null, and to take precedence
|
||||
"""
|
||||
|
||||
@patchable()
|
||||
def match_listener(
|
||||
self,
|
||||
data: Optional[tuple] = None,
|
||||
listener_type: ListenerTypes = ListenerTypes.MESSAGE,
|
||||
identifier_pattern: Optional[tuple] = None,
|
||||
) -> tuple:
|
||||
if data:
|
||||
listeners = self.listeners[listener_type]
|
||||
# case with 3 args on identifier
|
||||
# most probably waiting for a specific user
|
||||
# to click a button in a specific message
|
||||
if data in listeners:
|
||||
return listeners[data], data
|
||||
|
||||
# cases with 2 args on identifier
|
||||
# (None, user, message) does not make
|
||||
# sense since the message_id is not unique
|
||||
elif (data[0], data[1], None) in listeners:
|
||||
matched = (data[0], data[1], None)
|
||||
elif (data[0], None, data[2]) in listeners:
|
||||
matched = (data[0], None, data[2])
|
||||
|
||||
# cases with 1 arg on identifier
|
||||
# (None, None, message) does not make sense as well
|
||||
elif (data[0], None, None) in listeners:
|
||||
matched = (data[0], None, None)
|
||||
elif (None, data[1], None) in listeners:
|
||||
matched = (None, data[1], None)
|
||||
else:
|
||||
return None, None
|
||||
|
||||
return listeners[matched], matched
|
||||
elif identifier_pattern:
|
||||
|
||||
def match_identifier(pattern, identifier):
|
||||
comparison = (
|
||||
pattern[0] in (identifier[0], None),
|
||||
pattern[1] in (identifier[1], None),
|
||||
pattern[2] in (identifier[2], None),
|
||||
)
|
||||
return comparison == (True, True, True)
|
||||
|
||||
for identifier, listener in self.listeners[listener_type].items():
|
||||
if match_identifier(identifier_pattern, identifier):
|
||||
return listener, identifier
|
||||
return None, None
|
||||
|
||||
@patchable()
|
||||
def stop_listening(
|
||||
self,
|
||||
data: Optional[tuple] = None,
|
||||
listener_type: ListenerTypes = ListenerTypes.MESSAGE,
|
||||
identifier_pattern: Optional[tuple] = None,
|
||||
):
|
||||
listener, identifier = self.match_listener(
|
||||
data, listener_type, identifier_pattern
|
||||
)
|
||||
|
||||
if not listener:
|
||||
return
|
||||
elif listener["future"].done():
|
||||
del self.listeners[listener_type][identifier]
|
||||
return
|
||||
|
||||
if callable(PyromodConfig.stopped_handler):
|
||||
PyromodConfig.stopped_handler(identifier, listener)
|
||||
elif PyromodConfig.throw_exceptions:
|
||||
listener["future"].set_exception(ListenerStopped())
|
||||
|
||||
del self.listeners[listener_type][identifier]
|
||||
|
||||
|
||||
@patch(pyrogram.handlers.message_handler.MessageHandler)
|
||||
class MessageHandler:
|
||||
@patchable()
|
||||
def __init__(self, callback: Callable, filters=None):
|
||||
self.registered_handler = callback
|
||||
self.old__init__(self.resolve_future, filters)
|
||||
|
||||
@patchable()
|
||||
async def check(self, client, message):
|
||||
if user := getattr(message, "from_user", None):
|
||||
user = user.id
|
||||
try:
|
||||
listener = client.match_listener(
|
||||
(message.chat.id, user, message.id),
|
||||
ListenerTypes.MESSAGE,
|
||||
)[0]
|
||||
except AttributeError as err:
|
||||
logger.warning(f"Get : {err}\n\n{message}")
|
||||
raise err
|
||||
|
||||
listener_does_match = handler_does_match = False
|
||||
|
||||
if listener:
|
||||
filters = listener["filters"]
|
||||
if callable(filters):
|
||||
if iscoroutinefunction(filters.__call__):
|
||||
listener_does_match = await filters(client, message)
|
||||
else:
|
||||
listener_does_match = await client.loop.run_in_executor(
|
||||
None, filters, client, message
|
||||
)
|
||||
else:
|
||||
listener_does_match = True
|
||||
|
||||
if callable(self.filters):
|
||||
if iscoroutinefunction(self.filters.__call__):
|
||||
handler_does_match = await self.filters(client, message)
|
||||
else:
|
||||
handler_does_match = await client.loop.run_in_executor(
|
||||
None, self.filters, client, message
|
||||
)
|
||||
else:
|
||||
handler_does_match = True
|
||||
|
||||
# let handler get the chance to handle if listener
|
||||
# exists but its filters doesn't match
|
||||
return listener_does_match or handler_does_match
|
||||
|
||||
@patchable()
|
||||
async def resolve_future(self, client, message, *args):
|
||||
listener_type = ListenerTypes.MESSAGE
|
||||
if user := getattr(message, "from_user", None):
|
||||
user = user.id
|
||||
listener, identifier = client.match_listener(
|
||||
(message.chat.id, user, message.id),
|
||||
listener_type,
|
||||
)
|
||||
listener_does_match = False
|
||||
if listener:
|
||||
filters = listener["filters"]
|
||||
if callable(filters):
|
||||
if iscoroutinefunction(filters.__call__):
|
||||
listener_does_match = await filters(client, message)
|
||||
else:
|
||||
listener_does_match = await client.loop.run_in_executor(
|
||||
None, filters, client, message
|
||||
)
|
||||
else:
|
||||
listener_does_match = True
|
||||
|
||||
if listener_does_match:
|
||||
if not listener["future"].done():
|
||||
listener["future"].set_result(message)
|
||||
del client.listeners[listener_type][identifier]
|
||||
raise pyrogram.StopPropagation
|
||||
else:
|
||||
await self.registered_handler(client, message, *args)
|
||||
|
||||
|
||||
@patch(pyrogram.handlers.callback_query_handler.CallbackQueryHandler)
|
||||
class CallbackQueryHandler:
|
||||
@patchable()
|
||||
def __init__(self, callback: Callable, filters=None):
|
||||
self.registered_handler = callback
|
||||
self.old__init__(self.resolve_future, filters)
|
||||
|
||||
@patchable()
|
||||
async def check(self, client, query):
|
||||
chatID, mID = None, None
|
||||
if message := getattr(query, "message", None):
|
||||
chatID, mID = message.chat.id, message.id
|
||||
try:
|
||||
listener = client.match_listener(
|
||||
(chatID, query.from_user.id, mID),
|
||||
ListenerTypes.CALLBACK_QUERY,
|
||||
)[0]
|
||||
except AttributeError as err:
|
||||
logger.warning(f"Get : {err}\n\n{message}")
|
||||
raise err
|
||||
|
||||
# managing unallowed user clicks
|
||||
if PyromodConfig.unallowed_click_alert:
|
||||
permissive_listener = client.match_listener(
|
||||
identifier_pattern=(
|
||||
chatID,
|
||||
None,
|
||||
mID,
|
||||
),
|
||||
listener_type=ListenerTypes.CALLBACK_QUERY,
|
||||
)[0]
|
||||
|
||||
if (permissive_listener and not listener) and permissive_listener[
|
||||
"unallowed_click_alert"
|
||||
]:
|
||||
alert = (
|
||||
permissive_listener["unallowed_click_alert"]
|
||||
if type(permissive_listener["unallowed_click_alert"]) is str
|
||||
else PyromodConfig.unallowed_click_alert_text
|
||||
)
|
||||
await query.answer(alert)
|
||||
return False
|
||||
|
||||
filters = listener["filters"] if listener else self.filters
|
||||
|
||||
if callable(filters):
|
||||
if iscoroutinefunction(filters.__call__):
|
||||
return await filters(client, query)
|
||||
else:
|
||||
return await client.loop.run_in_executor(None, filters, client, query)
|
||||
else:
|
||||
return True
|
||||
|
||||
@patchable()
|
||||
async def resolve_future(self, client, query, *args):
|
||||
listener_type = ListenerTypes.CALLBACK_QUERY
|
||||
chatID, mID = None, None
|
||||
if message := getattr(query, "message", None):
|
||||
chatID, mID = message.chat.id, message.id
|
||||
listener, identifier = client.match_listener(
|
||||
(chatID, query.from_user.id, mID),
|
||||
listener_type,
|
||||
)
|
||||
|
||||
if listener and not listener["future"].done():
|
||||
listener["future"].set_result(query)
|
||||
del client.listeners[listener_type][identifier]
|
||||
else:
|
||||
await self.registered_handler(client, query, *args)
|
||||
|
||||
|
||||
@patch(pyrogram.types.messages_and_media.message.Message)
|
||||
class Message(pyrogram.types.messages_and_media.message.Message):
|
||||
@patchable()
|
||||
async def wait_for_click(
|
||||
self,
|
||||
from_user_id: Optional[int] = None,
|
||||
timeout: Optional[int] = None,
|
||||
filters=None,
|
||||
alert: Union[str, bool] = True,
|
||||
):
|
||||
return await self._client.listen(
|
||||
(self.chat.id, from_user_id, self.id),
|
||||
listener_type=ListenerTypes.CALLBACK_QUERY,
|
||||
timeout=timeout,
|
||||
filters=filters,
|
||||
unallowed_click_alert=alert,
|
||||
)
|
||||
|
||||
|
||||
@patch(pyrogram.types.user_and_chats.chat.Chat)
|
||||
class Chat(pyrogram.types.Chat):
|
||||
@patchable()
|
||||
def listen(self, *args, **kwargs):
|
||||
return self._client.listen((self.id, None, None), *args, **kwargs)
|
||||
|
||||
@patchable()
|
||||
def ask(self, text, *args, **kwargs):
|
||||
return self._client.ask(text, (self.id, None, None), *args, **kwargs)
|
||||
|
||||
@patchable()
|
||||
def stop_listening(self, *args, **kwargs):
|
||||
return self._client.stop_listening(
|
||||
*args, identifier_pattern=(self.id, None, None), **kwargs
|
||||
)
|
||||
|
||||
|
||||
@patch(pyrogram.types.user_and_chats.user.User)
|
||||
class User(pyrogram.types.User):
|
||||
@patchable()
|
||||
def listen(self, *args, **kwargs):
|
||||
return self._client.listen((None, self.id, None), *args, **kwargs)
|
||||
|
||||
@patchable()
|
||||
def ask(self, text, *args, **kwargs):
|
||||
return self._client.ask(text, (self.id, self.id, None), *args, **kwargs)
|
||||
|
||||
@patchable()
|
||||
def stop_listening(self, *args, **kwargs):
|
||||
return self._client.stop_listening(
|
||||
*args, identifier_pattern=(None, self.id, None), **kwargs
|
||||
)
|
||||
|
|
@ -2,6 +2,7 @@ import asyncio
|
|||
from typing import Union
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.types import Message
|
||||
|
||||
|
||||
async def edit_message_text(
|
||||
|
|
@ -11,7 +12,7 @@ async def edit_message_text(
|
|||
text: str,
|
||||
del_in: int = 0,
|
||||
*args,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
) -> Union["Message", bool]:
|
||||
"""\nExample:
|
||||
message.edit_text("hello")
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ async def send_as_file(
|
|||
text: str,
|
||||
filename: str = "output.txt",
|
||||
caption: str = "",
|
||||
log: Union[bool, str] = False,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
) -> "Message":
|
||||
"""\nYou can send large outputs as file
|
||||
|
|
@ -29,10 +28,6 @@ async def send_as_file(
|
|||
file_name for output file.
|
||||
caption (``str``, *optional*):
|
||||
caption for output file.
|
||||
log (``bool`` | ``str``, *optional*):
|
||||
If ``True``, the message will be forwarded
|
||||
to the log channel.
|
||||
If ``str``, the logger name will be updated.
|
||||
reply_to_message_id (``int``, *optional*):
|
||||
If the message is a reply, ID of the original message.
|
||||
Returns:
|
||||
|
|
|
|||
|
|
@ -1,154 +1,158 @@
|
|||
# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram
|
||||
# Copyright (C) 2021 - 2022 Jayant Hegde Kageri <https://github.com/jayantkageri>
|
||||
|
||||
# This file is part of tgEasy.
|
||||
|
||||
# tgEasy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# tgEasy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import typing
|
||||
import pyrogram
|
||||
|
||||
|
||||
async def check_rights(
|
||||
chat_id: typing.Union[int, int],
|
||||
user_id: typing.Union[int, int],
|
||||
rights: typing.Union[str, list],
|
||||
client,
|
||||
) -> bool:
|
||||
"""
|
||||
### `check_rights`
|
||||
- Checks the Rights of an User
|
||||
- This is an Helper Function for `adminsOnly`
|
||||
|
||||
- Parameters:
|
||||
- chat_id (int):
|
||||
- The Chat ID of Which Chat have to check the Rights.
|
||||
|
||||
- user_id (int):
|
||||
- The User ID of Whose Rights have to Check.
|
||||
|
||||
- rights (str):
|
||||
- The Rights have to Check.
|
||||
|
||||
- client (`pyrogram.Client`):
|
||||
- From which Client to Check the Rights.
|
||||
|
||||
- Returns:
|
||||
- `True` if the User have the Right.
|
||||
- `False` if the User don't have the Right.
|
||||
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
import pyrogram
|
||||
|
||||
app = pyrogram.Client()
|
||||
|
||||
@app.command("ban", group_only=True, self_admin=True)
|
||||
async def ban(client, message):
|
||||
if not await check_rights(message.chat.id, message.from_user.id, "can_restrict_members"):
|
||||
return await message.reply_text("You don't have necessary rights to use this Command.")
|
||||
user = await get_user(message)
|
||||
await message.chat.kick_member(user.id)
|
||||
"""
|
||||
try:
|
||||
user = await client.get_chat_member(chat_id, user_id)
|
||||
except Exception:
|
||||
return False
|
||||
if user.status == "user":
|
||||
return False
|
||||
if user.status in (
|
||||
pyrogram.enums.ChatMemberStatus.OWNER,
|
||||
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,
|
||||
):
|
||||
permission = []
|
||||
if user.privileges.can_manage_chat:
|
||||
permission.append("can_manage_chat")
|
||||
|
||||
if user.privileges.can_delete_messages:
|
||||
permission.append("can_delete_messages")
|
||||
|
||||
if user.privileges.can_manage_video_chats:
|
||||
permission.append("can_manage_video_chats")
|
||||
|
||||
if user.privileges.can_restrict_members:
|
||||
permission.append("can_restrict_members")
|
||||
|
||||
if user.privileges.can_promote_members:
|
||||
permission.append("can_promote_members")
|
||||
|
||||
if user.privileges.can_change_info:
|
||||
permission.append("can_change_info")
|
||||
|
||||
if user.privileges.can_post_messages:
|
||||
permission.append("can_post_messages")
|
||||
|
||||
if user.privileges.can_edit_messages:
|
||||
permission.append("can_edit_messages")
|
||||
|
||||
if user.privileges.can_invite_users:
|
||||
permission.append("can_invite_users")
|
||||
|
||||
if user.privileges.can_pin_messages:
|
||||
permission.append("can_pin_messages")
|
||||
|
||||
if user.privileges.is_anonymous:
|
||||
permission.append("is_anonymous")
|
||||
|
||||
if isinstance(rights, str):
|
||||
return rights in permission
|
||||
if isinstance(rights, list):
|
||||
for right in rights:
|
||||
return right in permission
|
||||
return False
|
||||
|
||||
|
||||
async def is_admin(
|
||||
chat_id: typing.Union[int, str], user_id: typing.Union[int, str], client
|
||||
) -> bool:
|
||||
"""
|
||||
### `is_admin`
|
||||
- A Functions to Check if the User is Admin or not
|
||||
|
||||
- Parameters:
|
||||
- chat_id (int):
|
||||
- The Chat ID of Which Chat have to check the Admin Status.
|
||||
|
||||
- user_id (int):
|
||||
- The User ID of Whose Admin Status have to Check.
|
||||
|
||||
- client (`pyrogram.Client`):
|
||||
- From which Client to Check the Admin Status.
|
||||
|
||||
- Returns:
|
||||
- `True` if the User is Admin.
|
||||
- `False` if the User is't Admin.
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
import pyrogram
|
||||
|
||||
app = pyrogram.Client()
|
||||
|
||||
@app.command("ban", group_only=True, self_admin=True)
|
||||
@app.adminsOnly("can_restrict_members")
|
||||
async def ban(client, message):
|
||||
if await is_admin(message.chat.id, (await get_user(mesasge)).id):
|
||||
return await message.reply_text("You can't Ban Admins.")
|
||||
await message.chat.kick_member((await get_user(message)).id)
|
||||
await message.reply_text("User has been Banned.")
|
||||
"""
|
||||
try:
|
||||
user = await client.get_chat_member(chat_id, user_id)
|
||||
except Exception:
|
||||
return False
|
||||
return user.status in (pyrogram.enums.ChatMemberStatus.OWNER, pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,)
|
||||
# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram
|
||||
# Copyright (C) 2021 - 2022 Jayant Hegde Kageri <https://github.com/jayantkageri>
|
||||
|
||||
# This file is part of tgEasy.
|
||||
|
||||
# tgEasy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# tgEasy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import typing
|
||||
|
||||
import pyrogram
|
||||
|
||||
|
||||
async def check_rights(
|
||||
chat_id: typing.Union[int, int],
|
||||
user_id: typing.Union[int, int],
|
||||
rights: typing.Union[str, list],
|
||||
client,
|
||||
) -> bool:
|
||||
"""
|
||||
### `check_rights`
|
||||
- Checks the Rights of an User
|
||||
- This is an Helper Function for `adminsOnly`
|
||||
|
||||
- Parameters:
|
||||
- chat_id (int):
|
||||
- The Chat ID of Which Chat have to check the Rights.
|
||||
|
||||
- user_id (int):
|
||||
- The User ID of Whose Rights have to Check.
|
||||
|
||||
- rights (str):
|
||||
- The Rights have to Check.
|
||||
|
||||
- client (`pyrogram.Client`):
|
||||
- From which Client to Check the Rights.
|
||||
|
||||
- Returns:
|
||||
- `True` if the User have the Right.
|
||||
- `False` if the User don't have the Right.
|
||||
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
import pyrogram
|
||||
|
||||
app = pyrogram.Client()
|
||||
|
||||
@app.command("ban", group_only=True, self_admin=True)
|
||||
async def ban(client, message):
|
||||
if not await check_rights(message.chat.id, message.from_user.id, "can_restrict_members"):
|
||||
return await message.reply_text("You don't have necessary rights to use this Command.")
|
||||
user = await get_user(message)
|
||||
await message.chat.kick_member(user.id)
|
||||
"""
|
||||
try:
|
||||
user = await client.get_chat_member(chat_id, user_id)
|
||||
except Exception:
|
||||
return False
|
||||
if user.status == "user":
|
||||
return False
|
||||
if user.status in (
|
||||
pyrogram.enums.ChatMemberStatus.OWNER,
|
||||
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,
|
||||
):
|
||||
permission = []
|
||||
if user.privileges.can_manage_chat:
|
||||
permission.append("can_manage_chat")
|
||||
|
||||
if user.privileges.can_delete_messages:
|
||||
permission.append("can_delete_messages")
|
||||
|
||||
if user.privileges.can_manage_video_chats:
|
||||
permission.append("can_manage_video_chats")
|
||||
|
||||
if user.privileges.can_restrict_members:
|
||||
permission.append("can_restrict_members")
|
||||
|
||||
if user.privileges.can_promote_members:
|
||||
permission.append("can_promote_members")
|
||||
|
||||
if user.privileges.can_change_info:
|
||||
permission.append("can_change_info")
|
||||
|
||||
if user.privileges.can_post_messages:
|
||||
permission.append("can_post_messages")
|
||||
|
||||
if user.privileges.can_edit_messages:
|
||||
permission.append("can_edit_messages")
|
||||
|
||||
if user.privileges.can_invite_users:
|
||||
permission.append("can_invite_users")
|
||||
|
||||
if user.privileges.can_pin_messages:
|
||||
permission.append("can_pin_messages")
|
||||
|
||||
if user.privileges.is_anonymous:
|
||||
permission.append("is_anonymous")
|
||||
|
||||
if isinstance(rights, str):
|
||||
return rights in permission
|
||||
if isinstance(rights, list):
|
||||
for right in rights:
|
||||
return right in permission
|
||||
return False
|
||||
|
||||
|
||||
async def is_admin(
|
||||
chat_id: typing.Union[int, str], user_id: typing.Union[int, str], client
|
||||
) -> bool:
|
||||
"""
|
||||
### `is_admin`
|
||||
- A Functions to Check if the User is Admin or not
|
||||
|
||||
- Parameters:
|
||||
- chat_id (int):
|
||||
- The Chat ID of Which Chat have to check the Admin Status.
|
||||
|
||||
- user_id (int):
|
||||
- The User ID of Whose Admin Status have to Check.
|
||||
|
||||
- client (`pyrogram.Client`):
|
||||
- From which Client to Check the Admin Status.
|
||||
|
||||
- Returns:
|
||||
- `True` if the User is Admin.
|
||||
- `False` if the User is't Admin.
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
import pyrogram
|
||||
|
||||
app = pyrogram.Client()
|
||||
|
||||
@app.command("ban", group_only=True, self_admin=True)
|
||||
@app.adminsOnly("can_restrict_members")
|
||||
async def ban(client, message):
|
||||
if await is_admin(message.chat.id, (await get_user(mesasge)).id):
|
||||
return await message.reply_text("You can't Ban Admins.")
|
||||
await message.chat.kick_member((await get_user(message)).id)
|
||||
await message.reply_text("User has been Banned.")
|
||||
"""
|
||||
try:
|
||||
user = await client.get_chat_member(chat_id, user_id)
|
||||
except Exception:
|
||||
return False
|
||||
return user.status in (
|
||||
pyrogram.enums.ChatMemberStatus.OWNER,
|
||||
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,122 +1,122 @@
|
|||
# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram
|
||||
# Copyright (C) 2021 - 2022 Jayant Hegde Kageri <https://github.com/jayantkageri>
|
||||
|
||||
# This file is part of tgEasy.
|
||||
|
||||
# tgEasy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# tgEasy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import contextlib
|
||||
import typing
|
||||
|
||||
import pyrogram
|
||||
|
||||
|
||||
async def get_user(
|
||||
m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery]
|
||||
) -> pyrogram.types.User or bool:
|
||||
"""
|
||||
### `tgEasy.get_user`
|
||||
- Gets a User from Message/RepliedMessage/CallbackQuery
|
||||
- Parameters:
|
||||
- m (`~pyrogram.types.Message` || `~pyrogram.types.CallbackQuery`)
|
||||
- Returns:
|
||||
- `pyrogram.types.User` on Success
|
||||
- `False` on Error
|
||||
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
from tgEasy import get_user, command, adminsOnly
|
||||
|
||||
@command("ban", group_only=True, self_admin=True)
|
||||
@adminsOnly("can_restrict_members")
|
||||
async def ban(client, message):
|
||||
user = await get_user(message)
|
||||
await message.chat.kick_member(user.id)
|
||||
"""
|
||||
if isinstance(m, pyrogram.types.Message):
|
||||
message = m
|
||||
client = m._client
|
||||
if isinstance(m, pyrogram.types.CallbackQuery):
|
||||
message = m.message
|
||||
client = message._client
|
||||
if message.reply_to_message:
|
||||
if message.reply_to_message.sender_chat:
|
||||
return False
|
||||
return await client.get_users(message.reply_to_message.from_user.id)
|
||||
|
||||
command = message.command[1] if len(message.command) > 1 else None
|
||||
if command and (command.startswith("@") or command.isdigit()):
|
||||
with contextlib.suppress(
|
||||
pyrogram.errors.exceptions.bad_request_400.UsernameNotOccupied,
|
||||
pyrogram.errors.exceptions.bad_request_400.UsernameInvalid,
|
||||
pyrogram.errors.exceptions.bad_request_400.PeerIdInvalid,
|
||||
IndexError,
|
||||
):
|
||||
return await client.get_users(message.command[1])
|
||||
if message.entities:
|
||||
for mention in message.entities:
|
||||
if mention.type == "text_mention":
|
||||
user = mention.user.id
|
||||
break
|
||||
with contextlib.suppress(Exception):
|
||||
return await client.get_users(user)
|
||||
return False
|
||||
|
||||
|
||||
async def get_user_adv(
|
||||
m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery]
|
||||
) -> pyrogram.types.User or bool:
|
||||
"""
|
||||
### `tgEasy.get_user_adv`
|
||||
- A Function to Get the User from the Message/CallbackQuery, If there is None arguments, returns the From User.
|
||||
- Parameters:
|
||||
- m (`pyrogram.types.Message` || `pyrogram.types.CallbackQuery`):
|
||||
- Message or Callbackquery.
|
||||
- Returns:
|
||||
- `pyrogram.types.User` on Success
|
||||
- `False` on Error
|
||||
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
from tgEasy import command, get_user_adv
|
||||
|
||||
@command("id")
|
||||
async def id(client, message):
|
||||
user = await get_user_adv(message)
|
||||
await message.reply_text(f"Your ID is `{user.id}`")
|
||||
"""
|
||||
if isinstance(m, pyrogram.types.Message):
|
||||
message = m
|
||||
if isinstance(m, pyrogram.types.CallbackQuery):
|
||||
message = m.message
|
||||
if message.sender_chat:
|
||||
return False
|
||||
with contextlib.suppress(IndexError, AttributeError):
|
||||
if len(message.command) > 1:
|
||||
if message.command[1].startswith("@"):
|
||||
return await get_user(message)
|
||||
if message.command[1].isdigit():
|
||||
return await get_user(message)
|
||||
if "text_mention" in message.entities:
|
||||
return await get_user(message)
|
||||
if "from_user" in str(message.reply_to_message):
|
||||
return await get_user(message)
|
||||
with contextlib.suppress(Exception):
|
||||
if "sender_chat" in str(message.reply_to_message):
|
||||
return False
|
||||
if "from_user" in str(message.reply_to_message):
|
||||
return await message._client.get_users(
|
||||
message.reply_to_message.from_user.id
|
||||
)
|
||||
return await message._client.get_users(message.from_user.id)
|
||||
# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram
|
||||
# Copyright (C) 2021 - 2022 Jayant Hegde Kageri <https://github.com/jayantkageri>
|
||||
|
||||
# This file is part of tgEasy.
|
||||
|
||||
# tgEasy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# tgEasy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import contextlib
|
||||
import typing
|
||||
|
||||
import pyrogram
|
||||
|
||||
|
||||
async def get_user(
|
||||
m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery],
|
||||
) -> pyrogram.types.User or bool:
|
||||
"""
|
||||
### `tgEasy.get_user`
|
||||
- Gets a User from Message/RepliedMessage/CallbackQuery
|
||||
- Parameters:
|
||||
- m (`~pyrogram.types.Message` || `~pyrogram.types.CallbackQuery`)
|
||||
- Returns:
|
||||
- `pyrogram.types.User` on Success
|
||||
- `False` on Error
|
||||
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
from tgEasy import get_user, command, adminsOnly
|
||||
|
||||
@command("ban", group_only=True, self_admin=True)
|
||||
@adminsOnly("can_restrict_members")
|
||||
async def ban(client, message):
|
||||
user = await get_user(message)
|
||||
await message.chat.kick_member(user.id)
|
||||
"""
|
||||
if isinstance(m, pyrogram.types.Message):
|
||||
message = m
|
||||
client = m._client
|
||||
if isinstance(m, pyrogram.types.CallbackQuery):
|
||||
message = m.message
|
||||
client = message._client
|
||||
if message.reply_to_message:
|
||||
if message.reply_to_message.sender_chat:
|
||||
return False
|
||||
return await client.get_users(message.reply_to_message.from_user.id)
|
||||
|
||||
command = message.command[1] if len(message.command) > 1 else None
|
||||
if command and (command.startswith("@") or command.isdigit()):
|
||||
with contextlib.suppress(
|
||||
pyrogram.errors.exceptions.bad_request_400.UsernameNotOccupied,
|
||||
pyrogram.errors.exceptions.bad_request_400.UsernameInvalid,
|
||||
pyrogram.errors.exceptions.bad_request_400.PeerIdInvalid,
|
||||
IndexError,
|
||||
):
|
||||
return await client.get_users(message.command[1])
|
||||
if message.entities:
|
||||
for mention in message.entities:
|
||||
if mention.type == "text_mention":
|
||||
user = mention.user.id
|
||||
break
|
||||
with contextlib.suppress(Exception):
|
||||
return await client.get_users(user)
|
||||
return False
|
||||
|
||||
|
||||
async def get_user_adv(
|
||||
m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery],
|
||||
) -> pyrogram.types.User or bool:
|
||||
"""
|
||||
### `tgEasy.get_user_adv`
|
||||
- A Function to Get the User from the Message/CallbackQuery, If there is None arguments, returns the From User.
|
||||
- Parameters:
|
||||
- m (`pyrogram.types.Message` || `pyrogram.types.CallbackQuery`):
|
||||
- Message or Callbackquery.
|
||||
- Returns:
|
||||
- `pyrogram.types.User` on Success
|
||||
- `False` on Error
|
||||
|
||||
#### Example
|
||||
.. code-block:: python
|
||||
from tgEasy import command, get_user_adv
|
||||
|
||||
@command("id")
|
||||
async def id(client, message):
|
||||
user = await get_user_adv(message)
|
||||
await message.reply_text(f"Your ID is `{user.id}`")
|
||||
"""
|
||||
if isinstance(m, pyrogram.types.Message):
|
||||
message = m
|
||||
if isinstance(m, pyrogram.types.CallbackQuery):
|
||||
message = m.message
|
||||
if message.sender_chat:
|
||||
return False
|
||||
with contextlib.suppress(IndexError, AttributeError):
|
||||
if len(message.command) > 1:
|
||||
if message.command[1].startswith("@"):
|
||||
return await get_user(message)
|
||||
if message.command[1].isdigit():
|
||||
return await get_user(message)
|
||||
if "text_mention" in message.entities:
|
||||
return await get_user(message)
|
||||
if "from_user" in str(message.reply_to_message):
|
||||
return await get_user(message)
|
||||
with contextlib.suppress(Exception):
|
||||
if "sender_chat" in str(message.reply_to_message):
|
||||
return False
|
||||
if "from_user" in str(message.reply_to_message):
|
||||
return await message._client.get_users(
|
||||
message.reply_to_message.from_user.id
|
||||
)
|
||||
return await message._client.get_users(message.from_user.id)
|
||||
|
|
|
|||
|
|
@ -1,86 +1,100 @@
|
|||
# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram
|
||||
# Copyright (C) 2021 - 2022 Jayant Hegde Kageri <https://github.com/jayantkageri>
|
||||
|
||||
# This file is part of tgEasy.
|
||||
|
||||
# tgEasy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# tgEasy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||
import contextlib
|
||||
import os
|
||||
import typing
|
||||
import logging
|
||||
import pyrogram
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from misskaty.vars import LOG_CHANNEL
|
||||
|
||||
|
||||
async def handle_error(
|
||||
error, m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery]
|
||||
):
|
||||
"""
|
||||
### `handle_error`
|
||||
- A Function to Handle the Errors in Functions.
|
||||
- This Sends the Error Log to the Log Group and Replies Sorry Message for the Users.
|
||||
- This is Helper for all of the functions for handling the Errors.
|
||||
|
||||
- Parameters:
|
||||
- error:
|
||||
- The Exceptation.
|
||||
|
||||
- m (`pyrogram.types.Message` or `pyrogram.types.CallbackQuery`):
|
||||
- The Message or Callback Query where the Error occurred.
|
||||
|
||||
#### Exapmle
|
||||
.. code-block:: python
|
||||
import pyrogram
|
||||
|
||||
app = tgClient(pyrogram.Client())
|
||||
|
||||
@app.command("start")
|
||||
async def start(client, message):
|
||||
try:
|
||||
await message.reply_text("Hi :D') # I intentionally made an bug for Example :/
|
||||
except Exceptation as e:
|
||||
return await handle_error(e, message)
|
||||
"""
|
||||
|
||||
logging = logging.getLogger(__name__)
|
||||
logging.exception(traceback.format_exc())
|
||||
|
||||
day = datetime.now()
|
||||
tgl_now = datetime.now()
|
||||
cap_day = f"{day.strftime('%A')}, {tgl_now.strftime('%d %B %Y %H:%M:%S')}"
|
||||
|
||||
with open(f"crash_{tgl_now.strftime('%d %B %Y')}.txt", "w+", encoding="utf-8") as log:
|
||||
log.write(traceback.format_exc())
|
||||
log.close()
|
||||
if isinstance(m, pyrogram.types.Message):
|
||||
with contextlib.suppress(Exception):
|
||||
await m.reply_text(
|
||||
"An Internal Error Occurred while Processing your Command, the Logs have been sent to the Owners of this Bot. Sorry for Inconvenience"
|
||||
)
|
||||
await m._client.send_document(
|
||||
LOG_CHANNEL, f"crash_{tgl_now.strftime('%d %B %Y')}.txt", caption=f"Crash Report of this Bot\n{cap_day}"
|
||||
)
|
||||
if isinstance(m, pyrogram.types.CallbackQuery):
|
||||
with contextlib.suppress(Exception):
|
||||
await m.message.delete()
|
||||
await m.message.reply_text(
|
||||
"An Internal Error Occurred while Processing your Command, the Logs have been sent to the Owners of this Bot. Sorry for Inconvenience"
|
||||
)
|
||||
await m.message._client.send_document(
|
||||
LOG_CHANNEL, f"crash_{tgl_now.strftime('%d %B %Y')}.txt", caption=f"Crash Report of this Bot\n{cap_day}"
|
||||
)
|
||||
os.remove(f"crash_{tgl_now.strftime('%d %B %Y')}.txt")
|
||||
return True
|
||||
# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram
|
||||
# Copyright (C) 2021 - 2022 Jayant Hegde Kageri <https://github.com/jayantkageri>
|
||||
|
||||
# This file is part of tgEasy.
|
||||
|
||||
# tgEasy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# tgEasy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
import traceback
|
||||
import typing
|
||||
from datetime import datetime
|
||||
|
||||
import pyrogram
|
||||
|
||||
from misskaty.vars import LOG_CHANNEL
|
||||
|
||||
LOGGER = logging.getLogger("MissKaty")
|
||||
|
||||
|
||||
async def handle_error(
|
||||
_, m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery]
|
||||
):
|
||||
"""
|
||||
### `handle_error`
|
||||
- A Function to Handle the Errors in Functions.
|
||||
- This Sends the Error Log to the Log Group and Replies Sorry Message for the Users.
|
||||
- This is Helper for all of the functions for handling the Errors.
|
||||
|
||||
- Parameters:
|
||||
- error:
|
||||
- The Exceptation.
|
||||
|
||||
- m (`pyrogram.types.Message` or `pyrogram.types.CallbackQuery`):
|
||||
- The Message or Callback Query where the Error occurred.
|
||||
|
||||
"""
|
||||
|
||||
day = datetime.now()
|
||||
tgl_now = datetime.now()
|
||||
cap_day = f"{day.strftime('%A')}, {tgl_now.strftime('%d %B %Y %H:%M:%S')}"
|
||||
f_errname = f"crash_{tgl_now.strftime('%d %B %Y')}.txt"
|
||||
LOGGER.error(traceback.format_exc())
|
||||
if isinstance(m, pyrogram.types.Message):
|
||||
with open(f_errname, "w+", encoding="utf-8") as log:
|
||||
log.write(
|
||||
f"✍️ Message: {m.text or m.caption}\n👱♂️ User: {m.from_user.id if m.from_user else m.sender_chat.id}\n\n{traceback.format_exc()}"
|
||||
)
|
||||
log.close()
|
||||
with contextlib.suppress(Exception):
|
||||
try:
|
||||
await m.reply_photo(
|
||||
"https://img.yasirweb.eu.org/file/3c9162b242567ae25d5af.jpg",
|
||||
caption="An Internal Error Occurred while Processing your Command, the Logs have been sent to the Owners of this Bot. Sorry for Inconvenience",
|
||||
)
|
||||
except:
|
||||
await m.reply_msg(
|
||||
"An Internal Error Occurred while Processing your Command, the Logs have been sent to the Owners of this Bot. Sorry for Inconvenience"
|
||||
)
|
||||
await m._client.send_document(
|
||||
LOG_CHANNEL,
|
||||
f_errname,
|
||||
caption=f"Crash Report of this Bot\n{cap_day}",
|
||||
)
|
||||
if isinstance(m, pyrogram.types.CallbackQuery):
|
||||
with open(f_errname, "w+", encoding="utf-8") as log:
|
||||
log.write(
|
||||
f"✍️ Message: {m.message.text or m.message.caption}\n👱♂️ User: {m.from_user.id if m.from_user else m.sender_chat.id}\n\n{traceback.format_exc()}"
|
||||
)
|
||||
log.close()
|
||||
with contextlib.suppress(Exception):
|
||||
await m.message.delete()
|
||||
try:
|
||||
await m.reply_photo(
|
||||
"https://img.yasirweb.eu.org/file/3c9162b242567ae25d5af.jpg",
|
||||
caption="An Internal Error Occurred while Processing your Command, the Logs have been sent to the Owners of this Bot. Sorry for Inconvenience",
|
||||
)
|
||||
except:
|
||||
await m.message.reply_msg(
|
||||
"An Internal Error Occurred while Processing your Command, the Logs have been sent to the Owners of this Bot. Sorry for Inconvenience"
|
||||
)
|
||||
await m.message._client.send_document(
|
||||
LOG_CHANNEL,
|
||||
f_errname,
|
||||
caption=f"Crash Report of this Bot\n{cap_day}",
|
||||
)
|
||||
if os.path.exists(f_errname):
|
||||
os.remove(f_errname)
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ GNU General Public License for more details.
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with pyromod. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from contextlib import asynccontextmanager, contextmanager
|
||||
from inspect import iscoroutinefunction
|
||||
from logging import getLogger
|
||||
|
|
@ -24,7 +25,7 @@ from typing import Callable
|
|||
|
||||
from pyrogram.sync import async_to_sync
|
||||
|
||||
logger = getLogger(__name__)
|
||||
logger = getLogger("MissKaty")
|
||||
|
||||
|
||||
class PyromodConfig:
|
||||
|
|
|
|||
|
|
@ -1,30 +1,35 @@
|
|||
import asyncio
|
||||
|
||||
from pyrogram import filters
|
||||
from pyrogram.errors import MessageDeleteForbidden
|
||||
|
||||
from misskaty.vars import SUDO, OWNER_ID
|
||||
|
||||
data = {}
|
||||
|
||||
|
||||
async def task(msg, warn=False, sec=None):
|
||||
try:
|
||||
await msg.delete()
|
||||
except:
|
||||
pass
|
||||
if warn:
|
||||
user = msg.from_user
|
||||
user = msg.from_user or msg.sender_chat
|
||||
ids = await msg.reply_msg(
|
||||
f"Sorry {user.mention} [<code>{user.id}</code>], you must wait for {sec}s before using command again.."
|
||||
f"Sorry {user.mention if msg.from_user else msg.sender_chat.title} [<code>{user.id}</code>], you must wait for {sec}s before using this feature again.."
|
||||
)
|
||||
try:
|
||||
await msg.delete_msg()
|
||||
except MessageDeleteForbidden:
|
||||
pass
|
||||
await asyncio.sleep(sec)
|
||||
await ids.edit_msg(
|
||||
f"Alright {user.mention} [<code>{user.id}</code>], your cooldown is over you can command again.",
|
||||
f"Alright {user.mention if msg.from_user else msg.sender_chat.title} [<code>{user.id}</code>], your cooldown is over you can command again.",
|
||||
del_in=3,
|
||||
)
|
||||
|
||||
|
||||
def wait(sec):
|
||||
async def ___(flt, cli, msg):
|
||||
user_id = msg.from_user.id
|
||||
async def ___(flt, _, msg):
|
||||
user_id = msg.from_user.id if msg.from_user else msg.sender_chat.id
|
||||
if user_id in SUDO or user_id == OWNER_ID:
|
||||
return True
|
||||
if user_id in data:
|
||||
if msg.date.timestamp() >= data[user_id]["timestamp"] + flt.data:
|
||||
data[user_id] = {"timestamp": msg.date.timestamp(), "warned": False}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
from typing import Union
|
||||
|
||||
from pyrate_limiter import (
|
||||
BucketFullException,
|
||||
Duration,
|
||||
Limiter,
|
||||
MemoryListBucket,
|
||||
RequestRate,
|
||||
)
|
||||
|
||||
|
||||
class RateLimiter:
|
||||
"""
|
||||
Implement rate limit logic using leaky bucket
|
||||
algorithm, via pyrate_limiter.
|
||||
(https://pypi.org/project/pyrate-limiter/)
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
# 1 requests per seconds
|
||||
self.second_rate = RequestRate(1, Duration.SECOND)
|
||||
|
||||
# 15 requests per minute.
|
||||
self.minute_rate = RequestRate(15, Duration.MINUTE)
|
||||
|
||||
# 100 requests per hour
|
||||
self.hourly_rate = RequestRate(100, Duration.HOUR)
|
||||
|
||||
# 500 requests per day
|
||||
self.daily_rate = RequestRate(500, Duration.DAY)
|
||||
|
||||
self.limiter = Limiter(
|
||||
self.minute_rate,
|
||||
self.hourly_rate,
|
||||
self.daily_rate,
|
||||
bucket_class=MemoryListBucket,
|
||||
)
|
||||
|
||||
async def acquire(self, userid: Union[int, str]) -> bool:
|
||||
"""
|
||||
Acquire rate limit per userid and return True / False
|
||||
based on userid ratelimit status.
|
||||
"""
|
||||
|
||||
try:
|
||||
self.limiter.try_acquire(userid)
|
||||
return False
|
||||
except BucketFullException:
|
||||
return True
|
||||
|
|
@ -4,9 +4,11 @@ from .functions import *
|
|||
from .http import *
|
||||
from .human_read import *
|
||||
from .kuso_utils import *
|
||||
from .localization import *
|
||||
from .media_helper import *
|
||||
from .misc import *
|
||||
from .pyro_progress import *
|
||||
from .sqlite_helper import Cache
|
||||
from .stickerset import *
|
||||
from .subscene_helper import *
|
||||
from .time_gap import *
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ def hhmmss(seconds):
|
|||
|
||||
|
||||
async def take_ss(video_file):
|
||||
out_put_file_name = f"genss{str(time.time())}.png"
|
||||
cmd = f"vcsi '{video_file}' -t -w 1340 -g 4x4 --timestamp-font assets/DejaVuSans.ttf --metadata-font assets/DejaVuSans-Bold.ttf --template misskaty/helper/ssgen_template.html --quality 100 --end-delay-percent 20 --metadata-font-size 30 --timestamp-font-size 20 -o {out_put_file_name}"
|
||||
out_put_file_name = f"genss-{str(time.time())}.png"
|
||||
cmd = f"""vcsi "{video_file}" -t -w 1340 -g 4x4 --timestamp-font assets/DejaVuSans.ttf --metadata-font assets/DejaVuSans-Bold.ttf --template misskaty/helper/ssgen_template.html --quality 100 --end-delay-percent 20 --metadata-font-size 30 -o {out_put_file_name} --timestamp-font-size 20"""
|
||||
await shell_exec(cmd)
|
||||
return out_put_file_name if os.path.lexists(out_put_file_name) else None
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
import math
|
||||
import os
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from re import sub as re_sub
|
|||
from string import ascii_lowercase
|
||||
|
||||
from pyrogram import enums
|
||||
from pyrogram.types import Message
|
||||
|
||||
from misskaty import app
|
||||
|
||||
|
|
@ -16,6 +17,22 @@ def get_urls_from_text(text: str) -> bool:
|
|||
return [x[0] for x in findall(regex, text)]
|
||||
|
||||
|
||||
def extract_urls(reply_markup):
|
||||
urls = []
|
||||
if reply_markup.inline_keyboard:
|
||||
buttons = reply_markup.inline_keyboard
|
||||
for i, row in enumerate(buttons):
|
||||
for j, button in enumerate(row):
|
||||
if button.url:
|
||||
name = (
|
||||
"\n~\nbutton"
|
||||
if i * len(row) + j == 0
|
||||
else f"button{i * len(row) + j + 1}"
|
||||
)
|
||||
urls.append((f"{name}", button.text, button.url))
|
||||
return urls
|
||||
|
||||
|
||||
async def alpha_to_int(user_id_alphabet: str) -> int:
|
||||
alphabet = list(ascii_lowercase)[:10]
|
||||
user_id = ""
|
||||
|
|
@ -94,7 +111,7 @@ async def extract_user(message):
|
|||
return (await extract_user_and_reason(message))[0]
|
||||
|
||||
|
||||
async def time_converter(message, time_value: str) -> int:
|
||||
async def time_converter(message: Message, time_value: str) -> datetime:
|
||||
unit = ["m", "h", "d"] # m == minutes | h == hours | d == days
|
||||
check_unit = "".join(list(filter(time_value[-1].lower().endswith, unit)))
|
||||
currunt_time = datetime.now()
|
||||
|
|
@ -109,7 +126,7 @@ async def time_converter(message, time_value: str) -> int:
|
|||
temp_time = currunt_time + timedelta(days=int(time_digit))
|
||||
else:
|
||||
return await message.reply_text("Incorrect time specified.")
|
||||
return int(datetime.timestamp(temp_time))
|
||||
return temp_time
|
||||
|
||||
|
||||
def extract_text_and_keyb(ikb, text: str, row_width: int = 2):
|
||||
|
|
|
|||
|
|
@ -1,43 +1,41 @@
|
|||
from asyncio import gather
|
||||
|
||||
import httpx
|
||||
from aiohttp import ClientSession
|
||||
|
||||
# Aiohttp Async Client
|
||||
session = ClientSession()
|
||||
from httpx import AsyncClient, Timeout
|
||||
|
||||
# HTTPx Async Client
|
||||
http = httpx.AsyncClient(
|
||||
http2=True,
|
||||
fetch = AsyncClient(
|
||||
verify=False,
|
||||
timeout=httpx.Timeout(40),
|
||||
headers={
|
||||
"Accept-Language": "en-US,en;q=0.9,id-ID;q=0.8,id;q=0.7",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edge/107.0.1418.42",
|
||||
},
|
||||
timeout=Timeout(20),
|
||||
)
|
||||
|
||||
|
||||
async def get(url: str, *args, **kwargs):
|
||||
async with session.get(url, *args, **kwargs) as resp:
|
||||
try:
|
||||
data = await resp.json()
|
||||
except Exception:
|
||||
data = await resp.text()
|
||||
try:
|
||||
resp = await fetch.get(url, *args, **kwargs)
|
||||
data = await resp.json()
|
||||
except Exception:
|
||||
data = await resp.text()
|
||||
return data
|
||||
|
||||
|
||||
async def head(url: str, *args, **kwargs):
|
||||
async with session.head(url, *args, **kwargs) as resp:
|
||||
try:
|
||||
data = await resp.json()
|
||||
except Exception:
|
||||
data = await resp.text()
|
||||
try:
|
||||
resp = await fetch.head(url, *args, **kwargs)
|
||||
data = await resp.json()
|
||||
except Exception:
|
||||
data = await resp.text()
|
||||
return data
|
||||
|
||||
|
||||
async def post(url: str, *args, **kwargs):
|
||||
async with session.post(url, *args, **kwargs) as resp:
|
||||
try:
|
||||
data = await resp.json()
|
||||
except Exception:
|
||||
data = await resp.text()
|
||||
try:
|
||||
resp = await fetch.post(url, *args, **kwargs)
|
||||
data = await resp.json()
|
||||
except Exception:
|
||||
data = await resp.text()
|
||||
return data
|
||||
|
||||
|
||||
|
|
@ -54,8 +52,8 @@ async def multipost(url: str, times: int, *args, **kwargs):
|
|||
|
||||
|
||||
async def resp_get(url: str, *args, **kwargs):
|
||||
return await session.get(url, *args, **kwargs)
|
||||
return await fetch.get(url, *args, **kwargs)
|
||||
|
||||
|
||||
async def resp_post(url: str, *args, **kwargs):
|
||||
return await session.post(url, *args, **kwargs)
|
||||
return await fetch.post(url, *args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -5,13 +5,12 @@ def get_readable_file_size(size_in_bytes) -> str:
|
|||
if size_in_bytes is None:
|
||||
return "0B"
|
||||
index = 0
|
||||
while size_in_bytes >= 1024:
|
||||
while size_in_bytes >= 1024 and index < len(SIZE_UNITS) - 1:
|
||||
size_in_bytes /= 1024
|
||||
index += 1
|
||||
try:
|
||||
return f"{round(size_in_bytes, 2)}{SIZE_UNITS[index]}"
|
||||
except IndexError:
|
||||
return "File too large"
|
||||
return (
|
||||
f"{size_in_bytes:.2f} {SIZE_UNITS[index]}" if index > 0 else f"{size_in_bytes}B"
|
||||
)
|
||||
|
||||
|
||||
def get_readable_time(seconds: int) -> str:
|
||||
|
|
|
|||
|
|
@ -1,33 +1,29 @@
|
|||
import logging
|
||||
import traceback
|
||||
from html import escape
|
||||
from typing import Optional
|
||||
|
||||
import chevron
|
||||
from bs4 import BeautifulSoup
|
||||
from telegraph.aio import Telegraph
|
||||
|
||||
from misskaty import BOT_USERNAME
|
||||
from misskaty.helper.http import http
|
||||
from misskaty.helper.http import fetch
|
||||
from misskaty.helper.media_helper import post_to_telegraph
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
headers = {
|
||||
"Accept": "*/*",
|
||||
"User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19582",
|
||||
}
|
||||
LOGGER = logging.getLogger("MissKaty")
|
||||
|
||||
|
||||
async def kusonimeBypass(url: str, slug=None):
|
||||
async def kusonimeBypass(url: str):
|
||||
result = {}
|
||||
_url = url
|
||||
if slug:
|
||||
noslug_url = "https://kusonime.com/{slug}"
|
||||
_url = noslug_url.format({"slug": slug})
|
||||
page = await fetch.get(url)
|
||||
if page.status_code != 200:
|
||||
raise Exception("ERROR: Hostname might be blocked by server!")
|
||||
try:
|
||||
page = await http.get(_url, headers=headers)
|
||||
soup = BeautifulSoup(page.text, "lxml")
|
||||
thumb = soup.find("div", {"class": "post-thumb"}).find("img").get("src")
|
||||
data = []
|
||||
# title = soup.select("#venkonten > div.vezone > div.venser > div.venutama > div.lexot > p:nth-child(3) > strong")[0].text.strip()
|
||||
try:
|
||||
title = soup.find("h1", {"class": "jdlz"}).text # fix title njing haha
|
||||
season = (
|
||||
|
|
@ -99,20 +95,32 @@ async def kusonimeBypass(url: str, slug=None):
|
|||
0,
|
||||
"None",
|
||||
)
|
||||
num = 1
|
||||
genre = []
|
||||
for _genre in soup.select(
|
||||
"#venkonten > div.vezone > div.venser > div.venutama > div.lexot > div.info > p:nth-child(2)"
|
||||
):
|
||||
gen = _genre.text.split(":").pop().strip().split(", ")
|
||||
genre = gen
|
||||
for num, smokedl in enumerate(
|
||||
soup.find("div", {"class": "dlbodz"}).find_all(
|
||||
"div", {"class": "smokeddlrh"}
|
||||
),
|
||||
start=1,
|
||||
for smokedl in soup.find("div", {"class": "dlbodz"}).find_all(
|
||||
"div", {"class": "smokeddlrh"}
|
||||
):
|
||||
if not smokedl:
|
||||
continue
|
||||
mendata = {"name": title, "links": []}
|
||||
for smokeurl in smokedl.find_all("div", {"class": "smokeurl"}):
|
||||
if not smokeurl:
|
||||
continue
|
||||
quality = smokeurl.find("strong").text
|
||||
links = []
|
||||
for link in smokeurl.find_all("a"):
|
||||
url = link.get("href")
|
||||
client = link.text
|
||||
links.append({"client": client, "url": url})
|
||||
mendata["links"].append({"quality": quality, "link_download": links})
|
||||
for smokeurl in smokedl.find_all("div", {"class": "smokeurlrh"}):
|
||||
if not smokeurl:
|
||||
continue
|
||||
quality = smokeurl.find("strong").text
|
||||
links = []
|
||||
for link in smokeurl.find_all("a"):
|
||||
|
|
@ -121,45 +129,77 @@ async def kusonimeBypass(url: str, slug=None):
|
|||
links.append({"client": client, "url": url})
|
||||
mendata["links"].append({"quality": quality, "link_download": links})
|
||||
data.append(mendata)
|
||||
result |= {
|
||||
"error": False,
|
||||
"title": title,
|
||||
"thumb": thumb,
|
||||
"genre": genre,
|
||||
"genre_string": ", ".join(genre),
|
||||
"status_anime": status_anime,
|
||||
"season": season,
|
||||
"tipe": tipe,
|
||||
"ep": ep,
|
||||
"score": score,
|
||||
"duration": duration,
|
||||
"rilis": rilis,
|
||||
"data": data,
|
||||
}
|
||||
except Exception:
|
||||
num += 1
|
||||
for smokedl in soup.find("div", {"class": "dlbodz"}).find_all(
|
||||
"div", {"class": "smokeddl"}
|
||||
):
|
||||
if not smokedl:
|
||||
continue
|
||||
mendata = {"name": title, "links": []}
|
||||
for smokeurl in smokedl.find_all("div", {"class": "smokeurl"}):
|
||||
if not smokeurl:
|
||||
continue
|
||||
quality = smokeurl.find("strong").text
|
||||
links = []
|
||||
for link in smokeurl.find_all("a"):
|
||||
url = link.get("href")
|
||||
client = link.text
|
||||
links.append({"client": client, "url": url})
|
||||
mendata["links"].append({"quality": quality, "link_download": links})
|
||||
for smokeurl in smokedl.find_all("div", {"class": "smokeurlrh"}):
|
||||
if not smokeurl:
|
||||
continue
|
||||
quality = smokeurl.find("strong").text
|
||||
links = []
|
||||
for link in smokeurl.find_all("a"):
|
||||
url = link.get("href")
|
||||
client = link.text
|
||||
links.append({"client": client, "url": url})
|
||||
mendata["links"].append({"quality": quality, "link_download": links})
|
||||
data.append(mendata)
|
||||
num += 1
|
||||
result.update(
|
||||
{
|
||||
"title": title,
|
||||
"thumb": thumb,
|
||||
"genre": genre,
|
||||
"genre_string": ", ".join(genre),
|
||||
"status_anime": status_anime,
|
||||
"season": season,
|
||||
"tipe": tipe,
|
||||
"ep": ep,
|
||||
"score": score,
|
||||
"duration": duration,
|
||||
"rilis": rilis,
|
||||
"data": data,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
if result:
|
||||
result.clear()
|
||||
err = traceback.format_exc()
|
||||
LOGGER.error(err)
|
||||
result |= {"error": True, "error_message": err}
|
||||
await http.delete(_url)
|
||||
return result
|
||||
LOGGER.error(f"class: {e.__class__.__name_}, {err}")
|
||||
raise Exception(f"ERROR: {err}")
|
||||
finally:
|
||||
return result
|
||||
|
||||
|
||||
async def byPassPh(url: str, name: str):
|
||||
async def byPassPh(url: str, name: str) -> Optional[str]:
|
||||
kusonime = await kusonimeBypass(url)
|
||||
results = {"error": True, "error_message": kusonime}
|
||||
if not kusonime["error"]:
|
||||
template = """
|
||||
if not isinstance(kusonime, dict):
|
||||
return kusonime
|
||||
template = """
|
||||
<img src={{{thumb}}}>
|
||||
|
||||
<p><b>Title</b> : <code>{{title}}</code></p>
|
||||
<p><b>Genre</b> : <code>{{genre_string}}</code></p>
|
||||
<br><br><p><b>Season</b> : <code>{{season}}</code></p>
|
||||
<br><br><p><b>Type</b> : <code>{{tipe}}</code></p>
|
||||
<br><br><p><b>Status</b> : <code>{{status_anime}}</code></p>
|
||||
<br><br><p><b>Total Episode</b> : <code>{{ep}}</code></p>
|
||||
<br><br><p><b>Score</b> : <code>{{score}}</code></p>
|
||||
<br><br><p><b>Duration</b> : <code>{{duration}}</code></p>
|
||||
<br><br><p><b>Released on</b> : <code>{{rilis}}</code></p>
|
||||
<br><p><b>Season</b> : <code>{{season}}</code></p>
|
||||
<br><p><b>Type</b> : <code>{{tipe}}</code></p>
|
||||
<br><p><b>Status</b> : <code>{{status_anime}}</code></p>
|
||||
<br><p><b>Total Episode</b> : <code>{{ep}}</code></p>
|
||||
<br><p><b>Score</b> : <code>{{score}}</code></p>
|
||||
<br><p><b>Duration</b> : <code>{{duration}}</code></p>
|
||||
<br><p><b>Released on</b> : <code>{{rilis}}</code></p>
|
||||
<br><br>
|
||||
{{#data}}
|
||||
<h4>{{name}}</h4>
|
||||
|
|
@ -172,20 +212,15 @@ async def byPassPh(url: str, name: str):
|
|||
<br>
|
||||
{{/data}}
|
||||
""".strip()
|
||||
html = chevron.render(template, kusonime)
|
||||
telegraph = Telegraph()
|
||||
if not telegraph.get_access_token():
|
||||
await telegraph.create_account(short_name=BOT_USERNAME)
|
||||
page = await telegraph.create_page(
|
||||
f"{kusonime.get('title')} By {escape(name)}", html_content=html
|
||||
)
|
||||
results |= {"error": False, "url": f'https://telegra.ph/{page["path"]}'}
|
||||
del results["error_message"]
|
||||
return results
|
||||
return await post_to_telegraph(
|
||||
False,
|
||||
f"{kusonime.get('title')} By {escape(name)}",
|
||||
chevron.render(template, kusonime),
|
||||
)
|
||||
|
||||
|
||||
class Kusonime:
|
||||
def __init__(self):
|
||||
def __init__(self): # skipcq
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from glob import glob
|
|||
from typing import Dict, List
|
||||
|
||||
from pyrogram.enums import ChatType
|
||||
from pyrogram.types import CallbackQuery, InlineQuery, Message
|
||||
from pyrogram.types import CallbackQuery, ChatMemberUpdated, InlineQuery, Message
|
||||
|
||||
from database.locale_db import get_db_lang
|
||||
|
||||
|
|
@ -15,6 +15,7 @@ enabled_locales: List[str] = [
|
|||
"en-US", # English (United States)
|
||||
"id-ID", # Indonesian
|
||||
"id-JW", # Javanese
|
||||
"ru-RU", # Russian
|
||||
]
|
||||
|
||||
default_language: str = "en-US"
|
||||
|
|
@ -54,7 +55,7 @@ def get_locale_string(
|
|||
async def get_lang(message) -> str:
|
||||
if isinstance(message, CallbackQuery):
|
||||
chat = message.message.chat
|
||||
elif isinstance(message, Message):
|
||||
elif isinstance(message, (Message, ChatMemberUpdated)):
|
||||
chat = message.chat
|
||||
elif isinstance(message, InlineQuery):
|
||||
chat, chat.type = message.from_user, ChatType.PRIVATE
|
||||
|
|
|
|||
|
|
@ -13,17 +13,17 @@ async def post_to_telegraph(is_media: bool, title=None, content=None, media=None
|
|||
if telegraph.get_access_token() is None:
|
||||
await telegraph.create_account(short_name=BOT_USERNAME)
|
||||
if is_media:
|
||||
"""Create a Telegram Post Foto/Video"""
|
||||
# Create a Telegram Post Foto/Video
|
||||
response = await telegraph.upload_file(media)
|
||||
return f"https://telegra.ph{response[0]['src']}"
|
||||
"""Create a Telegram Post using HTML Content"""
|
||||
return f"https://img.yasirweb.eu.org{response[0]['src']}"
|
||||
# Create a Telegram Post using HTML Content
|
||||
response = await telegraph.create_page(
|
||||
title,
|
||||
html_content=content,
|
||||
author_url=f"https://t.me/{BOT_USERNAME}",
|
||||
author_name=BOT_USERNAME,
|
||||
)
|
||||
return response["url"]
|
||||
return f"https://te.legra.ph/{response['path']}"
|
||||
|
||||
|
||||
async def run_subprocess(cmd):
|
||||
|
|
@ -49,7 +49,7 @@ async def get_media_info(file_link):
|
|||
"-show_chapters",
|
||||
"-show_programs",
|
||||
]
|
||||
data, err = await run_subprocess(ffprobe_cmd)
|
||||
data, _ = await run_subprocess(ffprobe_cmd)
|
||||
return data
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,226 +1,78 @@
|
|||
css = """
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'JetBrainsMono';
|
||||
src: url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff/JetBrainsMono-Regular.woff') format('woff'),
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/ttf/JetBrainsMono-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
.loader-pulse {
|
||||
position: relative;
|
||||
bottom: 0.17rem;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
float: left;
|
||||
border-radius: 50%;
|
||||
background: #50fa7b;
|
||||
animation: load-pulse 1s infinite linear;
|
||||
}
|
||||
@keyframes load-pulse {
|
||||
0% {
|
||||
transform: scale(0.05);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.5);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
}
|
||||
::-webkit-scrollbar-corner ,
|
||||
::-webkit-scrollbar-track {
|
||||
display: none;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 8px;
|
||||
background-color: #50fa7b;
|
||||
}
|
||||
body {
|
||||
background-color: #282a36;
|
||||
font-family: "Josefin Sans", sans-serif;
|
||||
color: #f8f8f2;
|
||||
overscroll-behaviour: contain;
|
||||
}
|
||||
code {
|
||||
font-family: "Ubuntu mono", "Courier prime";
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.container.heading {
|
||||
display: block;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
margin-bottom: 50px;
|
||||
padding: 20px 20px 20px 20px;
|
||||
font-size: 1rem;
|
||||
overflow: scroll;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 7px;
|
||||
border-style: dashed;
|
||||
font-family: "Ubuntu mono", "Courier prime";
|
||||
border: 2px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.container.heading::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.container.subheading {
|
||||
margin-left: 15px;
|
||||
margin-bottom:15px;
|
||||
padding-top: 10px;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
.container.infobox {
|
||||
margin-bottom: 40px;
|
||||
margin-left: 12px;
|
||||
margin-right: 12px;
|
||||
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 2.5px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 10px 15px 15px 10px;
|
||||
white-space: pre ;
|
||||
overflow: scroll;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.subtitle {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns:320px;
|
||||
overflow-x: auto;
|
||||
overscroll-behaviour-inline: contain;
|
||||
margin-bottom: 40px;
|
||||
margin-left: 7px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.subtitle::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.container.subtitlebox {
|
||||
margin-left: 5px;
|
||||
margin-right: 15px;
|
||||
margin-bottom: 0px;
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 3px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 15px 15px 15px 10px;
|
||||
white-space: nowrap;
|
||||
overflow: scroll;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.icons {
|
||||
float: left;
|
||||
margin-left: 15px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 32px;
|
||||
width: 100%;
|
||||
font-family: JetBrainsMono;
|
||||
background-color: #212121;
|
||||
}
|
||||
.footer-text{
|
||||
display: block;
|
||||
justify-content: space-between;
|
||||
font-size: 0.9rem;
|
||||
padding-left: 0.7rem;
|
||||
padding-right: 1.5rem;
|
||||
padding-top: 0.25rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
|
||||
from .http import http
|
||||
|
||||
|
||||
def html_builder(title: str, text: str) -> str:
|
||||
"""
|
||||
Make proper html with css from given content.
|
||||
"""
|
||||
|
||||
heading = "<span class='container heading'><b>{content}</b></span>"
|
||||
subheading = "<span class='container subheading'><b>{content}</b></span>"
|
||||
infobox = "<span class='container infobox'>"
|
||||
subtitlebox = "<span class='container subtitlebox'>"
|
||||
icon = "<img class='icons' src={icon_url} width='35px' height='35px' alt='' >"
|
||||
html_msg = f"<body>{heading.format(content=title)}"
|
||||
|
||||
for line in text.splitlines():
|
||||
if ":" not in line and bool(line):
|
||||
if "Text #" in line:
|
||||
if bool(re.search("Text #1$", line)):
|
||||
subtitle_count = len(re.findall("Text #", text))
|
||||
html_msg += icon.format(
|
||||
icon_url="https://te.legra.ph/file/9d4a676445544d0f2d6db.png"
|
||||
)
|
||||
html_msg += subheading.format(
|
||||
content=f"Subtitles ({subtitle_count} subtitle)"
|
||||
)
|
||||
html_msg += "<span style='padding: 10px 0vw;' class='subtitle'>"
|
||||
|
||||
elif "General" in line:
|
||||
html_msg += icon.format(
|
||||
icon_url="https://te.legra.ph/file/638fb0416f2600e7c5aa3.png"
|
||||
)
|
||||
html_msg += subheading.format(content="General")
|
||||
|
||||
elif "Video" in line:
|
||||
html_msg += icon.format(
|
||||
icon_url="https://te.legra.ph/file/fbc30d71cf71c9a54e59d.png"
|
||||
)
|
||||
html_msg += subheading.format(content="Video")
|
||||
|
||||
elif "Audio" in line:
|
||||
html_msg += icon.format(
|
||||
icon_url="https://te.legra.ph/file/a3c431be457fedbae2286.png"
|
||||
)
|
||||
html_msg += subheading.format(content=f"{line.strip()}")
|
||||
|
||||
elif "Menu" in line:
|
||||
html_msg += "</span>"
|
||||
html_msg += icon.format(
|
||||
icon_url="https://te.legra.ph/file/3023b0c2bc202ec9d6d0d.png"
|
||||
)
|
||||
html_msg += subheading.format(content="Chapters")
|
||||
|
||||
else:
|
||||
html_msg += subheading.format(content=f"{line.strip()}")
|
||||
html_msg += subtitlebox if "Text #" in line else infobox
|
||||
|
||||
elif ":" in line:
|
||||
if "Attachments" not in line and "ErrorDetectionType" not in line:
|
||||
html_msg += f"<div><code>{line.strip()}</code></div>"
|
||||
|
||||
else:
|
||||
html_msg += "</span>"
|
||||
|
||||
html_msg += "</span>"
|
||||
return css + html_msg
|
||||
|
||||
|
||||
async def mediainfo_paste(text: str, title: str) -> str:
|
||||
html_content = html_builder(title, text)
|
||||
URL = "https://mediainfo-1-y5870653.deta.app/api"
|
||||
response = await http.post(URL, json={"content": html_content})
|
||||
return (
|
||||
f"https://mediainfo-1-y5870653.deta.app/{json.loads(response.content)['key']}"
|
||||
)
|
||||
import json
|
||||
import re
|
||||
|
||||
from .http import fetch
|
||||
|
||||
|
||||
def html_builder(title: str, text: str) -> str:
|
||||
"""
|
||||
Make proper html with css from given content.
|
||||
"""
|
||||
|
||||
heading = "<span class='container heading'><b>{content}</b></span>"
|
||||
subheading = "<span class='container subheading'><b>{content}</b></span>"
|
||||
infobox = "<span class='container infobox'>"
|
||||
subtitlebox = "<span class='container subtitlebox'>"
|
||||
icon = "<img class='icons' src={icon_url} width='35px' height='35px' alt='' >"
|
||||
html_msg = f"<body>{heading.format(content=title)}"
|
||||
|
||||
for line in text.splitlines():
|
||||
if ":" not in line and bool(line):
|
||||
if "Text #" in line:
|
||||
if bool(re.search("Text #1$", line)):
|
||||
subtitle_count = len(re.findall("Text #", text))
|
||||
html_msg += icon.format(
|
||||
icon_url="https://te.legra.ph/file/9d4a676445544d0f2d6db.png"
|
||||
)
|
||||
html_msg += subheading.format(
|
||||
content=f"Subtitles ({subtitle_count} subtitle)"
|
||||
)
|
||||
html_msg += "<span style='padding: 10px 0vw;' class='subtitle'>"
|
||||
|
||||
elif "General" in line:
|
||||
html_msg += icon.format(
|
||||
icon_url="https://te.legra.ph/file/638fb0416f2600e7c5aa3.png"
|
||||
)
|
||||
html_msg += subheading.format(content="General")
|
||||
|
||||
elif "Video" in line:
|
||||
html_msg += icon.format(
|
||||
icon_url="https://te.legra.ph/file/fbc30d71cf71c9a54e59d.png"
|
||||
)
|
||||
html_msg += subheading.format(content="Video")
|
||||
|
||||
elif "Audio" in line:
|
||||
html_msg += icon.format(
|
||||
icon_url="https://te.legra.ph/file/a3c431be457fedbae2286.png"
|
||||
)
|
||||
html_msg += subheading.format(content=f"{line.strip()}")
|
||||
|
||||
elif "Menu" in line:
|
||||
html_msg += "</span>"
|
||||
html_msg += icon.format(
|
||||
icon_url="https://te.legra.ph/file/3023b0c2bc202ec9d6d0d.png"
|
||||
)
|
||||
html_msg += subheading.format(content="Chapters")
|
||||
|
||||
else:
|
||||
html_msg += subheading.format(content=f"{line.strip()}")
|
||||
html_msg += subtitlebox if "Text #" in line else infobox
|
||||
|
||||
elif ":" in line:
|
||||
if "Attachments" not in line and "ErrorDetectionType" not in line:
|
||||
html_msg += f"<div><code>{line.strip()}</code></div>"
|
||||
|
||||
else:
|
||||
html_msg += "</span>"
|
||||
|
||||
html_msg += "</span>"
|
||||
return html_msg
|
||||
|
||||
|
||||
async def mediainfo_paste(text: str, title: str) -> str:
|
||||
html_content = html_builder(title, text)
|
||||
URL = "https://yasirr.eu.org/mediainfo"
|
||||
response = await fetch.post(URL, data={"content": html_content})
|
||||
return (
|
||||
f"https://yasirr.eu.org/mediainfo-{json.loads(response.content)['key']}"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -75,6 +75,14 @@ def paginate_modules(page_n, module_dict, prefix, chat=None):
|
|||
),
|
||||
)
|
||||
]
|
||||
else:
|
||||
pairs = pairs[modulo_page * COLUMN_SIZE : COLUMN_SIZE * (modulo_page + 1)] + [
|
||||
(
|
||||
EqInlineKeyboardButton(
|
||||
"Back", callback_data=f"{prefix}_home({modulo_page})"
|
||||
),
|
||||
)
|
||||
]
|
||||
|
||||
return pairs
|
||||
|
||||
|
|
|
|||
560
misskaty/helper/sqlite_helper.py
Normal file
560
misskaty/helper/sqlite_helper.py
Normal file
|
|
@ -0,0 +1,560 @@
|
|||
import os
|
||||
import pickle
|
||||
import sqlite3
|
||||
from contextlib import suppress
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from functools import wraps
|
||||
from pathlib import Path
|
||||
from threading import local
|
||||
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple
|
||||
|
||||
__all__ = ["Cache"]
|
||||
|
||||
|
||||
class Cache:
|
||||
"""Simple SQLite Cache."""
|
||||
|
||||
PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL
|
||||
DEFAULT_TIMEOUT = 300
|
||||
DEFAULT_PRAGMA = {
|
||||
"mmap_size": 2**26, # https://www.sqlite.org/pragma.html#pragma_mmap_size
|
||||
"cache_size": 8192, # https://www.sqlite.org/pragma.html#pragma_cache_size
|
||||
"wal_autocheckpoint": 1000, # https://www.sqlite.org/pragma.html#pragma_wal_autocheckpoint
|
||||
"auto_vacuum": "none", # https://www.sqlite.org/pragma.html#pragma_auto_vacuum
|
||||
"synchronous": "off", # https://www.sqlite.org/pragma.html#pragma_synchronous
|
||||
"journal_mode": "wal", # https://www.sqlite.org/pragma.html#pragma_journal_mode
|
||||
"temp_store": "file", # https://www.sqlite.org/pragma.html#pragma_temp_store
|
||||
}
|
||||
|
||||
_transaction_sql = "BEGIN EXCLUSIVE TRANSACTION; {} COMMIT TRANSACTION;"
|
||||
|
||||
_create_sql = "CREATE TABLE IF NOT EXISTS cache (key TEXT PRIMARY KEY, value BLOB, exp FLOAT);"
|
||||
_create_index_sql = "CREATE UNIQUE INDEX IF NOT EXISTS cache_key ON cache(key);"
|
||||
_set_pragma = "PRAGMA {};"
|
||||
_set_pragma_equal = "PRAGMA {}={};"
|
||||
|
||||
_add_sql = (
|
||||
"INSERT INTO cache (key, value, exp) VALUES (:key, :value, :exp) "
|
||||
"ON CONFLICT(key) DO UPDATE SET value = :value, exp = :exp "
|
||||
"WHERE (exp <> -1.0 AND DATETIME(exp, 'unixepoch') <= DATETIME('now'));"
|
||||
)
|
||||
_get_sql = "SELECT value, exp FROM cache WHERE key = :key;"
|
||||
_set_sql = (
|
||||
"INSERT INTO cache (key, value, exp) VALUES (:key, :value, :exp) "
|
||||
"ON CONFLICT(key) DO UPDATE SET value = :value, exp = :exp;"
|
||||
)
|
||||
_check_sql = (
|
||||
"SELECT value, exp FROM cache WHERE key = :key "
|
||||
"AND (exp = -1.0 OR DATETIME(exp, 'unixepoch') > DATETIME('now'));"
|
||||
)
|
||||
_update_sql = (
|
||||
"UPDATE cache SET value = :value WHERE key = :key "
|
||||
"AND (exp = -1.0 OR DATETIME(exp, 'unixepoch') > DATETIME('now'));"
|
||||
)
|
||||
|
||||
# TODO: add 'RETURNING COUNT(*)!=0' to these when sqlite3 version >=3.35.0
|
||||
_delete_sql = "DELETE FROM cache WHERE key = :key;"
|
||||
_touch_sql = (
|
||||
"UPDATE cache SET exp = :exp WHERE key = :key "
|
||||
"AND (exp = -1.0 OR DATETIME(exp, 'unixepoch') > DATETIME('now'));"
|
||||
)
|
||||
_clear_sql = "DELETE FROM cache;"
|
||||
|
||||
_add_many_sql = (
|
||||
"INSERT INTO cache (key, value, exp) VALUES {}"
|
||||
"ON CONFLICT(key) DO UPDATE SET value = excluded.value, exp = excluded.exp "
|
||||
"WHERE (exp <> -1.0 AND DATETIME(exp, 'unixepoch') <= DATETIME('now'));"
|
||||
)
|
||||
_get_many_sql = "SELECT key, value, exp FROM cache WHERE key IN ({});"
|
||||
_set_many_sql = (
|
||||
"INSERT INTO cache (key, value, exp) VALUES {}"
|
||||
"ON CONFLICT(key) DO UPDATE SET value = excluded.value, exp = excluded.exp;"
|
||||
)
|
||||
_delete_many_sql = "DELETE FROM cache WHERE key IN ({});"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
filename: str = ".cache",
|
||||
path: str = None,
|
||||
in_memory: bool = True,
|
||||
timeout: int = 5,
|
||||
isolation_level: Optional[
|
||||
Literal["DEFERRED", "IMMEDIATE", "EXCLUSIVE"]
|
||||
] = "DEFERRED",
|
||||
**kwargs,
|
||||
):
|
||||
"""Create a cache using sqlite3.
|
||||
|
||||
:param filename: Cache file name.
|
||||
:param path: Path string to the wanted db location. If None, use current directory.
|
||||
:param in_memory: Create database in-memory only. A file is still created, but nothing is stored in it.
|
||||
:param timeout: Cache connection timeout.
|
||||
:param isolation_level: Controls the transaction handling performed by sqlite3.
|
||||
If set to None, transactions are never implicitly opened.
|
||||
https://www.sqlite.org/lang_transaction.html
|
||||
:param kwargs: Pragma settings. https://www.sqlite.org/pragma.html
|
||||
"""
|
||||
|
||||
filepath = filename if path is None else str(Path(path) / filename)
|
||||
suffix = ":?mode=memory&cache=shared" if in_memory else ""
|
||||
self.connection_string = f"{filepath}{suffix}"
|
||||
self.pragma = {**kwargs, **self.DEFAULT_PRAGMA}
|
||||
self.timeout = timeout
|
||||
self.path = path
|
||||
self.isolation_level = isolation_level
|
||||
self.local = local()
|
||||
self.local.instances = getattr(self.local, "instances", 0) + 1
|
||||
|
||||
self._con.execute(self._create_sql)
|
||||
self._con.execute(self._create_index_sql)
|
||||
self._con.commit()
|
||||
|
||||
@property
|
||||
def _con(self) -> sqlite3.Connection:
|
||||
if not os.path.exists(self.path):
|
||||
os.mkdir(self.path)
|
||||
try:
|
||||
return self.local.con
|
||||
except AttributeError:
|
||||
self.local.con = sqlite3.connect(
|
||||
self.connection_string,
|
||||
timeout=self.timeout,
|
||||
isolation_level=self.isolation_level,
|
||||
)
|
||||
self._apply_pragma()
|
||||
return self.local.con
|
||||
|
||||
def __getitem__(self, item: str) -> Any:
|
||||
value = self.get(item)
|
||||
if value is None:
|
||||
raise KeyError("Key not in cache.")
|
||||
return value
|
||||
|
||||
def __setitem__(self, item: str, value: Any) -> None:
|
||||
self.set(item, value)
|
||||
|
||||
def __delitem__(self, key):
|
||||
self.delete(key)
|
||||
|
||||
def __contains__(self, key):
|
||||
return self._con.execute(self._check_sql, {"key": key}).fetchone() is not None
|
||||
|
||||
def __enter__(self):
|
||||
self._con # noqa pylint: disable=W0104
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.close()
|
||||
|
||||
def __del__(self):
|
||||
self.local.instances = getattr(self.local, "instances", 0) - 1
|
||||
if self.local.instances <= 0:
|
||||
self.close()
|
||||
|
||||
def close(self) -> None:
|
||||
"""Closes the cache."""
|
||||
self._con.execute(
|
||||
self._set_pragma.format("optimize")
|
||||
) # https://www.sqlite.org/pragma.html#pragma_optimize
|
||||
self._con.close()
|
||||
with suppress(AttributeError):
|
||||
delattr(self.local, "con")
|
||||
|
||||
def _apply_pragma(self):
|
||||
for key, value in self.pragma.items():
|
||||
self._con.execute(self._set_pragma_equal.format(key, value))
|
||||
|
||||
@staticmethod
|
||||
def _exp_timestamp(timeout: int = DEFAULT_TIMEOUT) -> float:
|
||||
if timeout < 0:
|
||||
return -1.0
|
||||
return (datetime.now(tz=timezone.utc) + timedelta(seconds=timeout)).timestamp()
|
||||
|
||||
@staticmethod
|
||||
def _exp_datetime(exp: float) -> Optional[datetime]:
|
||||
return None if exp == -1.0 else datetime.utcfromtimestamp(exp)
|
||||
|
||||
def _stream(self, value: Any) -> bytes:
|
||||
return pickle.dumps(value, protocol=self.PICKLE_PROTOCOL)
|
||||
|
||||
def _unstream(self, value: bytes) -> Any:
|
||||
return pickle.loads(value) # noqa: S301
|
||||
|
||||
def add(self, key: str, value: Any, timeout: int = DEFAULT_TIMEOUT) -> None:
|
||||
"""Set the value to the cache only if the key is not already in the cache,
|
||||
or the found value has expired.
|
||||
|
||||
:param key: Cache key.
|
||||
:param value: Picklable object to store.
|
||||
:param timeout: How long the value is valid in the cache.
|
||||
Negative numbers will keep the key in cache until manually removed.
|
||||
"""
|
||||
data = {
|
||||
"key": key,
|
||||
"value": self._stream(value),
|
||||
"exp": self._exp_timestamp(timeout),
|
||||
}
|
||||
self._con.execute(self._add_sql, data)
|
||||
self._con.commit()
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
"""Get the value under some key. Return `default` if key not in the cache or expired.
|
||||
|
||||
:param key: Cache key.
|
||||
:param default: Value to return if key not in the cache.
|
||||
"""
|
||||
result: Optional[Tuple[bytes, float]] = self._con.execute(
|
||||
self._get_sql, {"key": key}
|
||||
).fetchone()
|
||||
|
||||
if result is None:
|
||||
return default
|
||||
|
||||
exp = self._exp_datetime(result[1])
|
||||
if exp is not None and datetime.utcnow() >= exp:
|
||||
self._con.execute(self._delete_sql, {"key": key})
|
||||
self._con.commit()
|
||||
return default
|
||||
|
||||
return self._unstream(result[0])
|
||||
|
||||
def set(self, key: str, value: Any, timeout: int = DEFAULT_TIMEOUT) -> None:
|
||||
"""Set a value in cache under some key.
|
||||
|
||||
:param key: Cache key.
|
||||
:param value: Picklable object to store.
|
||||
:param timeout: How long the value is valid in the cache.
|
||||
Negative numbers will keep the key in cache until manually removed.
|
||||
"""
|
||||
data = {
|
||||
"key": key,
|
||||
"value": self._stream(value),
|
||||
"exp": self._exp_timestamp(timeout),
|
||||
}
|
||||
self._con.execute(self._set_sql, data)
|
||||
self._con.commit()
|
||||
|
||||
def update(self, key: str, value: Any) -> None:
|
||||
"""Update value in the cache. Does nothing if key not in the cache or expired.
|
||||
|
||||
:param key: Cache key.
|
||||
:param value: Picklable object to store.
|
||||
"""
|
||||
data = {"key": key, "value": self._stream(value)}
|
||||
self._con.execute(self._update_sql, data)
|
||||
self._con.commit()
|
||||
|
||||
def touch(self, key: str, timeout: int = DEFAULT_TIMEOUT) -> None:
|
||||
"""Extend the lifetime of an object in cache. Does nothing if key is not in the cache or is expired.
|
||||
|
||||
:param key: Cache key.
|
||||
:param timeout: How long the value is valid in the cache.
|
||||
Negative numbers will keep the key in cache until manually removed.
|
||||
"""
|
||||
data = {"exp": self._exp_timestamp(timeout), "key": key}
|
||||
self._con.execute(self._touch_sql, data)
|
||||
self._con.commit()
|
||||
|
||||
def delete(self, key: str) -> None:
|
||||
"""Remove the value under the given key from the cache. Does nothing if key is not in the cache.
|
||||
|
||||
:param key: Cache key.
|
||||
"""
|
||||
self._con.execute(self._delete_sql, {"key": key})
|
||||
self._con.commit()
|
||||
|
||||
def add_many(self, dict_: Dict[str, Any], timeout: int = DEFAULT_TIMEOUT) -> None:
|
||||
"""For all keys in the given dict, add the value to the cache only if the key is not
|
||||
already in the cache, or the found value has expired.
|
||||
|
||||
:param dict_: Cache keys with values to add.
|
||||
:param timeout: How long the value is valid in the cache.
|
||||
Negative numbers will keep the key in cache until manually removed.
|
||||
"""
|
||||
command = self._add_many_sql.format(
|
||||
", ".join([f"(:key{n}, :value{n}, :exp{n})" for n in range(len(dict_))])
|
||||
)
|
||||
|
||||
data = {}
|
||||
exp = self._exp_timestamp(timeout)
|
||||
for i, (key, value) in enumerate(dict_.items()):
|
||||
data[f"key{i}"] = key
|
||||
data[f"value{i}"] = self._stream(value)
|
||||
data[f"exp{i}"] = exp
|
||||
|
||||
self._con.execute(command, data)
|
||||
self._con.commit()
|
||||
|
||||
def get_many(self, keys: List[str]) -> Dict[str, Any]:
|
||||
"""Get all values that exist and aren't expired from the given cache keys, and return a dict.
|
||||
|
||||
:param keys: List of cache keys.
|
||||
"""
|
||||
seq = ", ".join([f"'{value}'" for value in keys])
|
||||
fetched: List[Tuple[str, Any, float]] = self._con.execute(
|
||||
self._get_many_sql.format(seq)
|
||||
).fetchall()
|
||||
|
||||
if not fetched:
|
||||
return {}
|
||||
|
||||
results: Dict[str, Any] = {}
|
||||
to_delete: List[str] = []
|
||||
for key, value, exp in fetched:
|
||||
exp = self._exp_datetime(exp)
|
||||
if exp is not None and datetime.utcnow() >= exp:
|
||||
to_delete.append(key)
|
||||
continue
|
||||
|
||||
results[key] = self._unstream(value)
|
||||
|
||||
if to_delete:
|
||||
self._con.execute(
|
||||
self._delete_many_sql.format(
|
||||
", ".join([f"'{value}'" for value in to_delete])
|
||||
)
|
||||
)
|
||||
self._con.commit()
|
||||
|
||||
return results
|
||||
|
||||
def set_many(self, dict_: Dict[str, Any], timeout: int = DEFAULT_TIMEOUT) -> None:
|
||||
"""Set values to the cache for all keys in the given dict.
|
||||
|
||||
:param dict_: Cache keys with values to set.
|
||||
:param timeout: How long the value is valid in the cache.
|
||||
Negative numbers will keep the key in cache until manually removed.
|
||||
"""
|
||||
command = self._set_many_sql.format(
|
||||
", ".join([f"(:key{n}, :value{n}, :exp{n})" for n in range(len(dict_))])
|
||||
)
|
||||
|
||||
data = {}
|
||||
exp = self._exp_timestamp(timeout)
|
||||
for i, (key, value) in enumerate(dict_.items()):
|
||||
data[f"key{i}"] = key
|
||||
data[f"value{i}"] = self._stream(value)
|
||||
data[f"exp{i}"] = exp
|
||||
|
||||
self._con.execute(command, data)
|
||||
self._con.commit()
|
||||
|
||||
def update_many(self, dict_: Dict[str, Any]) -> None:
|
||||
"""Update values to the cache for all keys in the given dict. Does nothing if key not in cache or expired.
|
||||
|
||||
:param dict_:Cache keys with values to update to.
|
||||
"""
|
||||
seq = [
|
||||
{"key": key, "value": self._stream(value)} for key, value in dict_.items()
|
||||
]
|
||||
self._con.executemany(self._update_sql, seq)
|
||||
self._con.commit()
|
||||
|
||||
def touch_many(self, keys: List[str], timeout: int = DEFAULT_TIMEOUT) -> None:
|
||||
"""Extend the lifetime for all objects under the given keys in cache.
|
||||
Does nothing if a key is not in the cache or is expired.
|
||||
|
||||
:param keys: List of cache keys.
|
||||
:param timeout: How long the value is valid in the cache.
|
||||
Negative numbers will keep the key in cache until manually removed.
|
||||
"""
|
||||
exp = self._exp_timestamp(timeout)
|
||||
seq = [{"key": key, "exp": exp} for key in keys]
|
||||
self._con.executemany(self._touch_sql, seq)
|
||||
self._con.commit()
|
||||
|
||||
def delete_many(self, keys: List[str]) -> None:
|
||||
"""Remove all the values under the given keys from the cache.
|
||||
|
||||
:param keys: List of cache keys.
|
||||
"""
|
||||
self._con.execute(
|
||||
self._delete_many_sql.format(", ".join([f"'{value}'" for value in keys]))
|
||||
)
|
||||
self._con.commit()
|
||||
|
||||
def get_or_set(self, key: str, default: Any, timeout: int = DEFAULT_TIMEOUT) -> Any:
|
||||
"""Get a value under some key, or set the default if key is not in cache.
|
||||
|
||||
:param key: Cache key.
|
||||
:param default: Picklable object to store if key is not in cache.
|
||||
:param timeout: How long the value is valid in the cache.
|
||||
Negative numbers will keep the key in cache until manually removed.
|
||||
"""
|
||||
result: Optional[Tuple[bytes, float]] = self._con.execute(
|
||||
self._get_sql, {"key": key}
|
||||
).fetchone()
|
||||
|
||||
if result is not None:
|
||||
exp = self._exp_datetime(result[1])
|
||||
if exp is not None and datetime.utcnow() >= exp:
|
||||
self._con.execute(self._delete_sql, {"key": key})
|
||||
else:
|
||||
return self._unstream(result[0])
|
||||
|
||||
data = {
|
||||
"key": key,
|
||||
"value": self._stream(default),
|
||||
"exp": self._exp_timestamp(timeout),
|
||||
}
|
||||
self._con.execute(self._set_sql, data)
|
||||
self._con.commit()
|
||||
return default
|
||||
|
||||
def get_all(self) -> Dict[str, Any]:
|
||||
"""Get all key-value pairs from the cache."""
|
||||
all_data = self._con.execute("SELECT key, value FROM cache;").fetchall()
|
||||
return {key: self._unstream(value) for key, value in all_data}
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Clear the cache from all values."""
|
||||
self._con.execute(self._clear_sql)
|
||||
self._con.commit()
|
||||
|
||||
def incr(self, key: str, delta: int = 1) -> int:
|
||||
"""Increment the value in cache by the given delta.
|
||||
Note that this is not an atomic transaction!
|
||||
|
||||
:param key: Cache key.
|
||||
:param delta: How much to increment.
|
||||
:raises ValueError: Value cannot be incremented.
|
||||
"""
|
||||
result: Optional[Tuple[bytes, float]] = self._con.execute(
|
||||
self._check_sql, {"key": key}
|
||||
).fetchone()
|
||||
|
||||
if result is None:
|
||||
raise ValueError("Nonexistent or expired cache key.")
|
||||
|
||||
value = self._unstream(result[0])
|
||||
if not isinstance(value, int):
|
||||
raise ValueError("Value is not a number.")
|
||||
|
||||
new_value = value + delta
|
||||
self._con.execute(
|
||||
self._update_sql, {"key": key, "value": self._stream(new_value)}
|
||||
)
|
||||
self._con.commit()
|
||||
return new_value
|
||||
|
||||
def decr(self, key: str, delta: int = 1) -> int:
|
||||
"""Decrement the value in cache by the given delta.
|
||||
Note that this is not an atomic transaction!
|
||||
|
||||
:param key: Cache key.
|
||||
:param delta: How much to decrement.
|
||||
:raises ValueError: Value cannot be decremented.
|
||||
"""
|
||||
result: Optional[Tuple[bytes, float]] = self._con.execute(
|
||||
self._check_sql, {"key": key}
|
||||
).fetchone()
|
||||
|
||||
if result is None:
|
||||
raise ValueError("Nonexistent or expired cache key.")
|
||||
|
||||
value = self._unstream(result[0])
|
||||
if not isinstance(value, int):
|
||||
raise ValueError("Value is not a number.")
|
||||
|
||||
new_value = value - delta
|
||||
self._con.execute(
|
||||
self._update_sql, {"key": key, "value": self._stream(new_value)}
|
||||
)
|
||||
self._con.commit()
|
||||
return new_value
|
||||
|
||||
def memoize(
|
||||
self, timeout: int = DEFAULT_TIMEOUT
|
||||
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
||||
"""Save the result of the decorated function in cache. Calls with different
|
||||
arguments are saved under different keys.
|
||||
|
||||
:param timeout: How long the value is valid in the cache.
|
||||
Negative numbers will keep the key in cache until manually removed.
|
||||
"""
|
||||
|
||||
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs) -> Callable[..., Any]:
|
||||
result = self.get(f"{func}-{args}-{kwargs}", obj)
|
||||
if result == obj:
|
||||
result = func(*args, **kwargs)
|
||||
self.set(f"{func}-{args}-{kwargs}", result, timeout)
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
obj = object()
|
||||
return decorator
|
||||
|
||||
memorize = memoize # for backwards compatibility
|
||||
|
||||
def ttl(self, key: str) -> int:
|
||||
"""How long the key is still valid in the cache in seconds.
|
||||
Returns `-1` if the value for the key does not expire.
|
||||
Returns `-2` if the value for the key has expired, or has not been set.
|
||||
|
||||
:param key: Cache key.
|
||||
"""
|
||||
result: Optional[Tuple[bytes, float]] = self._con.execute(
|
||||
self._get_sql, {"key": key}
|
||||
).fetchone()
|
||||
|
||||
if result is None:
|
||||
return -2
|
||||
|
||||
exp = self._exp_datetime(result[1])
|
||||
if exp is None:
|
||||
return -1
|
||||
|
||||
ttl = int((exp - datetime.utcnow()).total_seconds())
|
||||
if ttl <= 0:
|
||||
self._con.execute(self._delete_sql, {"key": key})
|
||||
self._con.commit()
|
||||
return -2
|
||||
|
||||
return ttl
|
||||
|
||||
def ttl_many(self, keys: List[str]) -> Dict[str, int]:
|
||||
"""How long the given keys are still valid in the cache in seconds.
|
||||
Returns `-1` if a value for the key does not expire.
|
||||
Returns `-2` if a value for the key has expired, or has not been set.
|
||||
|
||||
:param keys: List of cache keys.
|
||||
"""
|
||||
seq = ", ".join([f"'{value}'" for value in keys])
|
||||
fetched: List[Tuple[str, Any, float]] = self._con.execute(
|
||||
self._get_many_sql.format(seq)
|
||||
).fetchall()
|
||||
exp_by_key: Dict[str, float] = {key: exp for key, _, exp in fetched}
|
||||
|
||||
results: Dict[str, int] = {}
|
||||
to_delete: List[str] = []
|
||||
for key in keys:
|
||||
exp_ = exp_by_key.get(key)
|
||||
if exp_ is None:
|
||||
results[key] = -2
|
||||
continue
|
||||
|
||||
exp = self._exp_datetime(exp_)
|
||||
if exp is None:
|
||||
results[key] = -1
|
||||
continue
|
||||
|
||||
if datetime.utcnow() >= exp:
|
||||
to_delete.append(key)
|
||||
results[key] = -2
|
||||
continue
|
||||
|
||||
results[key] = int((exp - datetime.utcnow()).total_seconds())
|
||||
|
||||
if to_delete:
|
||||
self._con.execute(
|
||||
self._delete_many_sql.format(
|
||||
", ".join([f"'{value}'" for value in to_delete])
|
||||
)
|
||||
)
|
||||
self._con.commit()
|
||||
|
||||
return results
|
||||
|
|
@ -17,6 +17,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
|
||||
from pyrogram import Client, errors, raw
|
||||
|
|
@ -32,7 +33,7 @@ async def get_sticker_set_by_name(
|
|||
hash=0,
|
||||
)
|
||||
)
|
||||
except errors.exceptions.not_acceptable_406.StickersetInvalid:
|
||||
except errors.exceptions.bad_request_400.StickersetInvalid:
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,26 @@
|
|||
import logging
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import string
|
||||
import time
|
||||
from http.cookies import SimpleCookie
|
||||
from re import match as re_match
|
||||
from googletrans import Translator
|
||||
from typing import Union
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import psutil
|
||||
|
||||
from misskaty import BOT_NAME, UBOT_NAME, botStartTime
|
||||
from misskaty.helper.http import http
|
||||
from misskaty.core.decorator import asyncify
|
||||
from misskaty.helper.http import fetch
|
||||
from misskaty.helper.human_read import get_readable_time
|
||||
from misskaty.plugins import ALL_MODULES
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
LOGGER = logging.getLogger("MissKaty")
|
||||
URL_REGEX = r"(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])"
|
||||
GENRES_EMOJI = {
|
||||
"Action": "👊",
|
||||
|
|
@ -42,6 +48,12 @@ GENRES_EMOJI = {
|
|||
}
|
||||
|
||||
|
||||
async def gtranslate(text, source="auto", target="id"):
|
||||
async with Translator() as translator:
|
||||
result = await translator.translate(text, src=source, dest=target)
|
||||
return result
|
||||
|
||||
|
||||
def is_url(url):
|
||||
url = re_match(URL_REGEX, url)
|
||||
return bool(url)
|
||||
|
|
@ -87,7 +99,7 @@ def get_random_string(length: int = 5):
|
|||
async def rentry(teks):
|
||||
# buat dapetin cookie
|
||||
cookie = SimpleCookie()
|
||||
kuki = (await http.get("https://rentry.co")).cookies
|
||||
kuki = (await fetch.get("https://rentry.co")).cookies
|
||||
cookie.load(kuki)
|
||||
kukidict = {key: value.value for key, value in cookie.items()}
|
||||
# headernya
|
||||
|
|
@ -95,7 +107,7 @@ async def rentry(teks):
|
|||
payload = {"csrfmiddlewaretoken": kukidict["csrftoken"], "text": teks}
|
||||
return (
|
||||
(
|
||||
await http.post(
|
||||
await fetch.post(
|
||||
"https://rentry.co/api/new",
|
||||
data=payload,
|
||||
headers=header,
|
||||
|
|
@ -124,11 +136,11 @@ def get_provider(url):
|
|||
return pretty(netloc.split("."))
|
||||
|
||||
|
||||
async def search_jw(movie_name: str, locale: str):
|
||||
async def search_jw(movie_name: str, locale: Union[str, None] = "ID"):
|
||||
m_t_ = ""
|
||||
try:
|
||||
response = (
|
||||
await http.get(
|
||||
await fetch.get(
|
||||
f"https://yasirapi.eu.org/justwatch?q={movie_name}&locale={locale}"
|
||||
)
|
||||
).json()
|
||||
|
|
@ -137,17 +149,73 @@ async def search_jw(movie_name: str, locale: str):
|
|||
if not response.get("results"):
|
||||
LOGGER.error("JustWatch API Error or got Rate Limited.")
|
||||
return m_t_
|
||||
for item in response.get("results")["items"]:
|
||||
if movie_name == item.get("title", ""):
|
||||
offers = item.get("offers", [])
|
||||
for item in response["results"]["data"]["popularTitles"]["edges"]:
|
||||
if item["node"]["content"]["title"] == movie_name:
|
||||
t_m_ = []
|
||||
for offer in offers:
|
||||
url = offer.get("urls").get("standard_web")
|
||||
for offer in item["node"].get("offers", []):
|
||||
url = offer["standardWebURL"]
|
||||
if url not in t_m_:
|
||||
p_o = get_provider(url)
|
||||
m_t_ += f"<a href='{url}'>{p_o}</a> | "
|
||||
t_m_.append(url)
|
||||
if m_t_ != "":
|
||||
m_t_ = m_t_[:-2].strip()
|
||||
break
|
||||
if m_t_ != "":
|
||||
m_t_ = m_t_[:-2].strip()
|
||||
break
|
||||
return m_t_
|
||||
|
||||
|
||||
def isValidURL(str):
|
||||
# Regex to check valid URL
|
||||
regex = (
|
||||
"((http|https)://)(www.)?"
|
||||
+ "[a-zA-Z0-9@:%._\\+~#?&//=]"
|
||||
+ "{2,256}\\.[a-z]"
|
||||
+ "{2,6}\\b([-a-zA-Z0-9@:%"
|
||||
+ "._\\+~#?&//=]*)"
|
||||
)
|
||||
|
||||
# Compile the ReGex
|
||||
p = re.compile(regex)
|
||||
|
||||
# If the string is empty
|
||||
# return false
|
||||
return False if str is None else bool((re.search(p, str)))
|
||||
|
||||
|
||||
@asyncify
|
||||
def gen_trans_image(source, path):
|
||||
# load image
|
||||
img = cv2.imread(source)
|
||||
|
||||
# convert to graky
|
||||
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
# threshold input image as mask
|
||||
mask = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)[1]
|
||||
|
||||
# negate mask
|
||||
mask = 255 - mask
|
||||
|
||||
# apply morphology to remove isolated extraneous noise
|
||||
# use borderconstant of black since foreground touches the edges
|
||||
kernel = np.ones((3, 3), np.uint8)
|
||||
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
|
||||
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
|
||||
|
||||
# anti-alias the mask -- blur then stretch
|
||||
# blur alpha channel
|
||||
mask = cv2.GaussianBlur(
|
||||
mask, (0, 0), sigmaX=2, sigmaY=2, borderType=cv2.BORDER_DEFAULT
|
||||
)
|
||||
|
||||
# linear stretch so that 127.5 goes to 0, but 255 stays 255
|
||||
mask = (2 * (mask.astype(np.float32)) - 255.0).clip(0, 255).astype(np.uint8)
|
||||
|
||||
# put mask into alpha channel
|
||||
result = img.copy()
|
||||
result = cv2.cvtColor(result, cv2.COLOR_BGR2BGRA)
|
||||
result[:, :, 3] = mask
|
||||
|
||||
# save resulting masked image
|
||||
cv2.imwrite(path, result)
|
||||
return path
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
"""
|
||||
* @author yasir <yasiramunandar@gmail.com>
|
||||
* @date 2022-12-01 09:12:27
|
||||
* @projectName MissKatyPyro
|
||||
* Copyright ©YasirPedia All rights reserved
|
||||
"""
|
||||
* @author yasir <yasiramunandar@gmail.com>
|
||||
* @date 2022-12-01 09:12:27
|
||||
* @projectName MissKatyPyro
|
||||
* Copyright ©YasirPedia All rights reserved
|
||||
"""
|
||||
|
||||
import glob
|
||||
import importlib
|
||||
import sys
|
||||
|
|
@ -12,7 +13,7 @@ from os.path import basename, dirname, isfile
|
|||
|
||||
from misskaty import MOD_LOAD, MOD_NOLOAD
|
||||
|
||||
LOGGER = getLogger(__name__)
|
||||
LOGGER = getLogger("MissKaty")
|
||||
|
||||
|
||||
def __list_all_modules():
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import re
|
||||
|
|
@ -28,20 +29,22 @@ from logging import getLogger
|
|||
from time import time
|
||||
|
||||
from pyrogram import Client, enums, filters
|
||||
from pyrogram.errors import ChatAdminRequired, FloodWait
|
||||
from pyrogram.types import ChatPermissions, ChatPrivileges, Message
|
||||
from pyrogram.errors import (
|
||||
ChatAdminRequired,
|
||||
FloodWait,
|
||||
PeerIdInvalid,
|
||||
UsernameNotOccupied,
|
||||
)
|
||||
from pyrogram.types import ChatMember, ChatPermissions, ChatPrivileges, Message
|
||||
|
||||
from database.warn_db import add_warn, get_warn, remove_warns
|
||||
from misskaty import app
|
||||
from misskaty.core.decorator.errors import capture_err
|
||||
from misskaty.core.decorator.permissions import (
|
||||
admins_in_chat,
|
||||
adminsOnly,
|
||||
list_admins,
|
||||
member_permissions,
|
||||
require_admin,
|
||||
)
|
||||
from misskaty.core.decorator.ratelimiter import ratelimiter
|
||||
from misskaty.core.keyboard import ikb
|
||||
from misskaty.helper.functions import (
|
||||
extract_user,
|
||||
|
|
@ -50,9 +53,9 @@ from misskaty.helper.functions import (
|
|||
time_converter,
|
||||
)
|
||||
from misskaty.helper.localization import use_chat_lang
|
||||
from misskaty.vars import COMMAND_HANDLER, SUDO
|
||||
from misskaty.vars import COMMAND_HANDLER, SUDO, OWNER_ID
|
||||
|
||||
LOGGER = getLogger(__name__)
|
||||
LOGGER = getLogger("MissKaty")
|
||||
|
||||
__MODULE__ = "Admin"
|
||||
__HELP__ = """
|
||||
|
|
@ -83,11 +86,12 @@ __HELP__ = """
|
|||
/set_chat_title - Change The Name Of A Group/Channel.
|
||||
/set_chat_photo - Change The PFP Of A Group/Channel.
|
||||
/set_user_title - Change The Administrator Title Of An Admin.
|
||||
/mentionall - Mention all members in a groups.
|
||||
"""
|
||||
|
||||
|
||||
# Admin cache reload
|
||||
@app.on_chat_member_updated()
|
||||
@app.on_chat_member_updated(filters.group, group=5)
|
||||
async def admin_cache_func(_, cmu):
|
||||
if cmu.old_chat_member and cmu.old_chat_member.promoted_by:
|
||||
try:
|
||||
|
|
@ -107,12 +111,9 @@ async def admin_cache_func(_, cmu):
|
|||
|
||||
# Purge CMD
|
||||
@app.on_cmd("purge")
|
||||
@require_admin(permissions=["can_delete_messages"], allow_in_private=True)
|
||||
@ratelimiter
|
||||
@app.adminsOnly("can_delete_messages")
|
||||
@use_chat_lang()
|
||||
async def purge(self: Client, ctx: Message, strings) -> "Message":
|
||||
if not ctx.from_user:
|
||||
return
|
||||
async def purge(_, ctx: Message, strings):
|
||||
try:
|
||||
repliedmsg = ctx.reply_to_message
|
||||
await ctx.delete_msg()
|
||||
|
|
@ -162,23 +163,23 @@ async def purge(self: Client, ctx: Message, strings) -> "Message":
|
|||
|
||||
|
||||
# Kick members
|
||||
@app.on_cmd(["kick", "dkick"], group_only=True)
|
||||
@adminsOnly("can_restrict_members")
|
||||
@ratelimiter
|
||||
@app.on_cmd(["kick", "dkick"], self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_restrict_members")
|
||||
@use_chat_lang()
|
||||
async def kickFunc(client: Client, ctx: Message, strings) -> "Message":
|
||||
if not ctx.from_user:
|
||||
return
|
||||
user_id, reason = await extract_user_and_reason(ctx)
|
||||
if not user_id:
|
||||
return await ctx.reply_msg(strings("user_not_found"))
|
||||
if user_id == client.me.id:
|
||||
return await ctx.reply_msg(strings("kick_self_err"))
|
||||
if user_id in SUDO:
|
||||
if user_id in SUDO or user_id == OWNER_ID:
|
||||
return await ctx.reply_msg(strings("kick_sudo_err"))
|
||||
if user_id in (await list_admins(ctx.chat.id)):
|
||||
return await ctx.reply_msg(strings("kick_admin_err"))
|
||||
user = await app.get_users(user_id)
|
||||
try:
|
||||
user = await app.get_users(user_id)
|
||||
except PeerIdInvalid:
|
||||
return await ctx.reply_msg(strings("user_not_found"))
|
||||
msg = strings("kick_msg").format(
|
||||
mention=user.mention,
|
||||
id=user.id,
|
||||
|
|
@ -194,29 +195,33 @@ async def kickFunc(client: Client, ctx: Message, strings) -> "Message":
|
|||
await ctx.chat.unban_member(user_id)
|
||||
except ChatAdminRequired:
|
||||
await ctx.reply_msg(strings("no_ban_permission"))
|
||||
except Exception as e:
|
||||
await ctx.reply_msg(str(e))
|
||||
|
||||
|
||||
# Ban/DBan/TBan User
|
||||
@app.on_cmd(["ban", "dban", "tban"], group_only=True)
|
||||
@adminsOnly("can_restrict_members")
|
||||
@ratelimiter
|
||||
@app.on_cmd(["ban", "dban", "tban"], self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_restrict_members")
|
||||
@use_chat_lang()
|
||||
async def banFunc(client, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
user_id, reason = await extract_user_and_reason(message, sender_chat=True)
|
||||
try:
|
||||
user_id, reason = await extract_user_and_reason(message, sender_chat=True)
|
||||
except UsernameNotOccupied:
|
||||
return await message.reply_msg("Sorry, i didn't know that user.")
|
||||
|
||||
if not user_id:
|
||||
return await message.reply_text(strings("user_not_found"))
|
||||
if user_id == client.me.id:
|
||||
return await message.reply_text(strings("ban_self_err"))
|
||||
if user_id in SUDO:
|
||||
if user_id in SUDO or user_id == OWNER_ID:
|
||||
return await message.reply_text(strings("ban_sudo_err"))
|
||||
if user_id in (await list_admins(message.chat.id)):
|
||||
return await message.reply_text(strings("ban_admin_err"))
|
||||
|
||||
try:
|
||||
mention = (await app.get_users(user_id)).mention
|
||||
except PeerIdInvalid:
|
||||
return await message.reply_text(strings("user_not_found"))
|
||||
except IndexError:
|
||||
mention = (
|
||||
message.reply_to_message.sender_chat.title
|
||||
|
|
@ -253,19 +258,18 @@ async def banFunc(client, message, strings):
|
|||
keyboard = ikb({"🚨 Unban 🚨": f"unban_{user_id}"})
|
||||
try:
|
||||
await message.chat.ban_member(user_id)
|
||||
await message.reply_text(msg, reply_markup=keyboard)
|
||||
except Exception as err:
|
||||
await message.reply(f"ERROR: {err}")
|
||||
await message.reply_msg(msg, reply_markup=keyboard)
|
||||
except ChatAdminRequired:
|
||||
await message.reply("Please give me permission to banned members..!!!")
|
||||
except Exception as e:
|
||||
await message.reply_msg(str(e))
|
||||
|
||||
|
||||
# Unban members
|
||||
@app.on_cmd("unban", group_only=True)
|
||||
@adminsOnly("can_restrict_members")
|
||||
@ratelimiter
|
||||
@app.on_cmd("unban", self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_restrict_members")
|
||||
@use_chat_lang()
|
||||
async def unban_func(self, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
async def unban_func(_, message, strings):
|
||||
# we don't need reasons for unban, also, we
|
||||
# don't need to get "text_mention" entity, because
|
||||
# normal users won't get text_mention if the user
|
||||
|
|
@ -277,26 +281,28 @@ async def unban_func(self, message, strings):
|
|||
|
||||
if len(message.command) == 2:
|
||||
user = message.text.split(None, 1)[1]
|
||||
if not user.startswith("@"):
|
||||
user = int(user)
|
||||
elif len(message.command) == 1 and reply:
|
||||
user = message.reply_to_message.from_user.id
|
||||
else:
|
||||
return await message.reply_text(strings("give_unban_user"))
|
||||
await message.chat.unban_member(user)
|
||||
umention = (await app.get_users(user)).mention
|
||||
await message.reply_text(strings("unban_success").format(umention=umention))
|
||||
return await message.reply_msg(strings("give_unban_user"))
|
||||
try:
|
||||
await message.chat.unban_member(user)
|
||||
umention = (await app.get_users(user)).mention
|
||||
await message.reply_msg(strings("unban_success").format(umention=umention))
|
||||
except PeerIdInvalid:
|
||||
await message.reply_msg(strings("unknown_id", context="general"))
|
||||
except ChatAdminRequired:
|
||||
await message.reply("Please give me permission to unban members..!!!")
|
||||
except Exception as e:
|
||||
await message.reply_msg(str(e))
|
||||
|
||||
|
||||
# Ban users listed in a message
|
||||
@app.on_message(
|
||||
filters.user(SUDO) & filters.command("listban", COMMAND_HANDLER) & filters.group
|
||||
(filters.user(SUDO) | filters.user(OWNER_ID)) & filters.command("listban", COMMAND_HANDLER) & filters.group
|
||||
)
|
||||
@ratelimiter
|
||||
@use_chat_lang()
|
||||
async def list_ban_(c, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
userid, msglink_reason = await extract_user_and_reason(message)
|
||||
if not userid or not msglink_reason:
|
||||
return await message.reply_text(strings("give_idban_with_msg_link"))
|
||||
|
|
@ -313,7 +319,7 @@ async def list_ban_(c, message, strings):
|
|||
|
||||
if userid == c.me.id:
|
||||
return await message.reply_text(strings("ban_self_err"))
|
||||
if userid in SUDO:
|
||||
if userid in SUDO or user_id == OWNER_ID:
|
||||
return await message.reply_text(strings("ban_sudo_err"))
|
||||
splitted = messagelink.split("/")
|
||||
uname, mid = splitted[-2], int(splitted[-1])
|
||||
|
|
@ -347,13 +353,10 @@ async def list_ban_(c, message, strings):
|
|||
|
||||
# Unban users listed in a message
|
||||
@app.on_message(
|
||||
filters.user(SUDO) & filters.command("listunban", COMMAND_HANDLER) & filters.group
|
||||
(filters.user(SUDO) | filters.user(OWNER_ID)) & filters.command("listunban", COMMAND_HANDLER) & filters.group
|
||||
)
|
||||
@ratelimiter
|
||||
@use_chat_lang()
|
||||
async def list_unban_(c, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
async def list_unban(_, message, strings):
|
||||
userid, msglink = await extract_user_and_reason(message)
|
||||
if not userid or not msglink:
|
||||
return await message.reply_text(strings("give_idunban_with_msg_link"))
|
||||
|
|
@ -388,12 +391,9 @@ async def list_unban_(c, message, strings):
|
|||
|
||||
# Delete messages
|
||||
@app.on_cmd("del", group_only=True)
|
||||
@adminsOnly("can_delete_messages")
|
||||
@ratelimiter
|
||||
@app.adminsOnly("can_delete_messages")
|
||||
@use_chat_lang()
|
||||
async def deleteFunc(_, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
if not message.reply_to_message:
|
||||
return await message.reply_text(strings("delete_no_reply"))
|
||||
try:
|
||||
|
|
@ -404,99 +404,100 @@ async def deleteFunc(_, message, strings):
|
|||
|
||||
|
||||
# Promote Members
|
||||
@app.on_cmd(["promote", "fullpromote"], group_only=True)
|
||||
@adminsOnly("can_promote_members")
|
||||
@ratelimiter
|
||||
@app.on_cmd(["promote", "fullpromote"], self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_promote_members")
|
||||
@use_chat_lang()
|
||||
async def promoteFunc(client, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
try:
|
||||
user_id = await extract_user(message)
|
||||
umention = (await app.get_users(user_id)).mention
|
||||
umention = (await client.get_users(user_id)).mention
|
||||
except:
|
||||
return await message.reply(strings("invalid_id_uname"))
|
||||
if not user_id:
|
||||
return await message.reply_text(strings("user_not_found"))
|
||||
bot = await app.get_chat_member(message.chat.id, client.me.id)
|
||||
bot = (await client.get_chat_member(message.chat.id, client.me.id)).privileges
|
||||
if user_id == client.me.id:
|
||||
return await message.reply_text(strings("promote_self_err"))
|
||||
if not bot.privileges.can_promote_members:
|
||||
return await message.reply_text(strings("no_promote_perm"))
|
||||
if message.command[0][0] == "f":
|
||||
return await message.reply_msg(strings("promote_self_err"))
|
||||
if not bot:
|
||||
return await message.reply_msg("I'm not an admin in this chat.")
|
||||
if not bot.can_promote_members:
|
||||
return await message.reply_msg(strings("no_promote_perm"))
|
||||
try:
|
||||
if message.command[0][0] == "f":
|
||||
await message.chat.promote_member(
|
||||
user_id=user_id,
|
||||
privileges=ChatPrivileges(
|
||||
can_change_info=bot.can_change_info,
|
||||
can_invite_users=bot.can_invite_users,
|
||||
can_delete_messages=bot.can_delete_messages,
|
||||
can_restrict_members=bot.can_restrict_members,
|
||||
can_pin_messages=bot.can_pin_messages,
|
||||
can_promote_members=bot.can_promote_members,
|
||||
can_manage_chat=bot.can_manage_chat,
|
||||
can_manage_video_chats=bot.can_manage_video_chats,
|
||||
),
|
||||
)
|
||||
return await message.reply_text(
|
||||
strings("full_promote").format(umention=umention)
|
||||
)
|
||||
|
||||
await message.chat.promote_member(
|
||||
user_id=user_id,
|
||||
privileges=ChatPrivileges(
|
||||
can_change_info=bot.privileges.can_change_info,
|
||||
can_invite_users=bot.privileges.can_invite_users,
|
||||
can_delete_messages=bot.privileges.can_delete_messages,
|
||||
can_restrict_members=bot.privileges.can_restrict_members,
|
||||
can_pin_messages=bot.privileges.can_pin_messages,
|
||||
can_promote_members=bot.privileges.can_promote_members,
|
||||
can_manage_chat=bot.privileges.can_manage_chat,
|
||||
can_manage_video_chats=bot.privileges.can_manage_video_chats,
|
||||
can_change_info=False,
|
||||
can_invite_users=bot.can_invite_users,
|
||||
can_delete_messages=bot.can_delete_messages,
|
||||
can_restrict_members=bot.can_restrict_members,
|
||||
can_pin_messages=bot.can_pin_messages,
|
||||
can_promote_members=False,
|
||||
can_manage_chat=bot.can_manage_chat,
|
||||
can_manage_video_chats=bot.can_manage_video_chats,
|
||||
),
|
||||
)
|
||||
return await message.reply_text(
|
||||
strings("full_promote").format(umention=umention)
|
||||
)
|
||||
|
||||
await message.chat.promote_member(
|
||||
user_id=user_id,
|
||||
privileges=ChatPrivileges(
|
||||
can_change_info=False,
|
||||
can_invite_users=bot.privileges.can_invite_users,
|
||||
can_delete_messages=bot.privileges.can_delete_messages,
|
||||
can_restrict_members=bot.privileges.can_restrict_members,
|
||||
can_pin_messages=bot.privileges.can_pin_messages,
|
||||
can_promote_members=False,
|
||||
can_manage_chat=bot.privileges.can_manage_chat,
|
||||
can_manage_video_chats=bot.privileges.can_manage_video_chats,
|
||||
),
|
||||
)
|
||||
await message.reply_text(strings("normal_promote").format(umention=umention))
|
||||
await message.reply_msg(strings("normal_promote").format(umention=umention))
|
||||
except Exception as err:
|
||||
await message.reply_msg(err)
|
||||
|
||||
|
||||
# Demote Member
|
||||
@app.on_cmd("demote", group_only=True)
|
||||
@adminsOnly("can_restrict_members")
|
||||
@ratelimiter
|
||||
@app.on_cmd("demote", self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_restrict_members")
|
||||
@use_chat_lang()
|
||||
async def demote(client, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
user_id = await extract_user(message)
|
||||
if not user_id:
|
||||
return await message.reply_text(strings("user_not_found"))
|
||||
if user_id == client.me.id:
|
||||
return await message.reply_text(strings("demote_self_err"))
|
||||
if user_id in SUDO:
|
||||
if user_id in SUDO or user_id == OWNER_ID:
|
||||
return await message.reply_text(strings("demote_sudo_err"))
|
||||
await message.chat.promote_member(
|
||||
user_id=user_id,
|
||||
privileges=ChatPrivileges(
|
||||
can_change_info=False,
|
||||
can_invite_users=False,
|
||||
can_delete_messages=False,
|
||||
can_restrict_members=False,
|
||||
can_pin_messages=False,
|
||||
can_promote_members=False,
|
||||
can_manage_chat=False,
|
||||
can_manage_video_chats=False,
|
||||
),
|
||||
)
|
||||
umention = (await app.get_users(user_id)).mention
|
||||
await message.reply_text(f"Demoted! {umention}")
|
||||
try:
|
||||
await message.chat.promote_member(
|
||||
user_id=user_id,
|
||||
privileges=ChatPrivileges(
|
||||
can_change_info=False,
|
||||
can_invite_users=False,
|
||||
can_delete_messages=False,
|
||||
can_restrict_members=False,
|
||||
can_pin_messages=False,
|
||||
can_promote_members=False,
|
||||
can_manage_chat=False,
|
||||
can_manage_video_chats=False,
|
||||
),
|
||||
)
|
||||
umention = (await app.get_users(user_id)).mention
|
||||
await message.reply_text(f"Demoted! {umention}")
|
||||
except ChatAdminRequired:
|
||||
await message.reply("Please give permission to demote members..")
|
||||
except Exception as e:
|
||||
await message.reply_msg(str(e))
|
||||
|
||||
|
||||
# Pin Messages
|
||||
@app.on_cmd(["pin", "unpin"])
|
||||
@adminsOnly("can_pin_messages")
|
||||
@ratelimiter
|
||||
@app.adminsOnly("can_pin_messages")
|
||||
@use_chat_lang()
|
||||
async def pin(_, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
if not message.reply_to_message:
|
||||
return await message.reply_text(strings("pin_no_reply"))
|
||||
r = message.reply_to_message
|
||||
|
|
@ -517,16 +518,15 @@ async def pin(_, message, strings):
|
|||
strings("pin_no_perm"),
|
||||
disable_web_page_preview=True,
|
||||
)
|
||||
except Exception as e:
|
||||
await message.reply_msg(str(e))
|
||||
|
||||
|
||||
# Mute members
|
||||
@app.on_cmd(["mute", "tmute"], group_only=True)
|
||||
@adminsOnly("can_restrict_members")
|
||||
@ratelimiter
|
||||
@app.on_cmd(["mute", "tmute"], self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_restrict_members")
|
||||
@use_chat_lang()
|
||||
async def mute(client, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
try:
|
||||
user_id, reason = await extract_user_and_reason(message)
|
||||
except Exception as err:
|
||||
|
|
@ -535,7 +535,7 @@ async def mute(client, message, strings):
|
|||
return await message.reply_text(strings("user_not_found"))
|
||||
if user_id == client.me.id:
|
||||
return await message.reply_text(strings("mute_self_err"))
|
||||
if user_id in SUDO:
|
||||
if user_id in SUDO or user_id == OWNER_ID:
|
||||
return await message.reply_text(strings("mute_sudo_err"))
|
||||
if user_id in (await list_admins(message.chat.id)):
|
||||
return await message.reply_text(strings("mute_admin_err"))
|
||||
|
|
@ -557,7 +557,7 @@ async def mute(client, message, strings):
|
|||
if len(time_value[:-1]) < 3:
|
||||
await message.chat.restrict_member(
|
||||
user_id,
|
||||
permissions=ChatPermissions(),
|
||||
permissions=ChatPermissions(all_perms=False),
|
||||
until_date=temp_mute,
|
||||
)
|
||||
await message.reply_text(msg, reply_markup=keyboard)
|
||||
|
|
@ -568,40 +568,45 @@ async def mute(client, message, strings):
|
|||
return
|
||||
if reason:
|
||||
msg += strings("banned_reason").format(reas=reason)
|
||||
await message.chat.restrict_member(user_id, permissions=ChatPermissions())
|
||||
await message.reply_text(msg, reply_markup=keyboard)
|
||||
try:
|
||||
await message.chat.restrict_member(
|
||||
user_id, permissions=ChatPermissions(all_perms=False)
|
||||
)
|
||||
await message.reply_text(msg, reply_markup=keyboard)
|
||||
except Exception as e:
|
||||
await message.reply_msg(str(e))
|
||||
|
||||
|
||||
# Unmute members
|
||||
@app.on_cmd("unmute", group_only=True)
|
||||
@adminsOnly("can_restrict_members")
|
||||
@ratelimiter
|
||||
@app.on_cmd("unmute", self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_restrict_members")
|
||||
@use_chat_lang()
|
||||
async def unmute(_, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
user_id = await extract_user(message)
|
||||
if not user_id:
|
||||
return await message.reply_text(strings("user_not_found"))
|
||||
await message.chat.unban_member(user_id)
|
||||
umention = (await app.get_users(user_id)).mention
|
||||
await message.reply_text(strings("unmute_msg").format(umention=umention))
|
||||
try:
|
||||
await message.chat.unban_member(user_id)
|
||||
umention = (await app.get_users(user_id)).mention
|
||||
await message.reply_msg(strings("unmute_msg").format(umention=umention))
|
||||
except Exception as e:
|
||||
await message.reply_msg(str(e))
|
||||
|
||||
|
||||
@app.on_cmd(["warn", "dwarn"], group_only=True)
|
||||
@adminsOnly("can_restrict_members")
|
||||
@ratelimiter
|
||||
@app.on_cmd(["warn", "dwarn"], self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_restrict_members")
|
||||
@use_chat_lang()
|
||||
async def warn_user(client, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
user_id, reason = await extract_user_and_reason(message)
|
||||
try:
|
||||
user_id, reason = await extract_user_and_reason(message)
|
||||
except UsernameNotOccupied:
|
||||
return await message.reply_msg("Sorry, i didn't know that user.")
|
||||
chat_id = message.chat.id
|
||||
if not user_id:
|
||||
return await message.reply_text(strings("user_not_found"))
|
||||
if user_id == client.me.id:
|
||||
return await message.reply_text(strings("warn_self_err"))
|
||||
if user_id in SUDO:
|
||||
if user_id in SUDO or user_id == OWNER_ID:
|
||||
return await message.reply_text(strings("warn_sudo_err"))
|
||||
if user_id in (await list_admins(chat_id)):
|
||||
return await message.reply_text(strings("warn_admin_err"))
|
||||
|
|
@ -615,9 +620,12 @@ async def warn_user(client, message, strings):
|
|||
if message.command[0][0] == "d":
|
||||
await message.reply_to_message.delete()
|
||||
if warns >= 2:
|
||||
await message.chat.ban_member(user_id)
|
||||
await message.reply_text(strings("exceed_warn_msg").format(mention=mention))
|
||||
await remove_warns(chat_id, await int_to_alpha(user_id))
|
||||
try:
|
||||
await message.chat.ban_member(user_id)
|
||||
await message.reply_msg(strings("exceed_warn_msg").format(mention=mention))
|
||||
await remove_warns(chat_id, await int_to_alpha(user_id))
|
||||
except ChatAdminRequired:
|
||||
await message.reply_msg(strings("no_ban_permission"))
|
||||
else:
|
||||
warn = {"warns": warns + 1}
|
||||
msg = strings("warn_msg").format(
|
||||
|
|
@ -626,12 +634,11 @@ async def warn_user(client, message, strings):
|
|||
reas=reason or "No Reason Provided.",
|
||||
twarn=warns + 1,
|
||||
)
|
||||
await message.reply_text(msg, reply_markup=keyboard)
|
||||
await message.reply_msg(msg, reply_markup=keyboard)
|
||||
await add_warn(chat_id, await int_to_alpha(user_id), warn)
|
||||
|
||||
|
||||
@app.on_callback_query(filters.regex("unwarn_"))
|
||||
@ratelimiter
|
||||
@use_chat_lang()
|
||||
async def remove_warning(_, cq, strings):
|
||||
from_user = cq.from_user
|
||||
|
|
@ -662,7 +669,6 @@ async def remove_warning(_, cq, strings):
|
|||
|
||||
|
||||
@app.on_callback_query(filters.regex("unmute_"))
|
||||
@ratelimiter
|
||||
@use_chat_lang()
|
||||
async def unmute_user(_, cq, strings):
|
||||
from_user = cq.from_user
|
||||
|
|
@ -678,12 +684,14 @@ async def unmute_user(_, cq, strings):
|
|||
text = cq.message.text.markdown
|
||||
text = f"~~{text}~~\n\n"
|
||||
text += strings("rmmute_msg").format(mention=from_user.mention)
|
||||
await cq.message.chat.unban_member(user_id)
|
||||
await cq.message.edit(text)
|
||||
try:
|
||||
await cq.message.chat.unban_member(user_id)
|
||||
await cq.message.edit(text)
|
||||
except Exception as e:
|
||||
await cq.answer(str(e))
|
||||
|
||||
|
||||
@app.on_callback_query(filters.regex("unban_"))
|
||||
@ratelimiter
|
||||
@use_chat_lang()
|
||||
async def unban_user(_, cq, strings):
|
||||
from_user = cq.from_user
|
||||
|
|
@ -707,13 +715,10 @@ async def unban_user(_, cq, strings):
|
|||
|
||||
|
||||
# Remove Warn
|
||||
@app.on_cmd("rmwarn", group_only=True)
|
||||
@adminsOnly("can_restrict_members")
|
||||
@ratelimiter
|
||||
@app.on_cmd("rmwarn", self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_restrict_members")
|
||||
@use_chat_lang()
|
||||
async def remove_warnings(_, message, strings):
|
||||
if not message.from_user:
|
||||
return
|
||||
if not message.reply_to_message:
|
||||
return await message.reply_text(strings("reply_to_rm_warn"))
|
||||
user_id = message.reply_to_message.from_user.id
|
||||
|
|
@ -731,7 +736,6 @@ async def remove_warnings(_, message, strings):
|
|||
|
||||
# Warns
|
||||
@app.on_cmd("warns", group_only=True)
|
||||
@ratelimiter
|
||||
@use_chat_lang()
|
||||
async def check_warns(_, message, strings):
|
||||
if not message.from_user:
|
||||
|
|
@ -759,28 +763,27 @@ async def check_warns(_, message, strings):
|
|||
& filters.group
|
||||
)
|
||||
@capture_err
|
||||
@ratelimiter
|
||||
@use_chat_lang()
|
||||
async def report_user(_, ctx: Message, strings) -> "Message":
|
||||
if not ctx.reply_to_message:
|
||||
return await ctx.reply_text(strings("report_no_reply"))
|
||||
reply = ctx.reply_to_message
|
||||
if len(ctx.text.split()) <= 1 and not ctx.reply_to_message:
|
||||
return await ctx.reply_msg(strings("report_no_reply"))
|
||||
reply = ctx.reply_to_message if ctx.reply_to_message else ctx
|
||||
reply_id = reply.from_user.id if reply.from_user else reply.sender_chat.id
|
||||
user_id = ctx.from_user.id if ctx.from_user else ctx.sender_chat.id
|
||||
if reply_id == user_id:
|
||||
return await ctx.reply_text(strings("report_self_err"))
|
||||
return await ctx.reply_msg(strings("report_self_err"))
|
||||
|
||||
list_of_admins = await list_admins(ctx.chat.id)
|
||||
linked_chat = (await app.get_chat(ctx.chat.id)).linked_chat
|
||||
if linked_chat is None:
|
||||
if reply_id in list_of_admins or reply_id == ctx.chat.id:
|
||||
return await ctx.reply_text(strings("reported_is_admin"))
|
||||
return await ctx.reply_msg(strings("reported_is_admin"))
|
||||
elif (
|
||||
reply_id in list_of_admins
|
||||
or reply_id == ctx.chat.id
|
||||
or reply_id == linked_chat.id
|
||||
):
|
||||
return await ctx.reply_text(strings("reported_is_admin"))
|
||||
return await ctx.reply_msg(strings("reported_is_admin"))
|
||||
user_mention = (
|
||||
reply.from_user.mention if reply.from_user else reply.sender_chat.title
|
||||
)
|
||||
|
|
@ -796,24 +799,27 @@ async def report_user(_, ctx: Message, strings) -> "Message":
|
|||
# return bots or deleted admins
|
||||
continue
|
||||
text += f"<a href='tg://user?id={admin.user.id}'>\u2063</a>"
|
||||
await ctx.reply_msg(text, reply_to_message_id=ctx.reply_to_message.id)
|
||||
await reply.reply_msg(text)
|
||||
|
||||
|
||||
@app.on_cmd("set_chat_title", group_only=True)
|
||||
@adminsOnly("can_change_info")
|
||||
@app.on_cmd("set_chat_title", self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_change_info")
|
||||
async def set_chat_title(_, ctx: Message):
|
||||
if len(ctx.command) < 2:
|
||||
return await ctx.reply_text("**Usage:**\n/set_chat_title NEW NAME")
|
||||
return await ctx.reply_text(f"**Usage:**\n/{ctx.command[0]} NEW NAME")
|
||||
old_title = ctx.chat.title
|
||||
new_title = ctx.text.split(None, 1)[1]
|
||||
await ctx.chat.set_title(new_title)
|
||||
await ctx.reply_text(
|
||||
f"Successfully Changed Group Title From {old_title} To {new_title}"
|
||||
)
|
||||
try:
|
||||
await ctx.chat.set_title(new_title)
|
||||
await ctx.reply_text(
|
||||
f"Successfully Changed Group Title From {old_title} To {new_title}"
|
||||
)
|
||||
except Exception as e:
|
||||
await ctx.reply_msg(str(e))
|
||||
|
||||
|
||||
@app.on_cmd("set_user_title", group_only=True)
|
||||
@adminsOnly("can_change_info")
|
||||
@app.on_cmd("set_user_title", self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_change_info")
|
||||
async def set_user_title(_, ctx: Message):
|
||||
if not ctx.reply_to_message:
|
||||
return await ctx.reply_text("Reply to user's message to set his admin title")
|
||||
|
|
@ -826,14 +832,17 @@ async def set_user_title(_, ctx: Message):
|
|||
"**Usage:**\n/set_user_title NEW ADMINISTRATOR TITLE"
|
||||
)
|
||||
title = ctx.text.split(None, 1)[1]
|
||||
await app.set_administrator_title(chat_id, from_user.id, title)
|
||||
await ctx.reply_text(
|
||||
f"Successfully Changed {from_user.mention}'s Admin Title To {title}"
|
||||
)
|
||||
try:
|
||||
await app.set_administrator_title(chat_id, from_user.id, title)
|
||||
await ctx.reply_text(
|
||||
f"Successfully Changed {from_user.mention}'s Admin Title To {title}"
|
||||
)
|
||||
except Exception as e:
|
||||
await ctx.reply_msg(str(e))
|
||||
|
||||
|
||||
@app.on_cmd("set_chat_photo", group_only=True)
|
||||
@adminsOnly("can_change_info")
|
||||
@app.on_cmd("set_chat_photo", self_admin=True, group_only=True)
|
||||
@app.adminsOnly("can_change_info")
|
||||
async def set_chat_photo(_, ctx: Message):
|
||||
reply = ctx.reply_to_message
|
||||
|
||||
|
|
@ -856,3 +865,30 @@ async def set_chat_photo(_, ctx: Message):
|
|||
except Exception as err:
|
||||
await ctx.reply(f"Failed changed group photo. ERROR: {err}")
|
||||
os.remove(photo)
|
||||
|
||||
|
||||
@app.on_message(filters.group & filters.command("mentionall", COMMAND_HANDLER))
|
||||
async def mentionall(app: Client, msg: Message):
|
||||
user = await msg.chat.get_member(msg.from_user.id)
|
||||
if user.status in (
|
||||
enums.ChatMemberStatus.OWNER,
|
||||
enums.ChatMemberStatus.ADMINISTRATOR,
|
||||
):
|
||||
total = []
|
||||
async for member in app.get_chat_members(msg.chat.id):
|
||||
member: ChatMember
|
||||
if member.user.username:
|
||||
total.append(f"@{member.user.username}")
|
||||
else:
|
||||
total.append(member.user.mention())
|
||||
|
||||
NUM = 4
|
||||
for i in range(0, len(total), NUM):
|
||||
message = " ".join(total[i : i + NUM])
|
||||
await app.send_message(
|
||||
msg.chat.id, message, message_thread_id=msg.message_thread_id
|
||||
)
|
||||
else:
|
||||
await app.send_message(
|
||||
msg.chat.id, "Admins only can do that !", reply_to_message_id=msg.id
|
||||
)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ from pyrogram.types import Message
|
|||
from database.afk_db import add_afk, cleanmode_off, cleanmode_on, is_afk, remove_afk
|
||||
from misskaty import app
|
||||
from misskaty.core.decorator.permissions import adminsOnly
|
||||
from misskaty.core.decorator.ratelimiter import ratelimiter
|
||||
from misskaty.helper import get_readable_time2
|
||||
from misskaty.helper.localization import use_chat_lang
|
||||
from utils import put_cleanmode
|
||||
|
|
@ -33,7 +32,6 @@ Just type something in group to remove AFK Status."""
|
|||
|
||||
# Handle set AFK Command
|
||||
@app.on_cmd("afk")
|
||||
@ratelimiter
|
||||
@use_chat_lang()
|
||||
async def active_afk(_, ctx: Message, strings):
|
||||
if ctx.sender_chat:
|
||||
|
|
@ -207,7 +205,6 @@ async def active_afk(_, ctx: Message, strings):
|
|||
|
||||
|
||||
@app.on_cmd("afkdel", group_only=True)
|
||||
@ratelimiter
|
||||
@adminsOnly("can_change_info")
|
||||
@use_chat_lang()
|
||||
async def afk_state(_, ctx: Message, strings):
|
||||
|
|
@ -245,10 +242,13 @@ async def afk_watcher_func(self: Client, ctx: Message, strings):
|
|||
possible = ["/afk", f"/afk@{self.me.username}", "!afk"]
|
||||
message_text = ctx.text or ctx.caption
|
||||
for entity in ctx.entities:
|
||||
if (
|
||||
entity.type == enums.MessageEntityType.BOT_COMMAND
|
||||
and (message_text[0 : 0 + entity.length]).lower() in possible
|
||||
):
|
||||
try:
|
||||
if (
|
||||
entity.type == enums.MessageEntityType.BOT_COMMAND
|
||||
and (message_text[0 : 0 + entity.length]).lower() in possible
|
||||
):
|
||||
return
|
||||
except UnicodeDecodeError: # Some weird character make error
|
||||
return
|
||||
|
||||
msg = ""
|
||||
|
|
@ -380,7 +380,7 @@ async def afk_watcher_func(self: Client, ctx: Message, strings):
|
|||
if ctx.entities:
|
||||
entity = ctx.entities
|
||||
j = 0
|
||||
for x in range(len(entity)):
|
||||
for _ in range(len(entity)):
|
||||
if (entity[j].type) == enums.MessageEntityType.MENTION:
|
||||
found = re.findall("@([_0-9a-zA-Z]+)", ctx.text)
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ def shorten(description, info="anilist.co"):
|
|||
|
||||
@app.on_message(filters.command("anime", COMMAND_HANDLER))
|
||||
async def anime_search(_, mesg):
|
||||
search = mesg.text.split(" ", 1)
|
||||
search = mesg.text.split(None, 1)
|
||||
reply = await mesg.reply("⏳ <i>Please wait ...</i>", quote=True)
|
||||
if len(search) == 1:
|
||||
return await reply.edit("⚠️ <b>Give Anime name please.</b>")
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
"""
|
||||
* @author yasir <yasiramunandar@gmail.com>
|
||||
* @date 2022-12-01 09:12:27
|
||||
* @projectName MissKatyPyro
|
||||
* Copyright @YasirPedia All rights reserved
|
||||
* @author yasir <yasiramunandar@gmail.com>
|
||||
* @date 2022-12-01 09:12:27
|
||||
* @projectName MissKatyPyro
|
||||
* Copyright @YasirPedia All rights reserved
|
||||
"""
|
||||
|
||||
from pyrogram import filters
|
||||
from pyrogram.errors import UserAlreadyParticipant, UserIsBlocked
|
||||
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
|
||||
from misskaty import app
|
||||
from misskaty.core.decorator.errors import capture_err
|
||||
from misskaty.core.decorator.ratelimiter import ratelimiter
|
||||
|
||||
|
||||
# Filters Approve User by bot in channel @YMovieZNew
|
||||
|
|
@ -41,9 +41,8 @@ async def approve_join_chat(c, m):
|
|||
|
||||
|
||||
@app.on_callback_query(filters.regex(r"^approve"))
|
||||
@ratelimiter
|
||||
async def approve_chat(c, q):
|
||||
i, chat = q.data.split("_")
|
||||
_, chat = q.data.split("_")
|
||||
try:
|
||||
await q.message.edit(
|
||||
"Yeayy, selamat kamu bisa bergabung di Channel YMovieZ Reborn..."
|
||||
|
|
@ -58,9 +57,8 @@ async def approve_chat(c, q):
|
|||
|
||||
|
||||
@app.on_callback_query(filters.regex(r"^declined"))
|
||||
@ratelimiter
|
||||
async def decline_chat(c, q):
|
||||
i, chat = q.data.split("_")
|
||||
_, chat = q.data.split("_")
|
||||
try:
|
||||
await q.message.edit(
|
||||
"Yahh, kamu ditolak join channel. Biasakan rajin membaca yahhh.."
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue