Compare commits
63 Commits
af0fc57587
...
dev
Author | SHA1 | Date | |
---|---|---|---|
5c32c505f4 | |||
a5f3c6cd2b | |||
e2cb0926ae | |||
fa975ddc09 | |||
44b72df169 | |||
b059da9d32 | |||
9e1db9f591 | |||
7689b49e01 | |||
ea021904bd | |||
1c5f46a263 | |||
0dc5d668cb | |||
d9b8048cdd | |||
3f3e1a57fe | |||
8bf4770ab9 | |||
4b4c6c095c | |||
a0bee0c53e | |||
3bc6183228 | |||
c8ad0694d1 | |||
48c7337297 | |||
053e9c4513 | |||
eb80eddb6e
|
|||
98f19e0c23 | |||
54005c9d4f | |||
d118db519b | |||
8f3ca5a16d | |||
15175a64c8 | |||
5088b45753 | |||
a417609464 | |||
d22b36ef89 | |||
da028919ed | |||
c338e1bf57 | |||
67eafcf1a4 | |||
783d191f35 | |||
e61516f51f
|
|||
8eef171391
|
|||
0e76659511
|
|||
dfd39f47db
|
|||
9e00d38877
|
|||
ab39c111eb
|
|||
64e097737a
|
|||
e3376e5fe0
|
|||
569b0d51e5
|
|||
de00687cbf
|
|||
fdd5ad7ff0
|
|||
7a587ececa
|
|||
8a3012b9be
|
|||
e9ef008080
|
|||
aee5cdf6a5
|
|||
eadbdd1eda
|
|||
f6e85a7a2c
|
|||
5ad8cc750d
|
|||
860d9ac281
|
|||
822693decc
|
|||
7aafdf6c19
|
|||
370bcd0653
|
|||
07963d705c | |||
17026ae4d3 | |||
b04f5cae11 | |||
a94ca311b3 | |||
add623d636 | |||
b5487424b7 | |||
e712d4fd88
|
|||
e362e45b31
|
14
.renovaterc
@@ -2,5 +2,19 @@
|
|||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": [
|
||||||
"config:base"
|
"config:base"
|
||||||
|
],
|
||||||
|
"baseBranches": [
|
||||||
|
"dev"
|
||||||
|
],
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"matchUpdateTypes": [
|
||||||
|
"minor",
|
||||||
|
"patch",
|
||||||
|
"pin",
|
||||||
|
"digest"
|
||||||
|
],
|
||||||
|
"automerge": true
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
19
README.md
@@ -1 +1,20 @@
|
|||||||
# EmojiCaptchaBot
|
# EmojiCaptchaBot
|
||||||
|
|
||||||
|
Small Pyrogram bot for emoji captchas. Extremely useful for groups using
|
||||||
|
"People Nearby" feature or communities.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
* [Python](https://www.python.org) 3.8+ (Recommended 3.9 or 3.11)
|
||||||
|
* [MongoDB](https://www.mongodb.com)
|
||||||
|
* [Git](https://git-scm.com)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Install all [dependencies](#dependencies)
|
||||||
|
2. Clone this repository using `git clone https://git.end-play.xyz/profitroll/EmojiCaptchaBot.git`
|
||||||
|
3. Go to bot's folder (`cd EmojiCaptchaBot`) and create venv: `python3 -m venv .venv` or `virtualenv .venv`
|
||||||
|
4. Activate venv with `source .venv/bin/activate` (on Linux) or `.\.venv\Scripts\activate.ps1` (on Windows)
|
||||||
|
5. Install all bot's dependencies using `pip3 install -r requirements.txt`
|
||||||
|
6. Set up the bot by copying example to `config.json` (using `cp config_example.json config.json`) and editing it with text editor (Notepad, nano, vim, etc.)
|
||||||
|
7. Now you can start the bot using `python3 main.py`
|
||||||
|
BIN
assets/emojis/☁️.png
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
assets/emojis/⛑️.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
assets/emojis/❄️.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
assets/emojis/🌀.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
assets/emojis/🌏.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
assets/emojis/🌕.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/emojis/🌙.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/emojis/🌚.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/emojis/🌝.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
assets/emojis/🍎.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/emojis/🍑.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/emojis/🍒.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/emojis/🍔.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
assets/emojis/🍕.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/emojis/🍟.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/emojis/🍧.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/emojis/🍩.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
assets/emojis/🍫.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
assets/emojis/🍵.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/emojis/🍷.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
assets/emojis/🍺.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/emojis/🎃.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
assets/emojis/🎒.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/emojis/🎩.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/emojis/🎹.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
assets/emojis/🎻.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
assets/emojis/🐀.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
assets/emojis/🐇.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
assets/emojis/🐊.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
assets/emojis/🐋.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
assets/emojis/🐍.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/emojis/🐔.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
assets/emojis/🐗.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
assets/emojis/🐚.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/emojis/🐟.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
assets/emojis/🐠.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/emojis/🐢.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
assets/emojis/🐧.png
Normal file
After Width: | Height: | Size: 11 KiB |
52
classes/callbacks.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from pyrogram.types import CallbackQuery
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CallbackVerify:
|
||||||
|
user_id: int
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_callback(cls, callback: CallbackQuery):
|
||||||
|
action, user_id = str(callback.data).split(":")
|
||||||
|
if action.lower() != "verify":
|
||||||
|
raise ValueError("Callback provided is not a verification callback")
|
||||||
|
return cls(int(user_id))
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CallbackEmoji:
|
||||||
|
user_id: int
|
||||||
|
emoji: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_callback(cls, callback: CallbackQuery):
|
||||||
|
action, user_id, emoji = str(callback.data).split(":")
|
||||||
|
if action.lower() != "emoji":
|
||||||
|
raise ValueError("Callback provided is not an emoji button callback")
|
||||||
|
return cls(int(user_id), emoji)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CallbackBan:
|
||||||
|
user_id: int
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_callback(cls, callback: CallbackQuery):
|
||||||
|
action, user_id = str(callback.data).split(":")
|
||||||
|
if action.lower() != "ban":
|
||||||
|
raise ValueError("Callback provided is not a ban callback")
|
||||||
|
return cls(int(user_id))
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CallbackLanguage:
|
||||||
|
language: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_callback(cls, callback: CallbackQuery):
|
||||||
|
action, language = str(callback.data).split(":")
|
||||||
|
if action.lower() != "language":
|
||||||
|
raise ValueError("Callback provided is not a language callback")
|
||||||
|
return cls(language)
|
@@ -1,30 +1,37 @@
|
|||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from libbot.pyrogram.classes import PyroClient
|
from libbot.pyrogram.classes import PyroClient as LibPyroClient
|
||||||
from pyrogram.types import User
|
from pyrogram.types import User
|
||||||
|
|
||||||
|
from classes.pyrogroup import PyroGroup
|
||||||
from classes.pyrouser import PyroUser
|
from classes.pyrouser import PyroUser
|
||||||
from modules.database import col_users
|
|
||||||
|
|
||||||
|
|
||||||
class PyroClient(PyroClient):
|
class PyroClient(LibPyroClient):
|
||||||
async def find_user(self, user: Union[int, User], group: int) -> PyroUser:
|
async def find_user(self, user: Union[int, User], *args, **kwargs) -> PyroUser:
|
||||||
"""Find User by it's ID or User object
|
"""Find User by it's ID or User object.
|
||||||
|
|
||||||
### Args:
|
### Args:
|
||||||
* user (`Union[int, User]`): ID or User object to extract ID from
|
* user (`Union[int, User]`): ID or User object to extract ID from
|
||||||
* group (`int`): ID of the group
|
|
||||||
|
|
||||||
### Returns:
|
### Returns:
|
||||||
* `PyroUser`: PyroUser object
|
* `PyroUser`: PyroUser object
|
||||||
"""
|
"""
|
||||||
db_record = col_users.find_one(
|
|
||||||
{"id": user.id if isinstance(user, User) else user, "group": group}
|
return (
|
||||||
|
await PyroUser.find(user, *args, **kwargs)
|
||||||
|
if isinstance(user, int)
|
||||||
|
else await PyroUser.find(user.id, *args, **kwargs)
|
||||||
)
|
)
|
||||||
|
|
||||||
if db_record is None:
|
async def find_group(self, id: int, *args, **kwargs) -> PyroGroup:
|
||||||
raise KeyError(
|
"""Find Group by it's ID.
|
||||||
f"User with ID {user.id if isinstance(user, User) else user} was not found in the database"
|
|
||||||
)
|
|
||||||
|
|
||||||
return PyroUser(**db_record)
|
### Args:
|
||||||
|
* id (`int`): Group ID
|
||||||
|
|
||||||
|
### Returns:
|
||||||
|
* `PyroGroup`: PyroGroup object
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await PyroGroup.find(id, *args, **kwargs)
|
||||||
|
126
classes/pyrogroup.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import logging
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from bson import ObjectId
|
||||||
|
from libbot import sync
|
||||||
|
from libbot.pyrogram.classes import PyroClient
|
||||||
|
from pyrogram.types import User
|
||||||
|
|
||||||
|
from modules.database import col_groups
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PyroGroup:
|
||||||
|
"""Dataclass of DB entry of a group"""
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
"_id",
|
||||||
|
"id",
|
||||||
|
"locale",
|
||||||
|
"locale_auto",
|
||||||
|
"ban_failed",
|
||||||
|
"timeout_join",
|
||||||
|
"timeout_verify",
|
||||||
|
)
|
||||||
|
|
||||||
|
_id: ObjectId
|
||||||
|
id: int
|
||||||
|
locale: Union[str, None]
|
||||||
|
locale_auto: bool
|
||||||
|
ban_failed: bool
|
||||||
|
timeout_join: int
|
||||||
|
timeout_verify: int
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def find(
|
||||||
|
cls,
|
||||||
|
id: Union[int, float],
|
||||||
|
locale: Union[str, None] = sync.config_get("locale", "defaults", "group"),
|
||||||
|
locale_auto: bool = sync.config_get("locale_auto", "defaults", "group"),
|
||||||
|
ban_failed: bool = sync.config_get("ban_failed", "defaults", "group"),
|
||||||
|
timeout_join: int = sync.config_get("timeout_join", "defaults", "group"),
|
||||||
|
timeout_verify: int = sync.config_get("timeout_verify", "defaults", "group"),
|
||||||
|
):
|
||||||
|
db_entry = await col_groups.find_one(
|
||||||
|
{
|
||||||
|
"id": id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if db_entry is None:
|
||||||
|
inserted = await col_groups.insert_one(
|
||||||
|
{
|
||||||
|
"id": int(id),
|
||||||
|
"locale": locale,
|
||||||
|
"locale_auto": locale_auto,
|
||||||
|
"ban_failed": ban_failed,
|
||||||
|
"timeout_join": timeout_join,
|
||||||
|
"timeout_verify": timeout_verify,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
db_entry = await col_groups.find_one({"_id": inserted.inserted_id})
|
||||||
|
|
||||||
|
if db_entry is None:
|
||||||
|
raise RuntimeError("Could not find inserted group entry.")
|
||||||
|
|
||||||
|
return cls(**db_entry)
|
||||||
|
|
||||||
|
async def set_locale(self, locale: Union[str, None]) -> None:
|
||||||
|
logger.debug("Locale of group %s has been set to %s", self.id, locale)
|
||||||
|
await col_groups.update_one({"_id": self._id}, {"$set": {"locale": locale}})
|
||||||
|
|
||||||
|
async def set_locale_auto(self, enabled: bool) -> None:
|
||||||
|
logger.debug(
|
||||||
|
"Automatic locale selection of group %s has been set to %s",
|
||||||
|
self.id,
|
||||||
|
enabled,
|
||||||
|
)
|
||||||
|
await col_groups.update_one(
|
||||||
|
{"_id": self._id}, {"$set": {"locale_auto": enabled}}
|
||||||
|
)
|
||||||
|
|
||||||
|
async def set_ban_failed(self, enabled: bool) -> None:
|
||||||
|
logger.debug(
|
||||||
|
"Banning users that failed the captcha in group %s has been set to %s",
|
||||||
|
self.id,
|
||||||
|
enabled,
|
||||||
|
)
|
||||||
|
await col_groups.update_one(
|
||||||
|
{"_id": self._id}, {"$set": {"ban_failed": enabled}}
|
||||||
|
)
|
||||||
|
|
||||||
|
async def set_timeout_join(self, timeout: int) -> None:
|
||||||
|
logger.debug(
|
||||||
|
"Join timeout in group %s has been set to %s",
|
||||||
|
self.id,
|
||||||
|
timeout,
|
||||||
|
)
|
||||||
|
await col_groups.update_one(
|
||||||
|
{"_id": self._id}, {"$set": {"timeout_join": timeout}}
|
||||||
|
)
|
||||||
|
|
||||||
|
async def set_timeout_verify(self, timeout: int) -> None:
|
||||||
|
logger.debug(
|
||||||
|
"Verification timeout in group %s has been set to %s",
|
||||||
|
self.id,
|
||||||
|
timeout,
|
||||||
|
)
|
||||||
|
await col_groups.update_one(
|
||||||
|
{"_id": self._id}, {"$set": {"timeout_verify": timeout}}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Group settings
|
||||||
|
# User locale
|
||||||
|
def select_locale(
|
||||||
|
self, app: PyroClient, user: User, ignore_auto: bool = False
|
||||||
|
) -> str:
|
||||||
|
if user is None:
|
||||||
|
return app.default_locale
|
||||||
|
if not ignore_auto and self.locale_auto is True:
|
||||||
|
if user.language_code is not None:
|
||||||
|
return user.language_code
|
||||||
|
return self.locale if self.locale is not None else app.default_locale
|
||||||
|
return self.locale if self.locale is not None else app.default_locale
|
@@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List
|
from typing import List, Union
|
||||||
|
|
||||||
from bson import ObjectId
|
from bson import ObjectId
|
||||||
|
|
||||||
@@ -34,9 +34,9 @@ class PyroUser:
|
|||||||
mistakes: int
|
mistakes: int
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_if_not_exists(
|
async def find(
|
||||||
cls,
|
cls,
|
||||||
id: int,
|
id: Union[int, float],
|
||||||
group: int,
|
group: int,
|
||||||
failed: bool = False,
|
failed: bool = False,
|
||||||
emojis: List[str] = [],
|
emojis: List[str] = [],
|
||||||
@@ -44,11 +44,12 @@ class PyroUser:
|
|||||||
score: int = 0,
|
score: int = 0,
|
||||||
mistakes: int = 0,
|
mistakes: int = 0,
|
||||||
):
|
):
|
||||||
db_entry = col_users.find_one({"id": id, "group": group})
|
db_entry = await col_users.find_one({"id": id, "group": group})
|
||||||
|
|
||||||
if db_entry is None:
|
if db_entry is None:
|
||||||
inserted = col_users.insert_one(
|
inserted = await col_users.insert_one(
|
||||||
{
|
{
|
||||||
"id": id,
|
"id": int(id),
|
||||||
"group": group,
|
"group": group,
|
||||||
"failed": failed,
|
"failed": failed,
|
||||||
"emojis": emojis,
|
"emojis": emojis,
|
||||||
@@ -57,48 +58,47 @@ class PyroUser:
|
|||||||
"mistakes": mistakes,
|
"mistakes": mistakes,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
db_entry = {
|
db_entry = await col_users.find_one({"_id": inserted.inserted_id})
|
||||||
"_id": inserted.inserted_id,
|
|
||||||
"id": id,
|
if db_entry is None:
|
||||||
"group": group,
|
raise RuntimeError("Could not find inserted user entry.")
|
||||||
"failed": failed,
|
|
||||||
"emojis": emojis,
|
|
||||||
"selected": selected,
|
|
||||||
"score": 0,
|
|
||||||
"mistakes": mistakes,
|
|
||||||
}
|
|
||||||
return cls(**db_entry)
|
return cls(**db_entry)
|
||||||
|
|
||||||
def set_failed(self, failed: bool = True) -> None:
|
async def set_failed(self, failed: bool = True) -> None:
|
||||||
logger.debug("%s's failure state has been set to %s", self.id, failed)
|
logger.debug("%s's failure state has been set to %s", self.id, failed)
|
||||||
col_users.update_one({"_id": self._id}, {"$set": {"failed": failed}})
|
await col_users.update_one({"_id": self._id}, {"$set": {"failed": failed}})
|
||||||
|
|
||||||
def set_emojis(self, emojis: List[str]) -> None:
|
async def set_emojis(self, emojis: List[str]) -> None:
|
||||||
logger.debug("%s's emojis have been set to %s", self.id, emojis)
|
logger.debug("%s's emojis have been set to %s", self.id, emojis)
|
||||||
col_users.update_one({"_id": self._id}, {"$set": {"emojis": emojis}})
|
await col_users.update_one({"_id": self._id}, {"$set": {"emojis": emojis}})
|
||||||
|
|
||||||
def set_score(self, score: int = 0) -> None:
|
async def set_score(self, score: int = 0) -> None:
|
||||||
logger.debug("%s's score has been set to %s", self.id, score)
|
logger.debug("%s's score has been set to %s", self.id, score)
|
||||||
col_users.update_one({"_id": self._id}, {"$set": {"score": score}})
|
await col_users.update_one({"_id": self._id}, {"$set": {"score": score}})
|
||||||
|
|
||||||
def set_mistakes(self, mistakes: int = 0) -> None:
|
async def set_mistakes(self, mistakes: int = 0) -> None:
|
||||||
logger.debug("%s's mistakes count has been set to %s", self.id, mistakes)
|
logger.debug("%s's mistakes count has been set to %s", self.id, mistakes)
|
||||||
col_users.update_one({"_id": self._id}, {"$set": {"mistakes": mistakes}})
|
await col_users.update_one({"_id": self._id}, {"$set": {"mistakes": mistakes}})
|
||||||
|
|
||||||
def update_score(self, points: int = 1) -> None:
|
async def set_selected(self, emojis: List[str] = None) -> None:
|
||||||
|
logger.debug("%s's elected emojis have been set to %s", self.id, emojis)
|
||||||
|
await col_users.update_one({"_id": self._id}, {"$set": {"selected": emojis}})
|
||||||
|
|
||||||
|
async def update_score(self, points: int = 1) -> None:
|
||||||
logger.debug("%s point(s) have been added to %s score", points, self.id)
|
logger.debug("%s point(s) have been added to %s score", points, self.id)
|
||||||
col_users.update_one(
|
await col_users.update_one(
|
||||||
{"_id": self._id}, {"$set": {"score": self.score + points}}
|
{"_id": self._id}, {"$set": {"score": self.score + points}}
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_mistakes(self, points: int = 1) -> None:
|
async def update_mistakes(self, points: int = 1) -> None:
|
||||||
logger.debug("%s point(s) have been added to %s mistakes", points, self.id)
|
logger.debug("%s point(s) have been added to %s mistakes", points, self.id)
|
||||||
col_users.update_one(
|
await col_users.update_one(
|
||||||
{"_id": self._id}, {"$set": {"mistakes": self.mistakes + points}}
|
{"_id": self._id}, {"$set": {"mistakes": self.mistakes + points}}
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_selected(self, entry: str) -> None:
|
async def update_selected(self, entry: str) -> None:
|
||||||
logger.debug("Emoji %s has been added to %s's selection list", entry, self.id)
|
logger.debug("Emoji %s has been added to %s's selection list", entry, self.id)
|
||||||
col_users.update_one(
|
await col_users.update_one(
|
||||||
{"_id": self._id}, {"$set": {"selected": self.selected + [entry]}}
|
{"_id": self._id}, {"$set": {"selected": self.selected + [entry]}}
|
||||||
)
|
)
|
||||||
|
@@ -19,9 +19,14 @@
|
|||||||
"port": 27017,
|
"port": 27017,
|
||||||
"name": "captchabot"
|
"name": "captchabot"
|
||||||
},
|
},
|
||||||
"timeouts": {
|
"defaults": {
|
||||||
"join": 2,
|
"group": {
|
||||||
"verify": 3
|
"locale": null,
|
||||||
|
"locale_auto": true,
|
||||||
|
"ban_failed": true,
|
||||||
|
"timeout_join": 180,
|
||||||
|
"timeout_verify": 300
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"whitelist": {
|
"whitelist": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
@@ -105,5 +110,48 @@
|
|||||||
"😀",
|
"😀",
|
||||||
"🙊"
|
"🙊"
|
||||||
],
|
],
|
||||||
"commands": {}
|
"commands": {
|
||||||
|
"help": {
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "BotCommandScopeDefault"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ban_failed": {
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "BotCommandScopeAllChatAdministrators"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "BotCommandScopeAllChatAdministrators"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"language_auto": {
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "BotCommandScopeAllChatAdministrators"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"timeout_join": {
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "BotCommandScopeAllChatAdministrators"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"timeout_verify": {
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "BotCommandScopeAllChatAdministrators"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
54
locale/de.json
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"flag": "🇩🇪",
|
||||||
|
"name": "Deutsch",
|
||||||
|
"selectable": true,
|
||||||
|
"codes": [
|
||||||
|
"de",
|
||||||
|
"de-AT",
|
||||||
|
"de-CH",
|
||||||
|
"de-DE",
|
||||||
|
"de-LI",
|
||||||
|
"de-LU"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"help": "Nützliche Informationen anzeigen",
|
||||||
|
"ban_failed": "Umschaltverbot bei fehlender Verifizierung",
|
||||||
|
"language": "Sprache des Bots in dieser Gruppe einstellen",
|
||||||
|
"language_auto": "Automatische Sprachwahl umschalten",
|
||||||
|
"timeout_join": "Zeitlimit für die Anmeldung festlegen",
|
||||||
|
"timeout_verify": "Zeitlimit für die Überprüfung festlegen"
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"help": "Hallo! Dieser Bot kann Ihnen helfen, sich gegen nervige Bots zu schützen, indem er Emoji-Captchas beim Beitritt erzwingt.\n\nFügen Sie diesen Bot zu Ihrer gewünschten Gruppe hinzu und geben Sie ihm die Berechtigungen **Nachrichten löschen** und **Benutzer sperren**.\n\nVerfügbare Kommandos:\n• /ban_failed - Bannen bei Zeitüberschreitung oder fehlgeschlagenem Captcha umschalten\n• /language - Sprache des Bots ändern\n• /language_auto - automatische Sprache pro Benutzer umschalten\n\nKommandos, die oben aufgelistet sind, müssen in der Gruppe von ihren Admins oder dem Besitzer ausgeführt werden.\n\nDieser Bot ist Open Source, also können Sie ihn selbst hosten oder nach Ihren Wünschen verändern: https://git.end-play.xyz/profitroll/EmojiCaptchaBot\n\nWenn Sie einen Fehler melden oder einen Vorschlag machen möchten – besuchen Sie die [Problemseite](https://git.end-play.xyz/profitroll/EmojiCaptchaBot/issues) des Bots auf git.\n\nEinen schönen Tag noch :)",
|
||||||
|
"ban_failed_disabled": "Bann bei Fehlschlag wurde **deaktiviert**. Wenn Benutzer die Überprüfung ignorieren oder sie nicht bestehen, bleiben sie in der Gruppe nur zum Lesen zugelassen.",
|
||||||
|
"ban_failed_enabled": "Bann bei Fehlschlag wurde **aktiviert**. Wenn Benutzer die Überprüfung ignorieren oder sie nicht bestehen, werden sie sofort aus der Gruppe geworfen und gesperrt.",
|
||||||
|
"locale_auto_disabled": "Die automatische Sprachauswahl auf individueller Basis wurde **deaktiviert**. Bitte beachten Sie, dass jetzt die Sprache der Gruppe verwendet wird. Sie können sie mit /language ändern. Wenn \"Standard\" als Sprache eingestellt ist, wird die Standardsprache des Bots ({default_locale}) verwendet.",
|
||||||
|
"locale_auto_enabled": "Die automatische Sprachauswahl auf individueller Basis wurde **aktiviert**. Bitte beachten Sie, dass die Standardsprache der Gruppe oder des Bots verwendet wird, wenn der Bot kein Sprachpaket für den Benutzer hat. Sie können die Sprache der Gruppe mit /language ändern. Wenn \"Standard\" als Sprache eingestellt ist, wird die Standardsprache des Bots ({default_locale}) verwendet.",
|
||||||
|
"locale_choice": "Okay. Bitte wählen Sie die Sprache mit der Tastatur unten aus.\n\nBitte beachten Sie, dass Sie auch die automatische Sprachauswahl für jeden Benutzer mit dem Befehl /language_auto",
|
||||||
|
"permission_denied": "Dies ist Ihnen nicht erlaubt.",
|
||||||
|
"timeout_invalid_number": "Timeout muss ein ganzzahliger Wert im zwischen 10 und 600 (Sekunden) sein.",
|
||||||
|
"timeout_join_invalid": "Falsche Verwendung! Richtiges Beispiel:\n`/timeout_join SEKUNDEN`",
|
||||||
|
"timeout_join_set": "Join-Timeout wurde eingestellt: {timeout} Sek.",
|
||||||
|
"timeout_verify_invalid": "Falsche Verwendung! Richtiges Beispiel:\n`/timeout_verify SEKUNDEN`",
|
||||||
|
"timeout_verify_set": "Zeitüberschreitung für die Verifizierung wurde eingestellt: {timeout} Sek.",
|
||||||
|
"verify": "Bitte benutzen Sie die unten stehenden Tasten, um das Captcha auszufüllen. Tippen Sie auf die Tasten mit den Emojis, die Sie auf diesem Bild sehen.",
|
||||||
|
"welcome_verify": "Willkommen, {mention}! Um hier chatten zu können, müssen wir verifizieren, dass Sie ein Mensch sind. Bitte klicke auf die untenstehende Taste, um die Verifizierung zu starten.",
|
||||||
|
"welcome": "Willkommen zum Chat, {mention}!"
|
||||||
|
},
|
||||||
|
"callbacks": {
|
||||||
|
"captcha_failed_force": "Sie haben den Benutzer {user_id} gezwungen, das Captcha nicht zu bestehen.",
|
||||||
|
"captcha_failed": "Sie haben das Captcha nicht bestanden.",
|
||||||
|
"captcha_mistake": "Falsche Antwort. Restliche Versuche: {remaining}.",
|
||||||
|
"captcha_passed": "Sie haben das Captcha bestanden. Willkommen!",
|
||||||
|
"locale_set": "Die Sprache in dieser Gruppe ist jetzt: {locale}",
|
||||||
|
"nothing": "Diese Aktion ist schon abgeschlossen.",
|
||||||
|
"wrong_user": "Diese Nachricht ist nicht für Sie gedacht."
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"ban": "Bannen (für Admins)",
|
||||||
|
"locale_default": "Standard",
|
||||||
|
"verify": "Sich verifizieren"
|
||||||
|
}
|
||||||
|
}
|
@@ -1,19 +1,51 @@
|
|||||||
{
|
{
|
||||||
|
"metadata": {
|
||||||
|
"flag": "🇬🇧",
|
||||||
|
"name": "English",
|
||||||
|
"selectable": true,
|
||||||
|
"codes": [
|
||||||
|
"en",
|
||||||
|
"en-GB",
|
||||||
|
"en-US"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"help": "Show some helpful info",
|
||||||
|
"ban_failed": "Toggle ban on verification fail",
|
||||||
|
"language_auto": "Toggle automatic language selection",
|
||||||
|
"language": "Set bot's language in this group",
|
||||||
|
"timeout_join": "Set join timeout",
|
||||||
|
"timeout_verify": "Set verification timeout"
|
||||||
|
},
|
||||||
"messages": {
|
"messages": {
|
||||||
"verify": "Please, use the buttons below to complete the captcha. Tap on the buttons with emojis you see on this image.",
|
"help": "Hi! This bot can help you protect against annoying bots by enforcing emoji captcha on join.\n\nIn order to it, add this bot to your desired group and provide it with **Delete Messages** and **Ban Users** permissions.\n\nAvailable commands:\n• /ban_failed - toggle banning on timeout or captcha fail\n• /language - change bot's language\n• /language_auto - toggle automatic per-user language\n• /timeout_join - set timeout time on join (in seconds)\n• /timeout_verify - set timeout for verification (in seconds)\n\nCommands listed above must be executes in group by its admins or owner.\n\nThis bot is open source, so feel free to host it yourself or modify to your liking: https://git.end-play.xyz/profitroll/EmojiCaptchaBot\n\nIf you want to file an issue or make a suggestion – visit bot's [issues page](https://git.end-play.xyz/profitroll/EmojiCaptchaBot/issues) on bot's git.\n\nHave a nice day :)",
|
||||||
|
"ban_failed_disabled": "Ban on fail has been **disabled**. If users ignore verification or fail it, they will remain read-only in the group.",
|
||||||
|
"ban_failed_enabled": "Ban on fail has been **enabled**. If users ignore verification or fail it, they will be immediately kicked from the group and banned.",
|
||||||
|
"locale_auto_disabled": "Automatic language selection on a per-user basis has been **disabled**. Please note that the group's language will now be used. You can change it using /language. If \"Default\" language is set, bot's default language ({default_locale}) will be used.",
|
||||||
|
"locale_auto_enabled": "Automatic language selection on a per-user basis has been **enabled**. Please note that if bot does not have a language pack for user – group's or bot's default language will be used. You can change group's language using /language. If \"Default\" language is set, bot's default language ({default_locale}) will be used.",
|
||||||
|
"locale_choice": "Alright. Please choose the language using the keyboard below.\n\nNote, that you can also enable automatic language selection on a per-user basis using the command /language_auto",
|
||||||
|
"permission_denied": "You are not allowed to do so.",
|
||||||
|
"timeout_invalid_number": "Timeout must be an integer value in the range 10 to 600 (seconds).",
|
||||||
|
"timeout_join_invalid": "Wrong usage! Correct example:\n`/timeout_join SECONDS`",
|
||||||
|
"timeout_join_set": "Join timeout has been set: {timeout} sec.",
|
||||||
|
"timeout_verify_invalid": "Wrong usage! Correct example:\n`/timeout_verify SECONDS`",
|
||||||
|
"timeout_verify_set": "Verification timeout has been set: {timeout} sec.",
|
||||||
|
"verify": "Please, use the buttons below to complete the captcha. Tap on the buttons with emojis you see in this image.",
|
||||||
"welcome_verify": "Welcome, {mention}! In order to chat here, we need to verify you're a human. Please, press the button below to start the verification.",
|
"welcome_verify": "Welcome, {mention}! In order to chat here, we need to verify you're a human. Please, press the button below to start the verification.",
|
||||||
"welcome": "Welcome to the chat, {mention}!"
|
"welcome": "Welcome to the chat, {mention}!"
|
||||||
},
|
},
|
||||||
"callbacks": {
|
"callbacks": {
|
||||||
"captcha_failed": "You have failed the captcha.",
|
|
||||||
"captcha_failed_force": "You have forced {user_id} to fail the captcha.",
|
"captcha_failed_force": "You have forced {user_id} to fail the captcha.",
|
||||||
|
"captcha_failed": "You have failed the captcha.",
|
||||||
"captcha_mistake": "Invalid answer. Remaining attempts: {remaining}.",
|
"captcha_mistake": "Invalid answer. Remaining attempts: {remaining}.",
|
||||||
"captcha_passed": "You have passed the captcha. Welcome!",
|
"captcha_passed": "You have passed the captcha. Welcome!",
|
||||||
"nothing": "This actions has already been finished.",
|
"locale_set": "Group's language is now: {locale}",
|
||||||
|
"nothing": "This action has already been finished.",
|
||||||
"wrong_user": "This message is not for you."
|
"wrong_user": "This message is not for you."
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"ban": "Ban (for admins)",
|
"ban": "Ban (for admins)",
|
||||||
|
"locale_default": "Default",
|
||||||
"verify": "Verify"
|
"verify": "Verify"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,34 @@
|
|||||||
{
|
{
|
||||||
|
"metadata": {
|
||||||
|
"flag": "🇺🇦",
|
||||||
|
"name": "Українська",
|
||||||
|
"selectable": true,
|
||||||
|
"codes": [
|
||||||
|
"uk",
|
||||||
|
"uk-UA"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"help": "Показати корисну інформацію",
|
||||||
|
"ban_failed": "Перемикнути бан при невдалій верифікації",
|
||||||
|
"language": "Встановити мову бота в цій групі",
|
||||||
|
"language_auto": "Перемикнути автоматичний вибір мови",
|
||||||
|
"timeout_join": "Встановити таймаут приєднання",
|
||||||
|
"timeout_verify": "Встановити таймаут перевірки"
|
||||||
|
},
|
||||||
"messages": {
|
"messages": {
|
||||||
|
"help": "Привіт! Цей бот допоможе вам захиститися від настирливих ботів, запровадивши емодзі-капчу при вході в групу.\n\nДля цього додайте цього бота в потрібну вам групу і надайте йому права **Видаляти повідомлення** та **Банити користувачів**.\n\nДоступні команди:\n• /ban_failed - увімкнути заборону після таймауту або невдалого введення капчі\n• /language - змінити мову бота\n• /language_auto - перемкнути автоматичну мову для кожного користувача\n\nКоманди, перераховані вище, повинні виконуватися в групі її адміністраторами або власником.\n\nЦей бот має відкритий вихідний код, тому ви можете хостити його самостійно або модифікувати під себе: https://git.end-play.xyz/profitroll/EmojiCaptchaBot\n\nЯкщо ви хочете повідомити про проблему або внести пропозицію – відвідайте [сторінку підтримки бота](https://git.end-play.xyz/profitroll/EmojiCaptchaBot/issues) на гіті бота.\n\nГарного дня :)",
|
||||||
|
"ban_failed_disabled": "Блокування при невдалій верифікації було **вимкнено**. Якщо користувачі проігнорують верифікацію або не пройдуть її, вони залишаться в групі тільки для читання.",
|
||||||
|
"ban_failed_enabled": "Блокування при невдачі було **увімкнено**. Якщо користувач проігнорує верифікацію або не пройде її, його буде негайно вигнано з групи та забанено.",
|
||||||
|
"locale_auto_disabled": "Автоматичний вибір мови для кожного користувача було **вимкнено**. Зверніть увагу, що тепер буде використовуватися мова групи. Ви можете змінити її за допомогою /language. Якщо встановлено мову \"За замовчуванням\" – буде використовуватися мова бота за замовчуванням ({default_locale}).",
|
||||||
|
"locale_auto_enabled": "Автоматичний вибір мови для кожного користувача був **увімкнений**. Зверніть увагу, що якщо бот не має мовного пакета для користувача – буде використовуватися мова групи або бота за замовчуванням. Ви можете змінити мову групи за допомогою /language. Якщо встановлено мову \"За замовчуванням\" – буде використовуватися мова бота за замовчуванням ({default_locale}).",
|
||||||
|
"locale_choice": "Гаразд. Будь ласка, оберіть мову за допомогою клавіатури нижче.\n\nЗауважте, що ви також можете увімкнути автоматичний вибір мови для кожного користувача за допомогою команди /language_auto",
|
||||||
|
"permission_denied": "Вам не дозволено робити це.",
|
||||||
|
"timeout_invalid_number": "Таймаут має бути цілим числом у діапазоні від 10 до 600 (секунд).",
|
||||||
|
"timeout_join_invalid": "Неправильне використання! Правильний приклад:\n`/timeout_join СЕКУНДИ`",
|
||||||
|
"timeout_join_set": "Встановлено тайм-аут приєднання: {timeout} сек.",
|
||||||
|
"timeout_verify_invalid": "Неправильне використання! Правильний приклад:\n`/timeout_verify СЕКУНДИ`",
|
||||||
|
"timeout_verify_set": "Встановлено таймаут перевірки: {timeout} сек.",
|
||||||
"verify": "Будь ласка, використовуйте кнопки нижче, щоб заповнити капчу. Натисніть на кнопки зі смайликами, які ви бачите на цьому зображенні.",
|
"verify": "Будь ласка, використовуйте кнопки нижче, щоб заповнити капчу. Натисніть на кнопки зі смайликами, які ви бачите на цьому зображенні.",
|
||||||
"welcome_verify": "Ласкаво просимо, {mention}! Для того, щоб спілкуватися в чаті, ми повинні переконатися, що ви людина. Будь ласка, натисніть кнопку нижче, щоб почати перевірку.",
|
"welcome_verify": "Ласкаво просимо, {mention}! Для того, щоб спілкуватися в чаті, ми повинні переконатися, що ви людина. Будь ласка, натисніть кнопку нижче, щоб почати перевірку.",
|
||||||
"welcome": "Ласкаво просимо до чату, {mention}!"
|
"welcome": "Ласкаво просимо до чату, {mention}!"
|
||||||
@@ -9,11 +38,13 @@
|
|||||||
"captcha_failed_force": "Ви змусили {user_id} не пройти капчу.",
|
"captcha_failed_force": "Ви змусили {user_id} не пройти капчу.",
|
||||||
"captcha_mistake": "Неправильна відповідь. Спроби, що залишилися: {remaining}.",
|
"captcha_mistake": "Неправильна відповідь. Спроби, що залишилися: {remaining}.",
|
||||||
"captcha_passed": "Ви пройшли капчу. Ласкаво просимо!",
|
"captcha_passed": "Ви пройшли капчу. Ласкаво просимо!",
|
||||||
|
"locale_set": "Встановлено мову групи: {locale}",
|
||||||
"nothing": "Цю дію вже завершено.",
|
"nothing": "Цю дію вже завершено.",
|
||||||
"wrong_user": "Це повідомлення не для вас."
|
"wrong_user": "Це повідомлення не для вас."
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"ban": "Заблокувати (для адмінів)",
|
"ban": "Заблокувати (для адмінів)",
|
||||||
|
"locale_default": "За замовчуванням",
|
||||||
"verify": "Верифікуватись"
|
"verify": "Верифікуватись"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,11 +1,9 @@
|
|||||||
"""Module that provides all database columns"""
|
"""Module that provides all database columns"""
|
||||||
|
|
||||||
from pymongo import MongoClient
|
from async_pymongo import AsyncClient
|
||||||
from ujson import loads
|
from libbot import sync
|
||||||
|
|
||||||
with open("config.json", "r", encoding="utf-8") as f:
|
db_config = sync.config_get("database")
|
||||||
db_config = loads(f.read())["database"]
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
if db_config["user"] is not None and db_config["password"] is not None:
|
if db_config["user"] is not None and db_config["password"] is not None:
|
||||||
con_string = "mongodb://{0}:{1}@{2}:{3}/{4}".format(
|
con_string = "mongodb://{0}:{1}@{2}:{3}/{4}".format(
|
||||||
@@ -20,14 +18,9 @@ else:
|
|||||||
db_config["host"], db_config["port"], db_config["name"]
|
db_config["host"], db_config["port"], db_config["name"]
|
||||||
)
|
)
|
||||||
|
|
||||||
db_client = MongoClient(con_string)
|
db_client = AsyncClient(con_string)
|
||||||
db = db_client.get_database(name=db_config["name"])
|
db = db_client.get_database(name=db_config["name"])
|
||||||
|
|
||||||
collections = db.list_collection_names()
|
|
||||||
|
|
||||||
for collection in ["users", "schedule"]:
|
|
||||||
if collection not in collections:
|
|
||||||
db.create_collection(collection)
|
|
||||||
|
|
||||||
col_users = db.get_collection("users")
|
col_users = db.get_collection("users")
|
||||||
|
col_groups = db.get_collection("groups")
|
||||||
col_schedule = db.get_collection("schedule")
|
col_schedule = db.get_collection("schedule")
|
||||||
|
@@ -7,13 +7,16 @@ async def kick_unstarted(
|
|||||||
app: PyroClient, user_id: int, group_id: int, message_id: int
|
app: PyroClient, user_id: int, group_id: int, message_id: int
|
||||||
) -> None:
|
) -> None:
|
||||||
user = await app.find_user(user_id, group_id)
|
user = await app.find_user(user_id, group_id)
|
||||||
|
group = await app.find_group(group_id)
|
||||||
|
|
||||||
if user.score == 0 and user.failed == 0:
|
if user.score == 0 and user.failed == 0:
|
||||||
|
if group.ban_failed:
|
||||||
banned = await app.ban_chat_member(group_id, user_id)
|
banned = await app.ban_chat_member(group_id, user_id)
|
||||||
|
|
||||||
if isinstance(banned, Message):
|
if isinstance(banned, Message):
|
||||||
await banned.delete()
|
await banned.delete()
|
||||||
|
|
||||||
|
await user.set_failed(True)
|
||||||
await app.delete_messages(group_id, message_id)
|
await app.delete_messages(group_id, message_id)
|
||||||
|
|
||||||
|
|
||||||
@@ -21,11 +24,14 @@ async def kick_unverified(
|
|||||||
app: PyroClient, user_id: int, group_id: int, message_id: int
|
app: PyroClient, user_id: int, group_id: int, message_id: int
|
||||||
) -> None:
|
) -> None:
|
||||||
user = await app.find_user(user_id, group_id)
|
user = await app.find_user(user_id, group_id)
|
||||||
|
group = await app.find_group(group_id)
|
||||||
|
|
||||||
if user.score < 6 or user.failed:
|
if user.score < 6 or user.failed:
|
||||||
|
if group.ban_failed:
|
||||||
banned = await app.ban_chat_member(group_id, user_id)
|
banned = await app.ban_chat_member(group_id, user_id)
|
||||||
|
|
||||||
if isinstance(banned, Message):
|
if isinstance(banned, Message):
|
||||||
await banned.delete()
|
await banned.delete()
|
||||||
|
|
||||||
|
await user.set_failed(True)
|
||||||
await app.delete_messages(group_id, message_id)
|
await app.delete_messages(group_id, message_id)
|
||||||
|
@@ -1,12 +1,16 @@
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from random import randint, sample
|
from random import randint, sample
|
||||||
from typing import List
|
from typing import List, Union
|
||||||
|
|
||||||
from huepaper import generate
|
from huepaper import generate
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from pyrogram.enums.chat_member_status import ChatMemberStatus
|
||||||
|
from pyrogram.types import CallbackQuery, Message
|
||||||
|
|
||||||
from classes.captcha import Captcha
|
from classes.captcha import Captcha
|
||||||
|
from classes.pyroclient import PyroClient
|
||||||
|
from classes.pyrogroup import PyroGroup
|
||||||
|
|
||||||
|
|
||||||
def get_captcha_image(emojis: List[str]) -> Captcha:
|
def get_captcha_image(emojis: List[str]) -> Captcha:
|
||||||
@@ -49,3 +53,40 @@ def get_captcha_image(emojis: List[str]) -> Captcha:
|
|||||||
base_img.save(output, format="jpeg")
|
base_img.save(output, format="jpeg")
|
||||||
|
|
||||||
return Captcha(output, emojis_all, emojis_correct)
|
return Captcha(output, emojis_all, emojis_correct)
|
||||||
|
|
||||||
|
|
||||||
|
async def is_permitted(
|
||||||
|
app: PyroClient,
|
||||||
|
group: PyroGroup,
|
||||||
|
message: Union[Message, None] = None,
|
||||||
|
callback: Union[CallbackQuery, None] = None,
|
||||||
|
) -> bool:
|
||||||
|
"""Check if User is an admin or a creator of a group. Alternatively, if the User is actually a group itself.
|
||||||
|
|
||||||
|
### Args:
|
||||||
|
* app (`PyroClient`): Pyrogram Client
|
||||||
|
* group (`PyroGroup`): Group
|
||||||
|
* message (`Union[Message, None]`, *optional*): Message if the request originates from a command. Defaults to `None`.
|
||||||
|
* callback (`Union[CallbackQuery, None]`, *optional*): CallbackQuery if the request originates from a callback. Defaults to `None`.
|
||||||
|
|
||||||
|
### Returns:
|
||||||
|
* `bool`: `True` if permitted and `False` if not. Also `False` if no message or callback provided.
|
||||||
|
"""
|
||||||
|
if message is not None:
|
||||||
|
return (
|
||||||
|
message.sender_chat is not None and message.sender_chat.id == group.id
|
||||||
|
) or (
|
||||||
|
message.from_user is not None
|
||||||
|
and (await app.get_chat_member(group.id, message.from_user.id)).status
|
||||||
|
) in [
|
||||||
|
ChatMemberStatus.ADMINISTRATOR,
|
||||||
|
ChatMemberStatus.OWNER,
|
||||||
|
]
|
||||||
|
|
||||||
|
if callback is not None:
|
||||||
|
return (await app.get_chat_member(group.id, callback.from_user.id)).status in [
|
||||||
|
ChatMemberStatus.ADMINISTRATOR,
|
||||||
|
ChatMemberStatus.OWNER,
|
||||||
|
]
|
||||||
|
|
||||||
|
return False
|
||||||
|
@@ -1,25 +1,29 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pyrogram import filters
|
from pyrogram import filters
|
||||||
from pyrogram.enums.chat_member_status import ChatMemberStatus
|
|
||||||
from pyrogram.types import CallbackQuery, Message
|
from pyrogram.types import CallbackQuery, Message
|
||||||
|
|
||||||
|
from classes.callbacks import CallbackBan
|
||||||
from classes.pyroclient import PyroClient
|
from classes.pyroclient import PyroClient
|
||||||
|
from modules.utils import is_permitted
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_callback_query(filters.regex(r"ban;[\s\S]*"))
|
@PyroClient.on_callback_query(filters.regex(r"ban:[\s\S]*")) # type: ignore
|
||||||
async def callback_ban(app: PyroClient, callback: CallbackQuery):
|
async def callback_ban(app: PyroClient, callback: CallbackQuery):
|
||||||
if (
|
group = await app.find_group(callback.message.chat.id)
|
||||||
await app.get_chat_member(callback.message.chat.id, callback.from_user.id)
|
locale = group.select_locale(app, callback.message.from_user)
|
||||||
).status not in [ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER]:
|
|
||||||
await callback.answer(app._("wrong_user", "callbacks"), show_alert=True)
|
if not (await is_permitted(app, group, callback=callback)):
|
||||||
|
await callback.answer(
|
||||||
|
app._("wrong_user", "callbacks", locale=locale), show_alert=True
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
user = await app.find_user(
|
parsed = CallbackBan.from_callback(callback)
|
||||||
int(str(callback.data).split(";")[1]), callback.message.chat.id
|
|
||||||
)
|
user = await app.find_user(parsed.user_id, group.id)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"User %s has been marked as failed the captcha by %s",
|
"User %s has been marked as failed the captcha by %s",
|
||||||
@@ -27,14 +31,16 @@ async def callback_ban(app: PyroClient, callback: CallbackQuery):
|
|||||||
callback.from_user.id,
|
callback.from_user.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
user.set_mistakes(3)
|
await user.set_mistakes(3)
|
||||||
user.set_failed(True)
|
await user.set_failed(True)
|
||||||
await callback.answer(
|
await callback.answer(
|
||||||
app._("captcha_failed_force", "callbacks").format(user_id=user.id),
|
app._("captcha_failed_force", "callbacks", locale=locale).format(
|
||||||
|
user_id=user.id
|
||||||
|
),
|
||||||
show_alert=True,
|
show_alert=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
banned = await app.ban_chat_member(callback.message.chat.id, user.id)
|
banned = await app.ban_chat_member(group.id, user.id)
|
||||||
|
|
||||||
if isinstance(banned, Message):
|
if isinstance(banned, Message):
|
||||||
await banned.delete()
|
await banned.delete()
|
||||||
|
@@ -9,54 +9,59 @@ from pyrogram.types import (
|
|||||||
Message,
|
Message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from classes.callbacks import CallbackEmoji
|
||||||
from classes.pyroclient import PyroClient
|
from classes.pyroclient import PyroClient
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_callback_query(filters.regex(r"emoji;[\s\S]*"))
|
@PyroClient.on_callback_query(filters.regex(r"emoji:[\s\S]*")) # type: ignore
|
||||||
async def callback_emoji_button(app: PyroClient, callback: CallbackQuery):
|
async def callback_emoji_button(app: PyroClient, callback: CallbackQuery):
|
||||||
user_id = int(str(callback.data).split(";")[1])
|
parsed = CallbackEmoji.from_callback(callback)
|
||||||
emoji = str(callback.data).split(";")[2]
|
group = await app.find_group(callback.message.chat.id)
|
||||||
|
locale = group.select_locale(app, callback.message.from_user)
|
||||||
|
|
||||||
if callback.from_user.id != user_id:
|
if callback.from_user.id != parsed.user_id:
|
||||||
await callback.answer(app._("wrong_user", "callbacks"), show_alert=True)
|
await callback.answer(
|
||||||
|
app._("wrong_user", "callbacks", locale=locale), show_alert=True
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
user = await app.find_user(callback.from_user, callback.message.chat.id)
|
user = await app.find_user(callback.from_user, group.id)
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"User %s has pressed the %s emoji '%s'",
|
"User %s has pressed the %s emoji '%s'",
|
||||||
user.id,
|
user.id,
|
||||||
"correct" if emoji in user.emojis else "wrong",
|
"correct" if parsed.emoji in user.emojis else "wrong",
|
||||||
emoji,
|
parsed.emoji,
|
||||||
)
|
)
|
||||||
|
|
||||||
if emoji in user.selected:
|
if parsed.emoji in user.selected:
|
||||||
await callback.answer()
|
await callback.answer()
|
||||||
return
|
return
|
||||||
|
|
||||||
user.update_selected(emoji)
|
await user.update_selected(parsed.emoji)
|
||||||
|
|
||||||
if emoji in user.emojis:
|
if parsed.emoji in user.emojis:
|
||||||
user.update_score(1)
|
await user.update_score(1)
|
||||||
|
|
||||||
if user.score >= 5:
|
if user.score >= 5:
|
||||||
logger.info("User %s has passed the captcha", user.id)
|
logger.info("User %s has passed the captcha", user.id)
|
||||||
|
|
||||||
await callback.message.delete()
|
await callback.message.delete()
|
||||||
await callback.answer(app._("captcha_passed", "callbacks"), show_alert=True)
|
await callback.answer(
|
||||||
|
app._("captcha_passed", "callbacks", locale=locale), show_alert=True
|
||||||
|
)
|
||||||
await app.send_message(
|
await app.send_message(
|
||||||
callback.message.chat.id,
|
group.id,
|
||||||
app._(
|
app._("welcome", "messages", locale=locale).format(
|
||||||
"welcome",
|
mention=callback.from_user.mention
|
||||||
"messages",
|
),
|
||||||
).format(mention=callback.from_user.mention),
|
|
||||||
)
|
)
|
||||||
await app.restrict_chat_member(
|
await app.restrict_chat_member(
|
||||||
chat_id=callback.message.chat.id,
|
chat_id=group.id,
|
||||||
user_id=callback.from_user.id,
|
user_id=callback.from_user.id,
|
||||||
permissions=ChatPermissions(can_send_messages=True),
|
permissions=callback.message.chat.permissions,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -71,20 +76,21 @@ async def callback_emoji_button(app: PyroClient, callback: CallbackQuery):
|
|||||||
if user.mistakes >= 2:
|
if user.mistakes >= 2:
|
||||||
logger.info("User %s has failed the captcha", user.id)
|
logger.info("User %s has failed the captcha", user.id)
|
||||||
|
|
||||||
user.set_failed(True)
|
await user.set_failed(True)
|
||||||
await callback.answer(app._("captcha_failed", "callbacks"), show_alert=True)
|
await callback.answer(
|
||||||
|
app._("captcha_failed", "callbacks", locale=locale), show_alert=True
|
||||||
banned = await app.ban_chat_member(
|
|
||||||
callback.message.chat.id, callback.from_user.id
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if group.ban_failed:
|
||||||
|
banned = await app.ban_chat_member(group.id, callback.from_user.id)
|
||||||
|
|
||||||
if isinstance(banned, Message):
|
if isinstance(banned, Message):
|
||||||
await banned.delete()
|
await banned.delete()
|
||||||
|
|
||||||
await callback.message.delete()
|
await callback.message.delete()
|
||||||
return
|
return
|
||||||
|
|
||||||
user.update_mistakes(1)
|
await user.update_mistakes(1)
|
||||||
logger.info(
|
logger.info(
|
||||||
"User %s has made a mistake and has %s attempt(s) left",
|
"User %s has made a mistake and has %s attempt(s) left",
|
||||||
user.id,
|
user.id,
|
||||||
@@ -92,7 +98,9 @@ async def callback_emoji_button(app: PyroClient, callback: CallbackQuery):
|
|||||||
)
|
)
|
||||||
|
|
||||||
await callback.answer(
|
await callback.answer(
|
||||||
app._("captcha_mistake", "callbacks").format(remaining=2 - user.mistakes),
|
app._("captcha_mistake", "callbacks", locale=locale).format(
|
||||||
|
remaining=2 - user.mistakes
|
||||||
|
),
|
||||||
show_alert=True,
|
show_alert=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -102,7 +110,7 @@ async def callback_emoji_button(app: PyroClient, callback: CallbackQuery):
|
|||||||
|
|
||||||
for row_index, row in enumerate(callback.message.reply_markup.inline_keyboard):
|
for row_index, row in enumerate(callback.message.reply_markup.inline_keyboard):
|
||||||
for button_index, button in enumerate(row):
|
for button_index, button in enumerate(row):
|
||||||
if button.text == emoji:
|
if button.text == parsed.emoji:
|
||||||
button_replace = (row_index, button_index)
|
button_replace = (row_index, button_index)
|
||||||
|
|
||||||
new_keyboard = callback.message.reply_markup.inline_keyboard
|
new_keyboard = callback.message.reply_markup.inline_keyboard
|
||||||
|
@@ -4,6 +4,9 @@ from pyrogram.types import CallbackQuery
|
|||||||
from classes.pyroclient import PyroClient
|
from classes.pyroclient import PyroClient
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_callback_query(filters.regex(r"nothing"))
|
@PyroClient.on_callback_query(filters.regex(r"nothing")) # type: ignore
|
||||||
async def callback_nothing(app: PyroClient, callback: CallbackQuery):
|
async def callback_nothing(app: PyroClient, callback: CallbackQuery):
|
||||||
await callback.answer(app._("nothing", "callbacks"))
|
group = await app.find_group(callback.message.chat.id)
|
||||||
|
locale = group.select_locale(app, callback.message.from_user)
|
||||||
|
|
||||||
|
await callback.answer(app._("nothing", "callbacks", locale=locale))
|
||||||
|
@@ -7,6 +7,7 @@ from pykeyboard import InlineButton, InlineKeyboard
|
|||||||
from pyrogram import filters
|
from pyrogram import filters
|
||||||
from pyrogram.types import CallbackQuery
|
from pyrogram.types import CallbackQuery
|
||||||
|
|
||||||
|
from classes.callbacks import CallbackVerify
|
||||||
from classes.pyroclient import PyroClient
|
from classes.pyroclient import PyroClient
|
||||||
from modules.database import col_schedule
|
from modules.database import col_schedule
|
||||||
from modules.kicker import kick_unverified
|
from modules.kicker import kick_unverified
|
||||||
@@ -15,15 +16,19 @@ from modules.utils import get_captcha_image
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_callback_query(filters.regex(r"verify;[\s\S]*"))
|
@PyroClient.on_callback_query(filters.regex(r"verify:[\s\S]*")) # type: ignore
|
||||||
async def callback_verify(app: PyroClient, callback: CallbackQuery):
|
async def callback_verify(app: PyroClient, callback: CallbackQuery):
|
||||||
user_id = int(str(callback.data).split(";")[1])
|
parsed = CallbackVerify.from_callback(callback)
|
||||||
|
group = await app.find_group(callback.message.chat.id)
|
||||||
|
locale = group.select_locale(app, callback.message.from_user)
|
||||||
|
|
||||||
if callback.from_user.id != user_id:
|
if callback.from_user.id != parsed.user_id:
|
||||||
await callback.answer(app._("wrong_user", "callbacks"), show_alert=True)
|
await callback.answer(
|
||||||
|
app._("wrong_user", "callbacks", locale=locale), show_alert=True
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
user = await app.find_user(callback.from_user, callback.message.chat.id)
|
user = await app.find_user(callback.from_user, group.id)
|
||||||
captcha = get_captcha_image(app.config["emojis"])
|
captcha = get_captcha_image(app.config["emojis"])
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -33,18 +38,18 @@ async def callback_verify(app: PyroClient, callback: CallbackQuery):
|
|||||||
captcha.emojis_correct,
|
captcha.emojis_correct,
|
||||||
)
|
)
|
||||||
|
|
||||||
scheduled_job = col_schedule.find_one_and_delete(
|
scheduled_job = await col_schedule.find_one_and_delete(
|
||||||
{"user": user_id, "group": callback.message.chat.id}
|
{"user": parsed.user_id, "group": group.id}
|
||||||
)
|
)
|
||||||
|
|
||||||
if scheduled_job is not None and app.scheduler is not None:
|
if scheduled_job is not None and app.scheduler is not None:
|
||||||
with contextlib.suppress(JobLookupError):
|
with contextlib.suppress(JobLookupError):
|
||||||
app.scheduler.remove_job(scheduled_job["job_id"])
|
app.scheduler.remove_job(scheduled_job["job_id"])
|
||||||
|
|
||||||
user.set_emojis(captcha.emojis_correct)
|
await user.set_emojis(captcha.emojis_correct)
|
||||||
|
|
||||||
buttons = [
|
buttons = [
|
||||||
InlineButton(emoji, f"emoji;{user.id};{emoji}") for emoji in captcha.emojis_all
|
InlineButton(emoji, f"emoji:{user.id}:{emoji}") for emoji in captcha.emojis_all
|
||||||
]
|
]
|
||||||
|
|
||||||
keyboard = InlineKeyboard(3)
|
keyboard = InlineKeyboard(3)
|
||||||
@@ -53,9 +58,9 @@ async def callback_verify(app: PyroClient, callback: CallbackQuery):
|
|||||||
await callback.message.delete()
|
await callback.message.delete()
|
||||||
|
|
||||||
captcha_message = await app.send_photo(
|
captcha_message = await app.send_photo(
|
||||||
callback.message.chat.id,
|
group.id,
|
||||||
captcha.image,
|
captcha.image,
|
||||||
caption=app._("verify", "messages"),
|
caption=app._("verify", "messages", locale=locale),
|
||||||
reply_markup=keyboard,
|
reply_markup=keyboard,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -65,7 +70,6 @@ async def callback_verify(app: PyroClient, callback: CallbackQuery):
|
|||||||
app.scheduler.add_job(
|
app.scheduler.add_job(
|
||||||
kick_unverified,
|
kick_unverified,
|
||||||
"date",
|
"date",
|
||||||
[app, user.id, callback.message.chat.id, captcha_message.id],
|
[app, user.id, group.id, captcha_message.id],
|
||||||
run_date=datetime.now()
|
run_date=datetime.now() + timedelta(seconds=group.timeout_verify),
|
||||||
+ timedelta(seconds=app.config["timeouts"]["verify"]),
|
|
||||||
)
|
)
|
||||||
|
40
plugins/commands/ban_failed.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from pyrogram import filters
|
||||||
|
from pyrogram.types import Message
|
||||||
|
|
||||||
|
from classes.pyroclient import PyroClient
|
||||||
|
from modules.utils import is_permitted
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@PyroClient.on_message(
|
||||||
|
~filters.scheduled
|
||||||
|
& filters.group
|
||||||
|
& filters.command(["ban_failed"], prefixes=["/"]) # type: ignore
|
||||||
|
)
|
||||||
|
async def command_ban_failed(app: PyroClient, message: Message):
|
||||||
|
group = await app.find_group(message.chat.id)
|
||||||
|
locale = group.select_locale(app, message.from_user)
|
||||||
|
|
||||||
|
if not (await is_permitted(app, group, message=message)):
|
||||||
|
await message.reply_text(
|
||||||
|
app._("permission_denied", "messages", locale=locale), quote=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if group.ban_failed:
|
||||||
|
await message.reply_text(
|
||||||
|
app._("ban_failed_disabled", "messages", locale=locale)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await message.reply_text(app._("ban_failed_enabled", "messages", locale=locale))
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"Ban on fail in group %s has been set to %s",
|
||||||
|
group.id,
|
||||||
|
not group.ban_failed,
|
||||||
|
)
|
||||||
|
|
||||||
|
await group.set_ban_failed(not group.ban_failed)
|
20
plugins/commands/help.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from pyrogram import filters
|
||||||
|
from pyrogram.types import Message
|
||||||
|
|
||||||
|
from classes.pyroclient import PyroClient
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@PyroClient.on_message(
|
||||||
|
~filters.scheduled
|
||||||
|
& filters.private
|
||||||
|
& filters.command(["help", "start"], prefixes=["/"]) # type: ignore
|
||||||
|
)
|
||||||
|
async def command_help(app: PyroClient, message: Message):
|
||||||
|
await message.reply_text(
|
||||||
|
app._("help", "messages", locale=message.from_user.language_code),
|
||||||
|
disable_web_page_preview=True,
|
||||||
|
)
|
48
plugins/commands/language_auto.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from pyrogram import filters
|
||||||
|
from pyrogram.enums.chat_member_status import ChatMemberStatus
|
||||||
|
from pyrogram.types import Message
|
||||||
|
|
||||||
|
from classes.pyroclient import PyroClient
|
||||||
|
from modules.utils import is_permitted
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@PyroClient.on_message(
|
||||||
|
~filters.scheduled
|
||||||
|
& filters.group
|
||||||
|
& filters.command(["language_auto"], prefixes=["/"]) # type: ignore
|
||||||
|
)
|
||||||
|
async def command_language_auto(app: PyroClient, message: Message):
|
||||||
|
group = await app.find_group(message.chat.id)
|
||||||
|
locale = group.select_locale(app, message.from_user)
|
||||||
|
|
||||||
|
if not (await is_permitted(app, group, message=message)):
|
||||||
|
await message.reply_text(
|
||||||
|
app._("permission_denied", "messages", locale=locale), quote=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if group.locale_auto:
|
||||||
|
await message.reply_text(
|
||||||
|
app._("locale_auto_disabled", "messages", locale=locale).format(
|
||||||
|
default_locale=f"{app._('flag', 'metadata', locale=app.default_locale)} {app._('name', 'metadata', locale=app.default_locale)}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await message.reply_text(
|
||||||
|
app._("locale_auto_enabled", "messages", locale=locale).format(
|
||||||
|
default_locale=f"{app._('flag', 'metadata', locale=app.default_locale)} {app._('name', 'metadata', locale=app.default_locale)}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"Automatic locale selection of group %s has been set to %s (group: %s)",
|
||||||
|
group.id,
|
||||||
|
not group.locale_auto,
|
||||||
|
group.locale,
|
||||||
|
)
|
||||||
|
|
||||||
|
await group.set_locale_auto(not group.locale_auto)
|
51
plugins/commands/timeout_join.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from pyrogram import filters
|
||||||
|
from pyrogram.types import Message
|
||||||
|
|
||||||
|
from classes.pyroclient import PyroClient
|
||||||
|
from modules.utils import is_permitted
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@PyroClient.on_message(
|
||||||
|
~filters.scheduled
|
||||||
|
& filters.group
|
||||||
|
& filters.command(["timeout_join"], prefixes=["/"]) # type: ignore
|
||||||
|
)
|
||||||
|
async def command_timeout_join(app: PyroClient, message: Message):
|
||||||
|
group = await app.find_group(message.chat.id)
|
||||||
|
locale = group.select_locale(app, message.from_user)
|
||||||
|
|
||||||
|
if not (await is_permitted(app, group, message=message)):
|
||||||
|
await message.reply_text(
|
||||||
|
app._("permission_denied", "messages", locale=locale), quote=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(message.command) < 2 or not message.command[1].isdigit():
|
||||||
|
await message.reply_text(
|
||||||
|
app._("timeout_join_invalid", "messages", locale=locale), quote=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
timeout = int(message.command[1])
|
||||||
|
|
||||||
|
if timeout < 10 or timeout > 600:
|
||||||
|
await message.reply_text(
|
||||||
|
app._("timeout_invalid_number", "messages", locale=locale), quote=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
await message.reply_text(
|
||||||
|
app._("timeout_join_set", "messages", locale=locale).format(timeout=timeout)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"Timeout on join in group %s has been set to %s",
|
||||||
|
group.id,
|
||||||
|
timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
await group.set_timeout_join(timeout)
|
51
plugins/commands/timeout_verify.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from pyrogram import filters
|
||||||
|
from pyrogram.types import Message
|
||||||
|
|
||||||
|
from classes.pyroclient import PyroClient
|
||||||
|
from modules.utils import is_permitted
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@PyroClient.on_message(
|
||||||
|
~filters.scheduled
|
||||||
|
& filters.group
|
||||||
|
& filters.command(["timeout_verify"], prefixes=["/"]) # type: ignore
|
||||||
|
)
|
||||||
|
async def command_timeout_verify(app: PyroClient, message: Message):
|
||||||
|
group = await app.find_group(message.chat.id)
|
||||||
|
locale = group.select_locale(app, message.from_user)
|
||||||
|
|
||||||
|
if not (await is_permitted(app, group, message=message)):
|
||||||
|
await message.reply_text(
|
||||||
|
app._("permission_denied", "messages", locale=locale), quote=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(message.command) < 2 or not message.command[1].isdigit():
|
||||||
|
await message.reply_text(
|
||||||
|
app._("timeout_verify_invalid", "messages", locale=locale), quote=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
timeout = int(message.command[1])
|
||||||
|
|
||||||
|
if timeout < 10 or timeout > 600:
|
||||||
|
await message.reply_text(
|
||||||
|
app._("timeout_invalid_number", "messages", locale=locale), quote=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
await message.reply_text(
|
||||||
|
app._("timeout_verify_set", "messages", locale=locale).format(timeout=timeout)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"Verification timeout in group %s has been set to %s",
|
||||||
|
group.id,
|
||||||
|
timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
await group.set_timeout_verify(timeout)
|
14
plugins/handlers/bot_join.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from pyrogram import filters
|
||||||
|
from pyrogram.types import Message
|
||||||
|
|
||||||
|
from classes.pyroclient import PyroClient
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@PyroClient.on_message(filters.new_chat_members & filters.group & filters.me) # type: ignore
|
||||||
|
async def handler_bot_join(app: PyroClient, message: Message):
|
||||||
|
logger.info("Bot has joined the group %s")
|
||||||
|
await app.find_group(message.chat.id)
|
@@ -12,41 +12,44 @@ from pyrogram.types import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from classes.pyroclient import PyroClient
|
from classes.pyroclient import PyroClient
|
||||||
from classes.pyrouser import PyroUser
|
|
||||||
from modules.database import col_schedule
|
from modules.database import col_schedule
|
||||||
from modules.kicker import kick_unstarted
|
from modules.kicker import kick_unstarted
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@PyroClient.on_message(filters.new_chat_members & ~filters.me)
|
@PyroClient.on_message(
|
||||||
|
filters.new_chat_members & filters.group & ~filters.me & ~filters.bot # type: ignore
|
||||||
|
)
|
||||||
async def handler_user_join(app: PyroClient, message: Message):
|
async def handler_user_join(app: PyroClient, message: Message):
|
||||||
|
group = await app.find_group(message.chat.id)
|
||||||
|
locale = group.select_locale(app, message.from_user)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
app.config["whitelist"]["enabled"]
|
app.config["whitelist"]["enabled"]
|
||||||
and message.chat.id not in app.config["whitelist"]["groups"]
|
and group.id not in app.config["whitelist"]["groups"]
|
||||||
):
|
):
|
||||||
logger.info(
|
logger.info(
|
||||||
"User %s has joined the group %s, but it's not whitelisted, ignoring.",
|
"User %s has joined the group %s, but it's not whitelisted, ignoring.",
|
||||||
message.from_user.id,
|
message.from_user.id,
|
||||||
message.chat.id,
|
group.id,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info(
|
logger.info("User %s has joined the group %s", message.from_user.id, group.id)
|
||||||
"User %s has joined the group %s", message.from_user.id, message.chat.id
|
|
||||||
)
|
|
||||||
|
|
||||||
await message.delete()
|
await message.delete()
|
||||||
|
|
||||||
# If user has already failed the test and joined once more
|
# If user has already failed the test and joined once more
|
||||||
with contextlib.suppress(KeyError):
|
with contextlib.suppress(KeyError):
|
||||||
user = await app.find_user(message.from_user, message.chat.id)
|
user = await app.find_user(message.from_user, group.id)
|
||||||
if user.failed is True:
|
if user.failed is True:
|
||||||
logger.info(
|
logger.info(
|
||||||
"User %s has previously failed the captcha, kicking and banning him",
|
"User %s has previously failed the captcha, kicking and banning him",
|
||||||
user.id,
|
user.id,
|
||||||
)
|
)
|
||||||
banned = await app.ban_chat_member(message.chat.id, user.id)
|
if group.ban_failed:
|
||||||
|
banned = await app.ban_chat_member(group.id, user.id)
|
||||||
|
|
||||||
if isinstance(banned, Message):
|
if isinstance(banned, Message):
|
||||||
await banned.delete()
|
await banned.delete()
|
||||||
@@ -54,42 +57,38 @@ async def handler_user_join(app: PyroClient, message: Message):
|
|||||||
return
|
return
|
||||||
|
|
||||||
await app.restrict_chat_member(
|
await app.restrict_chat_member(
|
||||||
chat_id=message.chat.id,
|
chat_id=group.id,
|
||||||
user_id=message.from_user.id,
|
user_id=message.from_user.id,
|
||||||
permissions=ChatPermissions(can_send_messages=False),
|
permissions=ChatPermissions(can_send_messages=False),
|
||||||
)
|
)
|
||||||
|
|
||||||
user = PyroUser.create_if_not_exists(message.from_user.id, message.chat.id)
|
user = await app.find_user(message.from_user, group=group.id)
|
||||||
|
|
||||||
if user.mistakes > 0 or user.score > 0:
|
if user.mistakes > 0 or user.score > 0:
|
||||||
user.set_score(0)
|
await user.set_score(0)
|
||||||
user.set_mistakes(0)
|
await user.set_mistakes(0)
|
||||||
|
|
||||||
|
if len(user.selected) > 0:
|
||||||
|
await user.set_selected([])
|
||||||
|
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
verification_request = await app.send_message(
|
verification_request = await app.send_message(
|
||||||
chat_id=message.chat.id,
|
chat_id=group.id,
|
||||||
text=app._(
|
text=app._("welcome", "messages", locale=locale).format(
|
||||||
"welcome",
|
mention=message.from_user.mention
|
||||||
"messages",
|
),
|
||||||
).format(mention=message.from_user.mention),
|
|
||||||
reply_markup=InlineKeyboardMarkup(
|
reply_markup=InlineKeyboardMarkup(
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
InlineKeyboardButton(
|
InlineKeyboardButton(
|
||||||
app._(
|
app._("verify", "buttons", locale=locale),
|
||||||
"verify",
|
callback_data=f"verify:{user.id}",
|
||||||
"buttons",
|
|
||||||
),
|
|
||||||
callback_data=f"verify;{message.from_user.id}",
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
InlineKeyboardButton(
|
InlineKeyboardButton(
|
||||||
app._(
|
app._("ban", "buttons", locale=locale),
|
||||||
"ban",
|
callback_data=f"ban:{user.id}",
|
||||||
"buttons",
|
|
||||||
),
|
|
||||||
callback_data=f"ban;{message.from_user.id}",
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -101,8 +100,8 @@ async def handler_user_join(app: PyroClient, message: Message):
|
|||||||
kick_unstarted,
|
kick_unstarted,
|
||||||
"date",
|
"date",
|
||||||
[app, user.id, verification_request.chat.id, verification_request.id],
|
[app, user.id, verification_request.chat.id, verification_request.id],
|
||||||
run_date=datetime.now() + timedelta(seconds=app.config["timeouts"]["join"]),
|
run_date=datetime.now() + timedelta(seconds=group.timeout_join),
|
||||||
)
|
)
|
||||||
col_schedule.insert_one(
|
await col_schedule.insert_one(
|
||||||
{"user": message.from_user.id, "group": message.chat.id, "job_id": job.id}
|
{"user": int(user.id), "group": int(group.id), "job_id": job.id}
|
||||||
)
|
)
|
||||||
|
84
plugins/language.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from pykeyboard import InlineButton, InlineKeyboard
|
||||||
|
from pyrogram import filters
|
||||||
|
from pyrogram.client import Client
|
||||||
|
from pyrogram.types import CallbackQuery, Message
|
||||||
|
|
||||||
|
from classes.callbacks import CallbackLanguage
|
||||||
|
from classes.pyroclient import PyroClient
|
||||||
|
from modules.utils import is_permitted
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@PyroClient.on_message(
|
||||||
|
~filters.scheduled & filters.group & filters.command(["language"], prefixes=["/"]) # type: ignore
|
||||||
|
)
|
||||||
|
async def command_language(app: PyroClient, message: Message):
|
||||||
|
group = await app.find_group(message.chat.id)
|
||||||
|
locale = group.select_locale(app, message.from_user)
|
||||||
|
|
||||||
|
if not (await is_permitted(app, group, message=message)):
|
||||||
|
await message.reply_text(
|
||||||
|
app._("permission_denied", "messages", locale=locale), quote=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
keyboard = InlineKeyboard(row_width=2)
|
||||||
|
buttons = [
|
||||||
|
InlineButton(f"{data['flag']} {data['name']}", f"language:{language}")
|
||||||
|
for language, data in app.in_every_locale("metadata").items()
|
||||||
|
if data["selectable"]
|
||||||
|
]
|
||||||
|
buttons.append(
|
||||||
|
InlineButton(
|
||||||
|
f"🤖 {app._('locale_default', 'buttons', locale=locale)}", "language:default"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard.add(*buttons)
|
||||||
|
|
||||||
|
await message.reply_text(
|
||||||
|
app._("locale_choice", "messages", locale=locale),
|
||||||
|
reply_markup=keyboard,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@Client.on_callback_query(filters.regex(r"language:[\s\S]*")) # type: ignore
|
||||||
|
async def callback_language(app: PyroClient, callback: CallbackQuery):
|
||||||
|
group = await app.find_group(callback.message.chat.id)
|
||||||
|
locale = group.select_locale(app, callback.message.from_user)
|
||||||
|
|
||||||
|
if not (await is_permitted(app, group, callback=callback)):
|
||||||
|
await callback.answer(
|
||||||
|
app._("wrong_user", "callbacks", locale=locale), show_alert=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
parsed = CallbackLanguage.from_callback(callback)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"Locale of group %s has been set to %s (auto: %s)",
|
||||||
|
group.id,
|
||||||
|
parsed.language,
|
||||||
|
group.locale_auto,
|
||||||
|
)
|
||||||
|
|
||||||
|
if parsed.language == "default":
|
||||||
|
await group.set_locale(None)
|
||||||
|
await callback.answer(
|
||||||
|
app._("locale_set", "callbacks", locale=app.default_locale).format(
|
||||||
|
locale=app._("locale_default", "buttons", locale=locale)
|
||||||
|
),
|
||||||
|
show_alert=True,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
await group.set_locale(parsed.language)
|
||||||
|
await callback.answer(
|
||||||
|
app._("locale_set", "callbacks", locale=parsed.language).format(
|
||||||
|
locale=app._("name", "metadata", locale=parsed.language)
|
||||||
|
),
|
||||||
|
show_alert=True,
|
||||||
|
)
|
@@ -1,13 +1,8 @@
|
|||||||
aiofiles~=23.1.0
|
async_pymongo==0.1.6
|
||||||
apscheduler~=3.10.1
|
Pillow~=10.4.0
|
||||||
black~=23.7.0
|
pykeyboard==0.1.7
|
||||||
Pillow~=10.0.0
|
|
||||||
pykeyboard==0.1.5
|
|
||||||
pymongo==4.4.1
|
|
||||||
pyrogram==2.0.106
|
|
||||||
tgcrypto==1.2.5
|
tgcrypto==1.2.5
|
||||||
ujson==5.8.0
|
uvloop==0.20.0
|
||||||
uvloop==0.17.0
|
|
||||||
--extra-index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple
|
--extra-index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple
|
||||||
huepaper==0.0.3
|
huepaper==0.0.5
|
||||||
libbot[speed,pyrogram]==2.0.0
|
libbot[speed,pyrogram]==3.2.3
|