22 Commits

Author SHA1 Message Date
b5c9a0783e Merge pull request 'Data export, warnings' improvements, bug fixes' (#35) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#35
2023-04-02 23:27:31 +03:00
aef4dd091d Warnings can now be revoked using /warnings 2023-04-02 22:17:54 +02:00
a3f75bec7c /warnings now also considers user membership 2023-04-02 21:38:23 +02:00
1f45398de5 /warnings can now show all warnings 2023-04-02 21:31:45 +02:00
Profitroll
cb2f3358b2 Typo fixed 2023-04-02 19:54:16 +02:00
74ae30d841 Sorted imports 2023-04-02 18:48:35 +02:00
e235fe0ed2 Improved bans 2023-04-02 18:42:11 +02:00
bf8ec39584 Changed how export works (in context of #34) 2023-04-02 18:42:03 +02:00
972827d6c2 Does some tasks from #34 2023-04-02 16:44:46 +02:00
43e71c95c4 Cancel should also cancel the message listener 2023-03-26 19:50:10 +02:00
2a2ad9f96e Added permissions for users to use /message 2023-03-26 19:39:33 +02:00
ddb59fdfd5 Added additional locale string for /message 2023-03-26 19:38:59 +02:00
5c55af9e65 This commit closes #28 2023-03-26 19:32:07 +02:00
fd992e89e7 Bump fastapi to 0.95.0 and starlette to 0.26.1 2023-03-26 19:09:45 +02:00
3f6fb51a4f Merge pull request 'User bans, emoji and other bug fixes, age limiter, etc' (#26) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#26
2023-01-31 15:26:55 +02:00
95be1e72d3 Merge pull request 'Bug fixes and improvements' (#8) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#8
2023-01-11 17:22:34 +02:00
234b73add0 Merge pull request 'Bug fixes and small structural changes' (#7) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#7
2023-01-06 17:01:20 +02:00
f4fb85f7a4 Merge pull request 'Removed legacy, fixed some bugs, improved spoilers' (#6) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#6
2023-01-05 16:49:35 +02:00
4fba305b05 Merge pull request 'Small fix for spoiler with an empty description' (#5) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#5
2023-01-05 13:54:19 +02:00
68c7cc0ada Merge pull request 'Spoilers, major command system improvements' (#4) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#4
2023-01-05 13:45:14 +02:00
79304816b0 Merge pull request '/cancel, /identify, sponsorships improvements and fixes' (#3) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#3
2023-01-03 16:45:20 +02:00
2cfa5a8f8d Merge pull request '/nearby, subscriptions check, geocoding' (#2) from dev into master
Reviewed-on: profitroll/HoloCheckerBot#2
2023-01-02 12:16:38 +02:00
16 changed files with 485 additions and 106 deletions

3
app.py
View File

@@ -4,6 +4,7 @@ from modules.logging import logWrite
from modules.utils import configGet, jsonLoad
from pyrogram.client import Client
from pyrogram.errors import bad_request_400
from convopyro import Conversation
app = Client(
"holochecker",
@@ -12,6 +13,8 @@ app = Client(
api_hash=configGet("api_hash", "bot"),
)
Conversation(app)
async def isAnAdmin(admin_id):
# Check if user is mentioned in config

View File

@@ -82,6 +82,10 @@
"interval": 5,
"enabled": true,
"channels": []
},
"warnings_revocation": {
"interval": 6,
"enabled": true
}
},
"locations": {
@@ -182,6 +186,7 @@
},
"message": {
"permissions": [
"users",
"admins",
"group_admins"
],
@@ -208,16 +213,16 @@
"general"
]
},
"application": {
"export": {
"permissions": [
"admins",
"group_admins"
],
"modules": [
"applications"
"general"
]
},
"applications": {
"application": {
"permissions": [
"admins",
"group_admins"

View File

@@ -12,8 +12,8 @@ makedirs(f'{configGet("cache", "locations")}{sep}avatars', exist_ok=True)
# Importing
from modules.commands.application import *
from modules.commands.applications import *
from modules.commands.cancel import *
from modules.commands.export import *
from modules.commands.identify import *
from modules.commands.issue import *
from modules.commands.label import *
@@ -37,6 +37,7 @@ from modules.callbacks.spoiler import *
from modules.callbacks.sponsorship import *
from modules.callbacks.sub import *
from modules.callbacks.sus import *
from modules.callbacks.warnings import *
from modules.handlers.confirmation import *
from modules.handlers.contact import *

View File

@@ -76,11 +76,17 @@
"application_invalid_syntax": "Неправильний синтаксис!\nТреба: `/application ID/NAME/USERNAME`",
"warned": "Попереджено **{0}** (`{1}`) про порушення правил",
"warned_reason": "Попереджено **{0}** (`{1}`)\n\n**Причина:**\n{2}",
"warnings_1": "Користувач **{0}** (`{1}`) має **{2}** попередження",
"warnings_2": "Користувач **{0}** (`{1}`) має **{2}** попереджень",
"warnings_1": "Користувач **{0}** (`{1}`) має **{2}** попередження\n\nОбрати та зняти попередження:\n`/warnings {3} revoke`",
"warnings_2": "Користувач **{0}** (`{1}`) має **{2}** попереджень\n\nОбрати та зняти попередження:\n`/warnings {3} revoke`",
"warnings_all": "**Список попереджень**\n\n{0}\n\nДля перегляду попереджень окремо взятого користувача слід використовувати `/warnings ID/NAME/USERNAME`",
"warnings_entry": "• {0} (`{1}`)\n Попереджень: {2}",
"warnings_empty": "Щось тут порожньо...\nЗ іншого боку, це добре!",
"warnings_revoke": "**Попередження {0}:**\n\n{1}\n\nБудь ласка, користуйтесь клавіатурою щоб зняти попередження з відповідним номером.",
"no_warnings": "Користувач **{0}** (`{1}`) не має попереджень",
"no_user_warnings": "Не знайдено користувачів за запитом **{0}**",
"syntax_warnings": "Неправильний синтаксис!\nТреба: `/warnings ID/NAME/USERNAME`",
"syntax_export": "Неправильний синтаксис!\nТреба: `/export applications/warnings/sponsorships/bans`",
"message_enter": "Надішліть повідомлення, яке треба переслати адмінам.\n\nЗверніть увагу, що повідомлення може містити лише одне медіа або файл.",
"message_sent": "Повідомлення надіслано",
"message_no_user": "⚠️ **Помилка надсилання**\nВказано невірний ID користувача, тому не вдалось надіслати йому повідомлення. Перевірте чи в якості ID надано те число, яке було показане в анкеті.",
"message_invalid_syntax": "Неправильний синтаксис!\nТреба: `/message ID ПОВІДОМЛЕННЯ`",
@@ -255,7 +261,9 @@
"sponsor_accepted": "✅ Форму {0} схвалено",
"sponsor_rejected": "❌ Форму {0} відхилено",
"spoiler_sent": "✅ Повідомлення надіслано в холо-чат",
"spoiler_forbidden": "❌ Треба бути учасником чату"
"spoiler_forbidden": "❌ Треба бути учасником чату",
"warning_revoked": "✅ Попередження скасовано",
"warning_not_found": "❌ Попередження вже скасовано або не існує"
},
"inline": {
"forbidden": {
@@ -293,13 +301,13 @@
"rules_additional": "Додаткові правила, які несуть рекомендаційний характер та не мають явних покарань за порушення:\n1⃣) У чаті немає заборони на російську мову. Ми поважаємо кожного українця і не бажаємо розпалювати мовні конфлікти.\n2⃣) У чаті немає заборони на російський контент. Але майте на увазі, що учасники, здебільшого, не будуть зацікавлені у тому, щоб обговорювати його і він може бути проігнорованим.\n3⃣) Не зловживайте матами. Намагайтесь спілкуватись чистою мовою.\n4⃣) Поважайте авторські права контент-мейкерів. Якщо ви знаходите арт, анімацію, музику тощо на офіційних ресурсах (pixiv, twitter, deviantart тощо), відправляйте на нього посилання.\nЯкщо хтось із учасників відправив арт із не офіційного ресурсу і ви бажаєте дізнатись його автора, відправте у відповідь повідомлення із текстом /search на повідомлення із артом.\n5⃣) В особливо критичних ситуаціях порушник може отримати бан або бути повністю видаленим із чату, без попереджень.\n6⃣) Якщо з кимось із учасників у вас трапиться якесь непорозуміння і вам неприємно буде перебувати в чаті один з одним (навіть, якщо конфлікт стався з кимось із адміністраторів) - напишіть мені в особисті повідомлення @Chirkopol. Я, як засновник чату та головний адміністратор, найбільше зацікавлений у збереженні цілісності чату та розвитку нашого ком'юніті, і я зроблю все, що в моїх силах, щоб допомогти вирішити вашу ситуацію.",
"commands": {
"application": "Переглянути анкету користувача",
"applications": "Отримати всі анкети як JSON",
"cancel": "Відмінити актуальну дію",
"identify": "Дізнатись дані про користувача за айді",
"issue": "Задачі для покращення бота",
"label": "Встановити нікнейм користувачу",
"message": "Надіслати користувачу повідомлення",
"nearby": "Показати користувачів поблизу",
"export": "Експортувати дані як CSV та JSON",
"reapply": "Повторно заповнити анкету",
"reboot": "Перезапустити бота",
"resetcommands": "Відреєструвати всі команди",

View File

@@ -1,3 +1,4 @@
from datetime import datetime
from app import app, isAnAdmin
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
from pyrogram import filters
@@ -12,7 +13,9 @@ async def callback_query_reject(app: Client, clb: CallbackQuery):
fullclb = clb.data.split("_")
if not await isAnAdmin(int(fullclb[1])):
col_bans.insert_one({"user": int(fullclb[1])})
col_bans.insert_one(
{"user": int(fullclb[1]), "admin": clb.from_user.id, "date": datetime.now()}
)
edited_markup = [
[

View File

@@ -0,0 +1,27 @@
from datetime import datetime
from app import app
from pyrogram import filters
from pyrogram.types import CallbackQuery
from pyrogram.client import Client
from modules.utils import locale
from modules.database import col_warnings
from bson import ObjectId
@app.on_callback_query(filters.regex("w_rev_[\s\S]*"))
async def callback_query_warning_revoke(app: Client, clb: CallbackQuery):
warning = col_warnings.find_one({"_id": ObjectId(str(clb.data).split("_")[2])})
if warning is None:
await clb.answer(
text=locale("warning_not_found", "callback", locale=clb.from_user),
show_alert=True,
)
return
col_warnings.update_one(
{"_id": warning["_id"]},
{"$set": {"active": False, "revoke_date": datetime.now()}},
)
await clb.answer(
text=locale("warning_revoked", "callback", locale=clb.from_user).format(),
show_alert=True,
)

View File

@@ -1,39 +0,0 @@
from os import sep, makedirs, remove
from uuid import uuid1
from app import app
from pyrogram import filters
from pyrogram.types import Message
from pyrogram.client import Client
from pyrogram.enums.chat_action import ChatAction
from modules.logging import logWrite
from modules.utils import should_quote, jsonSave
from modules.database import col_applications
from modules import custom_filters
@app.on_message(
custom_filters.enabled_applications
& ~filters.scheduled
& filters.command(["applications"], prefixes=["/"])
& custom_filters.admin
)
async def cmd_applications(app: Client, msg: Message):
logWrite(f"Admin {msg.from_user.id} requested export of a database")
await app.send_chat_action(msg.chat.id, ChatAction.UPLOAD_DOCUMENT)
filename = uuid1()
output = []
for entry in col_applications.find():
del entry["_id"]
entry["date"] = entry["date"].strftime("%d.%m.%Y, %H:%M")
entry["application"]["2"] = entry["application"]["2"].strftime(
"%d.%m.%Y, %H:%M"
)
output.append(entry)
makedirs("tmp", exist_ok=True)
jsonSave(output, f"tmp{sep}{filename}.json")
await msg.reply_document(
document=f"tmp{sep}{filename}.json",
file_name="applications",
quote=should_quote(msg),
)
remove(f"tmp{sep}{filename}.json")

View File

@@ -16,6 +16,10 @@ from modules import custom_filters
async def command_cancel(app: Client, msg: Message):
col_tmp.delete_many({"user": msg.from_user.id, "sent": False})
col_spoilers.delete_many({"user": msg.from_user.id, "completed": False})
try:
await app.listen.Cancel(filters.user(msg.from_user.id))
except:
pass
if col_applications.find_one({"user": msg.from_user.id}) is None:
await msg.reply_text(
locale("cancel_reapply", "message", locale=msg.from_user),

182
modules/commands/export.py Normal file
View File

@@ -0,0 +1,182 @@
from csv import QUOTE_ALL
from os import makedirs, path, remove
from uuid import uuid1
import aiofiles
from aiocsv.writers import AsyncDictWriter
from pyrogram import filters
from pyrogram.client import Client
from pyrogram.enums.chat_action import ChatAction
from pyrogram.types import Message
from ujson import dumps
from app import app
from modules import custom_filters
from modules.database import col_applications, col_sponsorships, col_warnings
from modules.logging import logWrite
from modules.utils import locale, should_quote
@app.on_message(
custom_filters.enabled_general
& ~filters.scheduled
& filters.command(["export"], prefixes=["/"])
& custom_filters.admin
)
async def cmd_export(app: Client, msg: Message):
if len(msg.command) <= 1:
await msg.reply_text(
locale("syntax_export", "message", locale=msg.from_user),
quote=should_quote(msg),
)
return
selection = msg.command[1].lower()
if selection not in ["applications", "warnings", "sponsorships", "bans"]:
await msg.reply_text(
locale("syntax_export", "message", locale=msg.from_user),
quote=should_quote(msg),
)
return
logWrite(f"Admin {msg.from_user.id} requested export of {selection}")
makedirs("tmp", exist_ok=True)
temp_file = path.join("tmp", str(uuid1()))
await app.send_chat_action(msg.chat.id, ChatAction.TYPING)
output_csv = []
output_json = []
if selection == "applications":
header_csv = [
"user",
"date",
"admin",
"question_1",
"question_2",
"question_3",
"question_4",
"question_5",
"question_6",
"question_7",
"question_8",
"question_9",
"question_10",
]
for entry in list(col_applications.find()):
del entry["_id"]
entry["date"] = entry["date"].isoformat()
entry["application"]["2"] = entry["application"]["2"].isoformat()
output_json.append(entry)
for entry in list(col_applications.find()):
del entry["_id"]
entry["date"] = entry["date"].isoformat()
entry["application"]["2"] = entry["application"]["2"].isoformat()
for index, value in enumerate(entry["application"]):
entry[f"question_{index+1}"] = entry["application"][value]
entry[
"question_3"
] = f"{entry['application']['3']['name']} ({entry['application']['3']['adminName1']}, {entry['application']['3']['countryName']})"
del entry["application"]
output_csv.append(entry)
elif selection == "warnings":
header_csv = [
"id",
"user",
"admin",
"date",
"reason",
"active",
"revoke_date",
]
for entry in list(col_warnings.find()):
for k, v in list(entry.items()):
entry[{"_id": "id"}.get(k, k)] = entry.pop(k)
entry["id"] = str(entry["id"])
entry["date"] = entry["date"].isoformat()
if entry["revoke_date"] is not None:
entry["revoke_date"] = entry["revoke_date"].isoformat()
output_json.append(entry)
output_csv.append(entry)
elif selection == "sponsorships":
header_csv = [
"user",
"date",
"admin",
"streamer",
"expires",
"proof",
"label",
]
for entry in list(col_sponsorships.find()):
del entry["_id"]
entry["date"] = entry["date"].isoformat()
entry["sponsorship"]["expires"] = entry["sponsorship"][
"expires"
].isoformat()
output_json.append(entry)
for entry in list(col_sponsorships.find()):
del entry["_id"]
entry["date"] = entry["date"].isoformat()
entry["sponsorship"]["expires"] = entry["sponsorship"][
"expires"
].isoformat()
for index, value in enumerate(entry["sponsorship"]):
entry[value] = entry["sponsorship"][value]
del entry["sponsorship"]
output_csv.append(entry)
elif selection == "bans":
header_csv = ["user", "admin", "date"]
for entry in list(col_warnings.find()):
del entry["id"]
entry["date"] = entry["date"].isoformat()
output_json.append(entry)
output_csv.append(entry)
# Saving CSV
async with aiofiles.open(temp_file + ".csv", mode="w", encoding="utf-8") as file:
writer = AsyncDictWriter(file, header_csv, restval="NULL", quoting=QUOTE_ALL)
await writer.writeheader()
await writer.writerows(output_csv)
# Saving JSON
async with aiofiles.open(temp_file + ".json", mode="w", encoding="utf-8") as file:
await file.write(
dumps(
output_json, ensure_ascii=False, escape_forward_slashes=False, indent=4
)
)
# Sending CSV
await app.send_chat_action(msg.chat.id, ChatAction.UPLOAD_DOCUMENT)
await msg.reply_document(
document=temp_file + ".csv",
file_name=f"{selection}.csv",
quote=should_quote(msg),
)
# Sending JSON
await app.send_chat_action(msg.chat.id, ChatAction.UPLOAD_DOCUMENT)
await msg.reply_document(
document=temp_file + ".json",
file_name=f"{selection}.json",
quote=should_quote(msg),
)
del output_csv, output_json
# Removing temp files
remove(temp_file + ".csv")
remove(temp_file + ".json")

View File

@@ -1,26 +1,28 @@
from app import app
from app import app, isAnAdmin
from pyrogram import filters
from pyrogram.types import Message
from pyrogram.client import Client
from convopyro import listen_message
from classes.errors.holo_user import UserInvalidError
from classes.holo_user import HoloUser
from modules.utils import logWrite, locale, should_quote, find_user
from modules.utils import configGet, logWrite, locale, should_quote, find_user
from modules import custom_filters
from modules.database import col_messages
@app.on_message(
custom_filters.enabled_general
& ~filters.scheduled
& filters.command(["message"], prefixes=["/"])
& custom_filters.admin
# & custom_filters.admin
)
async def cmd_message(app: Client, msg: Message):
try:
if await isAnAdmin(msg.from_user.id):
try:
destination = HoloUser(int(msg.command[1]))
except (ValueError, UserInvalidError):
destination = HoloUser(await find_user(app, query=msg.command[1]))
if (msg.text is not None) and (len(str(msg.text).split()) > 2):
await destination.message(
context=msg,
@@ -57,6 +59,25 @@ async def cmd_message(app: Client, msg: Message):
animation=msg.animation,
adm_context=True,
)
else:
await msg.reply_text(
locale("message_enter", "message", locale=msg.from_user)
)
message = await listen_message(app, msg.chat.id, timeout=None)
if message.text is not None and message.text == "/cancel":
return
sent = await app.forward_messages(
configGet("admin", "groups"), msg.chat.id, message.id
)
col_messages.insert_one(
{
"origin": {"chat": message.chat.id, "id": message.id},
"destination": {"chat": sent.chat.id, "id": sent.id},
}
)
await message.reply_text(
locale("message_sent", "message", locale=message.from_user), quote=True
)
except IndexError:
await msg.reply_text(

View File

@@ -24,6 +24,8 @@ async def cmd_warn(app: Client, msg: Message):
"admin": msg.from_user.id,
"date": datetime.now(),
"reason": message,
"active": True,
"revoke_date": None,
}
)
if message == "":

View File

@@ -1,11 +1,13 @@
from os import path
from app import app
from pyrogram import filters
from pyrogram.types import Message
from pyrogram.client import Client
from pyrogram.enums.chat_members_filter import ChatMembersFilter
from modules.utils import configGet, locale, should_quote
from modules.utils import configGet, jsonLoad, locale, should_quote
from modules.database import col_users, col_warnings
from modules import custom_filters
from pykeyboard import InlineKeyboard, InlineButton
@app.on_message(
@@ -15,7 +17,42 @@ from modules import custom_filters
& custom_filters.admin
)
async def cmd_warnings(app: Client, msg: Message):
if len(msg.command) <= 1:
if len(msg.command) == 1:
warnings = {}
warnings_output = []
group_members = jsonLoad(
path.join(configGet("cache", "locations"), "group_members")
)
for warning in col_warnings.find({"active": True}):
if warning["user"] not in group_members:
continue
if str(warning["user"]) not in warnings:
warnings[str(warning["user"])] = {
"name": (col_users.find_one({"user": warning["user"]}))["tg_name"],
"warns": 1,
}
else:
warnings[str(warning["user"])]["warns"] += 1
for warning in warnings:
warnings_output.append(
locale("warnings_entry", "message", locale=msg.from_user).format(
warnings[warning]["name"], warning, warnings[warning]["warns"]
),
)
warnings_output = (
locale("warnings_empty", "message", locale=msg.from_user)
if len(warnings_output) == 0
else "\n".join(warnings_output)
)
await msg.reply_text(
locale("warnings_all", "message", locale=msg.from_user).format(
warnings_output
),
quote=should_quote(msg),
)
return
if len(msg.command) > 3:
await msg.reply_text(
locale("syntax_warnings", "message", locale=msg.from_user),
quote=should_quote(msg),
@@ -47,7 +84,36 @@ async def cmd_warnings(app: Client, msg: Message):
)
return
warns = col_warnings.count_documents({"user": target_id})
if len(msg.command) == 3 and msg.command[2].lower() == "revoke":
if col_warnings.count_documents({"user": target_id, "active": True}) == 0:
await msg.reply_text(
locale("no_warnings", "message", locale=msg.from_user).format(
target_name, target_id
),
quote=should_quote(msg),
)
return
keyboard = InlineKeyboard()
buttons = []
warnings = []
for index, warning in enumerate(
list(col_warnings.find({"user": target_id, "active": True}))
):
warnings.append(
f'{index+1}. {warning["date"].strftime("%d.%m.%Y, %H:%M")}\n Адмін: {warning["admin"]}\n Причина: {warning["reason"]}'
)
buttons.append(InlineButton(str(index + 1), f'w_rev_{str(warning["_id"])}'))
keyboard.add(*buttons)
await msg.reply_text(
locale("warnings_revoke", "message", locale=msg.from_user).format(
target_name, "\n".join(warnings)
),
reply_markup=keyboard,
quote=should_quote(msg),
)
return
warns = col_warnings.count_documents({"user": target_id, "active": True})
if warns == 0:
await msg.reply_text(
@@ -60,14 +126,14 @@ async def cmd_warnings(app: Client, msg: Message):
if warns <= 5:
await msg.reply_text(
locale("warnings_1", "message", locale=msg.from_user).format(
target_name, target_id, warns
target_name, target_id, warns, target_id
),
quote=should_quote(msg),
)
else:
await msg.reply_text(
locale("warnings_2", "message", locale=msg.from_user).format(
target_name, target_id, warns
target_name, target_id, warns, target_id
),
quote=should_quote(msg),
)

View File

@@ -18,7 +18,12 @@ from pyrogram.enums.chat_members_filter import ChatMembersFilter
from classes.holo_user import HoloUser
from modules.utils import configGet, jsonLoad, jsonSave, locale, logWrite
from dateutil.relativedelta import relativedelta
from modules.database import col_applications, col_sponsorships, col_youtube
from modules.database import (
col_applications,
col_sponsorships,
col_youtube,
col_warnings,
)
from xmltodict import parse
from requests import get
@@ -182,6 +187,45 @@ if configGet("enabled", "features", "sponsorships") is True:
logWrite("Sponsorships check performed")
# Revoke old warnings
if configGet("enabled", "features", "warnings") is True:
if configGet("enabled", "scheduler", "warnings_revocation") is True:
@scheduler.scheduled_job(
trigger="date", run_date=datetime.now() + timedelta(seconds=10)
)
@scheduler.scheduled_job(
trigger="interval",
hours=configGet("interval", "scheduler", "warnings_revocation"),
)
async def revoke_warnings():
for warning in list(
col_warnings.find(
{
"active": True,
"date": {"$lt": datetime.now() - timedelta(days=90)},
}
)
):
if (
col_warnings.count_documents(
{
"user": warning["user"],
"active": True,
"date": {"$gt": datetime.now() - timedelta(days=90)},
}
)
== 0
):
col_warnings.update_one(
{"_id": warning["_id"]},
{"$set": {"active": False, "revoke_date": datetime.now()}},
)
logWrite(
f'Revoked warning {str(warning["_id"])} of user {warning["user"]} because no active warnings for the last 90 days found.'
)
# Register all bot commands
@scheduler.scheduled_job(
trigger="date", run_date=datetime.now() + timedelta(seconds=10)

View File

@@ -1,12 +1,16 @@
APScheduler==3.10.1
fastapi~=0.88.0
aiocsv==1.2.3
aiofiles~=23.1.0
APScheduler~=3.10.1
convopyro==0.5
fastapi~=0.95.0
ftfy~=6.1.1
psutil==5.9.4
pymongo==4.3.3
Pyrogram~=2.0.102
requests==2.28.2
tgcrypto==1.2.5
python_dateutil==2.8.2
starlette~=0.22.0
pykeyboard==0.1.5
requests==2.28.2
starlette==0.26.1
tgcrypto==1.2.5
ujson~=5.7.0
ftfy~=6.1.1
xmltodict~=0.13.0
xmltodict==0.13.0

29
validation/bans.json Normal file
View File

@@ -0,0 +1,29 @@
{
"$jsonSchema": {
"required": [
"user",
"admin",
"date"
],
"properties": {
"user": {
"bsonType": [
"int",
"long"
],
"description": "Telegram ID of user"
},
"admin": {
"bsonType": [
"int",
"long"
],
"description": "Telegram ID of admin"
},
"date": {
"bsonType": "date",
"description": "Date and time of getting"
}
}
}
}

View File

@@ -4,15 +4,23 @@
"user",
"admin",
"date",
"reason"
"reason",
"active",
"revoke_date"
],
"properties": {
"user": {
"bsonType": ["int", "long"],
"bsonType": [
"int",
"long"
],
"description": "Telegram ID of user"
},
"admin": {
"bsonType": ["int", "long"],
"bsonType": [
"int",
"long"
],
"description": "Telegram ID of admin"
},
"date": {
@@ -22,6 +30,17 @@
"reason": {
"bsonType": "string",
"description": "Broken rule or admin's comment"
},
"active": {
"bsonType": "bool",
"description": "Whether warning is still present"
},
"revoke_date": {
"bsonType": [
"date",
"null"
],
"description": "Date when warning got inactive"
}
}
}