Compare commits
17 Commits
v0.2
...
6bd1234d3d
Author | SHA1 | Date | |
---|---|---|---|
6bd1234d3d
|
|||
154db69f20 | |||
e719da7750 | |||
1d88076285
|
|||
5e8506cc12
|
|||
235fa37252
|
|||
176f5d35c3
|
|||
cd26990b7e
|
|||
5b56919b80 | |||
0e9bed1277 | |||
fb37da4195
|
|||
065f704923 | |||
a45f6b620f | |||
fe8b562a7e | |||
63164d2169 | |||
be288776d9
|
|||
0c73c51936
|
@@ -50,7 +50,7 @@ class PyroClient(PyroClient):
|
||||
def __init__(self, scheduler: AsyncIOScheduler):
|
||||
super().__init__(locales_root=Path("locale"), scheduler=scheduler)
|
||||
|
||||
self.version: float = 0.2
|
||||
self.version: float = 0.3
|
||||
|
||||
self.owner: int = self.config["bot"]["owner"]
|
||||
self.admins: List[int] = self.config["bot"]["admins"] + [
|
||||
@@ -104,8 +104,8 @@ class PyroClient(PyroClient):
|
||||
logger.warning(
|
||||
"Could not send startup message to bot owner. Perhaps user has not started the bot yet."
|
||||
)
|
||||
except Exception as exp:
|
||||
logger.exception("Update check failed due to %s: %s", exp, format_exc())
|
||||
except Exception as exc:
|
||||
logger.exception("Update check failed due to %s: %s", exc, format_exc())
|
||||
|
||||
if self.config["mode"]["post"]:
|
||||
if self.config["posting"]["use_interval"]:
|
||||
@@ -141,7 +141,7 @@ class PyroClient(PyroClient):
|
||||
async def submit_media(
|
||||
self, id: str
|
||||
) -> Tuple[Union[Message, None], Union[str, None]]:
|
||||
db_entry = col_submitted.find_one({"_id": ObjectId(id)})
|
||||
db_entry = await col_submitted.find_one({"_id": ObjectId(id)})
|
||||
submission = None
|
||||
|
||||
if db_entry is None:
|
||||
@@ -155,8 +155,8 @@ class PyroClient(PyroClient):
|
||||
filepath = await self.download_media(
|
||||
submission, file_name=self.config["locations"]["tmp"] + sep
|
||||
)
|
||||
except Exception as exp:
|
||||
raise SubmissionUnavailableError() from exp
|
||||
except Exception as exc:
|
||||
raise SubmissionUnavailableError() from exc
|
||||
|
||||
elif not Path(
|
||||
f"{self.config['locations']['data']}/submissions/{db_entry['temp']['uuid']}/{db_entry['temp']['file']}",
|
||||
@@ -204,8 +204,8 @@ class PyroClient(PyroClient):
|
||||
# ),
|
||||
# caption="queue",
|
||||
# )
|
||||
except UnexpectedStatus as exp:
|
||||
raise SubmissionUnsupportedError(str(filepath)) from exp
|
||||
except UnexpectedStatus as exc:
|
||||
raise SubmissionUnsupportedError(str(filepath)) from exc
|
||||
|
||||
response_dict = (
|
||||
{}
|
||||
@@ -226,7 +226,7 @@ class PyroClient(PyroClient):
|
||||
)
|
||||
raise SubmissionDuplicatesError(str(filepath), duplicates)
|
||||
|
||||
col_submitted.find_one_and_update(
|
||||
await col_submitted.find_one_and_update(
|
||||
{"_id": ObjectId(id)}, {"$set": {"done": True}}
|
||||
)
|
||||
|
||||
@@ -258,12 +258,12 @@ class PyroClient(PyroClient):
|
||||
* `PyroUser`: PyroUser object
|
||||
"""
|
||||
if (
|
||||
col_users.find_one(
|
||||
await col_users.find_one(
|
||||
{"id": user.id if isinstance(user, User) else user}
|
||||
) # type: ignore
|
||||
is None
|
||||
):
|
||||
col_users.insert_one(
|
||||
await col_users.insert_one(
|
||||
{
|
||||
"id": user.id if isinstance(user, User) else user,
|
||||
"locale": user.language_code if isinstance(user, User) else None,
|
||||
@@ -273,7 +273,7 @@ class PyroClient(PyroClient):
|
||||
}
|
||||
) # type: ignore
|
||||
|
||||
db_record = col_users.find_one(
|
||||
db_record = await col_users.find_one(
|
||||
{"id": user.id if isinstance(user, User) else user}
|
||||
) # type: ignore
|
||||
|
||||
|
@@ -20,19 +20,19 @@ class PyroUser:
|
||||
cooldown: datetime
|
||||
subscription: dict
|
||||
|
||||
async def update_locale(self, locale: str):
|
||||
col_users.update_one({"_id": self._id}, {"$set": {"locale": locale}})
|
||||
async def update_locale(self, locale: str) -> None:
|
||||
await col_users.update_one({"_id": self._id}, {"$set": {"locale": locale}})
|
||||
|
||||
async def update_cooldown(self, time: datetime = datetime.now()):
|
||||
col_users.update_one({"_id": self._id}, {"$set": {"cooldown": time}})
|
||||
async def update_cooldown(self, time: datetime = datetime.now()) -> None:
|
||||
await col_users.update_one({"_id": self._id}, {"$set": {"cooldown": time}})
|
||||
|
||||
async def block(self) -> None:
|
||||
"""Ban user from using command and submitting content."""
|
||||
col_users.update_one({"_id": self._id}, {"$set": {"banned": True}})
|
||||
await col_users.update_one({"_id": self._id}, {"$set": {"banned": True}})
|
||||
|
||||
async def unblock(self) -> None:
|
||||
"""Allow user to use command and submit posts again."""
|
||||
col_users.update_one({"_id": self._id}, {"$set": {"banned": False}})
|
||||
await col_users.update_one({"_id": self._id}, {"$set": {"banned": False}})
|
||||
|
||||
async def is_limited(self, app: Union[PyroClient, None] = None) -> bool:
|
||||
"""Check if user is on a cooldown after submitting something.
|
||||
@@ -41,11 +41,9 @@ class PyroUser:
|
||||
`bool`: Must be `True` if on the cooldown and `False` if not
|
||||
"""
|
||||
admins = (
|
||||
app.admins
|
||||
if app is not None
|
||||
else (
|
||||
await config_get("admins", "bot") + [await config_get("owner", "bot")]
|
||||
)
|
||||
await config_get("admins", "bot") + [await config_get("owner", "bot")]
|
||||
if app is None
|
||||
else app.admins
|
||||
)
|
||||
|
||||
return (datetime.now() - self.cooldown).total_seconds() < (
|
||||
|
@@ -6,7 +6,7 @@
|
||||
"api_id": 0,
|
||||
"api_hash": "",
|
||||
"bot_token": "",
|
||||
"max_concurrent_transmissions": 3,
|
||||
"max_concurrent_transmissions": 1,
|
||||
"scoped_commands": true
|
||||
},
|
||||
"database": {
|
||||
@@ -83,7 +83,8 @@
|
||||
"address_external": "https://photos.domain.com",
|
||||
"username": "",
|
||||
"password": "",
|
||||
"album": ""
|
||||
"album": "",
|
||||
"timeout": 15.0
|
||||
}
|
||||
},
|
||||
"caption": {
|
||||
|
@@ -118,7 +118,7 @@ async def authorize(custom_session: Union[ClientSession, None] = None) -> str:
|
||||
|
||||
unauthorized_client = Client(
|
||||
base_url=sync.config_get("address", "posting", "api"),
|
||||
timeout=5.0,
|
||||
timeout=sync.config_get("timeout", "posting", "api"),
|
||||
verify_ssl=True,
|
||||
raise_on_unexpected_status=True,
|
||||
follow_redirects=False,
|
||||
@@ -142,7 +142,7 @@ if not isinstance(login_token, Token):
|
||||
|
||||
client = AuthenticatedClient(
|
||||
base_url=sync.config_get("address", "posting", "api"),
|
||||
timeout=5.0,
|
||||
timeout=sync.config_get("timeout", "posting", "api"),
|
||||
verify_ssl=True,
|
||||
raise_on_unexpected_status=True,
|
||||
token=login_token.access_token,
|
||||
|
@@ -27,13 +27,14 @@ parser.add_argument("--create-album", action="store_true")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
unauthorized_client = Client(
|
||||
base_url=sync.config_get("address", "posting", "api"),
|
||||
timeout=5.0,
|
||||
verify_ssl=True,
|
||||
raise_on_unexpected_status=True,
|
||||
follow_redirects=False,
|
||||
)
|
||||
if args.create_user or args.create_album:
|
||||
unauthorized_client = Client(
|
||||
base_url=sync.config_get("address", "posting", "api"),
|
||||
timeout=5.0,
|
||||
verify_ssl=True,
|
||||
raise_on_unexpected_status=True,
|
||||
follow_redirects=False,
|
||||
)
|
||||
|
||||
|
||||
async def cli_create_user() -> None:
|
||||
@@ -57,8 +58,8 @@ async def cli_create_user() -> None:
|
||||
none = input(
|
||||
"Alright. If you have email confirmation enabled - please confirm registration by using the link in your email. After that press Enter. Otherwise just press Enter."
|
||||
)
|
||||
except Exception as exp:
|
||||
print(f"Could not create a user due to {exp}", flush=True)
|
||||
except Exception as exc:
|
||||
print(f"Could not create a user due to {exc}", flush=True)
|
||||
print_exc()
|
||||
exit()
|
||||
if not args.create_album:
|
||||
@@ -96,8 +97,8 @@ async def cli_create_album() -> None:
|
||||
result_2 = await album_create(client=client, name=name, title=title)
|
||||
# asyncio.run(create_album(name, title))
|
||||
await config_set("album", name, "posting", "api")
|
||||
except Exception as exp:
|
||||
print(f"Could not create an album due to {exp}", flush=True)
|
||||
except Exception as exc:
|
||||
print(f"Could not create an album due to {exc}", flush=True)
|
||||
print_exc()
|
||||
exit()
|
||||
print("You're done!", flush=True)
|
||||
|
18
modules/custom_filters.py
Normal file
18
modules/custom_filters.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""Custom message filters"""
|
||||
|
||||
from pyrogram import filters
|
||||
from pyrogram.types import Message
|
||||
|
||||
from classes.pyroclient import PyroClient
|
||||
|
||||
|
||||
async def _mode_post_func(_, __: PyroClient, message: Message):
|
||||
return __.config["mode"]["post"]
|
||||
|
||||
|
||||
async def _mode_submit_func(_, __: PyroClient, message: Message):
|
||||
return __.config["mode"]["submit"]
|
||||
|
||||
|
||||
mode_post = filters.create(_mode_post_func)
|
||||
mode_submit = filters.create(_mode_submit_func)
|
@@ -1,11 +1,9 @@
|
||||
"""Module that provides all database columns"""
|
||||
|
||||
from pymongo import MongoClient
|
||||
from ujson import loads
|
||||
from async_pymongo import AsyncClient
|
||||
from libbot import sync
|
||||
|
||||
with open("config.json", "r", encoding="utf-8") as f:
|
||||
db_config = loads(f.read())["database"]
|
||||
f.close()
|
||||
db_config = sync.config_get("database")
|
||||
|
||||
if db_config["user"] is not None and db_config["password"] is not None:
|
||||
con_string = "mongodb://{0}:{1}@{2}:{3}/{4}".format(
|
||||
@@ -20,15 +18,9 @@ else:
|
||||
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"])
|
||||
|
||||
collections = db.list_collection_names()
|
||||
|
||||
for collection in ["sent", "users", "submitted"]:
|
||||
if collection not in collections:
|
||||
db.create_collection(collection)
|
||||
|
||||
col_sent = db.get_collection("sent")
|
||||
col_users = db.get_collection("users")
|
||||
col_submitted = db.get_collection("submitted")
|
||||
|
@@ -77,12 +77,12 @@ async def send_content(app: PyroClient, http_session: ClientSession) -> None:
|
||||
|
||||
try:
|
||||
response: File = await func_iter[1](id=media.id, client=client)
|
||||
except Exception as exp:
|
||||
except Exception as exc:
|
||||
print_exc()
|
||||
logger.error("Media is invalid: %s", exp)
|
||||
logger.error("Media is invalid: %s", exc)
|
||||
if app.config["reports"]["error"]:
|
||||
await app.send_message(
|
||||
app.owner, f"Media is invalid: {exp}"
|
||||
app.owner, f"Media is invalid: {exc}"
|
||||
)
|
||||
return
|
||||
|
||||
@@ -103,11 +103,11 @@ async def send_content(app: PyroClient, http_session: ClientSession) -> None:
|
||||
).results[0]
|
||||
try:
|
||||
response: File = await func[1](id=media.id, client=client)
|
||||
except Exception as exp:
|
||||
except Exception as exc:
|
||||
print_exc()
|
||||
logger.error("Media is invalid: %s", exp)
|
||||
logger.error("Media is invalid: %s", exc)
|
||||
if app.config["reports"]["error"]:
|
||||
await app.send_message(app.owner, f"Media is invalid: {exp}")
|
||||
await app.send_message(app.owner, f"Media is invalid: {exc}")
|
||||
return
|
||||
|
||||
except (KeyError, AttributeError, TypeError, IndexError):
|
||||
@@ -179,7 +179,7 @@ async def send_content(app: PyroClient, http_session: ClientSession) -> None:
|
||||
|
||||
del response
|
||||
|
||||
submitted = col_submitted.find_one({"temp.file": media.filename})
|
||||
submitted = await col_submitted.find_one({"temp.file": media.filename})
|
||||
|
||||
if submitted is not None and submitted["caption"] is not None:
|
||||
caption = submitted["caption"].strip()
|
||||
@@ -217,19 +217,19 @@ async def send_content(app: PyroClient, http_session: ClientSession) -> None:
|
||||
caption=caption,
|
||||
disable_notification=app.config["posting"]["silent"],
|
||||
)
|
||||
except Exception as exp:
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"Could not send media %s (%s) due to %s", media.filename, media.id, exp
|
||||
"Could not send media %s (%s) due to %s", media.filename, media.id, exc
|
||||
)
|
||||
if app.config["reports"]["error"]:
|
||||
await app.send_message(
|
||||
app.owner,
|
||||
app._("post_exception", "message").format(exp, format_exc()),
|
||||
app._("post_exception", "message").format(exc, format_exc()),
|
||||
)
|
||||
# rmtree(path.join(app.config['locations']['tmp'], tmp_dir), ignore_errors=True)
|
||||
return
|
||||
|
||||
col_sent.insert_one(
|
||||
await col_sent.insert_one(
|
||||
{
|
||||
"date": datetime.now(),
|
||||
"image": media.id,
|
||||
@@ -253,14 +253,14 @@ async def send_content(app: PyroClient, http_session: ClientSession) -> None:
|
||||
str(app.config["posting"]["silent"]),
|
||||
)
|
||||
|
||||
except Exception as exp:
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"Could not send content due to %s. Traceback: %s", exp, format_exc()
|
||||
"Could not send content due to %s. Traceback: %s", exc, format_exc()
|
||||
)
|
||||
if app.config["reports"]["error"]:
|
||||
await app.send_message(
|
||||
app.owner,
|
||||
app._("post_exception", "message").format(exp, format_exc()),
|
||||
app._("post_exception", "message").format(exc, format_exc()),
|
||||
)
|
||||
try:
|
||||
rmtree(
|
||||
|
@@ -25,7 +25,7 @@ async def callback_query_yes(app: PyroClient, clb: CallbackQuery):
|
||||
user = await app.find_user(clb.from_user)
|
||||
fullclb = str(clb.data).split("_")
|
||||
|
||||
db_entry = col_submitted.find_one({"_id": ObjectId(fullclb[2])})
|
||||
db_entry = await col_submitted.find_one({"_id": ObjectId(fullclb[2])})
|
||||
|
||||
try:
|
||||
submission = await app.submit_media(fullclb[2])
|
||||
@@ -43,21 +43,21 @@ async def callback_query_yes(app: PyroClient, clb: CallbackQuery):
|
||||
show_alert=True,
|
||||
)
|
||||
return
|
||||
except SubmissionDuplicatesError as exp:
|
||||
except SubmissionDuplicatesError as exc:
|
||||
await clb.answer(
|
||||
text=app._("sub_duplicates_found", "callback", locale=user.locale),
|
||||
show_alert=True,
|
||||
)
|
||||
await clb.message.reply_text(
|
||||
app._("sub_media_duplicates_list", "message", locale=user.locale).format(
|
||||
"\n • ".join(exp.duplicates)
|
||||
"\n • ".join(exc.duplicates)
|
||||
),
|
||||
quote=True,
|
||||
)
|
||||
logger.info(
|
||||
"Submission with ID '%s' could not be accepted because of the duplicates: %s",
|
||||
fullclb[2],
|
||||
str(exp.duplicates),
|
||||
str(exc.duplicates),
|
||||
)
|
||||
return
|
||||
|
||||
@@ -127,7 +127,7 @@ async def callback_query_no(app: PyroClient, clb: CallbackQuery):
|
||||
user = await app.find_user(clb.from_user)
|
||||
fullclb = str(clb.data).split("_")
|
||||
|
||||
db_entry = col_submitted.find_one_and_delete({"_id": ObjectId(fullclb[2])})
|
||||
db_entry = await col_submitted.find_one_and_delete({"_id": ObjectId(fullclb[2])})
|
||||
|
||||
if (
|
||||
db_entry["temp"]["uuid"] is not None
|
||||
@@ -146,7 +146,7 @@ async def callback_query_no(app: PyroClient, clb: CallbackQuery):
|
||||
submission = await app.get_messages(
|
||||
db_entry["user"], db_entry["telegram"]["msg_id"]
|
||||
)
|
||||
except Exception as exp:
|
||||
except Exception as exc:
|
||||
await clb.answer(
|
||||
text=app._("sub_msg_unavail", "message", locale=user.locale),
|
||||
show_alert=True,
|
||||
|
@@ -3,9 +3,14 @@ from pyrogram.client import Client
|
||||
from pyrogram.types import Message
|
||||
|
||||
from classes.pyroclient import PyroClient
|
||||
from modules import custom_filters
|
||||
|
||||
|
||||
@Client.on_message(~filters.scheduled & filters.command(["start"], prefixes="/"))
|
||||
@Client.on_message(
|
||||
custom_filters.mode_submit
|
||||
& ~filters.scheduled
|
||||
& filters.command(["start"], prefixes="/")
|
||||
)
|
||||
async def cmd_start(app: PyroClient, msg: Message):
|
||||
user = await app.find_user(msg.from_user)
|
||||
|
||||
@@ -16,7 +21,9 @@ async def cmd_start(app: PyroClient, msg: Message):
|
||||
|
||||
|
||||
@Client.on_message(
|
||||
~filters.scheduled & filters.command(["rules", "help"], prefixes="/")
|
||||
custom_filters.mode_submit
|
||||
& ~filters.scheduled
|
||||
& filters.command(["rules", "help"], prefixes="/")
|
||||
)
|
||||
async def cmd_rules(app: PyroClient, msg: Message):
|
||||
user = await app.find_user(msg.from_user)
|
||||
|
@@ -123,16 +123,16 @@ async def cmd_import(app: PyroClient, msg: Message):
|
||||
for name in handle.namelist()
|
||||
]
|
||||
_ = await asyncio.gather(*tasks)
|
||||
except Exception as exp:
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"Could not import '%s' due to %s: %s",
|
||||
answer.document.file_name,
|
||||
exp,
|
||||
exc,
|
||||
format_exc(),
|
||||
)
|
||||
await answer.reply_text(
|
||||
app._("import_unpack_error", "message", locale=user.locale).format(
|
||||
exp, format_exc()
|
||||
exc, format_exc()
|
||||
)
|
||||
)
|
||||
return
|
||||
@@ -165,12 +165,12 @@ async def cmd_import(app: PyroClient, msg: Message):
|
||||
compress=False,
|
||||
caption="queue",
|
||||
)
|
||||
except UnexpectedStatus as exp:
|
||||
except UnexpectedStatus as exc:
|
||||
logger.error(
|
||||
"Could not upload '%s' from '%s': %s",
|
||||
filename,
|
||||
Path(f"{app.config['locations']['tmp']}/{tmp_dir}"),
|
||||
exp,
|
||||
exc,
|
||||
)
|
||||
await msg.reply_text(
|
||||
app._(
|
||||
|
@@ -4,10 +4,12 @@ from pyrogram.client import Client
|
||||
from pyrogram.types import Message, User
|
||||
|
||||
from classes.pyroclient import PyroClient
|
||||
from modules import custom_filters
|
||||
|
||||
|
||||
@Client.on_message(
|
||||
~filters.scheduled
|
||||
custom_filters.mode_post
|
||||
& ~filters.scheduled
|
||||
& filters.chat(sync.config_get("comments", "posting"))
|
||||
& filters.reply
|
||||
& filters.command(["report"], prefixes=["", "/"])
|
||||
|
@@ -13,6 +13,7 @@ from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
|
||||
from classes.enums.submission_types import SubmissionType
|
||||
from classes.exceptions import SubmissionDuplicatesError, SubmissionUnsupportedError
|
||||
from classes.pyroclient import PyroClient
|
||||
from modules import custom_filters
|
||||
from modules.database import col_submitted
|
||||
from modules.utils import USERS_WITH_CONTEXT
|
||||
|
||||
@@ -20,7 +21,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@Client.on_message(
|
||||
~filters.scheduled & filters.private & filters.photo
|
||||
custom_filters.mode_submit & ~filters.scheduled & filters.private & filters.photo
|
||||
| filters.video
|
||||
# | filters.animation
|
||||
| filters.document
|
||||
@@ -152,7 +153,7 @@ async def get_submission(app: PyroClient, msg: Message):
|
||||
+ sep,
|
||||
)
|
||||
|
||||
inserted = col_submitted.insert_one(
|
||||
inserted = await col_submitted.insert_one(
|
||||
{
|
||||
"user": msg.from_user.id,
|
||||
"date": datetime.now(),
|
||||
@@ -165,7 +166,7 @@ async def get_submission(app: PyroClient, msg: Message):
|
||||
)
|
||||
|
||||
else:
|
||||
inserted = col_submitted.insert_one(
|
||||
inserted = await col_submitted.insert_one(
|
||||
{
|
||||
"user": msg.from_user.id,
|
||||
"date": datetime.now(),
|
||||
@@ -237,16 +238,16 @@ async def get_submission(app: PyroClient, msg: Message):
|
||||
quote=True,
|
||||
)
|
||||
return
|
||||
except SubmissionDuplicatesError as exp:
|
||||
except SubmissionDuplicatesError as exc:
|
||||
await msg.reply_text(
|
||||
app._(
|
||||
"sub_media_duplicates_list", "message", locale=user.locale
|
||||
).format("\n • ".join(exp.duplicates)),
|
||||
).format("\n • ".join(exc.duplicates)),
|
||||
quote=True,
|
||||
)
|
||||
return
|
||||
except Exception as exp:
|
||||
await msg.reply_text(format_exc(), quote=True)
|
||||
except Exception as exc:
|
||||
await msg.reply_text(exc, quote=True)
|
||||
return
|
||||
elif (
|
||||
msg.from_user.id not in app.admins
|
||||
@@ -270,12 +271,12 @@ async def get_submission(app: PyroClient, msg: Message):
|
||||
)
|
||||
)
|
||||
return
|
||||
except SubmissionDuplicatesError as exp:
|
||||
except SubmissionDuplicatesError as exc:
|
||||
await msg.reply_text(
|
||||
app._("sub_dup", "message", locale=user.locale), quote=True
|
||||
)
|
||||
return
|
||||
except Exception as exp:
|
||||
except Exception as exc:
|
||||
await app.send_message(
|
||||
app.owner,
|
||||
app._(
|
||||
|
@@ -1,15 +1,11 @@
|
||||
aiohttp~=3.8.4
|
||||
black~=23.3.0
|
||||
convopyro==0.5
|
||||
pillow~=10.0.0
|
||||
psutil~=5.9.4
|
||||
pillow~=10.1.0
|
||||
pykeyboard==0.1.5
|
||||
pymongo~=4.4.0
|
||||
pyrogram==2.0.106
|
||||
python_dateutil==2.8.2
|
||||
pytimeparse~=1.1.8
|
||||
tgcrypto==1.2.5
|
||||
uvloop==0.17.0
|
||||
uvloop==0.18.0
|
||||
--extra-index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple
|
||||
libbot[speed,pyrogram]==1.8
|
||||
async_pymongo==0.1.4
|
||||
libbot[speed,pyrogram]==2.0.1
|
||||
photosapi_client==0.5.0
|
Reference in New Issue
Block a user