46 Commits

Author SHA1 Message Date
98e9c5f5a2 Fixed line being displayed incorrectly 2023-03-09 16:17:35 +01:00
a8545dd097 Fixed project name align 2023-03-09 16:16:38 +01:00
3f22340852 Added license and code style badges 2023-03-09 11:48:50 +01:00
c2fb88ed65 Bumped APScheduler to 3.10.1 2023-03-09 11:35:02 +01:00
88692ebc85 Now using black for formatting 2023-03-09 11:33:02 +01:00
4331af415e Moved to aiohttp and aiofiles 2023-03-02 22:38:48 +01:00
a913ba19c6 WIP: aiohttp migration 2023-03-02 16:39:59 +01:00
2387823151 Added missing texts 2023-02-25 23:31:09 +01:00
88f8bb4a52 WIP: Photos and queue removal 2023-02-25 23:19:08 +01:00
cf6c1f03d7 Disabled notification for accepted submissions 2023-02-25 23:12:33 +01:00
0a309a9f59 Added one more rule 2023-02-25 23:10:57 +01:00
7810f3b7b9 Fixed absence of None check 2023-02-24 21:06:18 +01:00
7b2534012d Fixed typo 2023-02-24 13:48:06 -05:00
6e8b47cf4b Improved submissions randomness 2023-02-24 13:33:34 -05:00
c27b1c5a5b Temporarily disabled album creation on start 2023-02-24 13:25:57 -05:00
c7228a006b Improved randomness 2023-02-24 13:25:36 -05:00
64ae3fb047 Added option to mention submissions 2023-02-24 12:39:33 -05:00
Profitroll
056fc52353 This commit closes #4 and closes #6 2023-02-19 20:54:58 +01:00
Profitroll
8bafd0cb35 This commit closes #3 2023-02-19 20:44:00 +01:00
Profitroll
fd47217bad This commit closes #5 2023-02-19 20:31:08 +01:00
Profitroll
6b7b5c22f2 Reverted web preview disable 2023-02-18 00:58:09 +01:00
Profitroll
b3698cfa70 Added support for duplicates access tokens 2023-02-18 00:55:58 +01:00
Profitroll
7607003f55 Added callback message for "nothing" 2023-02-17 23:45:55 +01:00
Profitroll
7918049f49 Fixed language code typo 2023-02-17 23:18:54 +01:00
Profitroll
fcd59b7aca Fixed caption display 2023-02-17 23:18:34 +01:00
Profitroll
8c478072c6 Fixed reply message not getting quoted 2023-02-17 23:18:19 +01:00
Profitroll
b766d0c52c Improved linting and removed unused imports 2023-02-17 22:59:03 +01:00
Profitroll
807e629ae7 Moved cooldowns to PosterUser class 2023-02-17 22:48:37 +01:00
Profitroll
87af9fd333 Fixed captions not getting detected 2023-02-17 22:26:07 +01:00
Profitroll
a54081a2ae Fixed some issues with locale 2023-02-17 21:58:46 +01:00
Profitroll
664284a6f8 Integrated previous commits 2023-02-17 21:55:38 +01:00
Profitroll
bd9917fb17 Replaced user block logic 2023-02-17 21:54:52 +01:00
Profitroll
68ea087963 Integrated interval-based schedule 2023-02-17 21:54:30 +01:00
Profitroll
d1813856d9 Imported callback "nothing" 2023-02-17 21:54:14 +01:00
Profitroll
cf204577e4 Added external address for links 2023-02-17 21:53:57 +01:00
Profitroll
68c887999e Divided admins and owner 2023-02-17 21:53:43 +01:00
Profitroll
642e17ee55 Image resize when too big 2023-02-17 16:46:44 +01:00
Profitroll
25af9b31f8 Changed Client to PosterClient 2023-02-17 16:46:33 +01:00
Profitroll
28fc359593 WIP: export and import 2023-02-17 16:46:13 +01:00
Profitroll
07203a9db9 Changed the way exceptions are handled 2023-02-17 16:45:51 +01:00
Profitroll
663a7b0db8 Submission without confirmation added 2023-02-17 16:44:56 +01:00
Profitroll
0d2e9fa6ec Using PosterClient instead of Client 2023-02-17 16:44:30 +01:00
Profitroll
c90e5eb697 Added Pillow to requirements 2023-02-17 16:44:03 +01:00
Profitroll
f4359aa6cd Added upload_pic method 2023-02-17 11:51:38 +01:00
4cd37be5dc WIP: New submission system 2023-02-16 16:41:01 +01:00
05042f01c6 Disabled WIP warning 2023-02-15 14:07:40 +01:00
26 changed files with 1190 additions and 570 deletions

View File

@@ -1,9 +1,16 @@
# TelegramPoster <h1 align="center">TelegramPoster</h1>
<p align="center">
<a href="https://git.end-play.xyz/profitroll/TelegramPoster/src/branch/master/LICENSE"><img alt="License: GPL" src="https://img.shields.io/badge/License-GPL-blue"></a>
<a href="https://git.end-play.xyz/profitroll/TelegramPoster"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
</p>
> Шукаєш інструкцію українською? А вона [ось тут](https://git.end-play.xyz/profitroll/TelegramPoster/src/branch/master/README_uk.md) знаходиться) > Шукаєш інструкцію українською? А вона [ось тут](https://git.end-play.xyz/profitroll/TelegramPoster/src/branch/master/README_uk.md) знаходиться)
This bot is used for one and only task - post pictures from my personal archive. Here's its source code so you can also host a bot and have fun with it. Just don't exepect it to be brilliant. It is not. But hey, you can always fork it ;) This bot is used for one and only task - post pictures from my personal archive. Here's its source code so you can also host a bot and have fun with it. Just don't exepect it to be brilliant. It is not. But hey, you can always fork it ;)
## Installation ## Installation
To make this bot run at first you need to have a Python interpreter and git. Google is your friend finding it. You can also ignore git and simply download source code, should also work fine. After that you're ready to go. To make this bot run at first you need to have a Python interpreter and git. Google is your friend finding it. You can also ignore git and simply download source code, should also work fine. After that you're ready to go.
> In this README I assume that you're using default python in your > In this README I assume that you're using default python in your
@@ -42,17 +49,21 @@ To make this bot run at first you need to have a Python interpreter and git. Goo
Additionally there are `loop.sh` and `loop.bat` available if you want your bot to start again after being stopped or after using `/reboot` command. Additionally there are `loop.sh` and `loop.bat` available if you want your bot to start again after being stopped or after using `/reboot` command.
## Command line arguments ## Command line arguments
Of course bot also has them. You can perform some actions with them. Of course bot also has them. You can perform some actions with them.
* `--move-sent` - allows you to move all sent files from queue to sent directories * `--move-sent` - allows you to move all sent files from queue to sent directories
* `--cleanup` - purge files in both `queue` and `sent` folders if they're sent. Requires `--confirm` argument * `--cleanup` - purge files in both `queue` and `sent` folders if they're sent. Requires `--confirm` argument
* `--cleanup-index` - purge all sent entries from index. Requires `--confirm` argument * `--cleanup-index` - purge all sent entries from index. Requires `--confirm` argument
* `--norun` - allows you to execute above arguments without triggering the bot start itself * `--norun` - allows you to execute above arguments without triggering the bot start itself
Examples: Examples:
* `python3 ./main.py --move-sent --norun` * `python3 ./main.py --move-sent --norun`
* `python3 ./main.py --cleanup --confirm` * `python3 ./main.py --cleanup --confirm`
## Localization ## Localization
Bot is capable of using custom locales. There are some that are pre-installed (English and Ukrainian), however you can add your own locales too. Bot is capable of using custom locales. There are some that are pre-installed (English and Ukrainian), however you can add your own locales too.
All localization files are located in the `locale` folder, otherwise in folder specified in config file. Just copy locale file of your choice, name it in accordance to [IETF language tags](https://en.wikipedia.org/wiki/IETF_language_tag) (if you want your locale to be compatible with Telegram's locales) or define your own name. Save it as json and you're good to go. If you want to change default locale for messages, that cannot determine admin's locale - edit `"locale"` parameter in the `config.json`. If this locale is not available - `"locale_fallback"` will be used instead. If both are not available - error will be shown. For console output and logging locale you should edit `"locale_log"`. All localization files are located in the `locale` folder, otherwise in folder specified in config file. Just copy locale file of your choice, name it in accordance to [IETF language tags](https://en.wikipedia.org/wiki/IETF_language_tag) (if you want your locale to be compatible with Telegram's locales) or define your own name. Save it as json and you're good to go. If you want to change default locale for messages, that cannot determine admin's locale - edit `"locale"` parameter in the `config.json`. If this locale is not available - `"locale_fallback"` will be used instead. If both are not available - error will be shown. For console output and logging locale you should edit `"locale_log"`.

View File

@@ -1,7 +1,14 @@
# TelegramPoster <h1 align="center">TelegramPoster</h1>
<p align="center">
<a href="https://git.end-play.xyz/profitroll/TelegramPoster/src/branch/master/LICENSE"><img alt="License: GPL" src="https://img.shields.io/badge/License-GPL-blue"></a>
<a href="https://git.end-play.xyz/profitroll/TelegramPoster"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
</p>
Цей бот використовується для однієї-єдиної задачі - розміщувати фотографії з мого особистого архіву. Ось його код, тож Ви також можете захостити бота самостійно та розважитися з ним. Тільки не очікуйте, що він ідеальним. Не буде. Але гей, Ви завжди можете його доробити під себе ;) Цей бот використовується для однієї-єдиної задачі - розміщувати фотографії з мого особистого архіву. Ось його код, тож Ви також можете захостити бота самостійно та розважитися з ним. Тільки не очікуйте, що він ідеальним. Не буде. Але гей, Ви завжди можете його доробити під себе ;)
## Установка ## Установка
Для запуску цього бота спочатку потрібно мати інтерпретатор Python та встановлений git. Google — Ваш друг у пошуках. Ви також можете ігнорувати git і просто завантажити код, також має спрацювати добре. Після цього Ви готові до встановлення. Для запуску цього бота спочатку потрібно мати інтерпретатор Python та встановлений git. Google — Ваш друг у пошуках. Ви також можете ігнорувати git і просто завантажити код, також має спрацювати добре. Після цього Ви готові до встановлення.
> У цьому README я вважаю, що Ви використовуєте python за замовчуванням у своїй > У цьому README я вважаю, що Ви використовуєте python за замовчуванням у своїй
@@ -40,17 +47,21 @@
Крім того, доступні `loop.sh` і `loop.bat`, якщо ви хочете, щоб ваш бот запускався знову після зупинки або після використання команди `/reboot`. Крім того, доступні `loop.sh` і `loop.bat`, якщо ви хочете, щоб ваш бот запускався знову після зупинки або після використання команди `/reboot`.
## Аргументи командного рядка ## Аргументи командного рядка
Звичайно, у бота вони також є. З ними можна виконувати деякі дії. Звичайно, у бота вони також є. З ними можна виконувати деякі дії.
* `--move-sent` - дозволяє перемістити всі надіслані файли з черги до папки надісланих * `--move-sent` - дозволяє перемістити всі надіслані файли з черги до папки надісланих
* `--cleanup` - очистити файли в папках `queue` і `sent`, якщо вони вже надіслані. Потрібен аргумент `--confirm` * `--cleanup` - очистити файли в папках `queue` і `sent`, якщо вони вже надіслані. Потрібен аргумент `--confirm`
* `--cleanup-index` - видалити всі надіслані записи з індексу. Потрібен аргумент `--confirm` * `--cleanup-index` - видалити всі надіслані записи з індексу. Потрібен аргумент `--confirm`
* `--norun` - дозволяє виконувати наведені вище аргументи, не запускаючи самого бота * `--norun` - дозволяє виконувати наведені вище аргументи, не запускаючи самого бота
Приклади: Приклади:
* `python3 ./main.py --move-sent --norun` * `python3 ./main.py --move-sent --norun`
* `python3 ./main.py --cleanup --confirm` * `python3 ./main.py --cleanup --confirm`
## Локалізація ## Локалізація
Бот може використовувати різні мови. Є деякі попередньо встановлені (Англійська та Українська), однак Ви можете додавати свої власні локалізації теж. Бот може використовувати різні мови. Є деякі попередньо встановлені (Англійська та Українська), однак Ви можете додавати свої власні локалізації теж.
Всі файли локалізації знаходяться у папці `locale`, якщо в конфігураційному файлі не вказано іншу. Просто скопіюйте цікавлячий Вас файл, назвіть його відповідно до [тегів мови IETF](https://en.wikipedia.org/wiki/IETF_language_tag) (якщо Ви хочете, щоб переклад був сумісним з перекладами Telegram) або просто вкажіть свою власну назву. Збережіть свій переклад як json файл і все готово. Якщо ви хочете змінити мову за замовчуванням для повідомлень самого бота, які не можуть визначити мову адміністратора, відредагуйте параметр `"locale"` у `config.json`. Якщо ця мова недоступна, замість неї буде використано `"locale_fallback"`. Якщо обидві мови недоступні - буде показано помилку. Для зміни мови виведення консолі та логування вам слід відредагувати `"locale_log"`. Всі файли локалізації знаходяться у папці `locale`, якщо в конфігураційному файлі не вказано іншу. Просто скопіюйте цікавлячий Вас файл, назвіть його відповідно до [тегів мови IETF](https://en.wikipedia.org/wiki/IETF_language_tag) (якщо Ви хочете, щоб переклад був сумісним з перекладами Telegram) або просто вкажіть свою власну назву. Збережіть свій переклад як json файл і все готово. Якщо ви хочете змінити мову за замовчуванням для повідомлень самого бота, які не можуть визначити мову адміністратора, відредагуйте параметр `"locale"` у `config.json`. Якщо ця мова недоступна, замість неї буде використано `"locale_fallback"`. Якщо обидві мови недоступні - буде показано помилку. Для зміни мови виведення консолі та логування вам слід відредагувати `"locale_log"`.

22
classes/exceptions.py Normal file
View File

@@ -0,0 +1,22 @@
from typing import Any
class SubmissionUnavailableError(Exception):
pass
class SubmissionUploadError(Exception):
def __init__(self, file_path: str, status_code: int, content: Any) -> None:
self.status_code = status_code
self.content = content
super().__init__(
f"Could not upload photo '{file_path}' due to HTTP {self.status_code}: {self.content}"
)
class SubmissionDuplicatesError(Exception):
def __init__(self, file_path: str, duplicates: list) -> None:
self.duplicates = duplicates
super().__init__(
f"Found duplicates of a photo '{file_path}': {self.duplicates}"
)

96
classes/poster_client.py Normal file
View File

@@ -0,0 +1,96 @@
from os import path, remove, sep
from shutil import rmtree
from typing import Union
from pyrogram.client import Client
from pyrogram.types import Message
from classes.exceptions import SubmissionDuplicatesError, SubmissionUnavailableError
from modules.api_client import upload_pic
from modules.database import col_submitted
from bson import ObjectId
from modules.logger import logWrite
from modules.utils import configGet
class PosterClient(Client):
def __init__(self, name: str, **kwargs): # type: ignore
super().__init__(name, **kwargs)
self.owner = configGet("owner")
self.admins = configGet("admins") + [configGet("owner")]
async def submit_photo(self, id: str) -> Union[Message, None]:
db_entry = col_submitted.find_one({"_id": ObjectId(id)})
submission = None
if db_entry is None:
raise SubmissionUnavailableError()
else:
if db_entry["temp"]["uuid"] is not None:
if not path.exists(
path.join(
configGet("data", "locations"),
"submissions",
db_entry["temp"]["uuid"],
db_entry["temp"]["file"],
)
):
raise SubmissionUnavailableError()
else:
filepath = path.join(
configGet("data", "locations"),
"submissions",
db_entry["temp"]["uuid"],
db_entry["temp"]["file"],
)
try:
submission = await self.get_messages(
db_entry["user"], db_entry["telegram"]["msg_id"]
)
except:
pass
else:
try:
submission = await self.get_messages(
db_entry["user"], db_entry["telegram"]["msg_id"]
)
filepath = await self.download_media(
submission, file_name=configGet("tmp", "locations") + sep
)
except:
raise SubmissionUnavailableError()
response = await upload_pic(
str(filepath), allow_duplicates=configGet("allow_duplicates", "submission")
)
if len(response[1]) > 0:
raise SubmissionDuplicatesError(str(filepath), response[1])
col_submitted.find_one_and_update(
{"_id": ObjectId(id)}, {"$set": {"done": True}}
)
try:
if db_entry["temp"]["uuid"] is not None:
rmtree(
path.join(
configGet("data", "locations"),
"submissions",
db_entry["temp"]["uuid"],
),
ignore_errors=True,
)
else:
remove(str(filepath))
except (FileNotFoundError, NotADirectoryError):
logWrite(
f"Could not delete '{filepath}' on submission accepted", debug=True
)
return submission
async def ban_user(self, id: int) -> None:
pass
async def unban_user(self, id: int) -> None:
pass

55
classes/user.py Normal file
View File

@@ -0,0 +1,55 @@
from modules.app import app
from datetime import datetime
from modules.database import col_banned, col_users
from modules.utils import configGet
class PosterUser:
def __init__(self, id: int):
self.id = id
def is_blocked(self) -> bool:
"""Check if user is banned from submitting content.
### Returns:
`bool`: Must be `True` if banned and `False` if not
"""
return False if col_banned.find_one({"user": self.id}) is None else True
def block(self) -> None:
"""Ban user from using command and submitting content."""
if col_banned.find_one({"user": self.id}) is None:
col_banned.insert_one({"user": self.id, "date": datetime.now()})
def unblock(self) -> None:
"""Allow user to use command and submit posts again."""
col_banned.find_one_and_delete({"user": self.id})
def is_limited(self) -> bool:
"""Check if user is on a cooldown after submitting something.
### Returns:
`bool`: Must be `True` if on the cooldown and `False` if not
"""
if self.id in app.admins:
return False
else:
db_record = col_users.find_one({"user": self.id})
if db_record is None:
return False
return (
True
if (datetime.now() - db_record["cooldown"]).total_seconds()
< configGet("timeout", "submission")
else False
)
def limit(self) -> None:
"""Restart user's cooldown. Used after post has been submitted."""
if (
col_users.find_one_and_update(
{"user": self.id}, {"$set": {"cooldown": datetime.now()}}
)
is None
):
col_users.insert_one({"user": self.id, "cooldown": datetime.now()})

View File

@@ -3,7 +3,8 @@
"locale": "en", "locale": "en",
"locale_log": "en", "locale_log": "en",
"locale_fallback": "en", "locale_fallback": "en",
"admin": 0, "owner": 0,
"admins": [],
"bot": { "bot": {
"api_id": 0, "api_id": 0,
"api_hash": "", "api_hash": "",
@@ -46,7 +47,14 @@
"channel": 0, "channel": 0,
"silent": false, "silent": false,
"move_sent": false, "move_sent": false,
"interval": 1, "use_interval": false,
"interval": "1h30m",
"page_size": 300,
"submitted_caption": {
"enabled": true,
"ignore_admins": true,
"text": "#submitted"
},
"extensions": { "extensions": {
"photo": [ "photo": [
"jpg", "jpg",
@@ -74,6 +82,7 @@
], ],
"api": { "api": {
"address": "http://localhost:8054", "address": "http://localhost:8054",
"address_external": "https://photos.domain.com",
"username": "", "username": "",
"password": "", "password": "",
"album": "" "album": ""
@@ -88,6 +97,11 @@
"timeout": 30, "timeout": 30,
"file_size": 15728640, "file_size": 15728640,
"tmp_size": 15728640, "tmp_size": 15728640,
"allow_duplicates": false,
"require_confirmation": {
"users": true,
"admins": true
},
"mime_types": [ "mime_types": [
"image/png", "image/png",
"image/gif", "image/gif",
@@ -101,6 +115,8 @@
"rules" "rules"
], ],
"commands_admin": [ "commands_admin": [
"import",
"export",
"reboot" "reboot"
] ]
} }

View File

@@ -5,15 +5,21 @@
}, },
"commands_admin": { "commands_admin": {
"forwards": "Check post forwards", "forwards": "Check post forwards",
"import": "Submit .zip archive with photos",
"export": "Get .zip archive with all photos",
"remove": "Delete photo by its ID",
"purge": "Completely purge bot's queue",
"reboot": "Restart the bot" "reboot": "Restart the bot"
}, },
"message": { "message": {
"start": "Hi and welcome!\n\nYou can submit your pictures and videos here. We'll review and add them, if we like them. Make sure you send your stuff one at a time and have chosen media that corresponds to our rules.\n\nYou can also write something to us in the description field. We'll send it with the submission itself, if needed.\n\nAlso, make sure you follow the /rules of submission, otherwise your submission will be declined. In case of spam/abuse you may even be blocked.\n\nHave fun and happy submitting!", "start": "Hi and welcome!\n\nYou can submit your pictures and videos here. We'll review and add them, if we like them. Make sure you send your stuff one at a time and have chosen media that corresponds to our rules.\n\nYou can also write something to us in the description field. We'll send it with the submission itself, if needed.\n\nAlso, make sure you follow the /rules of submission, otherwise your submission will be declined. In case of spam/abuse you may even be blocked.\n\nHave fun and happy submitting!",
"rules": "Photos submission rules:\n1. No porn, only erotics and aesthetics\n2. Nipples are semi-allowed, should be either veiled or barely visible\n3. Genitalia strictly prohibited, but labia prints on clothes or nice pubes/panties/butts - are fine", "rules": "Photos submission rules:\n1. No porn, only erotics and aesthetics\n2. Nipples are semi-allowed, should be either veiled or barely visible\n3. Genitalia strictly prohibited, but labia prints on clothes or nice pubes/panties/butts - are fine\n4. Submitting russians is forbidden",
"shutdown": "Shutting down bot with pid `{0}`", "shutdown": "Shutting down bot with pid `{0}`",
"startup": "Starting with pid `{0}`", "startup": "Starting with pid `{0}`",
"sub_yes": "✅ Submission approved and accepted", "sub_yes": "✅ Submission approved and accepted",
"sub_yes_auto": "✅ Submission automatically accepted",
"sub_no": "❌ Submission reviewed and declined", "sub_no": "❌ Submission reviewed and declined",
"sub_dup": "⚠️ Submission automatically declined because database already contains this photo",
"sub_blocked": "You were blocked and you can't submit media anymore.", "sub_blocked": "You were blocked and you can't submit media anymore.",
"sub_unblocked": "You were unblocked and you can now submit media.", "sub_unblocked": "You were unblocked and you can now submit media.",
"sub_by": "\n\nSubmitted by:", "sub_by": "\n\nSubmitted by:",
@@ -25,12 +31,15 @@
"document_too_large": "File you've sent is too large. Please submit files not bigger than {0} MB", "document_too_large": "File you've sent is too large. Please submit files not bigger than {0} MB",
"mime_not_allowed": "File type not allowed. Please, consider using one of these: {0}", "mime_not_allowed": "File type not allowed. Please, consider using one of these: {0}",
"post_exception": "Could not send content due to `{0}`\n\nTraceback:\n```{1}```", "post_exception": "Could not send content due to `{0}`\n\nTraceback:\n```{1}```",
"post_invalid_pic": "__TO_BE_ADDED__", "post_invalid_pic": "⚠️ Error {0} while sending photo\n```python\n{1}\n```",
"api_queue_empty": "Could not send content: `Queue is empty or contains only unsupported files.`", "api_queue_empty": "Could not send content: `Queue is empty or contains only unsupported files.`",
"api_queue_error": "__TO_BE_ADDED__", "api_queue_error": "Could not get photo from API's queue. Check the log above or API's errors to get more info.",
"post_low": "Low amount of content: `There are only {0} files left in the queue.`", "post_low": "Low amount of content: `There are only {0} files left in the queue.`",
"api_creds_invalid": "__TO_BE_ADDED__", "api_creds_invalid": "Could not authorize API access. Please check whether provided in config file are valid and update them if they're not.",
"sub_wip": "Post submission is now WIP. It will be available again in a few days. Thank you for your patience." "sub_wip": "Post submission is now WIP. It will be available again in a few days. Thank you for your patience.",
"sub_duplicates_found": "__TO_BE_ADDED__",
"sub_error": "⚠️ Could not upload this image due to bot error. Admins are advised.",
"sub_error_admin": "User {0} could not submit photo without additional confirmation due to:\n```\n{1}\n```"
}, },
"button": { "button": {
"sub_yes": "✅ Accept", "sub_yes": "✅ Accept",
@@ -49,7 +58,10 @@
"sub_unblock": "User {0} has been unblocked", "sub_unblock": "User {0} has been unblocked",
"sub_msg_unavail": "Submission message no longer exist", "sub_msg_unavail": "Submission message no longer exist",
"sub_media_unavail": "Could not download submission", "sub_media_unavail": "Could not download submission",
"sub_done": "You've already decided what to do with submission" "sub_done": "You've already decided what to do with submission",
"sub_upload_failed": "__TO_BE_ADDED__",
"sub_duplicates_found": "There're duplicates in bot's database",
"nothing": "🏁 This action is already finished"
}, },
"console": { "console": {
"shutdown": "Shutting down bot with pid {0}", "shutdown": "Shutting down bot with pid {0}",
@@ -58,7 +70,7 @@
"exception_occured": "Exception {0} happened on task execution", "exception_occured": "Exception {0} happened on task execution",
"post_sent": "Sent {0} to {1} with caption {2} and silently {3}", "post_sent": "Sent {0} to {1} with caption {2} and silently {3}",
"post_exception": "Could not send content due to {0}. Traceback: {1}", "post_exception": "Could not send content due to {0}. Traceback: {1}",
"post_invalid_pic": "__TO_BE_ADDED__", "post_invalid_pic": "Error while sending photo HTTP {0}: {1}",
"post_empty": "Could not send content due to queue empty or contains only forbidden extensions", "post_empty": "Could not send content due to queue empty or contains only forbidden extensions",
"sub_mime_not_allowed": "Got submission from {0} but type of {1} which is not allowed", "sub_mime_not_allowed": "Got submission from {0} but type of {1} which is not allowed",
"sub_document_too_large": "Got submission from {0} but but file is too large ({1} > {2})", "sub_document_too_large": "Got submission from {0} but but file is too large ({1} > {2})",

View File

@@ -5,15 +5,21 @@
}, },
"commands_admin": { "commands_admin": {
"forwards": "Переглянути репости", "forwards": "Переглянути репости",
"import": "Надати боту .zip архів з фотографіями",
"export": "Отримати .zip архів з усіма фотографіями",
"remove": "Видалити фото за його ID",
"purge": "Повністю видалити всю чергу бота",
"reboot": "Перезапустити бота" "reboot": "Перезапустити бота"
}, },
"message": { "message": {
"start": "Привіт і ласкаво просимо!\n\nТут можна пропонувати свої фотографії та відео. Ми переглянемо та додамо їх, якщо вони нам сподобаються. Переконайтеся, що ви надсилаєте свої матеріали по одному та вибираєте медіа, які відповідають нашим правилам.\n\nВи також можете написати нам щось у полі опису. За потреби ми надішлемо це разом із самим фото.\n\nКрім того, переконайтеся, що ви дотримуєтеся /rules (правил) подання, інакше вашу пропозицію буде відхилено. У разі спаму/зловживань вас можуть навіть заблокувати.\n\nГарного дня та щасливого надсилання!", "start": "Привіт і ласкаво просимо!\n\nТут можна пропонувати свої фотографії та відео. Ми переглянемо та додамо їх, якщо вони нам сподобаються. Переконайтеся, що ви надсилаєте свої матеріали по одному та вибираєте медіа, які відповідають нашим правилам.\n\nВи також можете написати нам щось у полі опису. За потреби ми надішлемо це разом із самим фото.\n\nКрім того, переконайтеся, що ви дотримуєтеся /rules (правил) подання, інакше вашу пропозицію буде відхилено. У разі спаму/зловживань вас можуть навіть заблокувати.\n\nГарного дня та щасливого надсилання!",
"rules": "Правила пропонування фото:\n1. Ніякого порно, тільки еротика та естетика\n2. Соски можна, але або завуальовані, або зовсім ледь помітні\n3. Геніталії суворо ні, а ось відбитки статевих губ на одязі або гарні лобочки/трусики/попки - без проблем", "rules": "Правила пропонування фото:\n1. Ніякого порно, тільки еротика та естетика\n2. Соски можна, але або завуальовані, або зовсім ледь помітні\n3. Геніталії суворо ні, а ось відбитки статевих губ на одязі або гарні лобочки/трусики/попки - без проблем\n4. Пропонувати русню заборонено",
"shutdown": "Вимкнення бота з підом `{0}`", "shutdown": "Вимкнення бота з підом `{0}`",
"startup": "Запуск бота з підом `{0}`", "startup": "Запуск бота з підом `{0}`",
"sub_yes": "✅ Подання схвалено та прийнято", "sub_yes": "✅ Подання схвалено та прийнято",
"sub_yes_auto": "✅ Подання автоматично прийнято",
"sub_no": "❌ Подання розглянуто та відхилено", "sub_no": "❌ Подання розглянуто та відхилено",
"sub_dup": "⚠️ Подання автоматично відхилено через наявність цього фото в базі даних",
"sub_blocked": "Вас заблокували, ви більше не можете надсилати медіафайли.", "sub_blocked": "Вас заблокували, ви більше не можете надсилати медіафайли.",
"sub_unblocked": "Вас розблокували, тепер ви можете надсилати медіафайли.", "sub_unblocked": "Вас розблокували, тепер ви можете надсилати медіафайли.",
"sub_by": "\n\nПредставлено:", "sub_by": "\n\nПредставлено:",
@@ -25,12 +31,15 @@
"document_too_large": "Надісланий файл завеликий. Будь ласка, надсилайте файли не більше {0} Мб", "document_too_large": "Надісланий файл завеликий. Будь ласка, надсилайте файли не більше {0} Мб",
"mime_not_allowed": "Тип файлу не дозволений. Розгляньте можливість використання одного з цих: {0}", "mime_not_allowed": "Тип файлу не дозволений. Розгляньте можливість використання одного з цих: {0}",
"post_exception": "Не вдалося надіслати контент через `{0}`\n\nTraceback:\n```{1}```", "post_exception": "Не вдалося надіслати контент через `{0}`\n\nTraceback:\n```{1}```",
"post_invalid_pic": "__TO_BE_ADDED__", "post_invalid_pic": "⚠️ Помилка надсилання фото {0}\n```python\n{1}\n```",
"api_queue_empty": "Не вдалося надіслати контент: `Черга порожня або містить лише непідтримувані файли`.", "api_queue_empty": "Не вдалося надіслати контент: `Черга порожня або містить лише непідтримувані файли`.",
"api_queue_error": "__TO_BE_ADDED__", "api_queue_error": "Не вдалось отримати фото з черги API. Погляньте на логи вище а також на лог помилок API щоб дізнатись подробиці.",
"post_low": "Мала кількість контенту: `Залишилось всього {0} файлів в черзі.`", "post_low": "Мала кількість контенту: `Залишилось всього {0} файлів в черзі.`",
"api_creds_invalid": "__TO_BE_ADDED__", "api_creds_invalid": "Не вдалося авторизувати запит до API. Будь ласка, перевірте чи дані авторизації в конфігураційному файлі вірні та оновіть їх, якщо це не так.",
"sub_wip": "Подання постів зараз знаходиться у розробці. Він буде знову доступний через кілька днів. Дякуємо за ваше терпіння." "sub_wip": "Подання постів зараз знаходиться у розробці. Він буде знову доступний через кілька днів. Дякуємо за ваше терпіння.",
"sub_duplicates_found": "__TO_BE_ADDED__",
"sub_error": "⚠️ Не вдалось завантажити фото через помилку бота. Адміністрацію повідомлено.",
"sub_error_admin": "Користувач {0} не зміг надіслати фото без додаткової перевірки через помилку:\n```\n{1}\n```"
}, },
"button": { "button": {
"sub_yes": "✅ Прийняти", "sub_yes": "✅ Прийняти",
@@ -49,7 +58,10 @@
"sub_unblock": "Користувача {0} розблоковано", "sub_unblock": "Користувача {0} розблоковано",
"sub_msg_unavail": "Повідомлення більше не існує", "sub_msg_unavail": "Повідомлення більше не існує",
"sub_media_unavail": "Не вдалося завантажити подання", "sub_media_unavail": "Не вдалося завантажити подання",
"sub_done": "Ви вже обрали що зробити з цим поданням" "sub_done": "Ви вже обрали що зробити з цим поданням",
"sub_upload_failed": "__TO_BE_ADDED__",
"sub_duplicates_found": "Знайдено дублікати в базі даних бота",
"nothing": "🏁 Цю дію вже було завершено"
}, },
"console": { "console": {
"shutdown": "Вимкнення бота з підом {0}", "shutdown": "Вимкнення бота з підом {0}",
@@ -58,7 +70,7 @@
"exception_occured": "Помилка {0} сталась під час виконання", "exception_occured": "Помилка {0} сталась під час виконання",
"post_sent": "Надіслано {0} у {1} з підписом {2} та без звуку {3}", "post_sent": "Надіслано {0} у {1} з підписом {2} та без звуку {3}",
"post_exception": "Не вдалося надіслати контент через {0}. Traceback: {1}", "post_exception": "Не вдалося надіслати контент через {0}. Traceback: {1}",
"post_invalid_pic": "__TO_BE_ADDED__", "post_invalid_pic": "Помилка надсилання фото HTTP {0}: {1}",
"post_empty": "Не вдалося надіслати контент адже черга з дозволеними розширеннями порожня", "post_empty": "Не вдалося надіслати контент адже черга з дозволеними розширеннями порожня",
"sub_mime_not_allowed": "Отримано подання від {0} але типу {1} який не є дозволеним", "sub_mime_not_allowed": "Отримано подання від {0} але типу {1} який не є дозволеним",
"sub_document_too_large": "Отримано подання від {0} але файл завеликий({1} > {2})", "sub_document_too_large": "Отримано подання від {0} але файл завеликий({1} > {2})",

View File

@@ -1,35 +1,67 @@
"""This is only a temporary solution. Complete Photos API client is yet to be developed."""
try:
from ujson import dumps
except ModuleNotFoundError:
from json import dumps
import asyncio import asyncio
from base64 import b64decode, b64encode from base64 import b64decode, b64encode
from traceback import print_exc
import aiofiles
from ujson import dumps
from os import makedirs, path, sep from os import makedirs, path, sep
from random import choice from random import choice
from typing import Tuple, Union from typing import Tuple, Union
from aiohttp import ClientSession, FormData
from requests import get, patch, post from classes.exceptions import SubmissionUploadError
from modules.logger import logWrite from modules.logger import logWrite
from modules.utils import configGet from modules.utils import configGet
http_session = ClientSession(
json_serialize=dumps,
)
async def authorize() -> str: async def authorize() -> str:
makedirs(configGet("cache", "locations"), exist_ok=True) makedirs(configGet("cache", "locations"), exist_ok=True)
if path.exists(configGet("cache", "locations") + sep + "api_access") is True: if path.exists(configGet("cache", "locations") + sep + "api_access") is True:
with open(configGet("cache", "locations")+sep+"api_access", "rb") as file: async with aiofiles.open(
token = b64decode(file.read()).decode("utf-8") configGet("cache", "locations") + sep + "api_access", "rb"
if get(configGet("address", "posting", "api")+"/users/me/", headers={"Authorization": f"Bearer {token}"}).status_code == 200: ) as file:
token = b64decode(await file.read()).decode("utf-8")
if (
await http_session.get(
configGet("address", "posting", "api") + "/users/me/",
headers={"Authorization": f"Bearer {token}"},
)
).status == 200:
return token return token
payload = { payload = {
"grant_type": "password", "grant_type": "password",
"scope": "me albums.list albums.read albums.write photos.list photos.read photos.write videos.list videos.read videos.write", "scope": "me albums.list albums.read albums.write photos.list photos.read photos.write videos.list videos.read videos.write",
"username": configGet("username", "posting", "api"), "username": configGet("username", "posting", "api"),
"password": configGet("password", "posting", "api") "password": configGet("password", "posting", "api"),
} }
response = post(configGet("address", "posting", "api")+"/token", data=payload) response = await http_session.post(
if response.status_code != 200: configGet("address", "posting", "api") + "/token", data=payload
logWrite(f'Incorrect API credentials! Could not login into "{configGet("address", "posting", "api")}" using login "{configGet("username", "posting", "api")}": HTTP {response.status_code}') )
if not response.ok:
logWrite(
f'Incorrect API credentials! Could not login into "{configGet("address", "posting", "api")}" using login "{configGet("username", "posting", "api")}": HTTP {response.status}'
)
raise ValueError raise ValueError
with open(configGet("cache", "locations")+sep+"api_access", "wb") as file: async with aiofiles.open(
file.write(b64encode(response.json()["access_token"].encode("utf-8"))) configGet("cache", "locations") + sep + "api_access", "wb"
return response.json()["access_token"] ) as file:
await file.write(
b64encode((await response.json())["access_token"].encode("utf-8"))
)
return (await response.json())["access_token"]
async def random_pic(token: Union[str, None] = None) -> Tuple[str, str]: async def random_pic(token: Union[str, None] = None) -> Tuple[str, str]:
"""Returns random image id and filename from the queue. """Returns random image id and filename from the queue.
@@ -37,41 +69,111 @@ async def random_pic(token: Union[str, None] = None) -> Tuple[str, str]:
### Returns: ### Returns:
* `Tuple[str, str]`: First value is an ID and the filename in the filesystem to be indexed. * `Tuple[str, str]`: First value is an ID and the filename in the filesystem to be indexed.
""" """
if token is None: token = await authorize() if token is None else token
token = await authorize() logWrite(
logWrite(f'{configGet("address", "posting", "api")}/albums/{configGet("album", "posting", "api")}/photos?q=&page_size=100&caption=queue') f'{configGet("address", "posting", "api")}/albums/{configGet("album", "posting", "api")}/photos?q=&page_size={configGet("page_size", "posting")}&caption=queue'
resp = get(f'{configGet("address", "posting", "api")}/albums/{configGet("album", "posting", "api")}/photos?q=&page_size=100&caption=queue', headers={"Authorization": f"Bearer {token}"}) )
if resp.status_code != 200: resp = await http_session.get(
logWrite(f'Could not get photos from album {configGet("album", "posting", "api")}: HTTP {resp.status_code}') f'{configGet("address", "posting", "api")}/albums/{configGet("album", "posting", "api")}/photos?q=&page_size={configGet("page_size", "posting")}&caption=queue',
logWrite(f'Could not get photos from "{configGet("address", "posting", "api")}/albums/{configGet("album", "posting", "api")}/photos?q=&page_size=100&caption=queue" using token "{token}": HTTP {resp.status_code}', debug=True) headers={"Authorization": f"Bearer {token}"},
)
print(await resp.json(), flush=True)
if resp.status != 200:
logWrite(
f'Could not get photos from album {configGet("album", "posting", "api")}: HTTP {resp.status}'
)
logWrite(
f'Could not get photos from "{configGet("address", "posting", "api")}/albums/{configGet("album", "posting", "api")}/photos?q=&page_size={configGet("page_size", "posting")}&caption=queue" using token "{token}": HTTP {resp.status}',
debug=True,
)
raise ValueError raise ValueError
if len(resp.json()["results"]) == 0: if len((await resp.json())["results"]) == 0:
raise KeyError raise KeyError
pic = choice(resp.json()["results"]) pic = choice((await resp.json())["results"])
return pic["id"], pic["filename"] return pic["id"], pic["filename"]
async def upload_pic(filepath: str) -> Tuple[bool, list]:
token = await authorize() async def upload_pic(
filepath: str, allow_duplicates: bool = False, token: Union[str, None] = None
) -> Tuple[bool, list]:
token = await authorize() if token is None else token
try: try:
pic_name = path.basename(filepath) pic_name = path.basename(filepath)
files = {'file': (pic_name, open(filepath, 'rb'), 'image/jpeg')} logWrite(f"Uploading {pic_name} to the API...", debug=True)
response = post(f'{configGet("address", "posting", "api")}/albums/{configGet("album", "posting", "api")}/photos&caption=queue', headers={"Authorization": f"Bearer {token}"}, files=files).json() async with aiofiles.open(filepath, "rb") as f:
print(response, flush=True) file_bytes = await f.read()
formdata = FormData()
formdata.add_field(
"file", file_bytes, filename=pic_name, content_type="image/jpeg"
)
response = await http_session.post(
f'{configGet("address", "posting", "api")}/albums/{configGet("album", "posting", "api")}/photos',
params={
"caption": "queue",
"compress": "false",
"ignore_duplicates": str(allow_duplicates).lower(),
},
headers={"Authorization": f"Bearer {token}"},
data=formdata,
)
if response.status != 200 and response.status != 409:
logWrite(
f"Could not upload '{filepath}' to API: HTTP {response.status} with message '{response.content}'"
)
raise SubmissionUploadError(
str(filepath), response.status, response.content
)
duplicates = [] duplicates = []
if "duplicates" in response: if "duplicates" in (await response.json()):
for duplicate in response["duplicates"]: for index, duplicate in enumerate((await response.json())["duplicates"]):
duplicates.append(f'{configGet("address", "posting", "api")}/photos/{duplicate["id"]}') if (await response.json())["access_token"] is None:
duplicates.append(
f'`{duplicate["id"]}`:\n{configGet("address_external", "posting", "api")}/photos/{duplicate["id"]}'
)
else:
duplicates.append(
f'`{duplicate["id"]}`:\n{configGet("address_external", "posting", "api")}/token/photo/{(await response.json())["access_token"]}?id={index}'
)
return True, duplicates return True, duplicates
except: except Exception as exp:
print_exc()
return False, [] return False, []
async def move_pic(id: str) -> bool:
token = await authorize() async def find_pic(
name: str, caption: Union[str, None] = None, token: Union[str, None] = None
) -> Union[dict, None]:
token = await authorize() if token is None else token
try: try:
patch(f'{configGet("address", "posting", "api")}/photos/{id}?caption=sent', headers={"Authorization": f"Bearer {token}"}) response = await http_session.get(
f'{configGet("address", "posting", "api")}/albums/{configGet("album", "posting", "api")}/photos',
params={"q": name, "caption": caption},
headers={"Authorization": f"Bearer {token}"},
)
# logWrite(response.json())
if response.status != 200:
return None
if len((await response.json())["results"]) == 0:
return None
return (await response.json())["results"]
except Exception as exp:
logWrite(
f"Could not find image with name '{name}' and caption '{caption}' due to: {exp}"
)
return None
async def move_pic(id: str, token: Union[str, None] = None) -> bool:
token = await authorize() if token is None else token
try:
await http_session.patch(
f'{configGet("address", "posting", "api")}/photos/{id}?caption=sent',
headers={"Authorization": f"Bearer {token}"},
)
return True return True
except: except:
return False return False
if __name__ == "__main__": if __name__ == "__main__":
print(asyncio.run(authorize())) print(asyncio.run(authorize()))

View File

@@ -1,4 +1,9 @@
from pyrogram.client import Client
from modules.utils import configGet from modules.utils import configGet
from classes.poster_client import PosterClient
app = Client("duptsiaposter", bot_token=configGet("bot_token", "bot"), api_id=configGet("api_id", "bot"), api_hash=configGet("api_hash", "bot")) app = PosterClient(
"duptsiaposter",
bot_token=configGet("bot_token", "bot"),
api_id=configGet("api_id", "bot"),
api_hash=configGet("api_hash", "bot"),
)

View File

@@ -1,23 +1,37 @@
from os import listdir from os import listdir
from pyrogram.client import Client from classes.poster_client import PosterClient
from pyrogram.types import BotCommand, BotCommandScopeChat from pyrogram.types import BotCommand, BotCommandScopeChat
from modules.utils import configGet, locale from modules.utils import configGet, locale
async def register_commands(app: Client):
async def register_commands(app: PosterClient) -> None:
if configGet("submit", "mode"): if configGet("submit", "mode"):
# Registering user commands # Registering user commands
for entry in listdir(configGet("locale", "locations")): for entry in listdir(configGet("locale", "locations")):
if entry.endswith(".json"): if entry.endswith(".json"):
commands_list = [] commands_list = []
for command in configGet("commands"): for command in configGet("commands"):
commands_list.append(BotCommand(command, locale(command, "commands", locale=entry.replace(".json", "")))) commands_list.append(
await app.set_bot_commands(commands_list, language_code=entry.replace(".json", "")) BotCommand(
command,
locale(
command, "commands", locale=entry.replace(".json", "")
),
)
)
await app.set_bot_commands(
commands_list, language_code=entry.replace(".json", "")
)
# Registering user commands for fallback locale # Registering user commands for fallback locale
commands_list = [] commands_list = []
for command in configGet("commands"): for command in configGet("commands"):
commands_list.append(BotCommand(command, locale(command, "commands", locale=configGet("locale_fallback")))) commands_list.append(
BotCommand(
command,
locale(command, "commands", locale=configGet("locale_fallback")),
)
)
await app.set_bot_commands(commands_list) await app.set_bot_commands(commands_list)
# Registering admin commands # Registering admin commands
@@ -25,9 +39,19 @@ async def register_commands(app: Client):
if configGet("submit", "mode"): if configGet("submit", "mode"):
for command in configGet("commands"): for command in configGet("commands"):
commands_admin_list.append(
commands_admin_list.append(BotCommand(command, locale(command, "commands", locale=configGet("locale")))) BotCommand(
command, locale(command, "commands", locale=configGet("locale"))
)
)
for command in configGet("commands_admin"): for command in configGet("commands_admin"):
commands_admin_list.append(BotCommand(command, locale(command, "commands_admin", locale=configGet("locale")))) commands_admin_list.append(
BotCommand(
command, locale(command, "commands_admin", locale=configGet("locale"))
)
)
await app.set_bot_commands(commands_admin_list, scope=BotCommandScopeChat(chat_id=configGet("admin"))) for admin in app.admins:
await app.set_bot_commands(
commands_admin_list, scope=BotCommandScopeChat(chat_id=admin)
)

View File

@@ -8,18 +8,16 @@ with open("config.json", "r", encoding="utf-8") as f:
f.close() 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(
db_config["user"], db_config["user"],
db_config["password"], db_config["password"],
db_config["host"], db_config["host"],
db_config["port"], db_config["port"],
db_config["name"] db_config["name"],
) )
else: else:
con_string = 'mongodb://{0}:{1}/{2}'.format( con_string = "mongodb://{0}:{1}/{2}".format(
db_config["host"], db_config["host"], db_config["port"], db_config["name"]
db_config["port"],
db_config["name"]
) )
db_client = MongoClient(con_string) db_client = MongoClient(con_string)

View File

@@ -9,15 +9,15 @@ from shutil import copyfileobj
from datetime import datetime from datetime import datetime
with open(getcwd()+path.sep+"config.json", "r", encoding='utf8') as file: with open(getcwd() + path.sep + "config.json", "r", encoding="utf8") as file:
json_contents = loads(file.read()) json_contents = loads(file.read())
log_size = json_contents["logging"]["size"] log_size = json_contents["logging"]["size"]
log_folder = json_contents["logging"]["location"] log_folder = json_contents["logging"]["location"]
file.close() file.close()
# Check latest log size
def checkSize(debug=False):
# Check latest log size
def checkSize(debug=False) -> None:
global log_folder global log_folder
if debug: if debug:
@@ -29,18 +29,26 @@ def checkSize(debug=False):
makedirs(log_folder, exist_ok=True) makedirs(log_folder, exist_ok=True)
log = stat(path.join(log_folder, log_file)) log = stat(path.join(log_folder, log_file))
if (log.st_size / 1024) > log_size: if (log.st_size / 1024) > log_size:
with open(path.join(log_folder, log_file), 'rb') as f_in: with open(path.join(log_folder, log_file), "rb") as f_in:
with gzipopen(path.join(log_folder, f'{datetime.now().strftime("%d.%m.%Y_%H:%M:%S")}.log.gz'), 'wb') as f_out: with gzipopen(
path.join(
log_folder,
f'{datetime.now().strftime("%d.%m.%Y_%H:%M:%S")}.log.gz',
),
"wb",
) as f_out:
copyfileobj(f_in, f_out) copyfileobj(f_in, f_out)
print(f'Copied {path.join(log_folder, datetime.now().strftime("%d.%m.%Y_%H:%M:%S"))}.log.gz') print(
open(path.join(log_folder, log_file), 'w').close() f'Copied {path.join(log_folder, datetime.now().strftime("%d.%m.%Y_%H:%M:%S"))}.log.gz'
)
open(path.join(log_folder, log_file), "w").close()
except FileNotFoundError: except FileNotFoundError:
print(f'Log file {path.join(log_folder, log_file)} does not exist') print(f"Log file {path.join(log_folder, log_file)} does not exist")
pass pass
# Append string to log
def logAppend(message, debug=False):
# Append string to log
def logAppend(message, debug=False) -> None:
global log_folder global log_folder
message_formatted = f'[{datetime.now().strftime("%d.%m.%Y")}] [{datetime.now().strftime("%H:%M:%S")}] {message}' message_formatted = f'[{datetime.now().strftime("%d.%m.%Y")}] [{datetime.now().strftime("%H:%M:%S")}] {message}'
@@ -51,12 +59,13 @@ def logAppend(message, debug=False):
else: else:
log_file = "latest.log" log_file = "latest.log"
log = open(path.join(log_folder, log_file), 'a') log = open(path.join(log_folder, log_file), "a")
log.write(f'{message_formatted}\n') log.write(f"{message_formatted}\n")
log.close() log.close()
# Print to stdout and then to log # Print to stdout and then to log
def logWrite(message, debug=False): def logWrite(message, debug=False) -> None:
# save to log file and rotation is to be done # save to log file and rotation is to be done
logAppend(f'{message}', debug=debug) logAppend(f"{message}", debug=debug)
print(f"{message}", flush=True) print(f"{message}", flush=True)

View File

@@ -1,5 +1,6 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.schedulers.asyncio import AsyncIOScheduler
from pytimeparse.timeparse import timeparse
from modules.utils import configGet from modules.utils import configGet
from modules.sender import send_content from modules.sender import send_content
from modules.commands_register import register_commands from modules.commands_register import register_commands
@@ -8,10 +9,23 @@ from modules.app import app
scheduler = AsyncIOScheduler() scheduler = AsyncIOScheduler()
if configGet("post", "mode"): if configGet("post", "mode"):
# for entry in configGet("time", "posting"): if configGet("use_interval", "posting"):
# dt_obj = datetime.strptime(entry, "%H:%M") scheduler.add_job(
# Is only used for debug now! send_content,
scheduler.add_job(send_content, "interval", seconds=30, args=[app]) "interval",
# scheduler.add_job(send_content, "cron", hour=dt_obj.hour, minute=dt_obj.minute, args=[app]) seconds=timeparse(configGet("interval", "posting")),
args=[app],
)
else:
for entry in configGet("time", "posting"):
dt_obj = datetime.strptime(entry, "%H:%M")
scheduler.add_job(
send_content, "cron", hour=dt_obj.hour, minute=dt_obj.minute, args=[app]
)
scheduler.add_job(register_commands, "date", run_date=datetime.now()+timedelta(seconds=10), args=[app]) scheduler.add_job(
register_commands,
"date",
run_date=datetime.now() + timedelta(seconds=10),
args=[app],
)

View File

@@ -1,27 +1,28 @@
from datetime import datetime, timezone from datetime import datetime
from os import makedirs, path from os import makedirs, path
from shutil import copyfileobj, rmtree from shutil import rmtree
from traceback import format_exc from traceback import format_exc
from uuid import uuid4 from uuid import uuid4
from PIL import Image
import aiofiles
from bson import ObjectId from classes.poster_client import PosterClient
from pyrogram.client import Client
from requests import get
from modules.api_client import authorize, move_pic, random_pic from modules.api_client import authorize, move_pic, random_pic, http_session
from modules.database import col_sent, col_submitted from modules.database import col_sent, col_submitted
from modules.logger import logWrite from modules.logger import logWrite
from modules.utils import configGet, locale from modules.utils import configGet, locale
async def send_content(app: Client): async def send_content(app: PosterClient) -> None:
try: try:
try: try:
token = await authorize() token = await authorize()
except ValueError: except ValueError:
await app.send_message(configGet("admin"), locale("api_creds_invalid", "message", locale=configGet("locale"))) await app.send_message(
app.owner,
locale("api_creds_invalid", "message", locale=configGet("locale")),
)
return return
try: try:
@@ -29,19 +30,37 @@ async def send_content(app: Client):
except KeyError: except KeyError:
logWrite(locale("post_empty", "console", locale=configGet("locale"))) logWrite(locale("post_empty", "console", locale=configGet("locale")))
if configGet("error", "reports"): if configGet("error", "reports"):
await app.send_message(configGet("admin"), locale("api_queue_empty", "message", locale=configGet("locale"))) await app.send_message(
app.owner,
locale("api_queue_empty", "message", locale=configGet("locale")),
)
return return
except ValueError: except ValueError:
if configGet("error", "reports"): if configGet("error", "reports"):
await app.send_message(configGet("admin"), locale("api_queue_error", "message", locale=configGet("locale"))) await app.send_message(
app.owner,
locale("api_queue_error", "message", locale=configGet("locale")),
)
return return
response = get(f'{configGet("address", "posting", "api")}/photos/{pic[0]}', headers={"Authorization": f"Bearer {token}"}, stream=True) response = await http_session.get(
f'{configGet("address", "posting", "api")}/photos/{pic[0]}',
headers={"Authorization": f"Bearer {token}"},
)
if response.status_code != 200: if response.status != 200:
logWrite(locale("post_invalid_pic", "console", locale=configGet("locale")).format(str(response.json()))) logWrite(
locale(
"post_invalid_pic", "console", locale=configGet("locale")
).format(response.status, str(response.json()))
)
if configGet("error", "reports"): if configGet("error", "reports"):
await app.send_message(configGet("admin"), locale("post_invalid_pic", "message", locale=configGet("locale")).format(response.json())) await app.send_message(
app.owner,
locale(
"post_invalid_pic", "message", locale=configGet("locale")
).format(response.status, response.json()),
)
tmp_dir = str(uuid4()) tmp_dir = str(uuid4())
@@ -49,42 +68,101 @@ async def send_content(app: Client):
tmp_path = path.join(tmp_dir, pic[1]) tmp_path = path.join(tmp_dir, pic[1])
with open(path.join(configGet("tmp", "locations"), tmp_path), 'wb') as out_file: async with aiofiles.open(
copyfileobj(response.raw, out_file) path.join(configGet("tmp", "locations"), tmp_path), "wb"
) as out_file:
await out_file.write(await response.read())
logWrite(
f'Candidate {pic[1]} ({pic[0]}) is {path.getsize(path.join(configGet("tmp", "locations"), tmp_path))} bytes big',
debug=True,
)
if path.getsize(path.join(configGet("tmp", "locations"), tmp_path)) > 5242880:
image = Image.open(path.join(configGet("tmp", "locations"), tmp_path))
width, height = image.size
image = image.resize((int(width / 2), int(height / 2)), Image.ANTIALIAS)
if tmp_path.lower().endswith(".jpeg") or tmp_path.lower().endswith(".jpg"):
image.save(
path.join(configGet("tmp", "locations"), tmp_path),
"JPEG",
optimize=True,
quality=50,
)
elif tmp_path.lower().endswith(".png"):
image.save(
path.join(configGet("tmp", "locations"), tmp_path),
"PNG",
optimize=True,
compress_level=8,
)
image.close()
if path.getsize(path.join(configGet("tmp", "locations"), tmp_path)) > 5242880:
rmtree(
path.join(configGet("tmp", "locations"), tmp_dir), ignore_errors=True
)
raise BytesWarning
del response del response
submitted_caption = col_submitted.find_one( {"image": ObjectId(pic[0])} ) submitted = col_submitted.find_one({"temp.file": pic[1]})
if submitted_caption is not None: if submitted is not None and submitted["caption"] is not None:
caption = submitted_caption["caption"].strip() caption = submitted["caption"].strip()
else: else:
caption = "" caption = ""
if (
submitted is not None
and configGet("enabled", "posting", "submitted_caption")
and (
(submitted["user"] not in app.admins)
or (configGet("ignore_admins", "posting", "submitted_caption") is False)
)
):
caption = (
f"{caption}\n\n{configGet('text', 'posting', 'submitted_caption')}\n"
)
else:
caption = f"{caption}\n\n"
if configGet("enabled", "caption"): if configGet("enabled", "caption"):
if configGet("link", "caption") != None: if configGet("link", "caption") != None:
caption = f"{caption}\n\n[{configGet('text', 'caption')}]({configGet('link', 'caption')})" caption = f"{caption}[{configGet('text', 'caption')}]({configGet('link', 'caption')})"
else: else:
caption = f"{caption}\n\n{configGet('text', 'caption')}" caption = f"{caption}{configGet('text', 'caption')}"
else: else:
caption = caption caption = caption
try: try:
sent = await app.send_photo(configGet("channel", "posting"), path.join(configGet("tmp", "locations"), tmp_path), caption=caption, disable_notification=configGet("silent", "posting")) sent = await app.send_photo(
configGet("channel", "posting"),
path.join(configGet("tmp", "locations"), tmp_path),
caption=caption,
disable_notification=configGet("silent", "posting"),
)
except Exception as exp: except Exception as exp:
logWrite(f"Could not send image {pic[1]} ({pic[0]}) due to {exp}") logWrite(f"Could not send image {pic[1]} ({pic[0]}) due to {exp}")
if configGet("error", "reports"): if configGet("error", "reports"):
await app.send_message(configGet("admin"), locale("post_exception", "message", locale=configGet("locale")).format(exp, format_exc())) await app.send_message(
app.owner,
locale(
"post_exception", "message", locale=configGet("locale")
).format(exp, format_exc()),
)
# rmtree(path.join(configGet("tmp", "locations"), tmp_dir), ignore_errors=True) # rmtree(path.join(configGet("tmp", "locations"), tmp_dir), ignore_errors=True)
return return
col_sent.insert_one( col_sent.insert_one(
{ {
"date": datetime.now(tz=timezone.utc), "date": datetime.now(),
"image": pic[0], "image": pic[0],
"filename": pic[1], "filename": pic[1],
"channel": configGet("channel", "posting"), "channel": configGet("channel", "posting"),
"caption": None if submitted_caption is None else submitted_caption["caption"].strip() "caption": None
if (submitted is None or submitted["caption"] is None)
else submitted["caption"].strip(),
} }
) )
@@ -92,143 +170,31 @@ async def send_content(app: Client):
rmtree(path.join(configGet("tmp", "locations"), tmp_dir), ignore_errors=True) rmtree(path.join(configGet("tmp", "locations"), tmp_dir), ignore_errors=True)
logWrite(locale("post_sent", "console", locale=configGet("locale")).format(pic[0], str(configGet("channel", "posting")), caption.replace("\n", "%n"), str(configGet("silent", "posting")))) logWrite(
locale("post_sent", "console", locale=configGet("locale")).format(
pic[0],
str(configGet("channel", "posting")),
caption.replace("\n", "%n"),
str(configGet("silent", "posting")),
)
)
except Exception as exp: except Exception as exp:
logWrite(locale("post_exception", "console", locale=configGet("locale")).format(str(exp), format_exc())) logWrite(
locale("post_exception", "console", locale=configGet("locale")).format(
str(exp), format_exc()
)
)
if configGet("error", "reports"): if configGet("error", "reports"):
await app.send_message(configGet("admin"), locale("post_exception", "message", locale=configGet("locale")).format(exp, format_exc())) await app.send_message(
app.owner,
locale("post_exception", "message", locale=configGet("locale")).format(
# async def send_content_old(app: Client): exp, format_exc()
),
# # Send post to channel )
# try: try:
rmtree(
# index = jsonLoad(configGet("index", "locations")) path.join(configGet("tmp", "locations"), tmp_dir), ignore_errors=True
)
# if configGet("api_based", "mode"): except:
pass
# try:
# pic = random_pic()
# except:
# logWrite(locale("post_empty", "console", locale=configGet("locale")))
# if configGet("error", "reports"):
# await app.send_message(configGet("admin"), locale("post_empty", "message", locale=configGet("locale")))
# return
# token = authorize()
# response = get(f'{configGet("address", "posting", "api")}/photos/{pic[0]}', headers={"Authorization": f"Bearer {token}"}, stream=True)
# with open(configGet("tmp", "locations")+sep+pic[0]+".jpg", 'wb') as out_file:
# copyfileobj(response.raw, out_file)
# del response
# candidate = configGet("tmp", "locations")+sep+pic[0]+".jpg"
# candidate_file = pic[1]
# ext_type = "photo"
# if not configGet("api_based", "mode"):
# list_queue = listdir(configGet("queue", "locations"))
# for file in list_queue:
# if not file in index["sent"]:
# ext_match = False
# for ext in configGet("photo", "posting", "extensions"):
# if file.endswith(ext):
# ext_match = True
# ext_type = "photo"
# break
# for ext in configGet("video", "posting", "extensions"):
# if file.endswith(ext):
# ext_match = True
# ext_type = "video"
# break
# if not ext_match:
# list_queue.remove(file)
# else:
# list_queue.remove(file)
# if len(list_queue) > 0:
# candidate_file = choice(list_queue)
# candidate = configGet("queue", "locations")+sep+candidate_file
# else:
# logWrite(locale("post_empty", "console", locale=configGet("locale")))
# if configGet("error", "reports"):
# await app.send_message(configGet("admin"), locale("post_empty", "message", locale=configGet("locale")))
# return
# if candidate_file in index["captions"]:
# caption = index["captions"][candidate_file]
# else:
# caption = ""
# if configGet("enabled", "caption"):
# if configGet("link", "caption") != None:
# caption = f"{caption}\n\n[{configGet('text', 'caption')}]({configGet('link', 'caption')})"
# else:
# caption = f"{caption}\n\n{configGet('text', 'caption')}"
# else:
# caption = caption
# if ext_type == "photo":
# if configGet("enabled", "caption"):
# if configGet("link", "caption") != None:
# sent = await app.send_photo(configGet("channel", "posting"), candidate, caption=caption, disable_notification=configGet("silent", "posting"))
# else:
# sent = await app.send_photo(configGet("channel", "posting"), candidate, caption=caption, disable_notification=configGet("silent", "posting"))
# else:
# sent = await app.send_photo(configGet("channel", "posting"), candidate, caption=caption, disable_notification=configGet("silent", "posting"))
# elif ext_type == "video":
# if configGet("enabled", "caption"):
# if configGet("link", "caption") != None:
# sent = await app.send_video(configGet("channel", "posting"), candidate, caption=caption, disable_notification=configGet("silent", "posting"))
# else:
# sent = await app.send_video(configGet("channel", "posting"), candidate, caption=caption, disable_notification=configGet("silent", "posting"))
# else:
# sent = await app.send_video(configGet("channel", "posting"), candidate, caption=caption, disable_notification=configGet("silent", "posting"))
# else:
# return
# if configGet("api_based", "mode"):
# remove(configGet("tmp", "locations")+sep+pic[0]+".jpg")
# move_pic(pic[0])
# index["sent"].append(candidate_file)
# index["last_id"] = sent.id
# jsonSave(index, configGet("index", "locations"))
# if configGet("move_sent", "posting"):
# move(candidate, configGet("sent", "locations")+sep+candidate_file)
# logWrite(locale("post_sent", "console", locale=configGet("locale")).format(candidate, ext_type, str(configGet("channel", "posting")), caption.replace("\n", "%n"), str(configGet("silent", "posting"))))
# if configGet("sent", "reports"):
# await app.send_message(configGet("admin"), f"Posted `{candidate_file}`", disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup([
# [InlineKeyboardButton(locale("post_view", "button", locale=configGet("locale")), url=sent.link)]
# ]))
# except Exception as exp:
# logWrite(locale("post_exception", "console", locale=configGet("locale")).format(str(exp), format_exc()))
# if configGet("error", "reports"):
# await app.send_message(configGet("admin"), locale("post_exception", "message", locale=configGet("locale")).format(exp, format_exc()))
# pass
# Work in progress
# Check last posts forwards
# check_forwards(app)

View File

@@ -1,27 +0,0 @@
from datetime import datetime, timezone
from modules.utils import configGet
from modules.database import col_users, col_banned
from pyrogram.types.user_and_chats import User
def subLimit(user: User) -> None:
if col_users.find_one_and_update({"user": user.id}, {"$set": {"cooldown": datetime.now(tz=timezone.utc)}}) is None:
col_users.insert_one({"user": user.id, "cooldown": datetime.now(tz=timezone.utc)})
def subLimited(user: User) -> bool:
if user.id == configGet("admin"):
return False
else:
db_record = col_users.find_one({"user": user.id})
if db_record is None:
return False
return True if (datetime.now(tz=timezone.utc) - db_record["cooldown"]).total_seconds() < configGet("timeout", "submission") else False
def subBlocked(user: User) -> bool:
return False if col_banned.find_one({"user": user.id}) is None else True
def subBlock(user: User) -> None:
if col_banned.find_one({"user": user.id}) is None:
col_banned.insert_one({"user": user.id, "date": datetime.now(tz=timezone.utc)})
def subUnblock(user: User) -> None:
col_banned.find_one_and_delete({"user": user.id})

View File

@@ -9,27 +9,34 @@ from sys import exit
from os import sep, kill from os import sep, kill
from os import name as osname from os import name as osname
from traceback import print_exc from traceback import print_exc
from typing import Any
from modules.logger import logWrite from modules.logger import logWrite
def jsonLoad(filename):
def jsonLoad(filename: str) -> Any:
"""Loads arg1 as json and returns its contents""" """Loads arg1 as json and returns its contents"""
with open(filename, "r", encoding='utf8') as file: with open(filename, "r", encoding="utf8") as file:
try: try:
output = loads(file.read()) output = loads(file.read())
except JSONDecodeError: except JSONDecodeError:
logWrite(f"Could not load json file {filename}: file seems to be incorrect!\n{print_exc()}") logWrite(
f"Could not load json file {filename}: file seems to be incorrect!\n{print_exc()}"
)
raise raise
except FileNotFoundError: except FileNotFoundError:
logWrite(f"Could not load json file {filename}: file does not seem to exist!\n{print_exc()}") logWrite(
f"Could not load json file {filename}: file does not seem to exist!\n{print_exc()}"
)
raise raise
file.close() file.close()
return output return output
def jsonSave(contents, filename):
def jsonSave(contents: Any, filename: str) -> None:
"""Dumps dict/list arg1 to file arg2""" """Dumps dict/list arg1 to file arg2"""
try: try:
with open(filename, "w", encoding='utf8') as file: with open(filename, "w", encoding="utf8") as file:
file.write(dumps(contents, ensure_ascii=False, indent=4)) file.write(dumps(contents, ensure_ascii=False, indent=4))
file.close() file.close()
except Exception as exp: except Exception as exp:
@@ -56,6 +63,7 @@ def configSet(key: str, value, *args: str):
jsonSave(this_dict, "config.json") jsonSave(this_dict, "config.json")
return return
def configGet(key: str, *args: str): def configGet(key: str, *args: str):
"""Get value of the config key """Get value of the config key
Args: Args:
@@ -70,6 +78,7 @@ def configGet(key: str, *args: str):
this_key = this_key[dict_key] this_key = this_key[dict_key]
return this_key[key] return this_key[key]
def locale(key: str, *args: str, locale=configGet("locale")): def locale(key: str, *args: str, locale=configGet("locale")):
"""Get value of locale string """Get value of locale string
Args: Args:
@@ -79,17 +88,21 @@ def locale(key: str, *args: str, locale=configGet("locale")):
Returns: Returns:
* any: Value of provided locale key * any: Value of provided locale key
""" """
if (locale == None): if locale == None:
locale = configGet("locale") locale = configGet("locale")
try: try:
this_dict = jsonLoad(f'{configGet("locale", "locations")}{sep}{locale}.json') this_dict = jsonLoad(f'{configGet("locale", "locations")}{sep}{locale}.json')
except FileNotFoundError: except FileNotFoundError:
try: try:
this_dict = jsonLoad(f'{configGet("locale", "locations")}{sep}{configGet("locale")}.json') this_dict = jsonLoad(
f'{configGet("locale", "locations")}{sep}{configGet("locale")}.json'
)
except FileNotFoundError: except FileNotFoundError:
try: try:
this_dict = jsonLoad(f'{configGet("locale_fallback", "locations")}{sep}{configGet("locale")}.json') this_dict = jsonLoad(
f'{configGet("locale_fallback", "locations")}{sep}{configGet("locale")}.json'
)
except: except:
return f'⚠️ Locale in config is invalid: could not get "{key}" in {str(args)} from locale "{locale}"' return f'⚠️ Locale in config is invalid: could not get "{key}" in {str(args)} from locale "{locale}"'
@@ -102,16 +115,23 @@ def locale(key: str, *args: str, locale=configGet("locale")):
except KeyError: except KeyError:
return f'⚠️ Locale in config is invalid: could not get "{key}" in {str(args)} from locale "{locale}"' return f'⚠️ Locale in config is invalid: could not get "{key}" in {str(args)} from locale "{locale}"'
try: try:
from psutil import Process from psutil import Process
except ModuleNotFoundError: except ModuleNotFoundError:
print(locale("deps_missing", "console", locale=configGet("locale")), flush=True) print(locale("deps_missing", "console", locale=configGet("locale")), flush=True)
exit() exit()
def killProc(pid):
def killProc(pid: int) -> None:
"""Kill process by its PID. Meant to be used to kill the main process of bot itself.
### Args:
* pid (`int`): PID of the target
"""
if osname == "posix": if osname == "posix":
from signal import SIGKILL from signal import SIGKILL
kill(pid, SIGKILL) kill(pid, SIGKILL)
else: else:
p = Process(pid) Process(pid).kill()
p.kill()

View File

@@ -1,11 +1,12 @@
from modules.app import app from modules.app import app
from pyrogram import filters from pyrogram import filters
from pyrogram.types import CallbackQuery from pyrogram.types import CallbackQuery
from pyrogram.client import Client from classes.poster_client import PosterClient
from modules.utils import locale from modules.utils import locale
# Callback empty ===============================================================================================================
@app.on_callback_query(filters.regex("nothing")) @app.on_callback_query(filters.regex("nothing"))
async def callback_query_nothing(app: Client, clb: CallbackQuery): async def callback_query_nothing(app: PosterClient, clb: CallbackQuery):
await clb.answer(text=locale("nothing", "callback", locale=clb.from_user)) await clb.answer(
# ============================================================================================================================== text=locale("nothing", "callback", locale=clb.from_user.language_code)
)

View File

@@ -1,116 +1,223 @@
from os import path, sep from os import path
from pathlib import Path from shutil import rmtree
from pyrogram import filters from pyrogram import filters
from pyrogram.client import Client from pyrogram.types import CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
from pyrogram.types import CallbackQuery from classes.exceptions import SubmissionDuplicatesError, SubmissionUnavailableError
from classes.poster_client import PosterClient
from classes.user import PosterUser
from modules.api_client import upload_pic
from modules.app import app from modules.app import app
from modules.submissions import subBlock, subUnblock from modules.utils import configGet, locale
from modules.utils import configGet, jsonLoad, jsonSave, locale
from modules.database import col_submitted from modules.database import col_submitted
from bson import ObjectId from bson import ObjectId
@app.on_callback_query(filters.regex("sub_yes_[\s\S]*")) @app.on_callback_query(filters.regex("sub_yes_[\s\S]*"))
async def callback_query_yes(app: Client, clb: CallbackQuery): async def callback_query_yes(app: PosterClient, clb: CallbackQuery):
fullclb = str(clb.data).split("_")
fullclb = clb.data.split("_")
user_locale = clb.from_user.language_code user_locale = clb.from_user.language_code
# Check if submission is in DB and really exists
# Upload the file to the API server
# Modify submission in DB to state that it's accepted (or so called "done")
# Change keyboard to a completed variant
# Send replies to both user and admin about accepting the application
db_entry = col_submitted.find_one({"_id": ObjectId(fullclb[2])}) db_entry = col_submitted.find_one({"_id": ObjectId(fullclb[2])})
if db_entry is None:
await clb.answer(text=locale("sub_msg_unavail", "message", locale=user_locale), show_alert=True)
return
else:
if db_entry["tmp"]["uuid"] is not None:
if not path.exists(path.join(configGet("data", "locations"), "submissions", db_entry["tmp"]["uuid"], db_entry["tmp"]["file"])):
await clb.answer(text=locale("sub_msg_unavail", "message", locale=user_locale), show_alert=True)
return
else:
try: try:
submission = await app.get_messages(db_entry["user"], db_entry["telegram"]["msg_id"]) submission = await app.submit_photo(fullclb[2])
except: except SubmissionUnavailableError:
await clb.answer(text=locale("sub_msg_unavail", "message", locale=user_locale), show_alert=True) await clb.answer(
text=locale("sub_msg_unavail", "callback", locale=user_locale),
show_alert=True,
)
return
except SubmissionDuplicatesError as exp:
await clb.answer(
text=locale("sub_duplicates_found", "callback", locale=user_locale),
show_alert=True,
)
await clb.message.reply_text(
locale("sub_media_duplicates_list", "message", locale=user_locale).format(
"\n".join(exp.duplicates)
),
quote=True,
)
return return
try: if submission is not None:
if configGet("api_based", "mode") is True: await submission.reply_text(
media = await app.download_media(submission, file_name=configGet("tmp", "locations")+sep) locale("sub_yes", "message", locale=submission.from_user.language_code),
upload = upload_pic(media) quote=True,
if upload[0] is False: )
await clb.answer(text=locale("sub_media_failed", "message", locale=user_locale), show_alert=True) elif db_entry is not None:
elif len(upload[1]) > 0: await app.send_message(db_entry["user"], locale("sub_yes", "message"))
await clb.answer(text=locale("sub_media_duplicates", "message", locale=user_locale))
await clb.message.reply_text(locale("sub_media_duplicates_list", "message", locale=user_locale).format("\n".join(upload[1])))
else:
if clb.data.endswith("_caption"):
index = jsonLoad(configGet("index", "locations"))
index["captions"][Path(media).name] = submission.caption
jsonSave(index, configGet("index", "locations"))
else:
media = await app.download_media(submission, file_name=configGet("queue", "locations")+sep)
if clb.data.endswith("_caption"):
index = jsonLoad(configGet("index", "locations"))
index["captions"][Path(media).name] = submission.caption
jsonSave(index, configGet("index", "locations"))
except:
await clb.answer(text=locale("sub_media_unavail", "message", locale=user_locale), show_alert=True)
return
await submission.reply_text(locale("sub_yes", "message", locale=submission.from_user.language_code), quote=True)
await clb.answer(text=locale("sub_yes", "callback", locale=user_locale).format(fullclb[2]), show_alert=True)
# edited_markup = [[InlineKeyboardButton(text=str(locale("accepted", "button")), callback_data="nothing")]] await clb.answer(
# await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) text=locale("sub_yes", "callback", locale=user_locale).format(fullclb[2]),
show_alert=True,
)
edited_markup = (
[
[
InlineKeyboardButton(
text=str(locale("accepted", "button", locale=user_locale)),
callback_data="nothing",
)
],
clb.message.reply_markup.inline_keyboard[1],
]
if len(clb.message.reply_markup.inline_keyboard) > 1
else [
[
InlineKeyboardButton(
text=str(locale("accepted", "button", locale=user_locale)),
callback_data="nothing",
)
]
]
)
await clb.message.edit_reply_markup(
reply_markup=InlineKeyboardMarkup(edited_markup)
)
# try:
# if configGet("api_based", "mode") is True:
# media = await app.download_media(submission, file_name=configGet("tmp", "locations")+sep)
# upload = upload_pic(media)
# if upload[0] is False:
# await clb.answer(text=locale("sub_media_failed", "message", locale=user_locale), show_alert=True)
# elif len(upload[1]) > 0:
# await clb.answer(text=locale("sub_media_duplicates", "message", locale=user_locale))
# await clb.message.reply_text(locale("sub_media_duplicates_list", "message", locale=user_locale).format("\n • ".join(upload[1])))
# else:
# if clb.data.endswith("_caption"):
# index = jsonLoad(configGet("index", "locations"))
# index["captions"][Path(media).name] = submission.caption
# jsonSave(index, configGet("index", "locations"))
# else:
# media = await app.download_media(submission, file_name=configGet("queue", "locations")+sep)
# if clb.data.endswith("_caption"):
# index = jsonLoad(configGet("index", "locations"))
# index["captions"][Path(media).name] = submission.caption
# jsonSave(index, configGet("index", "locations"))
# except:
# await clb.answer(text=locale("sub_media_unavail", "message", locale=user_locale), show_alert=True)
# return
@app.on_callback_query(filters.regex("sub_no_[\s\S]*")) @app.on_callback_query(filters.regex("sub_no_[\s\S]*"))
async def callback_query_no(app: Client, clb: CallbackQuery): async def callback_query_no(app: PosterClient, clb: CallbackQuery):
fullclb = clb.data.split("_") fullclb = str(clb.data).split("_")
user_locale = clb.from_user.language_code user_locale = clb.from_user.language_code
try:
submission = await app.get_messages(int(fullclb[2]), int(fullclb[3]))
except:
await clb.answer(text=locale("sub_msg_unavail", "message", locale=user_locale), show_alert=True)
return
await submission.reply_text(locale("sub_no", "message", locale=submission.from_user.language_code), quote=True)
await clb.answer(text=locale("sub_no", "callback", locale=user_locale).format(fullclb[2]), show_alert=True)
# edited_markup = [[InlineKeyboardButton(text=str(locale("accepted", "button")), callback_data="nothing")]] db_entry = col_submitted.find_one_and_delete({"_id": ObjectId(fullclb[2])})
# await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup))
if db_entry["temp"]["uuid"] is not None:
if path.exists(
path.join(
configGet("data", "locations"), "submissions", db_entry["temp"]["uuid"]
)
):
rmtree(
path.join(
configGet("data", "locations"),
"submissions",
db_entry["temp"]["uuid"],
),
ignore_errors=True,
)
try:
submission = await app.get_messages(
db_entry["user"], db_entry["telegram"]["msg_id"]
)
except:
await clb.answer(
text=locale("sub_msg_unavail", "message", locale=user_locale),
show_alert=True,
)
return
await submission.reply_text(
locale("sub_no", "message", locale=submission.from_user.language_code),
quote=True,
)
await clb.answer(
text=locale("sub_no", "callback", locale=user_locale).format(fullclb[2]),
show_alert=True,
)
edited_markup = (
[
[
InlineKeyboardButton(
text=str(locale("declined", "button", locale=user_locale)),
callback_data="nothing",
)
],
clb.message.reply_markup.inline_keyboard[1],
]
if len(clb.message.reply_markup.inline_keyboard) > 1
else [
[
InlineKeyboardButton(
text=str(locale("declined", "button", locale=user_locale)),
callback_data="nothing",
)
]
]
)
await clb.message.edit_reply_markup(
reply_markup=InlineKeyboardMarkup(edited_markup)
)
@app.on_callback_query(filters.regex("sub_block_[\s\S]*")) @app.on_callback_query(filters.regex("sub_block_[\s\S]*"))
async def callback_query_block(app: Client, clb: CallbackQuery): async def callback_query_block(app: PosterClient, clb: CallbackQuery):
fullclb = clb.data.split("_") fullclb = str(clb.data).split("_")
user_locale = clb.from_user.language_code user_locale = clb.from_user.language_code
await app.send_message(int(fullclb[2]), locale("sub_msg_unavail", "message", locale=configGet("locale"))) await app.send_message(
subBlock(int(fullclb[2])) int(fullclb[2]), locale("sub_blocked", "message", locale=configGet("locale"))
await clb.answer(text=locale("sub_block", "callback", locale=user_locale).format(fullclb[2]), show_alert=True) )
PosterUser(int(fullclb[2])).block()
await clb.answer(
text=locale("sub_block", "callback", locale=user_locale).format(fullclb[2]),
show_alert=True,
)
# edited_markup = [[InlineKeyboardButton(text=str(locale("accepted", "button")), callback_data="nothing")]] edited_markup = [
# await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) clb.message.reply_markup.inline_keyboard[0],
[
InlineKeyboardButton(
text=str(locale("sub_unblock", "button", locale=user_locale)),
callback_data=f"sub_unblock_{fullclb[2]}",
)
],
]
await clb.message.edit_reply_markup(
reply_markup=InlineKeyboardMarkup(edited_markup)
)
@app.on_callback_query(filters.regex("sub_unblock_[\s\S]*")) @app.on_callback_query(filters.regex("sub_unblock_[\s\S]*"))
async def callback_query_unblock(app: Client, clb: CallbackQuery): async def callback_query_unblock(app: PosterClient, clb: CallbackQuery):
fullclb = clb.data.split("_") fullclb = str(clb.data).split("_")
user_locale = clb.from_user.language_code user_locale = clb.from_user.language_code
await app.send_message(int(fullclb[2]), locale("sub_msg_unavail", "message", locale=configGet("locale"))) await app.send_message(
subUnblock(int(fullclb[2])) int(fullclb[2]), locale("sub_unblocked", "message", locale=configGet("locale"))
await clb.answer(text=locale("sub_unblock", "callback", locale=user_locale).format(fullclb[2]), show_alert=True) )
PosterUser(int(fullclb[2])).unblock()
await clb.answer(
text=locale("sub_unblock", "callback", locale=user_locale).format(fullclb[2]),
show_alert=True,
)
# edited_markup = [[InlineKeyboardButton(text=str(locale("accepted", "button")), callback_data="nothing")]] edited_markup = [
# await clb.message.edit(text=clb.message.text, reply_markup=InlineKeyboardMarkup(edited_markup)) clb.message.reply_markup.inline_keyboard[0],
[
InlineKeyboardButton(
text=str(locale("sub_block", "button", locale=user_locale)),
callback_data=f"sub_block_{fullclb[2]}",
)
],
]
await clb.message.edit_reply_markup(
reply_markup=InlineKeyboardMarkup(edited_markup)
)

View File

@@ -1,7 +1,7 @@
from os import getpid from os import getpid
from pyrogram import filters from pyrogram import filters
from pyrogram.client import Client from classes.poster_client import PosterClient
from pyrogram.types import Message from pyrogram.types import Message
from modules.app import app from modules.app import app
@@ -9,11 +9,16 @@ from modules.logger import logWrite
from modules.utils import configGet, killProc, locale from modules.utils import configGet, killProc, locale
@app.on_message(~ filters.scheduled & filters.command(["kill", "die", "reboot"], prefixes=["", "/"])) @app.on_message(
async def cmd_kill(app: Client, msg: Message): ~filters.scheduled & filters.command(["kill", "die", "reboot"], prefixes=["", "/"])
)
if msg.from_user.id == configGet("admin"): async def cmd_kill(app: PosterClient, msg: Message):
if msg.from_user.id in app.admins:
pid = getpid() pid = getpid()
logWrite(locale("shutdown", "console", locale=configGet("locale")).format(str(pid))) logWrite(
await msg.reply_text(locale("shutdown", "message", locale=configGet("locale")).format(str(pid))) locale("shutdown", "console", locale=configGet("locale")).format(str(pid))
)
await msg.reply_text(
locale("shutdown", "message", locale=configGet("locale")).format(str(pid))
)
killProc(pid) killProc(pid)

View File

@@ -1,18 +1,23 @@
from pyrogram import filters from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import Message from pyrogram.types import Message
from modules.app import app from modules.app import app
from modules.submissions import subBlocked from modules.utils import locale
from modules.utils import configGet, jsonLoad, locale from classes.user import PosterUser
from classes.poster_client import PosterClient
@app.on_message(~filters.scheduled & filters.command(["start"], prefixes="/")) @app.on_message(~filters.scheduled & filters.command(["start"], prefixes="/"))
async def cmd_start(app: Client, msg: Message): async def cmd_start(app: PosterClient, msg: Message):
if subBlocked(msg.from_user) is False: if PosterUser(msg.from_user.id).is_blocked() is False:
await msg.reply_text(locale("start", "message", locale=msg.from_user.language_code)) await msg.reply_text(
locale("start", "message", locale=msg.from_user.language_code)
)
@app.on_message(~filters.scheduled & filters.command(["rules", "help"], prefixes="/")) @app.on_message(~filters.scheduled & filters.command(["rules", "help"], prefixes="/"))
async def cmd_rules(app: Client, msg: Message): async def cmd_rules(app: PosterClient, msg: Message):
if subBlocked(msg.from_user) is False: if PosterUser(msg.from_user.id).is_blocked() is False:
await msg.reply_text(locale("rules", "message", locale=msg.from_user.language_code)) await msg.reply_text(
locale("rules", "message", locale=msg.from_user.language_code)
)

View File

@@ -0,0 +1,29 @@
from pyrogram import filters
from pyrogram.types import Message
from classes.poster_client import PosterClient
from modules.app import app
@app.on_message(~filters.scheduled & filters.command(["import"], prefixes=["", "/"]))
async def cmd_import(app: PosterClient, msg: Message):
if msg.from_user.id in app.admins:
pass
@app.on_message(~filters.scheduled & filters.command(["export"], prefixes=["", "/"]))
async def cmd_export(app: PosterClient, msg: Message):
if msg.from_user.id in app.admins:
pass
@app.on_message(~filters.scheduled & filters.command(["remove"], prefixes=["", "/"]))
async def cmd_remove(app: PosterClient, msg: Message):
if msg.from_user.id in app.admins:
pass
@app.on_message(~filters.scheduled & filters.command(["purge"], prefixes=["", "/"]))
async def cmd_purge(app: PosterClient, msg: Message):
if msg.from_user.id in app.admins:
pass

View File

@@ -1,162 +1,262 @@
from datetime import datetime, timezone from datetime import datetime
from os import makedirs, path, sep from os import makedirs, path, sep
from traceback import format_exc
from uuid import uuid4 from uuid import uuid4
from pyrogram import filters from pyrogram import filters
from pyrogram.client import Client
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
from pyrogram.enums.chat_action import ChatAction
from classes.exceptions import SubmissionDuplicatesError
from classes.poster_client import PosterClient
from classes.user import PosterUser
from modules.app import app from modules.app import app
from modules.database import col_banned, col_submitted from modules.database import col_banned, col_submitted
from modules.logger import logWrite from modules.logger import logWrite
from modules.submissions import subLimit, subLimited
from modules.utils import configGet, locale from modules.utils import configGet, locale
from classes.enums.submission_types import SubmissionType from classes.enums.submission_types import SubmissionType
@app.on_message(~ filters.scheduled & filters.photo | filters.video | filters.animation | filters.document) @app.on_message(
async def get_submission(_: Client, msg: Message): ~filters.scheduled & filters.private & filters.photo
| filters.video
| filters.animation
| filters.document
)
async def get_submission(app: PosterClient, msg: Message):
try:
if col_banned.find_one({"user": msg.from_user.id}) is not None:
return
locale("sub_wip", "message", locale=msg.from_user.language_code) await app.send_chat_action(msg.chat.id, ChatAction.TYPING)
# try: user_locale = msg.from_user.language_code
save_tmp = True
contents = None
# if col_banned.find_one( {"user": msg.from_user.id} ) is not None: if PosterUser(msg.from_user.id).is_limited():
# return await msg.reply_text(
locale("sub_cooldown", "message", locale=user_locale).format(
str(configGet("timeout", "submission"))
)
)
return
# user_locale = msg.from_user.language_code if msg.document is not None:
# save_tmp = True if msg.document.mime_type not in configGet("mime_types", "submission"):
# contents = None await msg.reply_text(
locale("mime_not_allowed", "message", locale=user_locale),
quote=True,
)
return
if msg.document.file_size > configGet("file_size", "submission"):
await msg.reply_text(
locale("document_too_large", "message", locale=user_locale).format(
str(configGet("file_size", "submission") / 1024 / 1024)
),
quote=True,
)
return
if msg.document.file_size > configGet("tmp_size", "submission"):
save_tmp = False
contents = (
msg.document.file_id,
SubmissionType.DOCUMENT,
) # , msg.document.file_name
# if subLimited(msg.from_user): if msg.video is not None:
# await msg.reply_text(locale("sub_cooldown", "message", locale=user_locale).format(str(configGet("timeout", "submission")))) if msg.video.file_size > configGet("file_size", "submission"):
# return await msg.reply_text(
locale("document_too_large", "message", locale=user_locale).format(
str(configGet("file_size", "submission") / 1024 / 1024)
),
quote=True,
)
return
if msg.video.file_size > configGet("tmp_size", "submission"):
save_tmp = False
contents = msg.video.file_id, SubmissionType.VIDEO # , msg.video.file_name
# if msg.document is not None: if msg.animation is not None:
# if msg.document.mime_type not in configGet("mime_types", "submission"): if msg.animation.file_size > configGet("file_size", "submission"):
# await msg.reply_text(locale("mime_not_allowed", "message", locale=user_locale), quote=True) await msg.reply_text(
# return locale("document_too_large", "message", locale=user_locale).format(
# if msg.document.file_size > configGet("file_size", "submission"): str(configGet("file_size", "submission") / 1024 / 1024)
# await msg.reply_text(locale("document_too_large", "message", locale=user_locale).format(str(configGet("file_size", "submission")/1024/1024)), quote=True) ),
# return quote=True,
# if msg.document.file_size > configGet("tmp_size", "submission"): )
# save_tmp = False return
# contents = msg.document.file_id, SubmissionType.DOCUMENT #, msg.document.file_name if msg.animation.file_size > configGet("tmp_size", "submission"):
save_tmp = False
contents = (
msg.animation.file_id,
SubmissionType.ANIMATION,
) # , msg.animation.file_name
# if msg.video is not None: if msg.photo is not None:
# if msg.video.file_size > configGet("file_size", "submission"): contents = msg.photo.file_id, SubmissionType.PHOTO # , "please_generate"
# await msg.reply_text(locale("document_too_large", "message", locale=user_locale).format(str(configGet("file_size", "submission")/1024/1024)), quote=True)
# return
# if msg.video.file_size > configGet("tmp_size", "submission"):
# save_tmp = False
# contents = msg.video.file_id, SubmissionType.VIDEO #, msg.video.file_name
# if msg.animation is not None: if save_tmp is not None:
# if msg.animation.file_size > configGet("file_size", "submission"): if contents is None:
# await msg.reply_text(locale("document_too_large", "message", locale=user_locale).format(str(configGet("file_size", "submission")/1024/1024)), quote=True) return
# return
# if msg.animation.file_size > configGet("tmp_size", "submission"):
# save_tmp = False
# contents = msg.animation.file_id, SubmissionType.ANIMATION #, msg.animation.file_name
# if msg.photo is not None: tmp_id = str(uuid4())
# contents = msg.photo.file_id, SubmissionType.PHOTO #, "please_generate" # filename = tmp_id if contents[1] == "please_generate" else contents[1]
makedirs(
path.join(configGet("data", "locations"), "submissions", tmp_id),
exist_ok=True,
)
downloaded = await app.download_media(
msg,
path.join(configGet("data", "locations"), "submissions", tmp_id) + sep,
)
inserted = col_submitted.insert_one(
{
"user": msg.from_user.id,
"date": datetime.now(),
"done": False,
"type": contents[1].value,
"temp": {"uuid": tmp_id, "file": path.basename(str(downloaded))},
"telegram": {"msg_id": msg.id, "file_id": contents[0]},
"caption": str(msg.caption) if msg.caption is not None else None,
}
)
# if save_tmp is not None: else:
if contents is None:
return
# if contents is None: inserted = col_submitted.insert_one(
# return {
"user": msg.from_user.id,
"date": datetime.now(),
"done": False,
"type": contents[1].value,
"temp": {"uuid": None, "file": None},
"telegram": {"msg_id": msg.id, "file_id": contents[0]},
"caption": str(msg.caption) if msg.caption is not None else None,
}
)
# tmp_id = str(uuid4()) buttons = [
# # filename = tmp_id if contents[1] == "please_generate" else contents[1] [
# makedirs(path.join(configGet("data", "locations"), "submissions", tmp_id), exist_ok=True) InlineKeyboardButton(
# downloaded = await app.download_media(msg, path.join(configGet("data", "locations"), "submissions", tmp_id)+sep) text=locale("sub_yes", "button", locale=configGet("locale")),
# inserted = col_submitted.insert_one( callback_data=f"sub_yes_{str(inserted.inserted_id)}",
# { )
# "user": msg.from_user.id, ]
# "date": datetime.now(tz=timezone.utc), ]
# "done": False,
# "type": contents[1].value,
# "temp": {
# "uuid": tmp_id,
# "file": path.basename(str(downloaded))
# },
# "telegram": {
# "msg_id": msg.id,
# "file_id": contents[0]
# },
# "caption": str(msg.caption) if msg.caption is not None else None
# }
# )
# else: if msg.caption is not None:
caption = str(msg.caption)
buttons[0].append(
InlineKeyboardButton(
text=locale(
"sub_yes_caption", "button", locale=configGet("locale")
),
callback_data=f"sub_yes_{str(inserted.inserted_id)}_caption",
)
)
buttons[0].append(
InlineKeyboardButton(
text=locale("sub_no", "button", locale=configGet("locale")),
callback_data=f"sub_no_{str(inserted.inserted_id)}",
)
)
else:
caption = ""
buttons[0].append(
InlineKeyboardButton(
text=locale("sub_no", "button", locale=configGet("locale")),
callback_data=f"sub_no_{str(inserted.inserted_id)}",
)
)
# if contents is None: caption += locale("sub_by", "message", locale=locale(configGet("locale")))
# return
# inserted = col_submitted.insert_one( if msg.from_user.first_name is not None:
# { caption += f" {msg.from_user.first_name}"
# "user": msg.from_user.id, if msg.from_user.last_name is not None:
# "date": datetime.now(tz=timezone.utc), caption += f" {msg.from_user.last_name}"
# "done": False, if msg.from_user.username is not None:
# "type": contents[1].value, caption += f" (@{msg.from_user.username})"
# "temp": { if msg.from_user.phone_number is not None:
# "uuid": None, caption += f" ({msg.from_user.phone_number})"
# "file": None
# },
# "telegram": {
# "msg_id": msg.id,
# "file_id": contents[0]
# },
# "caption": str(msg.caption) if msg.caption is not None else None
# }
# )
# buttons = [ if (
# [ msg.from_user.id in app.admins
# InlineKeyboardButton(text=locale("sub_yes", "button", locale=configGet("locale")), callback_data=f"sub_yes_{str(inserted.inserted_id)}") and configGet("admins", "submission", "require_confirmation") is False
# ] ):
# ] try:
await app.submit_photo(str(inserted.inserted_id))
await msg.reply_text(
locale("sub_yes_auto", "message", locale=user_locale),
disable_notification=True,
quote=True,
)
await msg.copy(app.owner, caption=caption, disable_notification=True)
return
except SubmissionDuplicatesError as exp:
await msg.reply_text(
locale(
"sub_media_duplicates_list", "message", locale=user_locale
).format("\n".join(exp.duplicates)),
quote=True,
)
return
except Exception as exp:
await msg.reply_text(format_exc(), quote=True)
return
elif (
msg.from_user.id not in app.admins
and configGet("users", "submission", "require_confirmation") is False
):
try:
await app.submit_photo(str(inserted.inserted_id))
await msg.reply_text(
locale("sub_yes_auto", "message", locale=user_locale),
disable_notification=True,
quote=True,
)
await msg.copy(app.owner, caption=caption)
return
except SubmissionDuplicatesError as exp:
await msg.reply_text(
locale("sub_dup", "message", locale=user_locale), quote=True
)
return
except Exception as exp:
await app.send_message(
app.owner,
locale("sub_error_admin", "message").format(
msg.from_user.id, format_exc()
),
)
await msg.reply_text("sub_error", quote=True)
return
# if msg.caption is not None: if msg.from_user.id not in app.admins:
# caption = str(msg.caption) buttons += [
# buttons[0].append( [
# InlineKeyboardButton(text=locale("sub_yes_caption", "button", locale=configGet("locale")), callback_data=f"sub_yes_{str(inserted.inserted_id)}_caption") InlineKeyboardButton(
# ) text=locale("sub_block", "button", locale=configGet("locale")),
# buttons[0].append( callback_data=f"sub_block_{msg.from_user.id}",
# InlineKeyboardButton(text=locale("sub_no", "button", locale=configGet("locale")), callback_data=f"sub_no_{str(inserted.inserted_id)}") )
# ) ]
# else: ]
# caption = ""
# buttons[0].append(
# InlineKeyboardButton(text=locale("sub_no", "button", locale=configGet("locale")), callback_data=f"sub_no_{str(inserted.inserted_id)}")
# )
# caption += locale("sub_by", "message", locale=locale(configGet("locale"))) PosterUser(msg.from_user.id).limit()
# if msg.from_user.first_name is not None: if msg.from_user.id != app.owner:
# caption += f" {msg.from_user.first_name}" await msg.reply_text(
# if msg.from_user.last_name is not None: locale("sub_sent", "message", locale=user_locale),
# caption += f" {msg.from_user.last_name}" disable_notification=True,
# if msg.from_user.username is not None: quote=True,
# caption += f" (@{msg.from_user.username})" )
# if msg.from_user.phone_number is not None:
# caption += f" ({msg.from_user.phone_number})"
# if msg.from_user.id != configGet("admin"): await msg.copy(
# buttons += [ app.owner, caption=caption, reply_markup=InlineKeyboardMarkup(buttons)
# [ )
# InlineKeyboardButton(text=locale("sub_block", "button", locale=configGet("locale")), callback_data=f"sub_block_{msg.from_user.id}")
# ]
# # [
# # InlineKeyboardButton(text=locale("sub_unblock", "button", locale=configGet("locale")), callback_data=f"sub_unblock_{msg.from_user.id}")
# # ]
# ]
# await msg.reply_text(locale("sub_sent", "message", locale=user_locale), quote=True) except AttributeError:
# subLimit(msg.from_user) logWrite(f"from_user in function get_submission does not seem to contain id")
# await msg.copy(configGet("admin"), caption=caption, reply_markup=InlineKeyboardMarkup(buttons))
# except AttributeError:
# logWrite(f"from_user in function get_submission does not seem to contain id")

View File

@@ -10,7 +10,7 @@ from modules.utils import configGet, jsonLoad, jsonSave, killProc, locale
# Args ===================================================================================================================================== # Args =====================================================================================================================================
parser = ArgumentParser( parser = ArgumentParser(
prog="Telegram Poster", prog="Telegram Poster",
description = "Bot for posting some of your stuff and also receiving submissions." description="Bot for posting some of your stuff and also receiving submissions.",
) )
parser.add_argument("-m", "--move-sent", action="store_true") parser.add_argument("-m", "--move-sent", action="store_true")
@@ -24,11 +24,22 @@ args = parser.parse_args()
if args.move_sent: if args.move_sent:
for entry in jsonLoad(configGet("index", "locations"))["sent"]: for entry in jsonLoad(configGet("index", "locations"))["sent"]:
try: try:
move(configGet("queue", "locations")+sep+entry, configGet("sent", "locations")+sep+entry) move(
configGet("queue", "locations") + sep + entry,
configGet("sent", "locations") + sep + entry,
)
except FileNotFoundError: except FileNotFoundError:
logWrite(locale("move_sent_doesnt_exist", "console", locale=configGet("locale")).format(entry)) logWrite(
locale(
"move_sent_doesnt_exist", "console", locale=configGet("locale")
).format(entry)
)
except Exception as exp: except Exception as exp:
logWrite(locale("move_sent_doesnt_exception", "console", locale=configGet("locale")).format(entry, exp)) logWrite(
locale(
"move_sent_doesnt_exception", "console", locale=configGet("locale")
).format(entry, exp)
)
logWrite(locale("move_sent_completed", "console", locale=configGet("locale"))) logWrite(locale("move_sent_completed", "console", locale=configGet("locale")))
if args.cleanup: if args.cleanup:
@@ -45,7 +56,11 @@ if args.cleanup:
except FileNotFoundError: except FileNotFoundError:
pass pass
except Exception as exp: except Exception as exp:
logWrite(locale("cleanup_exception", "console", locale=configGet("locale")).format(entry, exp)) logWrite(
locale(
"cleanup_exception", "console", locale=configGet("locale")
).format(entry, exp)
)
jsonSave(index, jsonLoad(configGet("index", "locations"))) jsonSave(index, jsonLoad(configGet("index", "locations")))
logWrite(locale("cleanup_completed", "console", locale=configGet("locale"))) logWrite(locale("cleanup_completed", "console", locale=configGet("locale")))
else: else:
@@ -56,9 +71,13 @@ if args.cleanup_index:
index = jsonLoad(configGet("index", "locations")) index = jsonLoad(configGet("index", "locations"))
index["sent"] = [] index["sent"] = []
jsonSave(index, jsonLoad(configGet("index", "locations"))) jsonSave(index, jsonLoad(configGet("index", "locations")))
logWrite(locale("cleanup_index_completed", "console", locale=configGet("locale"))) logWrite(
locale("cleanup_index_completed", "console", locale=configGet("locale"))
)
else: else:
logWrite(locale("cleanup_index_unathorized", "console", locale=configGet("locale"))) logWrite(
locale("cleanup_index_unathorized", "console", locale=configGet("locale"))
)
if args.norun: if args.norun:
logWrite(locale("passed_norun", "console", locale=configGet("locale_log"))) logWrite(locale("passed_norun", "console", locale=configGet("locale_log")))
@@ -70,7 +89,6 @@ if args.norun:
try: try:
from modules.app import app from modules.app import app
from pyrogram.sync import idle from pyrogram.sync import idle
from requests import get, post
except ModuleNotFoundError: except ModuleNotFoundError:
print(locale("deps_missing", "console", locale=configGet("locale")), flush=True) print(locale("deps_missing", "console", locale=configGet("locale")), flush=True)
exit() exit()
@@ -119,6 +137,7 @@ pid = getpid()
from plugins.commands.general import * from plugins.commands.general import *
if configGet("submit", "mode"): if configGet("submit", "mode"):
from plugins.callbacks.nothing import *
from plugins.callbacks.submission import * from plugins.callbacks.submission import *
from plugins.commands.mode_submit import * from plugins.commands.mode_submit import *
from plugins.handlers.submission import * from plugins.handlers.submission import *
@@ -168,25 +187,30 @@ if configGet("api_based", "mode"):
# asyncio.run(main()) # asyncio.run(main())
if __name__ == "__main__": if __name__ == "__main__":
logWrite(locale("startup", "console", locale=configGet("locale")).format(str(pid))) logWrite(locale("startup", "console", locale=configGet("locale")).format(str(pid)))
app.start() app.start()
if configGet("startup", "reports"): if configGet("startup", "reports"):
app.send_message(configGet("admin"), locale("startup", "message", locale=configGet("locale")).format(str(pid))) app.send_message(
app.owner,
locale("startup", "message", locale=configGet("locale")).format(str(pid)),
)
if configGet("post", "mode"): if configGet("post", "mode"):
scheduler.start() scheduler.start()
if configGet("api_based", "mode"): # if configGet("api_based", "mode"):
token = authorize() # token = await authorize()
if len(get(f'{configGet("address", "posting", "api")}/albums?q={configGet("queue", "posting", "api", "albums")}', headers={"Authorization": f"Bearer {token}"}).json()["results"]) == 0: # if len(get(f'{configGet("address", "posting", "api")}/albums?q={configGet("queue", "posting", "api", "albums")}', headers={"Authorization": f"Bearer {token}"}).json()["results"]) == 0:
post(f'{configGet("address", "posting", "api")}/albums?name={configGet("queue", "posting", "api", "albums")}&title={configGet("queue", "posting", "api", "albums")}', headers={"Authorization": f"Bearer {token}"}) # post(f'{configGet("address", "posting", "api")}/albums?name={configGet("queue", "posting", "api", "albums")}&title={configGet("queue", "posting", "api", "albums")}', headers={"Authorization": f"Bearer {token}"})
idle() idle()
app.send_message(configGet("admin"), locale("shutdown", "message", locale=configGet("locale")).format(str(pid))) app.send_message(
app.owner,
locale("shutdown", "message", locale=configGet("locale")).format(str(pid)),
)
logWrite(locale("shutdown", "console", locale=configGet("locale")).format(str(pid))) logWrite(locale("shutdown", "console", locale=configGet("locale")).format(str(pid)))
killProc(pid) killProc(pid)

View File

@@ -1,5 +1,8 @@
apscheduler~=3.10.0 apscheduler~=3.10.1
pyrogram~=2.0.99 pyrogram~=2.0.100
requests~=2.28.2 aiohttp~=3.8.4
aiofiles~=23.1.0
psutil~=5.9.4 psutil~=5.9.4
pymongo~=4.3.3 pymongo~=4.3.3
pillow~=9.4.0
pytimeparse~=1.1.8