mirror of
https://github.com/yasirarism/MissKatyPyro.git
synced 2025-12-29 17: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]
|
[analyzers.meta]
|
||||||
runtime_version = "3.x.x"
|
runtime_version = "3.x.x"
|
||||||
|
|
||||||
|
[[transformers]]
|
||||||
|
name = "yapf"
|
||||||
|
|
||||||
[[transformers]]
|
[[transformers]]
|
||||||
name = "isort"
|
name = "isort"
|
||||||
|
|
||||||
[[transformers]]
|
[[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
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github: [yasirarism] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
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']
|
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']
|
||||||
QRIS: ['https://yasirpedia.eu.org/images/my-qris.jpg']
|
|
||||||
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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Notify the commit on Telegram.
|
- name: Notify the commit on Telegram YasirPediaFeed.
|
||||||
uses: EverythingSuckz/github-telegram-notify@main
|
uses: EverythingSuckz/github-telegram-notify@main
|
||||||
with:
|
with:
|
||||||
bot_token: '${{ secrets.BOT_TOKEN }}'
|
bot_token: '${{ secrets.BOT_TOKEN }}'
|
||||||
chat_id: '${{ secrets.CHAT_ID }}'
|
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'
|
||||||
|
|
|
||||||
10
Dockerfile
10
Dockerfile
|
|
@ -3,17 +3,17 @@
|
||||||
# * @projectName MissKatyPyro
|
# * @projectName MissKatyPyro
|
||||||
# * Copyright ©YasirPedia All rights reserved
|
# * Copyright ©YasirPedia All rights reserved
|
||||||
|
|
||||||
# Base Docker Using Alpine 3.18, Python 3.11.4 and Built In Pip
|
# Base Docker Using Ubuntu 24.04, Python 3.12 and Built In Pip
|
||||||
## With Built in Pip Package
|
## With Built in Pip Package
|
||||||
FROM yasirarism/misskaty-docker:alpine
|
FROM yasirarism/misskaty-docker:py3.13
|
||||||
## Without Built in Pip Package
|
## Without Built in Pip Package
|
||||||
# FROM yasirarism/misskaty-docker:free
|
# FROM yasirarism/misskaty-docker:free
|
||||||
|
|
||||||
# Set Hostname
|
# Set Hostname
|
||||||
ENV HOSTNAME yasir-server
|
ENV HOSTNAME=yasir-server
|
||||||
# Copy Files
|
# Copy Files
|
||||||
COPY . .
|
COPY . .
|
||||||
# Instal pip package
|
# Instal pip package if you use free depedencies
|
||||||
# RUN pip3 install --no-cache-dir -r requirements.txt --break-system-packages
|
# RUN pip3 install --no-cache-dir -r requirements.txt
|
||||||
# Set CMD Bot
|
# Set CMD Bot
|
||||||
CMD ["bash", "start.sh"]
|
CMD ["bash", "start.sh"]
|
||||||
|
|
|
||||||
1
Procfile
Normal file
1
Procfile
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
worker: bash start.sh
|
||||||
52
README.id.md
52
README.id.md
|
|
@ -1,4 +1,7 @@
|
||||||
# MissKatyPyro
|
# 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-->
|
<!--Badges-->
|
||||||
![MIT License][license-shield] ![Repository Size][repository-size-shield] ![Issue Closed][issue-closed-shield]
|
![MIT License][license-shield] ![Repository Size][repository-size-shield] ![Issue Closed][issue-closed-shield]
|
||||||
|
|
@ -9,12 +12,12 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!--Project Buttons-->
|
<!--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]
|
[![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-->
|
||||||
# Table of Contents
|
# Table of Contents
|
||||||
- [[1] About MissKaty](#1-about-misskaty)
|
- [[1] Tentang 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)
|
- [[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)
|
- [[3] Donation](#3-donation)
|
||||||
- [[4] Notes](#4-notes)
|
- [[4] Notes](#4-notes)
|
||||||
- [[5] Features](#5-features)
|
- [[5] Features](#5-features)
|
||||||
|
|
@ -26,20 +29,21 @@
|
||||||
- [[8] Credits](#8-thanks-to)
|
- [[8] Credits](#8-thanks-to)
|
||||||
- [[9] Disclaimer](#8-disclaimer)
|
- [[9] Disclaimer](#8-disclaimer)
|
||||||
|
|
||||||
# [1] About MissKaty
|
# [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.
|
*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
|
## [2] Alat Kerangka Dan Server Yang Digunakan Untuk Membangun Bot Ini
|
||||||
🌱 PyroFork v2.x.x (Fork Pyrogram dengan Dukungan Topik dan Beberapa Patch)<br>
|
🌱 PyroFork v2.x.x (Fork Pyrogram dengan Dukungan Topik, Stories dan Beberapa Patch)<br>
|
||||||
🌱 Dukungan Python 3.11<br>
|
🌱 Dukungan Python 3.11<br>
|
||||||
🌱 MongoDB sebagai Database<br>
|
🌱 MongoDB sebagai Database<br>
|
||||||
🌱 PyKeyboard for Building Pagination<br>
|
🌱 PyKeyboard for Building Pagination<br>
|
||||||
🌱 VS Code<br>
|
🌱 VS Code<br>
|
||||||
🌱 VPS/Server With Docker Support (Recommended)<br>
|
🌱 VPS/Server dengan Root dan Docker Support (Recommended)<br>
|
||||||
|
|
||||||
## [3] Donation
|
## [3] Donation
|
||||||
*Khusus Indonesia Saja:*<br>
|
*Khusus Indonesia Saja:*<br>
|
||||||
🌱 [QRIS][qris-url]<br>
|
🌱 [QRIS][qris-url]<br>
|
||||||
|
🌱 [Mayar ID][mayar]<br>
|
||||||
|
|
||||||
*Untuk Semua Negara:*<br>
|
*Untuk Semua Negara:*<br>
|
||||||
🌱 [Paypal][paypal-url]<br>
|
🌱 [Paypal][paypal-url]<br>
|
||||||
|
|
@ -56,7 +60,7 @@ Jika Anda ingin membantu saya memperbaiki beberapa kesalahan di bot saya, Anda d
|
||||||
| Downloader FB, TikTok and YT-DLP Support |✔️|
|
| Downloader FB, TikTok and YT-DLP Support |✔️|
|
||||||
| MultiLanguage Support (Still Beta) |⚠️|
|
| MultiLanguage Support (Still Beta) |⚠️|
|
||||||
| NightMode |✔️|
|
| NightMode |✔️|
|
||||||
| ChatBot based on OpenAI |✔️|
|
| ChatBot based on OpenAI, and Google Bard |✔️|
|
||||||
| MissKaty Mata |✔️|
|
| MissKaty Mata |✔️|
|
||||||
| Inline Search |✔️|
|
| Inline Search |✔️|
|
||||||
| Sticker Tools |✔️|
|
| Sticker Tools |✔️|
|
||||||
|
|
@ -72,21 +76,25 @@ Jika Anda ingin membantu saya memperbaiki beberapa kesalahan di bot saya, Anda d
|
||||||
* `BOT_TOKEN`: Buat bot menggunakan [@BotFather](https://t.me/BotFather), dan dapatkan token Telegram API.
|
* `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_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)
|
* `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)
|
* `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.
|
* `LOG_CHANNEL` : Channel untuk mencatat aktivitas bot. Pastikan bot adalah admin di channel.
|
||||||
|
|
||||||
### Variabel Opsional
|
### 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.
|
* `USER_SESSION` : String session untuk Userbot.
|
||||||
* `DATABASE_NAME`: Nama database di MongoDB
|
* `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`
|
* `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
|
* `SUDO`: User ID yang memiliki akses ke bot, dipisahkan dengan spasi
|
||||||
* `OPENAI_API`: Dapatkan dari Web OpenAI
|
* `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
|
* `CURRENCY_API`: Dapatkan API Key di https://app.exchangerate-api.com/sign-up
|
||||||
|
|
||||||
## [7] Tutorial Deploy (Recommended using Docker/Docker Compose)
|
## [7] Tutorial Deploy (Recommended using Docker/Docker Compose)
|
||||||
|
|
||||||
#### Bangun Dan Jalankan Menggunakan Metode Lama
|
#### Bangun Dan Jalankan Menggunakan Metode Lama
|
||||||
- Pastikan versi python minimum adalah 3.8 untuk mencegah beberapa error. Periksa dengan perintah ini:
|
- Pastikan versi python minimum adalah 3.8 dan maksimal python 3.11 untuk mencegah beberapa error. Periksa dengan perintah ini:
|
||||||
```
|
```
|
||||||
python3 --version
|
python3 --version
|
||||||
```
|
```
|
||||||
|
|
@ -101,7 +109,8 @@ pip3 install -r requirements.txt
|
||||||
```
|
```
|
||||||
*Python 3.11*
|
*Python 3.11*
|
||||||
```
|
```
|
||||||
Install venv dari terminal server kamu
|
python3 -m venv nama_venv
|
||||||
|
source nama_venv/bin/activate
|
||||||
pip3 install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
```
|
```
|
||||||
- Atur config environment saat menjalankan bot dan jangan lupa isi semua value yang wajib di isi.
|
- Atur config environment saat menjalankan bot dan jangan lupa isi semua value yang wajib di isi.
|
||||||
|
|
@ -156,19 +165,21 @@ sudo docker-compose stop <pid>
|
||||||
|
|
||||||
|
|
||||||
## [8] Thanks to
|
## [8] Thanks to
|
||||||
- Terima kasih Kepada Allah Swt.
|
- Terimakasih Kepada Allah Swt.
|
||||||
- Terima kasih Kepada Dan [Pyrogram Library](https://github.com/pyrogram/pyrogram).
|
- Terimakasih Kepada Dan [Pyrogram Library](https://github.com/pyrogram/pyrogram) sebagai base pyrofork.
|
||||||
- Terima kasih Kepada [The Hamker Cat](https://github.com/TheHamkerCat) Untuk Kode WilliamButcher.
|
- Terimakasih kepada Mayuri [Mayuri-Chan](https://github.com/Mayuri-Chan) sebagai pemilik library Pyrofork.
|
||||||
- Terima kasih Kepada [Team Yukki](https://github.com/TeamYukki) Untuk Kode AFK Bot.
|
- Terimakasih kepada TeamDriveCok dan Secret Group TBK di Telegram.
|
||||||
- Terima kasih Kepada [Wrench](https://github.com/EverythingSuckz) Untuk Beberapa Kode.
|
- Terimakasih Kepada [The Hamker Cat](https://github.com/TheHamkerCat) Untuk Kode WilliamButcher.
|
||||||
- Terima kasih Kepada [AmanoTeam](https://github.com/AmanoTeam) Untuk Template MultiBahasa.
|
- 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...
|
- Dan Semua Orang Yang Membantuku Dalam Hidupku...
|
||||||
Jika kode Anda digunakan dalam repo ini dan ingin memberikan kredit, silakan buka masalah..
|
Jika kode Anda digunakan dalam repo ini dan ingin memberikan kredit, silakan buka masalah..
|
||||||
|
|
||||||
## [9] Disclaimer
|
## [9] Disclaimer
|
||||||
[](https://www.gnu.org/licenses/agpl-3.0.en.html#header)
|
[](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)
|
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.
|
PERINGATAN: *Dilarang Keras* Menjual Kode Kepada Orang Lain Demi Uang Tanpa Seijin Saya. Atau saya akan menghentikan project ini selamanyaa....
|
||||||
|
|
||||||
<!--Url for Badges-->
|
<!--Url for Badges-->
|
||||||
[license-shield]: https://img.shields.io/github/license/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=04B4AE
|
[license-shield]: https://img.shields.io/github/license/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=04B4AE
|
||||||
|
|
@ -188,7 +199,8 @@ PERINGATAN: Menjual Kode Kepada Orang Lain Demi Uang *Dilarang Keras*. Tuhan sel
|
||||||
[readme-ko-url]: README.md
|
[readme-ko-url]: README.md
|
||||||
[kofi-url]: https://ko-fi.com/yasirarism
|
[kofi-url]: https://ko-fi.com/yasirarism
|
||||||
[paypal-url]: https://paypal.me/yasirarism
|
[paypal-url]: https://paypal.me/yasirarism
|
||||||
[qris-url]: https://telegra.ph/file/2acf7698f300ef3d9138f.jpg
|
[qris-url]: https://img.yasirweb.eu.org/file/2acf7698f300ef3d9138f.jpg
|
||||||
|
[mayar]: https://yasirarism.mayar.link/payme
|
||||||
[sociabuzz-url]: https://sociabuzz.com/yasirarism/tribe
|
[sociabuzz-url]: https://sociabuzz.com/yasirarism/tribe
|
||||||
[saweria-url]: https://saweria.co/yasirarism
|
[saweria-url]: https://saweria.co/yasirarism
|
||||||
[trakteer-url]: https://trakteer.id/yasir-aris-sp7cn
|
[trakteer-url]: https://trakteer.id/yasir-aris-sp7cn
|
||||||
43
README.md
43
README.md
|
|
@ -1,4 +1,7 @@
|
||||||
# MissKatyPyro
|
# 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-->
|
<!--Badges-->
|
||||||
![MIT License][license-shield] ![Repository Size][repository-size-shield] ![Issue Closed][issue-closed-shield]
|
![MIT License][license-shield] ![Repository Size][repository-size-shield] ![Issue Closed][issue-closed-shield]
|
||||||
|
|
@ -9,13 +12,13 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!--Project Buttons-->
|
<!--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]
|
[![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-->
|
||||||
# Table of Contents
|
# Table of Contents
|
||||||
- [[1] About MissKaty](#1-about-misskaty)
|
- [[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)
|
- [[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)
|
- [[3] Support Creator](#3-donation)
|
||||||
- [[4] Notes](#4-notes)
|
- [[4] Notes](#4-notes)
|
||||||
- [[5] Features](#5-features)
|
- [[5] Features](#5-features)
|
||||||
- [[6] Variables](#6-variables)
|
- [[6] Variables](#6-variables)
|
||||||
|
|
@ -30,12 +33,12 @@
|
||||||
*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.
|
*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
|
## [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>
|
🌱 PyroFork v2.x.x (Fork of Pyrogram with Topics, Stories Support and Some Patch)<br>
|
||||||
🌱 Python 3.11 Support<br>
|
🌱 Python 3.12 Support<br>
|
||||||
🌱 MongoDB as Database<br>
|
🌱 MongoDB as Database<br>
|
||||||
🌱 PyKeyboard for Building Pagination<br>
|
🌱 PyKeyboard for Building Pagination<br>
|
||||||
🌱 VS Code<br>
|
🌱 VS Code<br>
|
||||||
🌱 VPS/Server With Docker Support (Recommended)<br>
|
🌱 VPS/Server With Root and Docker Support (Recommended)<br>
|
||||||
|
|
||||||
## [3] Donation and Support
|
## [3] Donation and Support
|
||||||
*For Indonesian Only and some supported country:*<br>
|
*For Indonesian Only and some supported country:*<br>
|
||||||
|
|
@ -56,7 +59,7 @@ If you want help me fixing some error in my bot, you can make pull request to th
|
||||||
| Downloader FB, TikTok and YT-DLP Support |✔️|
|
| Downloader FB, TikTok and YT-DLP Support |✔️|
|
||||||
| MultiLanguage Support (Unfinished) |⚠️|
|
| MultiLanguage Support (Unfinished) |⚠️|
|
||||||
| NightMode |✔️|
|
| NightMode |✔️|
|
||||||
| ChatBot based on OpenAI |✔️|
|
| ChatBot based on OpenAI and Google Bard |✔️|
|
||||||
| MissKaty Mata |✔️|
|
| MissKaty Mata |✔️|
|
||||||
| Inline Search |✔️|
|
| Inline Search |✔️|
|
||||||
| Sticker Tools |✔️|
|
| Sticker Tools |✔️|
|
||||||
|
|
@ -72,21 +75,25 @@ If you want help me fixing some error in my bot, you can make pull request to th
|
||||||
* `BOT_TOKEN`: Create a bot using [@BotFather](https://t.me/BotFather), and get the Telegram API token.
|
* `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_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)
|
* `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)
|
* `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.
|
* `LOG_CHANNEL` : A channel to log the activities of bot. Make sure bot is an admin in the channel.
|
||||||
|
|
||||||
### Optional Variables
|
### 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.
|
* `USER_SESSION` : Session string for Userbot.
|
||||||
* `DATABASE_NAME`: Name of the database in MongoDB
|
* `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`
|
* `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
|
* `SUDO`: User ID that have access to bot, split by space
|
||||||
* `OPENAI_API`: Get it from OpenAI Web
|
* `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
|
* `CURRENCY_API`: Get API Key from https://app.exchangerate-api.com/sign-up
|
||||||
|
|
||||||
## [7] Tutorial Deploy (Recommended using Docker/Docker Compose)
|
## [7] Tutorial Deploy (Recommended using Docker/Docker Compose)
|
||||||
|
|
||||||
#### Build And Run Using Legacy Method
|
#### Build And Run Using Legacy Method
|
||||||
- Make sure minimum python version is 3.8 to prevent some errors. Check it with this command:
|
- Make sure minimum python version is 3.8 and max python 3.12 to prevent some errors. Check it with this command:
|
||||||
```
|
```
|
||||||
python3 --version
|
python3 --version
|
||||||
```
|
```
|
||||||
|
|
@ -94,14 +101,15 @@ python3 --version
|
||||||
```
|
```
|
||||||
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
|
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/>
|
- Install requirements.txt, if using python => 3.11, you need use venv when install pip package.<br/>
|
||||||
*Python < 3.10*
|
*Python < 3.10*
|
||||||
```
|
```
|
||||||
pip3 install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
```
|
```
|
||||||
*Python 3.11*
|
*Python => 3.11*
|
||||||
```
|
```
|
||||||
Install venv from your terminal and activate it
|
python3 -m venv nama_venv
|
||||||
|
source nama_venv/bin/activate
|
||||||
pip3 install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
```
|
```
|
||||||
- Setting your config.env or via environment and dont forget fill all required value.
|
- Setting your config.env or via environment and dont forget fill all required value.
|
||||||
|
|
@ -157,7 +165,9 @@ sudo docker-compose stop <pid>
|
||||||
|
|
||||||
## [8] Thanks to
|
## [8] Thanks to
|
||||||
- Thanks To Allah Swt.
|
- Thanks To Allah Swt.
|
||||||
- Thanks To Dan For His Awesome [Library](https://github.com/pyrogram/pyrogram).
|
- 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 [The Hamker Cat](https://github.com/TheHamkerCat) For WilliamButcher Code.
|
||||||
- Thanks To [Team Yukki](https://github.com/TeamYukki) For AFK Bot 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 [Wrench](https://github.com/EverythingSuckz) For Some Code.
|
||||||
|
|
@ -168,7 +178,7 @@ sudo docker-compose stop <pid>
|
||||||
## [9] Disclaimer
|
## [9] Disclaimer
|
||||||
[](https://www.gnu.org/licenses/agpl-3.0.en.html#header)
|
[](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)
|
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.
|
WARNING: Selling The Codes To Other People For Money Is *Strictly Prohibited*. Or i will stop this project forever.
|
||||||
|
|
||||||
<!--Url for Badges-->
|
<!--Url for Badges-->
|
||||||
[license-shield]: https://img.shields.io/github/license/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=04B4AE
|
[license-shield]: https://img.shields.io/github/license/yasirarism/MissKatyPyro?labelColor=D8D8D8&color=04B4AE
|
||||||
|
|
@ -178,7 +188,7 @@ WARNING: Selling The Codes To Other People For Money Is *Strictly Prohibited*. G
|
||||||
<!--Url for Buttons-->
|
<!--Url for Buttons-->
|
||||||
[readme-ko-shield]: https://img.shields.io/badge/-readme%20in%20Indonesian-2E2E2E?style=for-the-badge
|
[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-shield]: https://img.shields.io/badge/-%F0%9F%98%8E%20view%20demo-F3F781?style=for-the-badge
|
||||||
[view-demo-url]: https://t.me/MissKatyPyro
|
[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-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
|
[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-shield]: https://img.shields.io/badge/-%E2%9C%A8%20request%20feature-A9D0F5?style=for-the-badge
|
||||||
|
|
@ -188,7 +198,8 @@ WARNING: Selling The Codes To Other People For Money Is *Strictly Prohibited*. G
|
||||||
[readme-ko-url]: README.id.md
|
[readme-ko-url]: README.id.md
|
||||||
[kofi-url]: https://ko-fi.com/yasirarism
|
[kofi-url]: https://ko-fi.com/yasirarism
|
||||||
[paypal-url]: https://paypal.me/yasirarism
|
[paypal-url]: https://paypal.me/yasirarism
|
||||||
[qris-url]: https://telegra.ph/file/9427d61d6968b8ee4fb2f.jpg
|
[qris-url]: https://img.yasirweb.eu.org/file/ee74ce527fb8264b54691.jpg
|
||||||
|
[mayar]: https://yasirarism.mayar.link/payme
|
||||||
[sociabuzz-url]: https://sociabuzz.com/yasirarism/tribe
|
[sociabuzz-url]: https://sociabuzz.com/yasirarism/tribe
|
||||||
[saweria-url]: https://saweria.co/yasirarism
|
[saweria-url]: https://saweria.co/yasirarism
|
||||||
[trakteer-url]: https://trakteer.id/yasir-aris-sp7cn
|
[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_HASH=
|
||||||
API_ID=
|
API_ID=
|
||||||
BOT_TOKEN=
|
BOT_TOKEN=
|
||||||
DATABASE_URI=mongodb+srv://
|
DATABASE_URI=
|
||||||
LOG_CHANNEL=
|
LOG_CHANNEL=
|
||||||
|
|
||||||
# Optional Vars
|
# Optional Vars
|
||||||
|
|
@ -12,4 +12,5 @@ SUPPORT_CHAT=YasirPediaChannel
|
||||||
COMMAND_HANDLER=
|
COMMAND_HANDLER=
|
||||||
USER_SESSION=
|
USER_SESSION=
|
||||||
OPENAI_API=
|
OPENAI_API=
|
||||||
|
GOOGLEAI_API=
|
||||||
CURRENCY_API=
|
CURRENCY_API=
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
"""
|
"""
|
||||||
* @author yasir <yasiramunandar@gmail.com>
|
* @author yasir <yasiramunandar@gmail.com>
|
||||||
* @date 2022-09-06 10:12:09
|
* @date 2022-09-06 10:12:09
|
||||||
* @projectName MissKatyPyro
|
* @projectName MissKatyPyro
|
||||||
* Copyright @YasirPedia All rights reserved
|
* Copyright @YasirPedia All rights reserved
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from async_pymongo import AsyncClient
|
from async_pymongo import AsyncClient
|
||||||
|
|
||||||
from misskaty.vars import DATABASE_NAME, DATABASE_URI
|
from misskaty.vars import DATABASE_NAME, DATABASE_URI
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
from database import dbname
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
from database import dbname
|
||||||
|
|
||||||
blacklist_filtersdb = dbname["blacklistFilters"]
|
blacklist_filtersdb = dbname["blacklistFilters"]
|
||||||
|
|
||||||
|
|
||||||
async def get_blacklisted_words(chat_id: int) -> List[str]:
|
async def get_blacklisted_words(chat_id: int) -> List[str]:
|
||||||
_filters = await blacklist_filtersdb.find_one({"chat_id": chat_id})
|
_filters = await blacklist_filtersdb.find_one({"chat_id": chat_id})
|
||||||
if not _filters:
|
return [] if not _filters else _filters["filters"]
|
||||||
return []
|
|
||||||
return _filters["filters"]
|
|
||||||
|
|
||||||
async def save_blacklist_filter(chat_id: int, word: str):
|
async def save_blacklist_filter(chat_id: int, word: str):
|
||||||
word = word.lower().strip()
|
word = word.lower().strip()
|
||||||
|
|
@ -19,6 +20,7 @@ async def save_blacklist_filter(chat_id: int, word: str):
|
||||||
upsert=True,
|
upsert=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def delete_blacklist_filter(chat_id: int, word: str) -> bool:
|
async def delete_blacklist_filter(chat_id: int, word: str) -> bool:
|
||||||
filtersd = await get_blacklisted_words(chat_id)
|
filtersd = await get_blacklisted_words(chat_id)
|
||||||
word = word.lower().strip()
|
word = word.lower().strip()
|
||||||
|
|
|
||||||
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
|
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]:
|
async def get_filter(chat_id: int, name: str) -> Union[bool, dict]:
|
||||||
name = name.lower().strip()
|
name = name.lower().strip()
|
||||||
_filters = await _get_filters(chat_id)
|
_filters = await _get_filters(chat_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(
|
await notesdb.update_one(
|
||||||
{"chat_id": chat_id}, {"$set": {"notes": _notes}}, upsert=True
|
{"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):
|
def __init__(self, uri, database_name):
|
||||||
self._client = AsyncClient(uri)
|
self._client = AsyncClient(uri)
|
||||||
self.db = self._client[database_name]
|
self.db = self._client[database_name]
|
||||||
self.col = self.db["users"]
|
self.col = self.db["userlist"]
|
||||||
self.grp = self.db["groups"]
|
self.grp = self.db["groups"]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -44,17 +44,14 @@ class UsersData:
|
||||||
return await self.col.count_documents({})
|
return await self.col.count_documents({})
|
||||||
|
|
||||||
async def remove_ban(self, id):
|
async def remove_ban(self, id):
|
||||||
ban_status = dict(is_banned=False, ban_reason="")
|
return await self.col.delete_one({"_id": id})
|
||||||
await self.col.update_one({"id": id}, {"$set": {"ban_status": ban_status}})
|
|
||||||
|
|
||||||
async def ban_user(self, user_id, ban_reason="No Reason"):
|
async def ban_user(self, user_id, ban_reason="No Reason"):
|
||||||
ban_status = dict(is_banned=True, ban_reason=ban_reason)
|
return await self.col.insert_one({"_id": user_id, "reason": ban_reason})
|
||||||
await self.col.update_one({"id": user_id}, {"$set": {"ban_status": ban_status}})
|
|
||||||
|
|
||||||
async def get_ban_status(self, id):
|
async def get_ban_status(self, id):
|
||||||
default = dict(is_banned=False, ban_reason="")
|
user = await self.col.find_one({"_id": int(id)})
|
||||||
user = await self.col.find_one({"id": int(id)})
|
return user if user else False
|
||||||
return user.get("ban_status", default) if user else default
|
|
||||||
|
|
||||||
async def get_all_users(self):
|
async def get_all_users(self):
|
||||||
return self.col.find({})
|
return self.col.find({})
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
version: "3.3"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
misskaty:
|
||||||
|
image: misskaty
|
||||||
|
container_name: misskaty
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
command: bash start.sh
|
command: bash start.sh
|
||||||
restart: on-failure
|
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:
|
build:
|
||||||
docker:
|
docker:
|
||||||
worker: Dockerfile
|
misskaty: Dockerfile
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@
|
||||||
"ban_admin_err": "Lol, it's crazy if i can banned an admin.",
|
"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.",
|
"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.",
|
"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}",
|
"kick_msg": "**Kicked User:** {mention} [`{id}`]\n**Kicked By:** {kicker}\n**Reason:** {reasonmsg}",
|
||||||
"ban_msg": "**Banned User:** {mention} [{id}]\n**Banned By:** {banner}\n",
|
"ban_msg": "**Banned User:** {mention} [`{id}`]\n**Banned By:** {banner}\n",
|
||||||
"unban_msg": "__Banned removed by {mention}__",
|
"unban_msg": "__Banned removed by {mention}__",
|
||||||
"no_ban_permission": "Please give me ban permission to ban user in this group.",
|
"no_ban_permission": "Please give me ban permission to ban user in this group.",
|
||||||
"no_more_99": "You can't use more than 99",
|
"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..",
|
"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.",
|
"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}"
|
"answers_too_long": "Question for your answer has exceeded TG text limit, check this link to view.\n\n{answerlink}"
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
{
|
{
|
||||||
"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>",
|
"up_and_rest": "<b>Updated with default branch, restarting now.</b>",
|
||||||
"cl_btn": "❌ Close",
|
"cl_btn": "❌ Close",
|
||||||
"no_eval": "__No evaluate message!__",
|
"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_eval": "<i>Processing eval pyrogram..</i>",
|
||||||
"run_exec": "<i>Processing exec pyrogram..</i>",
|
"run_exec": "<i>Processing exec pyrogram..</i>",
|
||||||
"no_cmd": "No command to execute was given.",
|
"no_cmd": "No command to execute was given.",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"back_btn": "« Go back",
|
"back_btn": "« Go back",
|
||||||
"no_results": "No Results.",
|
"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!"
|
"exp_task": "😶🌫️ Timeout. Task has been cancelled!"
|
||||||
}
|
}
|
||||||
|
|
@ -9,5 +9,6 @@
|
||||||
"err_parse": "Failed parse URL, check logs..",
|
"err_parse": "Failed parse URL, check logs..",
|
||||||
"wait": "Please wait..",
|
"wait": "Please wait..",
|
||||||
"unauth": "Not Your Task..",
|
"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.",
|
"ban_admin_err": "Hah, sungguh gila jika saya bisa melarang seorang admin.",
|
||||||
"mute_admin_err": "Hah, sungguh gila jika saya bisa membisukan admin.",
|
"mute_admin_err": "Hah, sungguh gila jika saya bisa membisukan admin.",
|
||||||
"warn_admin_err": "Hah, sungguh gila jika saya bisa memperingatkan seorang 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}",
|
"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",
|
"ban_msg": "**Pengguna yang Dibanned:** {mention} [`{id}`]\n**Dibanned Oleh:** {banner}\n",
|
||||||
"unban_msg": "__Banned dihapus oleh {mention}__",
|
"unban_msg": "__Banned dihapus oleh {mention}__",
|
||||||
"no_ban_permission": "Tolong beri saya izin banned untuk membanned pengguna di grup ini.",
|
"no_ban_permission": "Tolong beri saya izin banned untuk membanned pengguna di grup ini.",
|
||||||
"no_more_99": "Anda tidak dapat menggunakan lebih dari 99",
|
"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..",
|
"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.",
|
"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>",
|
"up_and_rest": "<b>Diperbarui dengan branch default, dimulai ulang sekarang.</b>",
|
||||||
"cl_btn": "❌ Tutup",
|
"cl_btn": "❌ Tutup",
|
||||||
"no_eval": "__Tidak ada pesan eval!__",
|
"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_eval": "<i>Memproses pyrogram eval..</i>",
|
||||||
"run_exec": "<i>Memproses pyrogram eksekutif..</i>",
|
"run_exec": "<i>Memproses pyrogram eksekutif..</i>",
|
||||||
"no_cmd": "Tidak ada perintah untuk dieksekusi.",
|
"no_cmd": "Tidak ada perintah untuk dieksekusi.",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"back_btn": "« Kembali",
|
"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.",
|
"no_results": "Tidak ada hasil yang ditemukan.",
|
||||||
"exp_task": "😶🌫️ Waktu Habis. Tugas Telah Dibatalkan!"
|
"exp_task": "😶🌫️ Waktu Habis. Tugas Telah Dibatalkan!"
|
||||||
}
|
}
|
||||||
|
|
@ -9,5 +9,6 @@
|
||||||
"err_parse": "Gagal menguraikan URL, periksa log..",
|
"err_parse": "Gagal menguraikan URL, periksa log..",
|
||||||
"tunggu": "Harap tunggu..",
|
"tunggu": "Harap tunggu..",
|
||||||
"unauth": "Bukan Tugas Anda..",
|
"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.",
|
"ban_admin_err": "Lol, edan yen aku bisa nglarang admin.",
|
||||||
"mute_admin_err": "Lol, edan yen aku bisa bisu admin.",
|
"mute_admin_err": "Lol, edan yen aku bisa bisu admin.",
|
||||||
"warn_admin_err": "Lol, edan yen aku bisa ngelingake admin.",
|
"warn_admin_err": "Lol, edan yen aku bisa ngelingake admin.",
|
||||||
"kick_msg": "**Panganggo Ditendhang:** {mention} [{id}]\n**Ditendhang dening:** {kicker}\n**Alasan:** {reasonmsg}",
|
"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",
|
"ban_msg": "**Panganggo sing Dicekal:** {mention} [`{id}`]\n**Dicekal Dening:** {banner}\n",
|
||||||
"unban_msg": "__Dicekal dibusak kanthi {mention}__",
|
"unban_msg": "__Dicekal dibusak kanthi {mention}__",
|
||||||
"no_ban_permission": "Mangga kula nyuwun idin nglarang panganggo ing grup punika.",
|
"no_ban_permission": "Mangga kula nyuwun idin nglarang panganggo ing grup punika.",
|
||||||
"no_more_99": "Sampeyan ora bisa nggunakake luwih saka 99",
|
"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..",
|
"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.",
|
"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}"
|
"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>",
|
"up_and_rest": "<b>Dianyari nganggo branch standar, diwiwiti maneh saiki.</b>",
|
||||||
"cl_btn": "❌ Tutup",
|
"cl_btn": "❌ Tutup",
|
||||||
"no_eval": "__Ora ana pesen eval!__",
|
"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_eval": "<i>Ngolah pyrogram eval..</i>",
|
||||||
"run_exec": "<i>Ngolah pyrogram exec..</i>",
|
"run_exec": "<i>Ngolah pyrogram exec..</i>",
|
||||||
"no_cmd": "Ora ana prentah kanggo nglakokaké.",
|
"no_cmd": "Ora ana prentah kanggo nglakokaké.",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"back_btn": "« Balik Maneh",
|
"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.",
|
"no_results": "Ora ana asil sing ditemokake.",
|
||||||
"exp_task": "😶🌫️ Wektu wis entek. Tugas wis dibatalake!"
|
"exp_task": "😶🌫️ Wektu wis entek. Tugas wis dibatalake!"
|
||||||
}
|
}
|
||||||
|
|
@ -9,5 +9,6 @@
|
||||||
"err_parse": "Gagal ngurai URL, mriksa log..",
|
"err_parse": "Gagal ngurai URL, mriksa log..",
|
||||||
"ngenteni": "Tulung ngenteni..",
|
"ngenteni": "Tulung ngenteni..",
|
||||||
"unauth": "Ora Tugasmu..",
|
"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
|
# * @date 2023-06-21 22:12:27
|
||||||
# * @projectName MissKatyPyro
|
# * @projectName MissKatyPyro
|
||||||
# * Copyright ©YasirPedia All rights reserved
|
# * Copyright ©YasirPedia All rights reserved
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
|
from asyncio import get_event_loop
|
||||||
|
from faulthandler import enable as faulthandler_enable
|
||||||
from logging import ERROR, INFO, StreamHandler, basicConfig, getLogger, handlers
|
from logging import ERROR, INFO, StreamHandler, basicConfig, getLogger, handlers
|
||||||
|
|
||||||
|
import uvloop, uvicorn
|
||||||
from apscheduler.jobstores.mongodb import MongoDBJobStore
|
from apscheduler.jobstores.mongodb import MongoDBJobStore
|
||||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
from async_pymongo import AsyncClient
|
from async_pymongo import AsyncClient
|
||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
from pyrogram import Client
|
from pyrogram import Client
|
||||||
|
from web.webserver import api
|
||||||
|
|
||||||
from misskaty.core import misskaty_patch
|
|
||||||
from misskaty.vars import (
|
from misskaty.vars import (
|
||||||
API_HASH,
|
API_HASH,
|
||||||
API_ID,
|
API_ID,
|
||||||
BOT_TOKEN,
|
BOT_TOKEN,
|
||||||
DATABASE_NAME,
|
DATABASE_NAME,
|
||||||
DATABASE_URI,
|
DATABASE_URI,
|
||||||
|
PORT,
|
||||||
TZ,
|
TZ,
|
||||||
USER_SESSION,
|
USER_SESSION,
|
||||||
)
|
)
|
||||||
|
|
||||||
basicConfig(
|
basicConfig(
|
||||||
level=INFO,
|
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",
|
datefmt="%d-%b-%y %H:%M:%S",
|
||||||
handlers=[
|
handlers=[
|
||||||
handlers.RotatingFileHandler("MissKatyLogs.txt", mode="w+", maxBytes=1000000),
|
handlers.RotatingFileHandler(
|
||||||
|
"MissKatyLogs.txt", mode="w+", maxBytes=5242880, backupCount=1
|
||||||
|
),
|
||||||
StreamHandler(),
|
StreamHandler(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
getLogger("pyrogram").setLevel(ERROR)
|
getLogger("pyrogram").setLevel(ERROR)
|
||||||
getLogger("openai").setLevel(ERROR)
|
getLogger("openai").setLevel(ERROR)
|
||||||
getLogger("httpx").setLevel(ERROR)
|
getLogger("httpx").setLevel(ERROR)
|
||||||
|
getLogger("iytdl").setLevel(ERROR)
|
||||||
|
|
||||||
MOD_LOAD = []
|
MOD_LOAD = []
|
||||||
MOD_NOLOAD = ["subscene_dl"]
|
MOD_NOLOAD = ["subscene_dl"]
|
||||||
HELPABLE = {}
|
HELPABLE = {}
|
||||||
cleanmode = {}
|
cleanmode = {}
|
||||||
botStartTime = time.time()
|
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
|
# Pyrogram Bot Client
|
||||||
app = Client(
|
app = Client(
|
||||||
|
|
@ -48,13 +60,22 @@ app = Client(
|
||||||
api_id=API_ID,
|
api_id=API_ID,
|
||||||
api_hash=API_HASH,
|
api_hash=API_HASH,
|
||||||
bot_token=BOT_TOKEN,
|
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
|
# Pyrogram UserBot Client
|
||||||
user = Client(
|
user = Client(
|
||||||
"YasirUBot",
|
"YasirUBot",
|
||||||
session_string=USER_SESSION,
|
session_string=USER_SESSION,
|
||||||
|
mongodb=dict(connection=AsyncClient(DATABASE_URI), remove_peers=False),
|
||||||
|
sleep_threshold=180,
|
||||||
|
app_version="MissKaty Ubot",
|
||||||
)
|
)
|
||||||
|
|
||||||
jobstores = {
|
jobstores = {
|
||||||
|
|
@ -64,15 +85,26 @@ jobstores = {
|
||||||
}
|
}
|
||||||
scheduler = AsyncIOScheduler(jobstores=jobstores, timezone=TZ)
|
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()
|
app.start()
|
||||||
BOT_ID = app.me.id
|
BOT_ID = app.me.id
|
||||||
BOT_NAME = app.me.first_name
|
BOT_NAME = app.me.first_name
|
||||||
BOT_USERNAME = app.me.username
|
BOT_USERNAME = app.me.username
|
||||||
if USER_SESSION:
|
if USER_SESSION:
|
||||||
user.start()
|
try:
|
||||||
UBOT_ID = user.me.id
|
user.start()
|
||||||
UBOT_NAME = user.me.first_name
|
UBOT_ID = user.me.id
|
||||||
UBOT_USERNAME = user.me.username
|
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:
|
else:
|
||||||
UBOT_ID = None
|
UBOT_ID = None
|
||||||
UBOT_NAME = None
|
UBOT_NAME = None
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
"""
|
"""
|
||||||
* @author yasir <yasiramunandar@gmail.com>
|
* @author yasir <yasiramunandar@gmail.com>
|
||||||
* @date 2022-12-01 09:12:27
|
* @date 2022-12-01 09:12:27
|
||||||
* @projectName MissKatyPyro
|
* @projectName MissKatyPyro
|
||||||
* Copyright @YasirPedia All rights reserved
|
* Copyright @YasirPedia All rights reserved
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import importlib
|
import importlib
|
||||||
import os
|
import os
|
||||||
|
|
@ -15,14 +16,22 @@ from pyrogram import __version__, idle
|
||||||
from pyrogram.raw.all import layer
|
from pyrogram.raw.all import layer
|
||||||
|
|
||||||
from database import dbname
|
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 import ALL_MODULES
|
||||||
from misskaty.plugins.web_scraper import web
|
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
|
from utils import auto_clean
|
||||||
|
|
||||||
LOGGER = getLogger(__name__)
|
LOGGER = getLogger("MissKaty")
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
|
|
||||||
|
|
||||||
# Run Bot
|
# Run Bot
|
||||||
|
|
@ -48,25 +57,24 @@ async def start_bot():
|
||||||
LOGGER.info(bot_modules)
|
LOGGER.info(bot_modules)
|
||||||
LOGGER.info("+===============+===============+===============+===============+")
|
LOGGER.info("+===============+===============+===============+===============+")
|
||||||
LOGGER.info("[INFO]: BOT STARTED AS @%s!", BOT_USERNAME)
|
LOGGER.info("[INFO]: BOT STARTED AS @%s!", BOT_USERNAME)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
LOGGER.info("[INFO]: SENDING ONLINE STATUS")
|
LOGGER.info("[INFO]: SENDING ONLINE STATUS")
|
||||||
for i in SUDO:
|
if USER_SESSION:
|
||||||
if USER_SESSION:
|
await app.send_message(
|
||||||
await app.send_message(
|
OWNER_ID,
|
||||||
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>",
|
||||||
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:
|
||||||
else:
|
await app.send_message(
|
||||||
await app.send_message(
|
OWNER_ID,
|
||||||
i,
|
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>",
|
||||||
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>",
|
)
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOGGER.error(str(e))
|
LOGGER.error(str(e))
|
||||||
scheduler.start()
|
scheduler.start()
|
||||||
|
asyncio.create_task(run_wsgi())
|
||||||
if "web" not in await dbname.list_collection_names():
|
if "web" not in await dbname.list_collection_names():
|
||||||
webdb = dbname.web
|
webdb = dbname["web"]
|
||||||
for key, value in web.items():
|
for key, value in web.items():
|
||||||
await webdb.insert_one({key: value})
|
await webdb.insert_one({key: value})
|
||||||
if os.path.exists("restart.pickle"):
|
if os.path.exists("restart.pickle"):
|
||||||
|
|
@ -84,14 +92,15 @@ async def start_bot():
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
loop.run_until_complete(start_bot())
|
get_event_loop().run_until_complete(start_bot())
|
||||||
|
app.loop.run_forever()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
err = traceback.format_exc()
|
err = traceback.format_exc()
|
||||||
LOGGER.info(err)
|
LOGGER.info(err)
|
||||||
finally:
|
finally:
|
||||||
loop.stop()
|
app.loop.stop()
|
||||||
LOGGER.info(
|
LOGGER.info(
|
||||||
"------------------------ Stopped Services ------------------------"
|
"------------------------ Stopped Services ------------------------"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
# skipcq
|
from .errors import capture_err
|
||||||
from .errors import *
|
from .misc import asyncify, new_task
|
||||||
from .misc import *
|
from .permissions import adminsOnly, require_admin
|
||||||
from .permissions import *
|
|
||||||
from .ratelimiter import *
|
__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.errors.exceptions.forbidden_403 import ChatWriteForbidden
|
||||||
from pyrogram.types import CallbackQuery
|
from pyrogram.types import CallbackQuery
|
||||||
|
|
||||||
from misskaty import app
|
|
||||||
from misskaty.vars import LOG_CHANNEL
|
from misskaty.vars import LOG_CHANNEL
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -24,7 +23,7 @@ def capture_err(func):
|
||||||
try:
|
try:
|
||||||
return await func(client, message, *args, **kwargs)
|
return await func(client, message, *args, **kwargs)
|
||||||
except ChatWriteForbidden:
|
except ChatWriteForbidden:
|
||||||
return await app.leave_chat(message.chat.id)
|
return await client.leave_chat(message.chat.id)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
exc = traceback.format_exc()
|
exc = traceback.format_exc()
|
||||||
error_feedback = "ERROR | {} | {}\n\n{}\n\n{}\n".format(
|
error_feedback = "ERROR | {} | {}\n\n{}\n\n{}\n".format(
|
||||||
|
|
@ -45,7 +44,7 @@ def capture_err(func):
|
||||||
) as log:
|
) as log:
|
||||||
log.write(error_feedback)
|
log.write(error_feedback)
|
||||||
log.close()
|
log.close()
|
||||||
await app.send_document(
|
await client.send_document(
|
||||||
LOG_CHANNEL,
|
LOG_CHANNEL,
|
||||||
f"crash_{tgl_now.strftime('%d %B %Y')}.txt",
|
f"crash_{tgl_now.strftime('%d %B %Y')}.txt",
|
||||||
caption=f"Crash Report of this Bot\n{cap_day}",
|
caption=f"Crash Report of this Bot\n{cap_day}",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import asyncio
|
||||||
import logging
|
import logging
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger("MissKaty")
|
||||||
|
|
||||||
|
|
||||||
def asyncify(func):
|
def asyncify(func):
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ from time import time
|
||||||
from traceback import format_exc as err
|
from traceback import format_exc as err
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
from cachetools import TTLCache
|
|
||||||
from pyrogram import Client, enums
|
from pyrogram import Client, enums
|
||||||
from pyrogram.errors import ChannelPrivate, ChatAdminRequired, ChatWriteForbidden
|
from pyrogram.errors import ChannelPrivate, ChatAdminRequired, ChatWriteForbidden
|
||||||
from pyrogram.types import CallbackQuery, Message
|
from pyrogram.types import CallbackQuery, Message
|
||||||
|
|
||||||
from misskaty import app
|
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 (
|
from ...helper.localization import (
|
||||||
default_language,
|
default_language,
|
||||||
|
|
@ -93,7 +93,7 @@ async def check_perms(
|
||||||
return False
|
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):
|
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"]
|
return admins_in_chat[chat_id]["data"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
admins_in_chat[chat_id] = {
|
admins_in_chat.add(
|
||||||
"last_updated_at": time(),
|
chat_id,
|
||||||
"data": [
|
{
|
||||||
member.user.id
|
"last_updated_at": time(),
|
||||||
async for member in app.get_chat_members(
|
"data": [
|
||||||
chat_id, filter=enums.ChatMembersFilter.ADMINISTRATORS
|
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"]
|
return admins_in_chat[chat_id]["data"]
|
||||||
except ChannelPrivate:
|
except ChannelPrivate:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
async def authorised(func, subFunc2, client, message, *args, **kwargs):
|
async def authorised(func, subFunc2, client, message, *args, **kwargs):
|
||||||
chatID = message.chat.id
|
|
||||||
try:
|
try:
|
||||||
await func(client, message, *args, **kwargs)
|
await func(client, message, *args, **kwargs)
|
||||||
except ChatWriteForbidden:
|
except ChatWriteForbidden:
|
||||||
await app.leave_chat(chatID)
|
await message.chat.leave()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
try:
|
try:
|
||||||
await message.reply_text(str(e.MESSAGE))
|
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):
|
async def unauthorised(message: Message, permission, subFunc2):
|
||||||
text = f"You don't have the required permission to perform this action.\n**Permission:** __{permission}__"
|
text = f"You don't have the required permission to perform this action.\n**Permission:** __{permission}__"
|
||||||
chatID = message.chat.id
|
|
||||||
try:
|
try:
|
||||||
await message.reply_text(text)
|
await message.reply_text(text)
|
||||||
except ChatWriteForbidden:
|
except ChatWriteForbidden:
|
||||||
await app.leave_chat(chatID)
|
await message.chat.leave()
|
||||||
return subFunc2
|
return subFunc2
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -163,7 +165,7 @@ def adminsOnly(permission):
|
||||||
# For admins and sudo users
|
# For admins and sudo users
|
||||||
userID = message.from_user.id
|
userID = message.from_user.id
|
||||||
permissions = await member_permissions(chatID, userID)
|
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 unauthorised(message, permission, subFunc2)
|
||||||
return await authorised(func, subFunc2, client, message, *args, **kwargs)
|
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 typing import Union
|
||||||
|
|
||||||
from pyrogram.errors import (
|
from pyrogram.errors import (
|
||||||
|
ChannelPrivate,
|
||||||
ChatAdminRequired,
|
ChatAdminRequired,
|
||||||
|
ChatSendPlainForbidden,
|
||||||
ChatWriteForbidden,
|
ChatWriteForbidden,
|
||||||
FloodWait,
|
FloodWait,
|
||||||
MessageAuthorRequired,
|
MessageAuthorRequired,
|
||||||
|
|
@ -14,16 +16,16 @@ from pyrogram.errors import (
|
||||||
MessageIdInvalid,
|
MessageIdInvalid,
|
||||||
MessageNotModified,
|
MessageNotModified,
|
||||||
MessageTooLong,
|
MessageTooLong,
|
||||||
|
TopicClosed,
|
||||||
)
|
)
|
||||||
from pyrogram.types import Message
|
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 :]
|
@property
|
||||||
if len(m.command) > 1
|
def parse_cmd(msg):
|
||||||
else None
|
return msg.text.split(None, 1)[1] if len(msg.command) > 1 else None
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def reply_text(
|
async def reply_text(
|
||||||
|
|
@ -86,11 +88,14 @@ async def reply_text(
|
||||||
await asleep(del_in)
|
await asleep(del_in)
|
||||||
return bool(await msg.delete_msg())
|
return bool(await msg.delete_msg())
|
||||||
except FloodWait as e:
|
except FloodWait as e:
|
||||||
|
LOGGER.warning(f"Got floodwait in {self.chat.id} for {e.value}'s.")
|
||||||
await asleep(e.value)
|
await asleep(e.value)
|
||||||
return await reply_text(self, text, *args, **kwargs)
|
return await reply_text(self, text, *args, **kwargs)
|
||||||
except (ChatWriteForbidden, ChatAdminRequired):
|
except (TopicClosed, ChannelPrivate):
|
||||||
|
return
|
||||||
|
except (ChatWriteForbidden, ChatAdminRequired, ChatSendPlainForbidden):
|
||||||
LOGGER.info(
|
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()
|
return await self.chat.leave()
|
||||||
|
|
||||||
|
|
@ -130,10 +135,10 @@ async def edit_text(
|
||||||
await asleep(del_in)
|
await asleep(del_in)
|
||||||
return bool(await msg.delete_msg())
|
return bool(await msg.delete_msg())
|
||||||
except FloodWait as e:
|
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)
|
await asleep(e.value)
|
||||||
return await edit_text(self, text, *args, **kwargs)
|
return await edit_text(self, text, *args, **kwargs)
|
||||||
except MessageNotModified:
|
except (MessageNotModified, ChannelPrivate):
|
||||||
return False
|
return False
|
||||||
except (ChatWriteForbidden, ChatAdminRequired):
|
except (ChatWriteForbidden, ChatAdminRequired):
|
||||||
LOGGER.info(
|
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_or_send_as_file = reply_or_send_as_file
|
||||||
Message.reply_as_file = reply_as_file
|
Message.reply_as_file = reply_as_file
|
||||||
Message.delete_msg = delete
|
Message.delete_msg = delete
|
||||||
|
Message.input = parse_cmd
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
|
from .adminsOnly import adminsOnly
|
||||||
from .callback import callback
|
from .callback import callback
|
||||||
from .command import command
|
from .command import command
|
||||||
|
|
||||||
__all__ = ["callback", "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
|
||||||
|
|
@ -17,84 +17,86 @@
|
||||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.methods import Decorators
|
from pyrogram.methods import Decorators
|
||||||
|
|
||||||
from ..utils import handle_error
|
from ..utils import handle_error
|
||||||
|
|
||||||
|
|
||||||
def callback(
|
def callback(
|
||||||
self,
|
self,
|
||||||
data: typing.Union[str, list],
|
data: typing.Union[str, list],
|
||||||
self_admin: typing.Union[bool, bool] = False,
|
self_admin: typing.Union[bool, bool] = False,
|
||||||
filter: typing.Union[pyrogram.filters.Filter, pyrogram.filters.Filter] = None,
|
filtercb: typing.Union[pyrogram.filters.Filter] = None,
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
### `Client.callback`
|
### `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.*'))`
|
- 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:
|
- Parameters:
|
||||||
- data (str || list):
|
- data (str || list):
|
||||||
- The callback query to be handled for a function
|
- The callback query to be handled for a function
|
||||||
|
|
||||||
- self_admin (bool) **optional**:
|
- self_admin (bool) **optional**:
|
||||||
- If True, the command will only executeed if the Bot is Admin in the Chat, By Default False
|
- If True, the command will only executeed if the Bot is Admin in the Chat, By Default False
|
||||||
|
|
||||||
- filter (`~pyrogram.filters`) **optional**:
|
- filter (`~pyrogram.filters`) **optional**:
|
||||||
- Pyrogram Filters, hope you know about this, for Advaced usage. Use `and` for seaperating filters.
|
- Pyrogram Filters, hope you know about this, for Advaced usage. Use `and` for seaperating filters.
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
import pyrogram
|
import pyrogram
|
||||||
|
|
||||||
app = pyrogram.Client()
|
app = pyrogram.Client()
|
||||||
|
|
||||||
@app.command("start")
|
@app.command("start")
|
||||||
async def start(client, message):
|
async def start(client, message):
|
||||||
await message.reply_text(
|
await message.reply_text(
|
||||||
f"Hello {message.from_user.mention}",
|
f"Hello {message.from_user.mention}",
|
||||||
reply_markup=pyrogram.types.InlineKeyboardMarkup([[
|
reply_markup=pyrogram.types.InlineKeyboardMarkup([[
|
||||||
pyrogram.types.InlineKeyboardButton(
|
pyrogram.types.InlineKeyboardButton(
|
||||||
"Click Here",
|
"Click Here",
|
||||||
"data"
|
"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
|
@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
|
Decorators.on_cb = callback
|
||||||
|
|
@ -1,129 +1,133 @@
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from ..utils import handle_error
|
|
||||||
from pyrogram.methods import Decorators
|
from pyrogram.methods import Decorators
|
||||||
|
|
||||||
|
from misskaty.core import pyro_cooldown
|
||||||
from misskaty.vars import COMMAND_HANDLER
|
from misskaty.vars import COMMAND_HANDLER
|
||||||
|
|
||||||
|
from ..utils import handle_error
|
||||||
|
|
||||||
|
|
||||||
def command(
|
def command(
|
||||||
self,
|
self,
|
||||||
command: typing.Union[str, list],
|
cmd: typing.Union[str, list],
|
||||||
is_disabled: typing.Union[bool, bool] = False,
|
is_disabled: typing.Union[bool, bool] = False,
|
||||||
pm_only: typing.Union[bool, bool] = False,
|
pm_only: typing.Union[bool, bool] = False,
|
||||||
group_only: typing.Union[bool, bool] = False,
|
group_only: typing.Union[bool, bool] = False,
|
||||||
self_admin: typing.Union[bool, bool] = False,
|
self_admin: typing.Union[bool, bool] = False,
|
||||||
self_only: typing.Union[bool, bool] = False,
|
self_only: typing.Union[bool, bool] = False,
|
||||||
no_channel: typing.Union[bool, bool] = False,
|
no_channel: typing.Union[bool, bool] = False,
|
||||||
handler: typing.Optional[list] = None,
|
handler: typing.Optional[list] = None,
|
||||||
filter: typing.Union[pyrogram.filters.Filter, pyrogram.filters.Filter] = None,
|
filtercmd: typing.Union[pyrogram.filters.Filter] = None,
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
### `tgClient.command`
|
### `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'))`
|
- 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:
|
- Parameters:
|
||||||
- command (str || list):
|
- cmd (str || list):
|
||||||
- The command to be handled for a function
|
- The command to be handled for a function
|
||||||
|
|
||||||
- group_only (bool) **optional**:
|
- group_only (bool) **optional**:
|
||||||
- If True, the command will only executed in Groups only, By Default False.
|
- If True, the command will only executed in Groups only, By Default False.
|
||||||
|
|
||||||
- pm_only (bool) **optional**:
|
- pm_only (bool) **optional**:
|
||||||
- If True, the command will only executed in Private Messages only, By Default False.
|
- If True, the command will only executed in Private Messages only, By Default False.
|
||||||
|
|
||||||
- self_only (bool) **optional**:
|
- self_only (bool) **optional**:
|
||||||
- If True, the command will only excute if used by Self only, By Default False.
|
- If True, the command will only excute if used by Self only, By Default False.
|
||||||
|
|
||||||
- handler (list) **optional**:
|
- handler (list) **optional**:
|
||||||
- If set, the command will be handled by the specified Handler, By Default `Config.HANDLERS`.
|
- If set, the command will be handled by the specified Handler, By Default `Config.HANDLERS`.
|
||||||
|
|
||||||
- self_admin (bool) **optional**:
|
- self_admin (bool) **optional**:
|
||||||
- If True, the command will only executeed if the Bot is Admin in the Chat, By Default False
|
- If True, the command will only executeed if the Bot is Admin in the Chat, By Default False
|
||||||
|
|
||||||
- filter (`~pyrogram.filters`) **optional**:
|
- filtercmd (`~pyrogram.filters`) **optional**:
|
||||||
- Pyrogram Filters, hope you know about this, for Advaced usage. Use `and` for seaperating filters.
|
- Pyrogram Filters, hope you know about this, for Advaced usage. Use `and` for seaperating filters.
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
import pyrogram
|
import pyrogram
|
||||||
|
|
||||||
app = pyrogram.Client()
|
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)
|
@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):
|
async def start(client, message):
|
||||||
await message.reply_text(f"Hello {message.from_user.mention}")
|
await message.reply_text(f"Hello {message.from_user.mention}")
|
||||||
"""
|
"""
|
||||||
if handler is None:
|
if handler is None:
|
||||||
handler = COMMAND_HANDLER
|
handler = COMMAND_HANDLER
|
||||||
if filter:
|
if filtercmd:
|
||||||
if self_only:
|
if self_only:
|
||||||
filter = (
|
filtercmd = (
|
||||||
pyrogram.filters.command(command, prefixes=handler)
|
pyrogram.filters.command(cmd, prefixes=handler)
|
||||||
& filter
|
& filtercmd
|
||||||
& pyrogram.filters.me
|
& 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
|
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
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
Decorators.on_cmd = command
|
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 typing import Union
|
||||||
|
|
||||||
from pyrogram import Client
|
from pyrogram import Client
|
||||||
|
from pyrogram.types import Message
|
||||||
|
|
||||||
|
|
||||||
async def edit_message_text(
|
async def edit_message_text(
|
||||||
|
|
@ -11,7 +12,7 @@ async def edit_message_text(
|
||||||
text: str,
|
text: str,
|
||||||
del_in: int = 0,
|
del_in: int = 0,
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
) -> Union["Message", bool]:
|
) -> Union["Message", bool]:
|
||||||
"""\nExample:
|
"""\nExample:
|
||||||
message.edit_text("hello")
|
message.edit_text("hello")
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ async def send_as_file(
|
||||||
text: str,
|
text: str,
|
||||||
filename: str = "output.txt",
|
filename: str = "output.txt",
|
||||||
caption: str = "",
|
caption: str = "",
|
||||||
log: Union[bool, str] = False,
|
|
||||||
reply_to_message_id: Optional[int] = None,
|
reply_to_message_id: Optional[int] = None,
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""\nYou can send large outputs as file
|
"""\nYou can send large outputs as file
|
||||||
|
|
@ -29,10 +28,6 @@ async def send_as_file(
|
||||||
file_name for output file.
|
file_name for output file.
|
||||||
caption (``str``, *optional*):
|
caption (``str``, *optional*):
|
||||||
caption for output file.
|
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*):
|
reply_to_message_id (``int``, *optional*):
|
||||||
If the message is a reply, ID of the original message.
|
If the message is a reply, ID of the original message.
|
||||||
Returns:
|
Returns:
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -151,4 +152,7 @@ async def is_admin(
|
||||||
user = await client.get_chat_member(chat_id, user_id)
|
user = await client.get_chat_member(chat_id, user_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
return user.status in (pyrogram.enums.ChatMemberStatus.OWNER, pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,)
|
return user.status in (
|
||||||
|
pyrogram.enums.ChatMemberStatus.OWNER,
|
||||||
|
pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import pyrogram
|
||||||
|
|
||||||
|
|
||||||
async def get_user(
|
async def get_user(
|
||||||
m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery]
|
m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery],
|
||||||
) -> pyrogram.types.User or bool:
|
) -> pyrogram.types.User or bool:
|
||||||
"""
|
"""
|
||||||
### `tgEasy.get_user`
|
### `tgEasy.get_user`
|
||||||
|
|
@ -75,7 +75,7 @@ async def get_user(
|
||||||
|
|
||||||
|
|
||||||
async def get_user_adv(
|
async def get_user_adv(
|
||||||
m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery]
|
m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery],
|
||||||
) -> pyrogram.types.User or bool:
|
) -> pyrogram.types.User or bool:
|
||||||
"""
|
"""
|
||||||
### `tgEasy.get_user_adv`
|
### `tgEasy.get_user_adv`
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,21 @@
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
# along with tgEasy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
import contextlib
|
import contextlib
|
||||||
import os
|
|
||||||
import typing
|
|
||||||
import logging
|
import logging
|
||||||
import pyrogram
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
|
import typing
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
import pyrogram
|
||||||
|
|
||||||
from misskaty.vars import LOG_CHANNEL
|
from misskaty.vars import LOG_CHANNEL
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger("MissKaty")
|
||||||
|
|
||||||
|
|
||||||
async def handle_error(
|
async def handle_error(
|
||||||
error, m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery]
|
_, m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery]
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
### `handle_error`
|
### `handle_error`
|
||||||
|
|
@ -41,46 +45,56 @@ async def handle_error(
|
||||||
- m (`pyrogram.types.Message` or `pyrogram.types.CallbackQuery`):
|
- m (`pyrogram.types.Message` or `pyrogram.types.CallbackQuery`):
|
||||||
- The Message or Callback Query where the Error occurred.
|
- 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()
|
day = datetime.now()
|
||||||
tgl_now = datetime.now()
|
tgl_now = datetime.now()
|
||||||
cap_day = f"{day.strftime('%A')}, {tgl_now.strftime('%d %B %Y %H:%M:%S')}"
|
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"
|
||||||
with open(f"crash_{tgl_now.strftime('%d %B %Y')}.txt", "w+", encoding="utf-8") as log:
|
LOGGER.error(traceback.format_exc())
|
||||||
log.write(traceback.format_exc())
|
|
||||||
log.close()
|
|
||||||
if isinstance(m, pyrogram.types.Message):
|
if isinstance(m, pyrogram.types.Message):
|
||||||
with contextlib.suppress(Exception):
|
with open(f_errname, "w+", encoding="utf-8") as log:
|
||||||
await m.reply_text(
|
log.write(
|
||||||
"An Internal Error Occurred while Processing your Command, the Logs have been sent to the Owners of this Bot. Sorry for Inconvenience"
|
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(
|
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}"
|
LOG_CHANNEL,
|
||||||
|
f_errname,
|
||||||
|
caption=f"Crash Report of this Bot\n{cap_day}",
|
||||||
)
|
)
|
||||||
if isinstance(m, pyrogram.types.CallbackQuery):
|
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):
|
with contextlib.suppress(Exception):
|
||||||
await m.message.delete()
|
await m.message.delete()
|
||||||
await m.message.reply_text(
|
try:
|
||||||
"An Internal Error Occurred while Processing your Command, the Logs have been sent to the Owners of this Bot. Sorry for Inconvenience"
|
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(
|
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}"
|
LOG_CHANNEL,
|
||||||
|
f_errname,
|
||||||
|
caption=f"Crash Report of this Bot\n{cap_day}",
|
||||||
)
|
)
|
||||||
os.remove(f"crash_{tgl_now.strftime('%d %B %Y')}.txt")
|
if os.path.exists(f_errname):
|
||||||
|
os.remove(f_errname)
|
||||||
return True
|
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
|
You should have received a copy of the GNU General Public License
|
||||||
along with pyromod. If not, see <https://www.gnu.org/licenses/>.
|
along with pyromod. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from contextlib import asynccontextmanager, contextmanager
|
from contextlib import asynccontextmanager, contextmanager
|
||||||
from inspect import iscoroutinefunction
|
from inspect import iscoroutinefunction
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
@ -24,7 +25,7 @@ from typing import Callable
|
||||||
|
|
||||||
from pyrogram.sync import async_to_sync
|
from pyrogram.sync import async_to_sync
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger("MissKaty")
|
||||||
|
|
||||||
|
|
||||||
class PyromodConfig:
|
class PyromodConfig:
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,35 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from pyrogram import filters
|
from pyrogram import filters
|
||||||
|
from pyrogram.errors import MessageDeleteForbidden
|
||||||
|
|
||||||
|
from misskaty.vars import SUDO, OWNER_ID
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
|
|
||||||
async def task(msg, warn=False, sec=None):
|
async def task(msg, warn=False, sec=None):
|
||||||
try:
|
|
||||||
await msg.delete()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
if warn:
|
if warn:
|
||||||
user = msg.from_user
|
user = msg.from_user or msg.sender_chat
|
||||||
ids = await msg.reply_msg(
|
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 asyncio.sleep(sec)
|
||||||
await ids.edit_msg(
|
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,
|
del_in=3,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def wait(sec):
|
def wait(sec):
|
||||||
async def ___(flt, cli, msg):
|
async def ___(flt, _, msg):
|
||||||
user_id = msg.from_user.id
|
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 user_id in data:
|
||||||
if msg.date.timestamp() >= data[user_id]["timestamp"] + flt.data:
|
if msg.date.timestamp() >= data[user_id]["timestamp"] + flt.data:
|
||||||
data[user_id] = {"timestamp": msg.date.timestamp(), "warned": False}
|
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 .http import *
|
||||||
from .human_read import *
|
from .human_read import *
|
||||||
from .kuso_utils import *
|
from .kuso_utils import *
|
||||||
|
from .localization import *
|
||||||
from .media_helper import *
|
from .media_helper import *
|
||||||
from .misc import *
|
from .misc import *
|
||||||
from .pyro_progress import *
|
from .pyro_progress import *
|
||||||
|
from .sqlite_helper import Cache
|
||||||
from .stickerset import *
|
from .stickerset import *
|
||||||
from .subscene_helper import *
|
from .subscene_helper import *
|
||||||
from .time_gap import *
|
from .time_gap import *
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ def hhmmss(seconds):
|
||||||
|
|
||||||
|
|
||||||
async def take_ss(video_file):
|
async def take_ss(video_file):
|
||||||
out_put_file_name = f"genss{str(time.time())}.png"
|
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}"
|
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)
|
await shell_exec(cmd)
|
||||||
return out_put_file_name if os.path.lexists(out_put_file_name) else None
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ from re import sub as re_sub
|
||||||
from string import ascii_lowercase
|
from string import ascii_lowercase
|
||||||
|
|
||||||
from pyrogram import enums
|
from pyrogram import enums
|
||||||
|
from pyrogram.types import Message
|
||||||
|
|
||||||
from misskaty import app
|
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)]
|
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:
|
async def alpha_to_int(user_id_alphabet: str) -> int:
|
||||||
alphabet = list(ascii_lowercase)[:10]
|
alphabet = list(ascii_lowercase)[:10]
|
||||||
user_id = ""
|
user_id = ""
|
||||||
|
|
@ -94,7 +111,7 @@ async def extract_user(message):
|
||||||
return (await extract_user_and_reason(message))[0]
|
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
|
unit = ["m", "h", "d"] # m == minutes | h == hours | d == days
|
||||||
check_unit = "".join(list(filter(time_value[-1].lower().endswith, unit)))
|
check_unit = "".join(list(filter(time_value[-1].lower().endswith, unit)))
|
||||||
currunt_time = datetime.now()
|
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))
|
temp_time = currunt_time + timedelta(days=int(time_digit))
|
||||||
else:
|
else:
|
||||||
return await message.reply_text("Incorrect time specified.")
|
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):
|
def extract_text_and_keyb(ikb, text: str, row_width: int = 2):
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,41 @@
|
||||||
from asyncio import gather
|
from asyncio import gather
|
||||||
|
from httpx import AsyncClient, Timeout
|
||||||
import httpx
|
|
||||||
from aiohttp import ClientSession
|
|
||||||
|
|
||||||
# Aiohttp Async Client
|
|
||||||
session = ClientSession()
|
|
||||||
|
|
||||||
# HTTPx Async Client
|
# HTTPx Async Client
|
||||||
http = httpx.AsyncClient(
|
fetch = AsyncClient(
|
||||||
http2=True,
|
|
||||||
verify=False,
|
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 def get(url: str, *args, **kwargs):
|
||||||
async with session.get(url, *args, **kwargs) as resp:
|
try:
|
||||||
try:
|
resp = await fetch.get(url, *args, **kwargs)
|
||||||
data = await resp.json()
|
data = await resp.json()
|
||||||
except Exception:
|
except Exception:
|
||||||
data = await resp.text()
|
data = await resp.text()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
async def head(url: str, *args, **kwargs):
|
async def head(url: str, *args, **kwargs):
|
||||||
async with session.head(url, *args, **kwargs) as resp:
|
try:
|
||||||
try:
|
resp = await fetch.head(url, *args, **kwargs)
|
||||||
data = await resp.json()
|
data = await resp.json()
|
||||||
except Exception:
|
except Exception:
|
||||||
data = await resp.text()
|
data = await resp.text()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
async def post(url: str, *args, **kwargs):
|
async def post(url: str, *args, **kwargs):
|
||||||
async with session.post(url, *args, **kwargs) as resp:
|
try:
|
||||||
try:
|
resp = await fetch.post(url, *args, **kwargs)
|
||||||
data = await resp.json()
|
data = await resp.json()
|
||||||
except Exception:
|
except Exception:
|
||||||
data = await resp.text()
|
data = await resp.text()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -54,8 +52,8 @@ async def multipost(url: str, times: int, *args, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
async def resp_get(url: str, *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):
|
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:
|
if size_in_bytes is None:
|
||||||
return "0B"
|
return "0B"
|
||||||
index = 0
|
index = 0
|
||||||
while size_in_bytes >= 1024:
|
while size_in_bytes >= 1024 and index < len(SIZE_UNITS) - 1:
|
||||||
size_in_bytes /= 1024
|
size_in_bytes /= 1024
|
||||||
index += 1
|
index += 1
|
||||||
try:
|
return (
|
||||||
return f"{round(size_in_bytes, 2)}{SIZE_UNITS[index]}"
|
f"{size_in_bytes:.2f} {SIZE_UNITS[index]}" if index > 0 else f"{size_in_bytes}B"
|
||||||
except IndexError:
|
)
|
||||||
return "File too large"
|
|
||||||
|
|
||||||
|
|
||||||
def get_readable_time(seconds: int) -> str:
|
def get_readable_time(seconds: int) -> str:
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,29 @@
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
from html import escape
|
from html import escape
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import chevron
|
import chevron
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from telegraph.aio import Telegraph
|
from telegraph.aio import Telegraph
|
||||||
|
|
||||||
from misskaty import BOT_USERNAME
|
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__)
|
LOGGER = logging.getLogger("MissKaty")
|
||||||
|
|
||||||
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",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def kusonimeBypass(url: str, slug=None):
|
async def kusonimeBypass(url: str):
|
||||||
result = {}
|
result = {}
|
||||||
_url = url
|
page = await fetch.get(url)
|
||||||
if slug:
|
if page.status_code != 200:
|
||||||
noslug_url = "https://kusonime.com/{slug}"
|
raise Exception("ERROR: Hostname might be blocked by server!")
|
||||||
_url = noslug_url.format({"slug": slug})
|
|
||||||
try:
|
try:
|
||||||
page = await http.get(_url, headers=headers)
|
|
||||||
soup = BeautifulSoup(page.text, "lxml")
|
soup = BeautifulSoup(page.text, "lxml")
|
||||||
thumb = soup.find("div", {"class": "post-thumb"}).find("img").get("src")
|
thumb = soup.find("div", {"class": "post-thumb"}).find("img").get("src")
|
||||||
data = []
|
data = []
|
||||||
|
# title = soup.select("#venkonten > div.vezone > div.venser > div.venutama > div.lexot > p:nth-child(3) > strong")[0].text.strip()
|
||||||
try:
|
try:
|
||||||
title = soup.find("h1", {"class": "jdlz"}).text # fix title njing haha
|
title = soup.find("h1", {"class": "jdlz"}).text # fix title njing haha
|
||||||
season = (
|
season = (
|
||||||
|
|
@ -99,20 +95,32 @@ async def kusonimeBypass(url: str, slug=None):
|
||||||
0,
|
0,
|
||||||
"None",
|
"None",
|
||||||
)
|
)
|
||||||
|
num = 1
|
||||||
genre = []
|
genre = []
|
||||||
for _genre in soup.select(
|
for _genre in soup.select(
|
||||||
"#venkonten > div.vezone > div.venser > div.venutama > div.lexot > div.info > p:nth-child(2)"
|
"#venkonten > div.vezone > div.venser > div.venutama > div.lexot > div.info > p:nth-child(2)"
|
||||||
):
|
):
|
||||||
gen = _genre.text.split(":").pop().strip().split(", ")
|
gen = _genre.text.split(":").pop().strip().split(", ")
|
||||||
genre = gen
|
genre = gen
|
||||||
for num, smokedl in enumerate(
|
for smokedl in soup.find("div", {"class": "dlbodz"}).find_all(
|
||||||
soup.find("div", {"class": "dlbodz"}).find_all(
|
"div", {"class": "smokeddlrh"}
|
||||||
"div", {"class": "smokeddlrh"}
|
|
||||||
),
|
|
||||||
start=1,
|
|
||||||
):
|
):
|
||||||
|
if not smokedl:
|
||||||
|
continue
|
||||||
mendata = {"name": title, "links": []}
|
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"}):
|
for smokeurl in smokedl.find_all("div", {"class": "smokeurlrh"}):
|
||||||
|
if not smokeurl:
|
||||||
|
continue
|
||||||
quality = smokeurl.find("strong").text
|
quality = smokeurl.find("strong").text
|
||||||
links = []
|
links = []
|
||||||
for link in smokeurl.find_all("a"):
|
for link in smokeurl.find_all("a"):
|
||||||
|
|
@ -121,45 +129,77 @@ async def kusonimeBypass(url: str, slug=None):
|
||||||
links.append({"client": client, "url": url})
|
links.append({"client": client, "url": url})
|
||||||
mendata["links"].append({"quality": quality, "link_download": links})
|
mendata["links"].append({"quality": quality, "link_download": links})
|
||||||
data.append(mendata)
|
data.append(mendata)
|
||||||
result |= {
|
num += 1
|
||||||
"error": False,
|
for smokedl in soup.find("div", {"class": "dlbodz"}).find_all(
|
||||||
"title": title,
|
"div", {"class": "smokeddl"}
|
||||||
"thumb": thumb,
|
):
|
||||||
"genre": genre,
|
if not smokedl:
|
||||||
"genre_string": ", ".join(genre),
|
continue
|
||||||
"status_anime": status_anime,
|
mendata = {"name": title, "links": []}
|
||||||
"season": season,
|
for smokeurl in smokedl.find_all("div", {"class": "smokeurl"}):
|
||||||
"tipe": tipe,
|
if not smokeurl:
|
||||||
"ep": ep,
|
continue
|
||||||
"score": score,
|
quality = smokeurl.find("strong").text
|
||||||
"duration": duration,
|
links = []
|
||||||
"rilis": rilis,
|
for link in smokeurl.find_all("a"):
|
||||||
"data": data,
|
url = link.get("href")
|
||||||
}
|
client = link.text
|
||||||
except Exception:
|
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()
|
err = traceback.format_exc()
|
||||||
LOGGER.error(err)
|
LOGGER.error(f"class: {e.__class__.__name_}, {err}")
|
||||||
result |= {"error": True, "error_message": err}
|
raise Exception(f"ERROR: {err}")
|
||||||
await http.delete(_url)
|
finally:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
async def byPassPh(url: str, name: str):
|
async def byPassPh(url: str, name: str) -> Optional[str]:
|
||||||
kusonime = await kusonimeBypass(url)
|
kusonime = await kusonimeBypass(url)
|
||||||
results = {"error": True, "error_message": kusonime}
|
if not isinstance(kusonime, dict):
|
||||||
if not kusonime["error"]:
|
return kusonime
|
||||||
template = """
|
template = """
|
||||||
<img src={{{thumb}}}>
|
<img src={{{thumb}}}>
|
||||||
|
|
||||||
<p><b>Title</b> : <code>{{title}}</code></p>
|
<p><b>Title</b> : <code>{{title}}</code></p>
|
||||||
<p><b>Genre</b> : <code>{{genre_string}}</code></p>
|
<p><b>Genre</b> : <code>{{genre_string}}</code></p>
|
||||||
<br><br><p><b>Season</b> : <code>{{season}}</code></p>
|
<br><p><b>Season</b> : <code>{{season}}</code></p>
|
||||||
<br><br><p><b>Type</b> : <code>{{tipe}}</code></p>
|
<br><p><b>Type</b> : <code>{{tipe}}</code></p>
|
||||||
<br><br><p><b>Status</b> : <code>{{status_anime}}</code></p>
|
<br><p><b>Status</b> : <code>{{status_anime}}</code></p>
|
||||||
<br><br><p><b>Total Episode</b> : <code>{{ep}}</code></p>
|
<br><p><b>Total Episode</b> : <code>{{ep}}</code></p>
|
||||||
<br><br><p><b>Score</b> : <code>{{score}}</code></p>
|
<br><p><b>Score</b> : <code>{{score}}</code></p>
|
||||||
<br><br><p><b>Duration</b> : <code>{{duration}}</code></p>
|
<br><p><b>Duration</b> : <code>{{duration}}</code></p>
|
||||||
<br><br><p><b>Released on</b> : <code>{{rilis}}</code></p>
|
<br><p><b>Released on</b> : <code>{{rilis}}</code></p>
|
||||||
<br><br>
|
<br><br>
|
||||||
{{#data}}
|
{{#data}}
|
||||||
<h4>{{name}}</h4>
|
<h4>{{name}}</h4>
|
||||||
|
|
@ -172,20 +212,15 @@ async def byPassPh(url: str, name: str):
|
||||||
<br>
|
<br>
|
||||||
{{/data}}
|
{{/data}}
|
||||||
""".strip()
|
""".strip()
|
||||||
html = chevron.render(template, kusonime)
|
return await post_to_telegraph(
|
||||||
telegraph = Telegraph()
|
False,
|
||||||
if not telegraph.get_access_token():
|
f"{kusonime.get('title')} By {escape(name)}",
|
||||||
await telegraph.create_account(short_name=BOT_USERNAME)
|
chevron.render(template, kusonime),
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class Kusonime:
|
class Kusonime:
|
||||||
def __init__(self):
|
def __init__(self): # skipcq
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from glob import glob
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from pyrogram.enums import ChatType
|
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
|
from database.locale_db import get_db_lang
|
||||||
|
|
||||||
|
|
@ -15,6 +15,7 @@ enabled_locales: List[str] = [
|
||||||
"en-US", # English (United States)
|
"en-US", # English (United States)
|
||||||
"id-ID", # Indonesian
|
"id-ID", # Indonesian
|
||||||
"id-JW", # Javanese
|
"id-JW", # Javanese
|
||||||
|
"ru-RU", # Russian
|
||||||
]
|
]
|
||||||
|
|
||||||
default_language: str = "en-US"
|
default_language: str = "en-US"
|
||||||
|
|
@ -54,7 +55,7 @@ def get_locale_string(
|
||||||
async def get_lang(message) -> str:
|
async def get_lang(message) -> str:
|
||||||
if isinstance(message, CallbackQuery):
|
if isinstance(message, CallbackQuery):
|
||||||
chat = message.message.chat
|
chat = message.message.chat
|
||||||
elif isinstance(message, Message):
|
elif isinstance(message, (Message, ChatMemberUpdated)):
|
||||||
chat = message.chat
|
chat = message.chat
|
||||||
elif isinstance(message, InlineQuery):
|
elif isinstance(message, InlineQuery):
|
||||||
chat, chat.type = message.from_user, ChatType.PRIVATE
|
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:
|
if telegraph.get_access_token() is None:
|
||||||
await telegraph.create_account(short_name=BOT_USERNAME)
|
await telegraph.create_account(short_name=BOT_USERNAME)
|
||||||
if is_media:
|
if is_media:
|
||||||
"""Create a Telegram Post Foto/Video"""
|
# Create a Telegram Post Foto/Video
|
||||||
response = await telegraph.upload_file(media)
|
response = await telegraph.upload_file(media)
|
||||||
return f"https://telegra.ph{response[0]['src']}"
|
return f"https://img.yasirweb.eu.org{response[0]['src']}"
|
||||||
"""Create a Telegram Post using HTML Content"""
|
# Create a Telegram Post using HTML Content
|
||||||
response = await telegraph.create_page(
|
response = await telegraph.create_page(
|
||||||
title,
|
title,
|
||||||
html_content=content,
|
html_content=content,
|
||||||
author_url=f"https://t.me/{BOT_USERNAME}",
|
author_url=f"https://t.me/{BOT_USERNAME}",
|
||||||
author_name=BOT_USERNAME,
|
author_name=BOT_USERNAME,
|
||||||
)
|
)
|
||||||
return response["url"]
|
return f"https://te.legra.ph/{response['path']}"
|
||||||
|
|
||||||
|
|
||||||
async def run_subprocess(cmd):
|
async def run_subprocess(cmd):
|
||||||
|
|
@ -49,7 +49,7 @@ async def get_media_info(file_link):
|
||||||
"-show_chapters",
|
"-show_chapters",
|
||||||
"-show_programs",
|
"-show_programs",
|
||||||
]
|
]
|
||||||
data, err = await run_subprocess(ffprobe_cmd)
|
data, _ = await run_subprocess(ffprobe_cmd)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,155 +1,7 @@
|
||||||
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 json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .http import http
|
from .http import fetch
|
||||||
|
|
||||||
|
|
||||||
def html_builder(title: str, text: str) -> str:
|
def html_builder(title: str, text: str) -> str:
|
||||||
|
|
@ -214,13 +66,13 @@ def html_builder(title: str, text: str) -> str:
|
||||||
html_msg += "</span>"
|
html_msg += "</span>"
|
||||||
|
|
||||||
html_msg += "</span>"
|
html_msg += "</span>"
|
||||||
return css + html_msg
|
return html_msg
|
||||||
|
|
||||||
|
|
||||||
async def mediainfo_paste(text: str, title: str) -> str:
|
async def mediainfo_paste(text: str, title: str) -> str:
|
||||||
html_content = html_builder(title, text)
|
html_content = html_builder(title, text)
|
||||||
URL = "https://mediainfo-1-y5870653.deta.app/api"
|
URL = "https://yasirr.eu.org/mediainfo"
|
||||||
response = await http.post(URL, json={"content": html_content})
|
response = await fetch.post(URL, data={"content": html_content})
|
||||||
return (
|
return (
|
||||||
f"https://mediainfo-1-y5870653.deta.app/{json.loads(response.content)['key']}"
|
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
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from pyrogram import Client, errors, raw
|
from pyrogram import Client, errors, raw
|
||||||
|
|
@ -32,7 +33,7 @@ async def get_sticker_set_by_name(
|
||||||
hash=0,
|
hash=0,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except errors.exceptions.not_acceptable_406.StickersetInvalid:
|
except errors.exceptions.bad_request_400.StickersetInvalid:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,26 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
|
import re
|
||||||
import string
|
import string
|
||||||
import time
|
import time
|
||||||
from http.cookies import SimpleCookie
|
from http.cookies import SimpleCookie
|
||||||
from re import match as re_match
|
from re import match as re_match
|
||||||
|
from googletrans import Translator
|
||||||
|
from typing import Union
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
from misskaty import BOT_NAME, UBOT_NAME, botStartTime
|
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.helper.human_read import get_readable_time
|
||||||
from misskaty.plugins import ALL_MODULES
|
from misskaty.plugins import ALL_MODULES
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger("MissKaty")
|
||||||
URL_REGEX = r"(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])"
|
URL_REGEX = r"(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])"
|
||||||
GENRES_EMOJI = {
|
GENRES_EMOJI = {
|
||||||
"Action": "👊",
|
"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):
|
def is_url(url):
|
||||||
url = re_match(URL_REGEX, url)
|
url = re_match(URL_REGEX, url)
|
||||||
return bool(url)
|
return bool(url)
|
||||||
|
|
@ -87,7 +99,7 @@ def get_random_string(length: int = 5):
|
||||||
async def rentry(teks):
|
async def rentry(teks):
|
||||||
# buat dapetin cookie
|
# buat dapetin cookie
|
||||||
cookie = SimpleCookie()
|
cookie = SimpleCookie()
|
||||||
kuki = (await http.get("https://rentry.co")).cookies
|
kuki = (await fetch.get("https://rentry.co")).cookies
|
||||||
cookie.load(kuki)
|
cookie.load(kuki)
|
||||||
kukidict = {key: value.value for key, value in cookie.items()}
|
kukidict = {key: value.value for key, value in cookie.items()}
|
||||||
# headernya
|
# headernya
|
||||||
|
|
@ -95,7 +107,7 @@ async def rentry(teks):
|
||||||
payload = {"csrfmiddlewaretoken": kukidict["csrftoken"], "text": teks}
|
payload = {"csrfmiddlewaretoken": kukidict["csrftoken"], "text": teks}
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
await http.post(
|
await fetch.post(
|
||||||
"https://rentry.co/api/new",
|
"https://rentry.co/api/new",
|
||||||
data=payload,
|
data=payload,
|
||||||
headers=header,
|
headers=header,
|
||||||
|
|
@ -124,11 +136,11 @@ def get_provider(url):
|
||||||
return pretty(netloc.split("."))
|
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_ = ""
|
m_t_ = ""
|
||||||
try:
|
try:
|
||||||
response = (
|
response = (
|
||||||
await http.get(
|
await fetch.get(
|
||||||
f"https://yasirapi.eu.org/justwatch?q={movie_name}&locale={locale}"
|
f"https://yasirapi.eu.org/justwatch?q={movie_name}&locale={locale}"
|
||||||
)
|
)
|
||||||
).json()
|
).json()
|
||||||
|
|
@ -137,17 +149,73 @@ async def search_jw(movie_name: str, locale: str):
|
||||||
if not response.get("results"):
|
if not response.get("results"):
|
||||||
LOGGER.error("JustWatch API Error or got Rate Limited.")
|
LOGGER.error("JustWatch API Error or got Rate Limited.")
|
||||||
return m_t_
|
return m_t_
|
||||||
for item in response.get("results")["items"]:
|
for item in response["results"]["data"]["popularTitles"]["edges"]:
|
||||||
if movie_name == item.get("title", ""):
|
if item["node"]["content"]["title"] == movie_name:
|
||||||
offers = item.get("offers", [])
|
|
||||||
t_m_ = []
|
t_m_ = []
|
||||||
for offer in offers:
|
for offer in item["node"].get("offers", []):
|
||||||
url = offer.get("urls").get("standard_web")
|
url = offer["standardWebURL"]
|
||||||
if url not in t_m_:
|
if url not in t_m_:
|
||||||
p_o = get_provider(url)
|
p_o = get_provider(url)
|
||||||
m_t_ += f"<a href='{url}'>{p_o}</a> | "
|
m_t_ += f"<a href='{url}'>{p_o}</a> | "
|
||||||
t_m_.append(url)
|
t_m_.append(url)
|
||||||
if m_t_ != "":
|
if m_t_ != "":
|
||||||
m_t_ = m_t_[:-2].strip()
|
m_t_ = m_t_[:-2].strip()
|
||||||
break
|
break
|
||||||
return m_t_
|
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>
|
* @author yasir <yasiramunandar@gmail.com>
|
||||||
* @date 2022-12-01 09:12:27
|
* @date 2022-12-01 09:12:27
|
||||||
* @projectName MissKatyPyro
|
* @projectName MissKatyPyro
|
||||||
* Copyright ©YasirPedia All rights reserved
|
* Copyright ©YasirPedia All rights reserved
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
import importlib
|
import importlib
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -12,7 +13,7 @@ from os.path import basename, dirname, isfile
|
||||||
|
|
||||||
from misskaty import MOD_LOAD, MOD_NOLOAD
|
from misskaty import MOD_LOAD, MOD_NOLOAD
|
||||||
|
|
||||||
LOGGER = getLogger(__name__)
|
LOGGER = getLogger("MissKaty")
|
||||||
|
|
||||||
|
|
||||||
def __list_all_modules():
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
@ -28,20 +29,22 @@ from logging import getLogger
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from pyrogram import Client, enums, filters
|
from pyrogram import Client, enums, filters
|
||||||
from pyrogram.errors import ChatAdminRequired, FloodWait
|
from pyrogram.errors import (
|
||||||
from pyrogram.types import ChatPermissions, ChatPrivileges, Message
|
ChatAdminRequired,
|
||||||
|
FloodWait,
|
||||||
|
PeerIdInvalid,
|
||||||
|
UsernameNotOccupied,
|
||||||
|
)
|
||||||
|
from pyrogram.types import ChatMember, ChatPermissions, ChatPrivileges, Message
|
||||||
|
|
||||||
from database.warn_db import add_warn, get_warn, remove_warns
|
from database.warn_db import add_warn, get_warn, remove_warns
|
||||||
from misskaty import app
|
from misskaty import app
|
||||||
from misskaty.core.decorator.errors import capture_err
|
from misskaty.core.decorator.errors import capture_err
|
||||||
from misskaty.core.decorator.permissions import (
|
from misskaty.core.decorator.permissions import (
|
||||||
admins_in_chat,
|
admins_in_chat,
|
||||||
adminsOnly,
|
|
||||||
list_admins,
|
list_admins,
|
||||||
member_permissions,
|
member_permissions,
|
||||||
require_admin,
|
|
||||||
)
|
)
|
||||||
from misskaty.core.decorator.ratelimiter import ratelimiter
|
|
||||||
from misskaty.core.keyboard import ikb
|
from misskaty.core.keyboard import ikb
|
||||||
from misskaty.helper.functions import (
|
from misskaty.helper.functions import (
|
||||||
extract_user,
|
extract_user,
|
||||||
|
|
@ -50,9 +53,9 @@ from misskaty.helper.functions import (
|
||||||
time_converter,
|
time_converter,
|
||||||
)
|
)
|
||||||
from misskaty.helper.localization import use_chat_lang
|
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"
|
__MODULE__ = "Admin"
|
||||||
__HELP__ = """
|
__HELP__ = """
|
||||||
|
|
@ -83,11 +86,12 @@ __HELP__ = """
|
||||||
/set_chat_title - Change The Name Of A Group/Channel.
|
/set_chat_title - Change The Name Of A Group/Channel.
|
||||||
/set_chat_photo - Change The PFP Of A Group/Channel.
|
/set_chat_photo - Change The PFP Of A Group/Channel.
|
||||||
/set_user_title - Change The Administrator Title Of An Admin.
|
/set_user_title - Change The Administrator Title Of An Admin.
|
||||||
|
/mentionall - Mention all members in a groups.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# Admin cache reload
|
# Admin cache reload
|
||||||
@app.on_chat_member_updated()
|
@app.on_chat_member_updated(filters.group, group=5)
|
||||||
async def admin_cache_func(_, cmu):
|
async def admin_cache_func(_, cmu):
|
||||||
if cmu.old_chat_member and cmu.old_chat_member.promoted_by:
|
if cmu.old_chat_member and cmu.old_chat_member.promoted_by:
|
||||||
try:
|
try:
|
||||||
|
|
@ -107,12 +111,9 @@ async def admin_cache_func(_, cmu):
|
||||||
|
|
||||||
# Purge CMD
|
# Purge CMD
|
||||||
@app.on_cmd("purge")
|
@app.on_cmd("purge")
|
||||||
@require_admin(permissions=["can_delete_messages"], allow_in_private=True)
|
@app.adminsOnly("can_delete_messages")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def purge(self: Client, ctx: Message, strings) -> "Message":
|
async def purge(_, ctx: Message, strings):
|
||||||
if not ctx.from_user:
|
|
||||||
return
|
|
||||||
try:
|
try:
|
||||||
repliedmsg = ctx.reply_to_message
|
repliedmsg = ctx.reply_to_message
|
||||||
await ctx.delete_msg()
|
await ctx.delete_msg()
|
||||||
|
|
@ -162,23 +163,23 @@ async def purge(self: Client, ctx: Message, strings) -> "Message":
|
||||||
|
|
||||||
|
|
||||||
# Kick members
|
# Kick members
|
||||||
@app.on_cmd(["kick", "dkick"], group_only=True)
|
@app.on_cmd(["kick", "dkick"], self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_restrict_members")
|
@app.adminsOnly("can_restrict_members")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def kickFunc(client: Client, ctx: Message, strings) -> "Message":
|
async def kickFunc(client: Client, ctx: Message, strings) -> "Message":
|
||||||
if not ctx.from_user:
|
|
||||||
return
|
|
||||||
user_id, reason = await extract_user_and_reason(ctx)
|
user_id, reason = await extract_user_and_reason(ctx)
|
||||||
if not user_id:
|
if not user_id:
|
||||||
return await ctx.reply_msg(strings("user_not_found"))
|
return await ctx.reply_msg(strings("user_not_found"))
|
||||||
if user_id == client.me.id:
|
if user_id == client.me.id:
|
||||||
return await ctx.reply_msg(strings("kick_self_err"))
|
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"))
|
return await ctx.reply_msg(strings("kick_sudo_err"))
|
||||||
if user_id in (await list_admins(ctx.chat.id)):
|
if user_id in (await list_admins(ctx.chat.id)):
|
||||||
return await ctx.reply_msg(strings("kick_admin_err"))
|
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(
|
msg = strings("kick_msg").format(
|
||||||
mention=user.mention,
|
mention=user.mention,
|
||||||
id=user.id,
|
id=user.id,
|
||||||
|
|
@ -194,29 +195,33 @@ async def kickFunc(client: Client, ctx: Message, strings) -> "Message":
|
||||||
await ctx.chat.unban_member(user_id)
|
await ctx.chat.unban_member(user_id)
|
||||||
except ChatAdminRequired:
|
except ChatAdminRequired:
|
||||||
await ctx.reply_msg(strings("no_ban_permission"))
|
await ctx.reply_msg(strings("no_ban_permission"))
|
||||||
|
except Exception as e:
|
||||||
|
await ctx.reply_msg(str(e))
|
||||||
|
|
||||||
|
|
||||||
# Ban/DBan/TBan User
|
# Ban/DBan/TBan User
|
||||||
@app.on_cmd(["ban", "dban", "tban"], group_only=True)
|
@app.on_cmd(["ban", "dban", "tban"], self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_restrict_members")
|
@app.adminsOnly("can_restrict_members")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def banFunc(client, message, strings):
|
async def banFunc(client, message, strings):
|
||||||
if not message.from_user:
|
try:
|
||||||
return
|
user_id, reason = await extract_user_and_reason(message, sender_chat=True)
|
||||||
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:
|
if not user_id:
|
||||||
return await message.reply_text(strings("user_not_found"))
|
return await message.reply_text(strings("user_not_found"))
|
||||||
if user_id == client.me.id:
|
if user_id == client.me.id:
|
||||||
return await message.reply_text(strings("ban_self_err"))
|
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"))
|
return await message.reply_text(strings("ban_sudo_err"))
|
||||||
if user_id in (await list_admins(message.chat.id)):
|
if user_id in (await list_admins(message.chat.id)):
|
||||||
return await message.reply_text(strings("ban_admin_err"))
|
return await message.reply_text(strings("ban_admin_err"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mention = (await app.get_users(user_id)).mention
|
mention = (await app.get_users(user_id)).mention
|
||||||
|
except PeerIdInvalid:
|
||||||
|
return await message.reply_text(strings("user_not_found"))
|
||||||
except IndexError:
|
except IndexError:
|
||||||
mention = (
|
mention = (
|
||||||
message.reply_to_message.sender_chat.title
|
message.reply_to_message.sender_chat.title
|
||||||
|
|
@ -253,19 +258,18 @@ async def banFunc(client, message, strings):
|
||||||
keyboard = ikb({"🚨 Unban 🚨": f"unban_{user_id}"})
|
keyboard = ikb({"🚨 Unban 🚨": f"unban_{user_id}"})
|
||||||
try:
|
try:
|
||||||
await message.chat.ban_member(user_id)
|
await message.chat.ban_member(user_id)
|
||||||
await message.reply_text(msg, reply_markup=keyboard)
|
await message.reply_msg(msg, reply_markup=keyboard)
|
||||||
except Exception as err:
|
except ChatAdminRequired:
|
||||||
await message.reply(f"ERROR: {err}")
|
await message.reply("Please give me permission to banned members..!!!")
|
||||||
|
except Exception as e:
|
||||||
|
await message.reply_msg(str(e))
|
||||||
|
|
||||||
|
|
||||||
# Unban members
|
# Unban members
|
||||||
@app.on_cmd("unban", group_only=True)
|
@app.on_cmd("unban", self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_restrict_members")
|
@app.adminsOnly("can_restrict_members")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def unban_func(self, message, strings):
|
async def unban_func(_, message, strings):
|
||||||
if not message.from_user:
|
|
||||||
return
|
|
||||||
# we don't need reasons for unban, also, we
|
# we don't need reasons for unban, also, we
|
||||||
# don't need to get "text_mention" entity, because
|
# don't need to get "text_mention" entity, because
|
||||||
# normal users won't get text_mention if the user
|
# 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:
|
if len(message.command) == 2:
|
||||||
user = message.text.split(None, 1)[1]
|
user = message.text.split(None, 1)[1]
|
||||||
if not user.startswith("@"):
|
|
||||||
user = int(user)
|
|
||||||
elif len(message.command) == 1 and reply:
|
elif len(message.command) == 1 and reply:
|
||||||
user = message.reply_to_message.from_user.id
|
user = message.reply_to_message.from_user.id
|
||||||
else:
|
else:
|
||||||
return await message.reply_text(strings("give_unban_user"))
|
return await message.reply_msg(strings("give_unban_user"))
|
||||||
await message.chat.unban_member(user)
|
try:
|
||||||
umention = (await app.get_users(user)).mention
|
await message.chat.unban_member(user)
|
||||||
await message.reply_text(strings("unban_success").format(umention=umention))
|
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
|
# Ban users listed in a message
|
||||||
@app.on_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()
|
@use_chat_lang()
|
||||||
async def list_ban_(c, message, strings):
|
async def list_ban_(c, message, strings):
|
||||||
if not message.from_user:
|
|
||||||
return
|
|
||||||
userid, msglink_reason = await extract_user_and_reason(message)
|
userid, msglink_reason = await extract_user_and_reason(message)
|
||||||
if not userid or not msglink_reason:
|
if not userid or not msglink_reason:
|
||||||
return await message.reply_text(strings("give_idban_with_msg_link"))
|
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:
|
if userid == c.me.id:
|
||||||
return await message.reply_text(strings("ban_self_err"))
|
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"))
|
return await message.reply_text(strings("ban_sudo_err"))
|
||||||
splitted = messagelink.split("/")
|
splitted = messagelink.split("/")
|
||||||
uname, mid = splitted[-2], int(splitted[-1])
|
uname, mid = splitted[-2], int(splitted[-1])
|
||||||
|
|
@ -347,13 +353,10 @@ async def list_ban_(c, message, strings):
|
||||||
|
|
||||||
# Unban users listed in a message
|
# Unban users listed in a message
|
||||||
@app.on_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()
|
@use_chat_lang()
|
||||||
async def list_unban_(c, message, strings):
|
async def list_unban(_, message, strings):
|
||||||
if not message.from_user:
|
|
||||||
return
|
|
||||||
userid, msglink = await extract_user_and_reason(message)
|
userid, msglink = await extract_user_and_reason(message)
|
||||||
if not userid or not msglink:
|
if not userid or not msglink:
|
||||||
return await message.reply_text(strings("give_idunban_with_msg_link"))
|
return await message.reply_text(strings("give_idunban_with_msg_link"))
|
||||||
|
|
@ -388,12 +391,9 @@ async def list_unban_(c, message, strings):
|
||||||
|
|
||||||
# Delete messages
|
# Delete messages
|
||||||
@app.on_cmd("del", group_only=True)
|
@app.on_cmd("del", group_only=True)
|
||||||
@adminsOnly("can_delete_messages")
|
@app.adminsOnly("can_delete_messages")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def deleteFunc(_, message, strings):
|
async def deleteFunc(_, message, strings):
|
||||||
if not message.from_user:
|
|
||||||
return
|
|
||||||
if not message.reply_to_message:
|
if not message.reply_to_message:
|
||||||
return await message.reply_text(strings("delete_no_reply"))
|
return await message.reply_text(strings("delete_no_reply"))
|
||||||
try:
|
try:
|
||||||
|
|
@ -404,99 +404,100 @@ async def deleteFunc(_, message, strings):
|
||||||
|
|
||||||
|
|
||||||
# Promote Members
|
# Promote Members
|
||||||
@app.on_cmd(["promote", "fullpromote"], group_only=True)
|
@app.on_cmd(["promote", "fullpromote"], self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_promote_members")
|
@app.adminsOnly("can_promote_members")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def promoteFunc(client, message, strings):
|
async def promoteFunc(client, message, strings):
|
||||||
if not message.from_user:
|
|
||||||
return
|
|
||||||
try:
|
try:
|
||||||
user_id = await extract_user(message)
|
user_id = await extract_user(message)
|
||||||
umention = (await app.get_users(user_id)).mention
|
umention = (await client.get_users(user_id)).mention
|
||||||
except:
|
except:
|
||||||
return await message.reply(strings("invalid_id_uname"))
|
return await message.reply(strings("invalid_id_uname"))
|
||||||
if not user_id:
|
if not user_id:
|
||||||
return await message.reply_text(strings("user_not_found"))
|
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:
|
if user_id == client.me.id:
|
||||||
return await message.reply_text(strings("promote_self_err"))
|
return await message.reply_msg(strings("promote_self_err"))
|
||||||
if not bot.privileges.can_promote_members:
|
if not bot:
|
||||||
return await message.reply_text(strings("no_promote_perm"))
|
return await message.reply_msg("I'm not an admin in this chat.")
|
||||||
if message.command[0][0] == "f":
|
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(
|
await message.chat.promote_member(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
privileges=ChatPrivileges(
|
privileges=ChatPrivileges(
|
||||||
can_change_info=bot.privileges.can_change_info,
|
can_change_info=False,
|
||||||
can_invite_users=bot.privileges.can_invite_users,
|
can_invite_users=bot.can_invite_users,
|
||||||
can_delete_messages=bot.privileges.can_delete_messages,
|
can_delete_messages=bot.can_delete_messages,
|
||||||
can_restrict_members=bot.privileges.can_restrict_members,
|
can_restrict_members=bot.can_restrict_members,
|
||||||
can_pin_messages=bot.privileges.can_pin_messages,
|
can_pin_messages=bot.can_pin_messages,
|
||||||
can_promote_members=bot.privileges.can_promote_members,
|
can_promote_members=False,
|
||||||
can_manage_chat=bot.privileges.can_manage_chat,
|
can_manage_chat=bot.can_manage_chat,
|
||||||
can_manage_video_chats=bot.privileges.can_manage_video_chats,
|
can_manage_video_chats=bot.can_manage_video_chats,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return await message.reply_text(
|
await message.reply_msg(strings("normal_promote").format(umention=umention))
|
||||||
strings("full_promote").format(umention=umention)
|
except Exception as err:
|
||||||
)
|
await message.reply_msg(err)
|
||||||
|
|
||||||
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))
|
|
||||||
|
|
||||||
|
|
||||||
# Demote Member
|
# Demote Member
|
||||||
@app.on_cmd("demote", group_only=True)
|
@app.on_cmd("demote", self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_restrict_members")
|
@app.adminsOnly("can_restrict_members")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def demote(client, message, strings):
|
async def demote(client, message, strings):
|
||||||
if not message.from_user:
|
|
||||||
return
|
|
||||||
user_id = await extract_user(message)
|
user_id = await extract_user(message)
|
||||||
if not user_id:
|
if not user_id:
|
||||||
return await message.reply_text(strings("user_not_found"))
|
return await message.reply_text(strings("user_not_found"))
|
||||||
if user_id == client.me.id:
|
if user_id == client.me.id:
|
||||||
return await message.reply_text(strings("demote_self_err"))
|
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"))
|
return await message.reply_text(strings("demote_sudo_err"))
|
||||||
await message.chat.promote_member(
|
try:
|
||||||
user_id=user_id,
|
await message.chat.promote_member(
|
||||||
privileges=ChatPrivileges(
|
user_id=user_id,
|
||||||
can_change_info=False,
|
privileges=ChatPrivileges(
|
||||||
can_invite_users=False,
|
can_change_info=False,
|
||||||
can_delete_messages=False,
|
can_invite_users=False,
|
||||||
can_restrict_members=False,
|
can_delete_messages=False,
|
||||||
can_pin_messages=False,
|
can_restrict_members=False,
|
||||||
can_promote_members=False,
|
can_pin_messages=False,
|
||||||
can_manage_chat=False,
|
can_promote_members=False,
|
||||||
can_manage_video_chats=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}")
|
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
|
# Pin Messages
|
||||||
@app.on_cmd(["pin", "unpin"])
|
@app.on_cmd(["pin", "unpin"])
|
||||||
@adminsOnly("can_pin_messages")
|
@app.adminsOnly("can_pin_messages")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def pin(_, message, strings):
|
async def pin(_, message, strings):
|
||||||
if not message.from_user:
|
|
||||||
return
|
|
||||||
if not message.reply_to_message:
|
if not message.reply_to_message:
|
||||||
return await message.reply_text(strings("pin_no_reply"))
|
return await message.reply_text(strings("pin_no_reply"))
|
||||||
r = message.reply_to_message
|
r = message.reply_to_message
|
||||||
|
|
@ -517,16 +518,15 @@ async def pin(_, message, strings):
|
||||||
strings("pin_no_perm"),
|
strings("pin_no_perm"),
|
||||||
disable_web_page_preview=True,
|
disable_web_page_preview=True,
|
||||||
)
|
)
|
||||||
|
except Exception as e:
|
||||||
|
await message.reply_msg(str(e))
|
||||||
|
|
||||||
|
|
||||||
# Mute members
|
# Mute members
|
||||||
@app.on_cmd(["mute", "tmute"], group_only=True)
|
@app.on_cmd(["mute", "tmute"], self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_restrict_members")
|
@app.adminsOnly("can_restrict_members")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def mute(client, message, strings):
|
async def mute(client, message, strings):
|
||||||
if not message.from_user:
|
|
||||||
return
|
|
||||||
try:
|
try:
|
||||||
user_id, reason = await extract_user_and_reason(message)
|
user_id, reason = await extract_user_and_reason(message)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
|
@ -535,7 +535,7 @@ async def mute(client, message, strings):
|
||||||
return await message.reply_text(strings("user_not_found"))
|
return await message.reply_text(strings("user_not_found"))
|
||||||
if user_id == client.me.id:
|
if user_id == client.me.id:
|
||||||
return await message.reply_text(strings("mute_self_err"))
|
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"))
|
return await message.reply_text(strings("mute_sudo_err"))
|
||||||
if user_id in (await list_admins(message.chat.id)):
|
if user_id in (await list_admins(message.chat.id)):
|
||||||
return await message.reply_text(strings("mute_admin_err"))
|
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:
|
if len(time_value[:-1]) < 3:
|
||||||
await message.chat.restrict_member(
|
await message.chat.restrict_member(
|
||||||
user_id,
|
user_id,
|
||||||
permissions=ChatPermissions(),
|
permissions=ChatPermissions(all_perms=False),
|
||||||
until_date=temp_mute,
|
until_date=temp_mute,
|
||||||
)
|
)
|
||||||
await message.reply_text(msg, reply_markup=keyboard)
|
await message.reply_text(msg, reply_markup=keyboard)
|
||||||
|
|
@ -568,40 +568,45 @@ async def mute(client, message, strings):
|
||||||
return
|
return
|
||||||
if reason:
|
if reason:
|
||||||
msg += strings("banned_reason").format(reas=reason)
|
msg += strings("banned_reason").format(reas=reason)
|
||||||
await message.chat.restrict_member(user_id, permissions=ChatPermissions())
|
try:
|
||||||
await message.reply_text(msg, reply_markup=keyboard)
|
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
|
# Unmute members
|
||||||
@app.on_cmd("unmute", group_only=True)
|
@app.on_cmd("unmute", self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_restrict_members")
|
@app.adminsOnly("can_restrict_members")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def unmute(_, message, strings):
|
async def unmute(_, message, strings):
|
||||||
if not message.from_user:
|
|
||||||
return
|
|
||||||
user_id = await extract_user(message)
|
user_id = await extract_user(message)
|
||||||
if not user_id:
|
if not user_id:
|
||||||
return await message.reply_text(strings("user_not_found"))
|
return await message.reply_text(strings("user_not_found"))
|
||||||
await message.chat.unban_member(user_id)
|
try:
|
||||||
umention = (await app.get_users(user_id)).mention
|
await message.chat.unban_member(user_id)
|
||||||
await message.reply_text(strings("unmute_msg").format(umention=umention))
|
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)
|
@app.on_cmd(["warn", "dwarn"], self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_restrict_members")
|
@app.adminsOnly("can_restrict_members")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def warn_user(client, message, strings):
|
async def warn_user(client, message, strings):
|
||||||
if not message.from_user:
|
try:
|
||||||
return
|
user_id, reason = await extract_user_and_reason(message)
|
||||||
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
|
chat_id = message.chat.id
|
||||||
if not user_id:
|
if not user_id:
|
||||||
return await message.reply_text(strings("user_not_found"))
|
return await message.reply_text(strings("user_not_found"))
|
||||||
if user_id == client.me.id:
|
if user_id == client.me.id:
|
||||||
return await message.reply_text(strings("warn_self_err"))
|
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"))
|
return await message.reply_text(strings("warn_sudo_err"))
|
||||||
if user_id in (await list_admins(chat_id)):
|
if user_id in (await list_admins(chat_id)):
|
||||||
return await message.reply_text(strings("warn_admin_err"))
|
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":
|
if message.command[0][0] == "d":
|
||||||
await message.reply_to_message.delete()
|
await message.reply_to_message.delete()
|
||||||
if warns >= 2:
|
if warns >= 2:
|
||||||
await message.chat.ban_member(user_id)
|
try:
|
||||||
await message.reply_text(strings("exceed_warn_msg").format(mention=mention))
|
await message.chat.ban_member(user_id)
|
||||||
await remove_warns(chat_id, await int_to_alpha(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:
|
else:
|
||||||
warn = {"warns": warns + 1}
|
warn = {"warns": warns + 1}
|
||||||
msg = strings("warn_msg").format(
|
msg = strings("warn_msg").format(
|
||||||
|
|
@ -626,12 +634,11 @@ async def warn_user(client, message, strings):
|
||||||
reas=reason or "No Reason Provided.",
|
reas=reason or "No Reason Provided.",
|
||||||
twarn=warns + 1,
|
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)
|
await add_warn(chat_id, await int_to_alpha(user_id), warn)
|
||||||
|
|
||||||
|
|
||||||
@app.on_callback_query(filters.regex("unwarn_"))
|
@app.on_callback_query(filters.regex("unwarn_"))
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def remove_warning(_, cq, strings):
|
async def remove_warning(_, cq, strings):
|
||||||
from_user = cq.from_user
|
from_user = cq.from_user
|
||||||
|
|
@ -662,7 +669,6 @@ async def remove_warning(_, cq, strings):
|
||||||
|
|
||||||
|
|
||||||
@app.on_callback_query(filters.regex("unmute_"))
|
@app.on_callback_query(filters.regex("unmute_"))
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def unmute_user(_, cq, strings):
|
async def unmute_user(_, cq, strings):
|
||||||
from_user = cq.from_user
|
from_user = cq.from_user
|
||||||
|
|
@ -678,12 +684,14 @@ async def unmute_user(_, cq, strings):
|
||||||
text = cq.message.text.markdown
|
text = cq.message.text.markdown
|
||||||
text = f"~~{text}~~\n\n"
|
text = f"~~{text}~~\n\n"
|
||||||
text += strings("rmmute_msg").format(mention=from_user.mention)
|
text += strings("rmmute_msg").format(mention=from_user.mention)
|
||||||
await cq.message.chat.unban_member(user_id)
|
try:
|
||||||
await cq.message.edit(text)
|
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_"))
|
@app.on_callback_query(filters.regex("unban_"))
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def unban_user(_, cq, strings):
|
async def unban_user(_, cq, strings):
|
||||||
from_user = cq.from_user
|
from_user = cq.from_user
|
||||||
|
|
@ -707,13 +715,10 @@ async def unban_user(_, cq, strings):
|
||||||
|
|
||||||
|
|
||||||
# Remove Warn
|
# Remove Warn
|
||||||
@app.on_cmd("rmwarn", group_only=True)
|
@app.on_cmd("rmwarn", self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_restrict_members")
|
@app.adminsOnly("can_restrict_members")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def remove_warnings(_, message, strings):
|
async def remove_warnings(_, message, strings):
|
||||||
if not message.from_user:
|
|
||||||
return
|
|
||||||
if not message.reply_to_message:
|
if not message.reply_to_message:
|
||||||
return await message.reply_text(strings("reply_to_rm_warn"))
|
return await message.reply_text(strings("reply_to_rm_warn"))
|
||||||
user_id = message.reply_to_message.from_user.id
|
user_id = message.reply_to_message.from_user.id
|
||||||
|
|
@ -731,7 +736,6 @@ async def remove_warnings(_, message, strings):
|
||||||
|
|
||||||
# Warns
|
# Warns
|
||||||
@app.on_cmd("warns", group_only=True)
|
@app.on_cmd("warns", group_only=True)
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def check_warns(_, message, strings):
|
async def check_warns(_, message, strings):
|
||||||
if not message.from_user:
|
if not message.from_user:
|
||||||
|
|
@ -759,28 +763,27 @@ async def check_warns(_, message, strings):
|
||||||
& filters.group
|
& filters.group
|
||||||
)
|
)
|
||||||
@capture_err
|
@capture_err
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def report_user(_, ctx: Message, strings) -> "Message":
|
async def report_user(_, ctx: Message, strings) -> "Message":
|
||||||
if not ctx.reply_to_message:
|
if len(ctx.text.split()) <= 1 and not ctx.reply_to_message:
|
||||||
return await ctx.reply_text(strings("report_no_reply"))
|
return await ctx.reply_msg(strings("report_no_reply"))
|
||||||
reply = ctx.reply_to_message
|
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
|
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
|
user_id = ctx.from_user.id if ctx.from_user else ctx.sender_chat.id
|
||||||
if reply_id == user_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)
|
list_of_admins = await list_admins(ctx.chat.id)
|
||||||
linked_chat = (await app.get_chat(ctx.chat.id)).linked_chat
|
linked_chat = (await app.get_chat(ctx.chat.id)).linked_chat
|
||||||
if linked_chat is None:
|
if linked_chat is None:
|
||||||
if reply_id in list_of_admins or reply_id == ctx.chat.id:
|
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 (
|
elif (
|
||||||
reply_id in list_of_admins
|
reply_id in list_of_admins
|
||||||
or reply_id == ctx.chat.id
|
or reply_id == ctx.chat.id
|
||||||
or reply_id == linked_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 = (
|
user_mention = (
|
||||||
reply.from_user.mention if reply.from_user else reply.sender_chat.title
|
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
|
# return bots or deleted admins
|
||||||
continue
|
continue
|
||||||
text += f"<a href='tg://user?id={admin.user.id}'>\u2063</a>"
|
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)
|
@app.on_cmd("set_chat_title", self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_change_info")
|
@app.adminsOnly("can_change_info")
|
||||||
async def set_chat_title(_, ctx: Message):
|
async def set_chat_title(_, ctx: Message):
|
||||||
if len(ctx.command) < 2:
|
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
|
old_title = ctx.chat.title
|
||||||
new_title = ctx.text.split(None, 1)[1]
|
new_title = ctx.text.split(None, 1)[1]
|
||||||
await ctx.chat.set_title(new_title)
|
try:
|
||||||
await ctx.reply_text(
|
await ctx.chat.set_title(new_title)
|
||||||
f"Successfully Changed Group Title From {old_title} To {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)
|
@app.on_cmd("set_user_title", self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_change_info")
|
@app.adminsOnly("can_change_info")
|
||||||
async def set_user_title(_, ctx: Message):
|
async def set_user_title(_, ctx: Message):
|
||||||
if not ctx.reply_to_message:
|
if not ctx.reply_to_message:
|
||||||
return await ctx.reply_text("Reply to user's message to set his admin title")
|
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"
|
"**Usage:**\n/set_user_title NEW ADMINISTRATOR TITLE"
|
||||||
)
|
)
|
||||||
title = ctx.text.split(None, 1)[1]
|
title = ctx.text.split(None, 1)[1]
|
||||||
await app.set_administrator_title(chat_id, from_user.id, title)
|
try:
|
||||||
await ctx.reply_text(
|
await app.set_administrator_title(chat_id, from_user.id, title)
|
||||||
f"Successfully Changed {from_user.mention}'s Admin Title To {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)
|
@app.on_cmd("set_chat_photo", self_admin=True, group_only=True)
|
||||||
@adminsOnly("can_change_info")
|
@app.adminsOnly("can_change_info")
|
||||||
async def set_chat_photo(_, ctx: Message):
|
async def set_chat_photo(_, ctx: Message):
|
||||||
reply = ctx.reply_to_message
|
reply = ctx.reply_to_message
|
||||||
|
|
||||||
|
|
@ -856,3 +865,30 @@ async def set_chat_photo(_, ctx: Message):
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
await ctx.reply(f"Failed changed group photo. ERROR: {err}")
|
await ctx.reply(f"Failed changed group photo. ERROR: {err}")
|
||||||
os.remove(photo)
|
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 database.afk_db import add_afk, cleanmode_off, cleanmode_on, is_afk, remove_afk
|
||||||
from misskaty import app
|
from misskaty import app
|
||||||
from misskaty.core.decorator.permissions import adminsOnly
|
from misskaty.core.decorator.permissions import adminsOnly
|
||||||
from misskaty.core.decorator.ratelimiter import ratelimiter
|
|
||||||
from misskaty.helper import get_readable_time2
|
from misskaty.helper import get_readable_time2
|
||||||
from misskaty.helper.localization import use_chat_lang
|
from misskaty.helper.localization import use_chat_lang
|
||||||
from utils import put_cleanmode
|
from utils import put_cleanmode
|
||||||
|
|
@ -33,7 +32,6 @@ Just type something in group to remove AFK Status."""
|
||||||
|
|
||||||
# Handle set AFK Command
|
# Handle set AFK Command
|
||||||
@app.on_cmd("afk")
|
@app.on_cmd("afk")
|
||||||
@ratelimiter
|
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def active_afk(_, ctx: Message, strings):
|
async def active_afk(_, ctx: Message, strings):
|
||||||
if ctx.sender_chat:
|
if ctx.sender_chat:
|
||||||
|
|
@ -207,7 +205,6 @@ async def active_afk(_, ctx: Message, strings):
|
||||||
|
|
||||||
|
|
||||||
@app.on_cmd("afkdel", group_only=True)
|
@app.on_cmd("afkdel", group_only=True)
|
||||||
@ratelimiter
|
|
||||||
@adminsOnly("can_change_info")
|
@adminsOnly("can_change_info")
|
||||||
@use_chat_lang()
|
@use_chat_lang()
|
||||||
async def afk_state(_, ctx: Message, strings):
|
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"]
|
possible = ["/afk", f"/afk@{self.me.username}", "!afk"]
|
||||||
message_text = ctx.text or ctx.caption
|
message_text = ctx.text or ctx.caption
|
||||||
for entity in ctx.entities:
|
for entity in ctx.entities:
|
||||||
if (
|
try:
|
||||||
entity.type == enums.MessageEntityType.BOT_COMMAND
|
if (
|
||||||
and (message_text[0 : 0 + entity.length]).lower() in possible
|
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
|
return
|
||||||
|
|
||||||
msg = ""
|
msg = ""
|
||||||
|
|
@ -380,7 +380,7 @@ async def afk_watcher_func(self: Client, ctx: Message, strings):
|
||||||
if ctx.entities:
|
if ctx.entities:
|
||||||
entity = ctx.entities
|
entity = ctx.entities
|
||||||
j = 0
|
j = 0
|
||||||
for x in range(len(entity)):
|
for _ in range(len(entity)):
|
||||||
if (entity[j].type) == enums.MessageEntityType.MENTION:
|
if (entity[j].type) == enums.MessageEntityType.MENTION:
|
||||||
found = re.findall("@([_0-9a-zA-Z]+)", ctx.text)
|
found = re.findall("@([_0-9a-zA-Z]+)", ctx.text)
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ def shorten(description, info="anilist.co"):
|
||||||
|
|
||||||
@app.on_message(filters.command("anime", COMMAND_HANDLER))
|
@app.on_message(filters.command("anime", COMMAND_HANDLER))
|
||||||
async def anime_search(_, mesg):
|
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)
|
reply = await mesg.reply("⏳ <i>Please wait ...</i>", quote=True)
|
||||||
if len(search) == 1:
|
if len(search) == 1:
|
||||||
return await reply.edit("⚠️ <b>Give Anime name please.</b>")
|
return await reply.edit("⚠️ <b>Give Anime name please.</b>")
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
"""
|
"""
|
||||||
* @author yasir <yasiramunandar@gmail.com>
|
* @author yasir <yasiramunandar@gmail.com>
|
||||||
* @date 2022-12-01 09:12:27
|
* @date 2022-12-01 09:12:27
|
||||||
* @projectName MissKatyPyro
|
* @projectName MissKatyPyro
|
||||||
* Copyright @YasirPedia All rights reserved
|
* Copyright @YasirPedia All rights reserved
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyrogram import filters
|
from pyrogram import filters
|
||||||
from pyrogram.errors import UserAlreadyParticipant, UserIsBlocked
|
from pyrogram.errors import UserAlreadyParticipant, UserIsBlocked
|
||||||
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
|
|
||||||
from misskaty import app
|
from misskaty import app
|
||||||
from misskaty.core.decorator.errors import capture_err
|
from misskaty.core.decorator.errors import capture_err
|
||||||
from misskaty.core.decorator.ratelimiter import ratelimiter
|
|
||||||
|
|
||||||
|
|
||||||
# Filters Approve User by bot in channel @YMovieZNew
|
# 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"))
|
@app.on_callback_query(filters.regex(r"^approve"))
|
||||||
@ratelimiter
|
|
||||||
async def approve_chat(c, q):
|
async def approve_chat(c, q):
|
||||||
i, chat = q.data.split("_")
|
_, chat = q.data.split("_")
|
||||||
try:
|
try:
|
||||||
await q.message.edit(
|
await q.message.edit(
|
||||||
"Yeayy, selamat kamu bisa bergabung di Channel YMovieZ Reborn..."
|
"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"))
|
@app.on_callback_query(filters.regex(r"^declined"))
|
||||||
@ratelimiter
|
|
||||||
async def decline_chat(c, q):
|
async def decline_chat(c, q):
|
||||||
i, chat = q.data.split("_")
|
_, chat = q.data.split("_")
|
||||||
try:
|
try:
|
||||||
await q.message.edit(
|
await q.message.edit(
|
||||||
"Yahh, kamu ditolak join channel. Biasakan rajin membaca yahhh.."
|
"Yahh, kamu ditolak join channel. Biasakan rajin membaca yahhh.."
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ from misskaty.vars import (
|
||||||
MINIMUM_FILE_SIZE,
|
MINIMUM_FILE_SIZE,
|
||||||
)
|
)
|
||||||
|
|
||||||
LOGGER = getLogger(__name__)
|
LOGGER = getLogger("MissKaty")
|
||||||
|
|
||||||
|
|
||||||
async def FilterMessage(message: Message):
|
async def FilterMessage(message: Message):
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue