refactor for sql

This commit is contained in:
kr0sh512
2025-02-09 16:30:25 +03:00
parent 2cab4dccfc
commit b2db3a4b76
15 changed files with 468 additions and 775 deletions
+3 -1
View File
@@ -10,4 +10,6 @@ nohup.out
config.yaml config.yaml
old_files/ old_files/
yaml_groups/* yaml_groups/*
!yaml_groups/202.yaml !yaml_groups/202.yaml
main_yaml_groups/*
.env
Binary file not shown.
Binary file not shown.
+32 -42
View File
@@ -1,7 +1,8 @@
# Telegram Bot Schedule # Telegram Bot Schedule
This project is a Telegram bot designed to manage and display schedules. The bot can help users keep track of their daily activities, appointments, and events.
A working version of the [bot for cmc msu](t.me/vmk_schedule_bot) students This project is a Telegram bot designed to manage and display schedules. The bot helps users keep track of their daily activities, appointments, and events.
A working version of the [bot for CMC MSU](t.me/vmk_schedule_bot) students.
## Features ## Features
@@ -13,47 +14,46 @@ A working version of the [bot for cmc msu](t.me/vmk_schedule_bot) students
## Installation ## Installation
1. Clone the repository: 1. Clone the repository:
```sh ```sh
git clone https://github.com/yourusername/Telegram-bot-schedule.git git clone https://github.com/yourusername/Telegram-bot-schedule.git
``` ```
2. Navigate to the project directory: 2. Navigate to the project directory:
```sh ```sh
cd Telegram-bot-schedule cd Telegram-bot-schedule
``` ```
3. Install the required dependencies: 3. Install the required dependencies:
```sh ```sh
pip install -r requirements.txt pip install -r requirements.txt
``` ```
## Usage ## Usage
1. Create a new bot on Telegram and get the API token. 1. Create a new bot on Telegram and get the API token.
2. Set the API token in the environment variables or in a configuration file. 2. Set the API token in the environment variables or in a configuration file.
3. You need to create schedules files for groups. 3. Create schedule files for groups.
4. Run the bot: 4. Run the bot:
```sh ```sh
python bot.py python bot.py
``` ```
## Commands ## Commands
- `/start` - use to change the group number. - `/start` - Change the group number.
- `/schedule` - use to get today's schedule. - `/schedule` - Get today's schedule.
- `/random` - create a queue of people. - `/info` - Find out your bot settings.
- `/info` - use to find out your bot settings. - `/pause` - Stop receiving messages from the bot.
- `/pause` - use to stop receiving messages from the bot. - `/thread` - Use in the desired channel chat to send messages there.
- `/thread` - use in the desired channel chat so that the bot sends messages there. - `/timeout` - Set the time when the bot will send you a message.
- `/timeout` - set the time when the bot will send you a message. - `/request` - Send a request to the developer.
- `/request` - use to send a request to the developer. - `/source` - Bot's page on GitHub.
- `/source` - Bot's page on Github. - `/restart` - Restart the bot (admin only).
- `/restart` - restart the bot (admin only). - `/update` - Update schedule tasks (admin only).
- `/update` - update schedule tasks (admin only). - `/stats` - Get statistics (admin only).
- `/stats` - get statistics (admin only). - `/json` - Get the users and schedule file (admin only).
- `/json` - get the users and schedule file (admin only). - `/info <user_id>` - Find out the user's settings (admin only).
- `/info` <i>user_id</i> - find out the user's settings (admin only). - `/spam` - Send a broadcast message (admin only).
- `/spam` - send a broadcast message (admin only). - `/pause_all` - Pause the bot for everyone (holidays/weekends) (admin only).
- `/pause_all` - pause the bot for everyone (holidays/weekends) (admin only). - `/stop <user_id>` - Stop sending messages to the user (admin only).
- `/stop` <i>user_id</i> - stop sending messages to the user (admin only).
## Contributing ## Contributing
@@ -71,13 +71,3 @@ This bot is particularly useful for students to track their lessons and manage t
- Cannot create or edit schedule entities directly - Cannot create or edit schedule entities directly
- Only works with preloaded schedules and groups - Only works with preloaded schedules and groups
+5 -1
View File
@@ -1,6 +1,10 @@
import schedules as bot import schedules as bot
from dotenv import load_dotenv
import os
admin_id = "856850518" load_dotenv()
admin_id = str(os.environ.get("TG_ADMIN_ID"))
def admin_command(func): def admin_command(func):
+293
View File
@@ -0,0 +1,293 @@
#!/usr/bin/python3.3
import json, yaml, schedule
import schedules as bot
from datetime import datetime
import os
from dotenv import load_dotenv
from sshtunnel import SSHTunnelForwarder
from psycopg2 import pool
allow_update = True
PARITY_FIRST = 0
load_dotenv()
if os.environ.get("ENV") == "dev":
print("DEV: Connecting to local database")
server = SSHTunnelForwarder(
(os.environ.get("SSH_HOST"), 22),
ssh_private_key=os.environ.get("SSH_KEY"),
ssh_username=os.environ.get("SSH_USER"),
ssh_password=(
os.environ.get("SSH_PASSWORD") if os.environ.get("SSH_PASSWORD") else None
),
remote_bind_address=("localhost", int(os.environ.get("DB_PORT"))),
)
server.start()
db = pool.SimpleConnectionPool(
1,
20,
user=os.environ.get("DB_USER"),
password=os.environ.get("DB_PASSWORD"),
host="localhost" if os.environ.get("ENV") == "dev" else os.environ.get("DB_HOST"),
port=(
server.local_bind_port
if os.environ.get("ENV") == "dev"
else os.environ.get("DB_PORT")
),
database=os.environ.get("DB_NAME"),
)
def into_dict(data: tuple, column_name: list[tuple]) -> dict:
cols = [desc[0] for desc in column_name]
return dict(zip(cols, data)) if data else None
def into_list(rows: list[tuple], column_name: list[tuple]) -> list[dict]:
cols = [desc[0] for desc in column_name]
return [dict(zip(cols, row)) for row in rows] if rows else []
def save_user(infos):
conn = db.getconn()
cursor = conn.cursor()
cursor.execute(
"INSERT INTO users (id, username, first_name, last_name, group) VALUES (%s, %s, %s, %s, %s)",
infos,
)
conn.commit()
db.putconn(conn)
return
def change_user_param(id, key, value):
conn = db.getconn()
cursor = conn.cursor()
cursor.execute(
"UPDATE users SET {} = %s WHERE id = %s".format(key),
(value, id),
)
conn.commit()
db.putconn(conn)
create_schedule_tasks()
return
def parse_lesson(lesson: dict[str, str]) -> str:
text = ""
start, end = lesson["begin_time"], lesson["end_time"]
if not lesson["course"]:
return ""
if lesson["parity"] == 0:
lesson["course"] += " (чёт.)"
if lesson["parity"] == 1:
lesson["course"] += " (нечёт.)"
text = "{}-{} | {}\
\n<b>{}</b>\
\n<i>{}</i>".format(
start,
end,
lesson["room"],
lesson["course"],
lesson["lector"],
)
return text
def create_schedule_tasks(manual=False):
global allow_update
if not manual:
if not allow_update:
return
allow_update = True
schedule.clear()
conn = db.getconn()
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
data = cursor.fetchall()
users = into_list(data, cursor.description)
for user in users:
if user["allow_message"] != "yes":
continue
group = user["group"]
if group == "other":
continue
cursor.execute("SELECT * FROM schedule WHERE group = %s", (group,))
data = cursor.fetchall()
schdl = into_list(data, cursor.description)
for lesson in schdl:
if not lesson["course"]:
continue
start = lesson["begin_time"]
text = parse_lesson(lesson)
if not text:
continue
thread_id = user["thread"]
delta = "00:" + user["timeout"]
format = "%H:%M"
start_task = datetime.strptime(start, format) - datetime.strptime(
delta, format
)
tmp = ""
for i in str(start_task).split(":"):
tmp += (("0" + i) if len(i) < 2 else i) + ":"
start_task = tmp[:-1]
if lesson["day_of_week"] == "mon":
schedule.every().monday.at(start_task).do(
bot.send_message, user["id"], text, thread_id, lesson["parity"]
)
elif lesson["day_of_week"] == "tue":
schedule.every().tuesday.at(start_task).do(
bot.send_message, user["id"], text, thread_id, lesson["parity"]
)
elif lesson["day_of_week"] == "wed":
schedule.every().wednesday.at(start_task).do(
bot.send_message, user["id"], text, thread_id, lesson["parity"]
)
elif lesson["day_of_week"] == "thu":
schedule.every().thursday.at(start_task).do(
bot.send_message, user["id"], text, thread_id, lesson["parity"]
)
elif lesson["day_of_week"] == "fri":
schedule.every().friday.at(start_task).do(
bot.send_message, user["id"], text, thread_id, lesson["parity"]
)
elif lesson["day_of_week"] == "sat":
schedule.every().saturday.at(start_task).do(
bot.send_message, user["id"], text, thread_id, lesson["parity"]
)
db.putconn(conn)
return
def groups_in_json() -> list[str]:
conn = db.getconn()
cursor = conn.cursor()
cursor.execute("SELECT DISTINCT group FROM schedule ORDER BY group")
data = cursor.fetchall()
groups = [row[0] for row in data]
db.putconn(conn)
return groups
def students_in_group(group) -> list[str]:
group = str(group)
conn = db.getconn()
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE group = %s", (group,))
data = cursor.fetchall()
students = [row[0] for row in data]
db.putconn(conn)
return students
def return_infos(id) -> dict[str, str] | None:
conn = db.getconn()
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = %s", (id,))
data = cursor.fetchone()
user = into_dict(data, cursor.description)
db.putconn(conn)
return user
def pause_bot():
global allow_update
if allow_update:
schedule.clear()
allow_update = False
bot.send_admin_message("Бот больше не отправляет расписание")
else:
create_schedule_tasks(True)
allow_update = True
bot.send_admin_message("Бот возобновил рассылку!")
return
def get_schedule(id: str, day: str) -> str:
id = str(id)
if day == "sun":
day = "mon"
days = ["mon", "tue", "wed", "thu", "fri", "sat"]
russian_days = ["понедельник", "вторник", "среду", "четверг", "пятницу", "субботу"]
text = "<u>Расписание на {}</u>:\n\n".format(russian_days[days.index(day)])
conn = db.getconn()
cursor = conn.cursor()
cursor.execute("SELECT group FROM users WHERE id = %s", (id,))
data = cursor.fetchone()
group = data[0]
db.putconn(conn)
if group == "other":
return "У тебя не выбрана группа для рассылки сообщений"
conn = db.getconn()
cursor = conn.cursor()
cursor.execute("SELECT * FROM schedule WHERE group = %s", (group,))
data = cursor.fetchall()
schdl = into_list(data, cursor.description)
db.putconn(conn)
schdl_today = [lesson for lesson in schdl if lesson["day_of_week"] == day]
if not schdl_today:
return "Расписания твоей группы на {} нет".format(russian_days[days.index(day)])
for lesson in schdl_today:
str_lesson = parse_lesson(lesson)
if str_lesson:
text += "" + str_lesson + "\n\n"
if text == "<u>Расписание на {}</u>:\n\n".format(russian_days[days.index(day)]):
return "Расписания твоей группы на {} нет".format(russian_days[days.index(day)])
count_of_weeks = (datetime.now() - datetime(2024, 2, 5)).days // 7
is_odd = (count_of_weeks + 1) % 2 == PARITY_FIRST
text = ("(нечёт) " if is_odd else "(чёт) ") + text
return text
if __name__ == "__main__":
print(groups_in_json())
pass
+42 -129
View File
@@ -1,16 +1,18 @@
#!/usr/bin/python3.3 #!/usr/bin/python3.3
import threading, telebot, schedule, time, random, yaml import threading, telebot, schedule, time, random
from datetime import datetime from datetime import datetime
import os, sys import os, sys
from telebot import types from telebot import types
from telegram.constants import ParseMode from telegram.constants import ParseMode
import for_json import db
from admin import admin_command, is_admin, send_admin_message, send_admin_document from admin import admin_command, is_admin, send_admin_message, send_admin_document
from dotenv import load_dotenv
config = yaml.safe_load(open("config.yaml")) load_dotenv()
bot = telebot.TeleBot(config["test_token"])
parity_FIRST = 1 bot = telebot.TeleBot(os.environ.get("TG_TEST_TOKEN"))
PARITY_FIRST = 0
@bot.message_handler(commands=["help", "faq"]) @bot.message_handler(commands=["help", "faq"])
@@ -34,7 +36,6 @@ def help(message):
\n/restart - перезапуск бота.\ \n/restart - перезапуск бота.\
\n/update - обновление schedule задач\ \n/update - обновление schedule задач\
\n/stats - получание статистики\ \n/stats - получание статистики\
\n/json - получить файл пользователей и расписания\
\n/info <i>id_пользователя</i> - узнать настройки пользователя\ \n/info <i>id_пользователя</i> - узнать настройки пользователя\
\n/spam - сделать рассылку\ \n/spam - сделать рассылку\
\n/pause_all - приостановить бота для всех (каникулы/выходные)\ \n/pause_all - приостановить бота для всех (каникулы/выходные)\
@@ -47,22 +48,6 @@ def help(message):
### --//-- ### --//--
@bot.message_handler(commands=["test", "t"])
@admin_command
def test(message):
text = '<b>Жирный текст</b>\
\n<i>Курсивный текст</i>\
\n<u>Подчёркнутый</u>\
\n<s>Перечёркнутый текст</s>\
\n<a href="google.com">Ссылка</a>\
\n<code>Моноширинный текст</code>\
\n<pre>Форматированный с сохранением пробелов</pre>\
\n<blockquote>Цитата</blockquote>'
send_admin_message(text)
return
@bot.message_handler(commands=["restart", "r"]) @bot.message_handler(commands=["restart", "r"])
@admin_command @admin_command
def restart_bot(message): def restart_bot(message):
@@ -73,7 +58,7 @@ def restart_bot(message):
@bot.message_handler(commands=["update"]) @bot.message_handler(commands=["update"])
@admin_command @admin_command
def update_schedules(message): def update_schedules(message):
for_json.create_schedule_tasks(manual=True) db.create_schedule_tasks(manual=True)
send_admin_message("Произошёл update") send_admin_message("Произошёл update")
return return
@@ -89,18 +74,18 @@ def send_stat(message):
sum_dis_not = 0 sum_dis_not = 0
sum_chats = 0 sum_chats = 0
groups = list(for_json.groups_in_json()) groups = list(db.groups_in_json())
groups.append("other") groups.append("other")
for i in groups: for i in groups:
users = for_json.students_in_group(i) users = db.students_in_group(i)
sum_users += len(users) sum_users += len(users)
dis_not = len( dis_not = len(
[ [
i i
for i, x in enumerate(users) for i, x in enumerate(users)
if for_json.return_infos(x)["allow_message"] == "no" if db.return_infos(x)["allow_message"] == "no"
] ]
) )
sum_dis_not += dis_not sum_dis_not += dis_not
@@ -117,19 +102,6 @@ def send_stat(message):
return return
@bot.message_handler(commands=["json"])
@admin_command
def send_json(message):
with open(for_json.path_users, "rb") as json_file:
send_admin_document(json_file)
with open(for_json.path_schedule, "rb") as json_file:
send_admin_document(json_file)
with open(for_json.path_students, "rb") as json_file:
send_admin_document(json_file)
return
# info user_id # info user_id
@@ -170,10 +142,10 @@ def spam_cnf(message, data):
if data == "all": if data == "all":
text += "<u>всем</u>" text += "<u>всем</u>"
elif data in for_json.groups_in_json(): elif data in db.groups_in_json():
text += "{} группе".format(data) text += "{} группе".format(data)
elif for_json.return_infos(data) != None: elif db.return_infos(data) != None:
text += "пользователю @{}".format(for_json.return_infos(data)["username"]) text += "пользователю @{}".format(db.return_infos(data)["username"])
else: else:
send_admin_message("Ошибка при выборе пользователей") send_admin_message("Ошибка при выборе пользователей")
return return
@@ -208,13 +180,13 @@ def spam_send(call):
users = [] users = []
if data == "all": if data == "all":
groups = for_json.groups_in_json() groups = db.groups_in_json()
for group in groups: for group in groups:
users += for_json.students_in_group(group) users += db.students_in_group(group)
users += for_json.students_in_group("other") users += db.students_in_group("other")
elif data in for_json.groups_in_json(): elif data in db.groups_in_json():
users = for_json.students_in_group(data) users = db.students_in_group(data)
elif for_json.return_infos(data) != None: elif db.return_infos(data) != None:
users = [data] users = [data]
for i in users: for i in users:
@@ -233,7 +205,7 @@ def spam_send(call):
@bot.message_handler(commands=["pause_all"]) @bot.message_handler(commands=["pause_all"])
@admin_command @admin_command
def pause_bot(message): def pause_bot(message):
for_json.pause_bot() db.pause_bot()
return return
@@ -245,7 +217,7 @@ def stop_msg(message):
send_admin_message("Эта команда вида /stop <i>id_пользователя</i>") send_admin_message("Эта команда вида /stop <i>id_пользователя</i>")
return return
for_json.change_user_param(str(message.text).split(" ")[1], "allow_message", "no") db.change_user_param(str(message.text).split(" ")[1], "allow_message", "no")
send_admin_message("Успешно") send_admin_message("Успешно")
return return
@@ -280,7 +252,7 @@ def start(message):
temp, temp,
] ]
if temp in for_json.groups_in_json(): if temp in db.groups_in_json():
send_message( send_message(
message.chat.id, message.chat.id,
"Отлично! Теперь я буду присылать вам расписание {} группы".format( "Отлично! Теперь я буду присылать вам расписание {} группы".format(
@@ -301,7 +273,7 @@ def start(message):
) )
) )
temp = "other" temp = "other"
for_json.save_user(infos) db.save_user(infos)
else: else:
send_message( send_message(
message.chat.id, message.chat.id,
@@ -314,7 +286,7 @@ def start(message):
send_message(message.chat.id, start_txt) send_message(message.chat.id, start_txt)
markup = types.InlineKeyboardMarkup() markup = types.InlineKeyboardMarkup()
# for i in for_json.groups_in_json(): # for i in db.groups_in_json():
# markup.add(types.InlineKeyboardButton(text=i, callback_data=i)) # markup.add(types.InlineKeyboardButton(text=i, callback_data=i))
markup.add(types.InlineKeyboardButton(text="1 курс", callback_data="1course")) markup.add(types.InlineKeyboardButton(text="1 курс", callback_data="1course"))
markup.add(types.InlineKeyboardButton(text="2 курс", callback_data="2course")) markup.add(types.InlineKeyboardButton(text="2 курс", callback_data="2course"))
@@ -330,7 +302,7 @@ def start(message):
@bot.callback_query_handler( @bot.callback_query_handler(
func=lambda call: call.data in for_json.groups_in_json() func=lambda call: call.data in db.groups_in_json()
or call.data in ["other", "1course", "2course"] or call.data in ["other", "1course", "2course"]
) )
def callback_inline(call): def callback_inline(call):
@@ -338,7 +310,7 @@ def callback_inline(call):
course = call.data[0] course = call.data[0]
markup = types.InlineKeyboardMarkup() markup = types.InlineKeyboardMarkup()
for i in [i for i in for_json.groups_in_json() if i[0] == course]: for i in [i for i in db.groups_in_json() if i[0] == course]:
markup.add(types.InlineKeyboardButton(text=i, callback_data=i)) markup.add(types.InlineKeyboardButton(text=i, callback_data=i))
markup.add( markup.add(
@@ -368,7 +340,7 @@ def callback_inline(call):
call.data, call.data,
] ]
for_json.save_user(infos) db.save_user(infos)
if call.data == "other": if call.data == "other":
bot.edit_message_text( bot.edit_message_text(
@@ -396,7 +368,7 @@ def send_schedule(message):
) )
try: try:
day = datetime.today().strftime("%A").lower()[:3] # mon tue ... day = datetime.today().strftime("%A").lower()[:3] # mon tue ...
text = for_json.get_schedule(message.chat.id, day) text = db.get_schedule(message.chat.id, day)
except Exception as e: except Exception as e:
# send_admin_message( # send_admin_message(
# "Schedule error from: {} \n\n {}".format(message.chat.id, str(e)) # "Schedule error from: {} \n\n {}".format(message.chat.id, str(e))
@@ -437,7 +409,7 @@ def change_schedule(call):
delta = (1) if (call.data == "right") else (-1) delta = (1) if (call.data == "right") else (-1)
ind = ((i + delta) % 6) if (i + delta > 0) else (i + delta) ind = ((i + delta) % 6) if (i + delta > 0) else (i + delta)
text = for_json.get_schedule(call.message.chat.id, days[ind]) text = db.get_schedule(call.message.chat.id, days[ind])
markup = types.InlineKeyboardMarkup() markup = types.InlineKeyboardMarkup()
markup.add( markup.add(
@@ -456,73 +428,13 @@ def change_schedule(call):
return return
@bot.message_handler(commands=["random"])
def random_people(message):
list_of_num = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6", "7", "8", "9"]
list_of_var = for_json.students_in_json(message.chat.id)
text = "Для вашей группы эта команда не настроена.\n\nНапиши запрос через /request"
if list_of_var == None:
send_message(message.chat.id, text)
send_admin_message(
"Запрос на random команду от {}".format(
for_json.return_infos(message.chat.id)["group"]
)
)
return
text = "• Выбери как именно мне перемешать вас:\n"
l = []
for i, val in enumerate(list_of_var):
text += "\n{} {}".format(list_of_num[i], val)
l.append(
types.InlineKeyboardButton(text=list_of_num[i], callback_data="rand#" + val)
)
l = [l]
markup = types.InlineKeyboardMarkup(l)
bot.send_message(message.chat.id, text, ParseMode.HTML, reply_markup=markup)
return
@bot.callback_query_handler(func=lambda call: "rand#" in call.data)
def make_random(call):
list_of_stud = for_json.students_in_json(
call.message.chat.id, call.data.split("#")[1]
)
random.shuffle(list_of_stud)
text = "<b><u>{}</u></b>:\n\n".format(call.data.split("#")[1])
for i, name in enumerate(list_of_stud):
text += "<code>{}{}</code>. {}\n".format(" " if i + 1 < 10 else "", i + 1, name)
bot.edit_message_text(
"Хорошо, вот ваше распределение!",
call.message.chat.id,
call.message.message_id,
parse_mode=ParseMode.HTML,
)
send_message(call.message.chat.id, text)
return
@bot.message_handler(commands=["info"]) @bot.message_handler(commands=["info"])
def send_info(message): def send_info(message):
if len(message.text.split(" ")) == 2 and is_admin(message): if len(message.text.split(" ")) == 2 and is_admin(message):
send_admin_message( send_admin_message(
"Окей, держи настройки <code>{}</code>".format(message.text.split(" ")[1]) "Окей, держи настройки <code>{}</code>".format(message.text.split(" ")[1])
) )
infos = for_json.return_infos(message.text.split(" ")[1]) infos = db.return_infos(message.text.split(" ")[1])
text = "<i>Никнейм</i>: <b>@{}</b>\ text = "<i>Никнейм</i>: <b>@{}</b>\
\n<i>Имя</i>: <b>{} {}</b>\ \n<i>Имя</i>: <b>{} {}</b>\
\n<i>Выбранная группа</i>: <b>{}</b>\ \n<i>Выбранная группа</i>: <b>{}</b>\
@@ -539,7 +451,7 @@ def send_info(message):
return return
send_message(message.chat.id, "Хорошо, вот твои настройки:") send_message(message.chat.id, "Хорошо, вот твои настройки:")
infos = for_json.return_infos(message.chat.id) infos = db.return_infos(message.chat.id)
text = "<i>Выбранная группа</i>: <b>{}</b>\ text = "<i>Выбранная группа</i>: <b>{}</b>\
\n<i>Время напоминания до урока</i>: <b>{}</b>\ \n<i>Время напоминания до урока</i>: <b>{}</b>\
\n<i>Разрешены ли напоминания</i>: <b>{}</b>".format( \n<i>Разрешены ли напоминания</i>: <b>{}</b>".format(
@@ -554,17 +466,17 @@ def send_info(message):
@bot.message_handler(commands=["pause"]) @bot.message_handler(commands=["pause"])
def pause_schedule(message): def pause_schedule(message):
infos = for_json.return_infos(message.chat.id)["allow_message"] infos = db.return_infos(message.chat.id)["allow_message"]
if infos == "yes": if infos == "yes":
for_json.change_user_param(str(message.chat.id), "allow_message", "no") db.change_user_param(str(message.chat.id), "allow_message", "no")
send_message( send_message(
message.chat.id, message.chat.id,
"Рассылка сообщений прекращена. \ "Рассылка сообщений прекращена. \
\nДля возобновления воспользуйтесь командой\n/pause", \nДля возобновления воспользуйтесь командой\n/pause",
) )
else: else:
for_json.change_user_param(str(message.chat.id), "allow_message", "yes") db.change_user_param(str(message.chat.id), "allow_message", "yes")
send_message(message.chat.id, "Рассылка сообщений возоблена!") send_message(message.chat.id, "Рассылка сообщений возоблена!")
return return
@@ -579,7 +491,7 @@ def change_thread(message):
except AttributeError: except AttributeError:
thread_id = "General" thread_id = "General"
for_json.change_user_param(str(message.chat.id), "thread", thread_id) db.change_user_param(str(message.chat.id), "thread", thread_id)
send_message( send_message(
message.chat.id, message.chat.id,
@@ -608,7 +520,7 @@ def save_timeout(message):
and int(message.text) <= 59 and int(message.text) <= 59
and int(message.text) >= 1 and int(message.text) >= 1
): ):
for_json.change_user_param(message.chat.id, "timeout", str(message.text)) db.change_user_param(message.chat.id, "timeout", str(message.text))
send_message( send_message(
message.chat.id, message.chat.id,
"Хорошо, теперь ты будешь получать напоминания за {} минут до урока".format( "Хорошо, теперь ты будешь получать напоминания за {} минут до урока".format(
@@ -693,9 +605,10 @@ def send_message(id, text, thread_id="General", parity=None):
if thread_id == "General": if thread_id == "General":
thread_id = None thread_id = None
if parity: if parity != None:
parity = int(parity)
count_of_weeks = (datetime.now() - datetime(2024, 2, 5)).days // 7 count_of_weeks = (datetime.now() - datetime(2024, 2, 5)).days // 7
is_odd = (count_of_weeks + 1) % 2 == parity_FIRST is_odd = (count_of_weeks + 1) % 2 == PARITY_FIRST
if is_odd != bool(parity): if is_odd != bool(parity):
return return
@@ -720,7 +633,7 @@ def send_message(id, text, thread_id="General", parity=None):
) )
except Exception as e: except Exception as e:
text_error = "Error from user: @{} <code>{}</code>\n{}".format( text_error = "Error from user: @{} <code>{}</code>\n{}".format(
for_json.return_infos(id)["username"], id, str(e) db.return_infos(id)["username"], id, str(e)
) )
# send_admin_message(text_error) # send_admin_message(text_error)
print(f"--- {text_error} ---") print(f"--- {text_error} ---")
@@ -744,7 +657,7 @@ def send_document(id, file, text=""):
if __name__ == "__main__": if __name__ == "__main__":
for_json.create_schedule_tasks(True) db.create_schedule_tasks(True)
send_admin_message("Я перезапустился!") send_admin_message("Я перезапустился!")
print("-------------------------") print("-------------------------")
-40
View File
@@ -1,40 +0,0 @@
import os
import json
import yaml
def convert_json_to_yaml(json_dir, yaml_dir):
# Iterate over all files in the directory
for filename in os.listdir(json_dir):
if filename.endswith(".json"):
json_path = os.path.join(json_dir, filename)
# Read JSON file
with open(json_path, "r", encoding="utf-8") as json_file:
data = json.load(json_file)
# Convert to YAML
yaml_data = yaml.dump(
data,
default_flow_style=None,
encoding="utf-8",
allow_unicode=True,
width=float("inf"),
sort_keys=False,
)
# Write YAML file
yaml_path = os.path.join(yaml_dir, filename.replace(".json", ".yaml"))
with open(yaml_path, "wb") as yaml_file:
yaml_file.write(yaml_data)
print("Conversion completed.")
if __name__ == "__main__":
json_dir = "json_remake"
yaml_dir = "yaml_groups"
convert_json_to_yaml(json_dir, yaml_dir)
pass
+24
View File
@@ -0,0 +1,24 @@
CREATE TABLE IF NOT EXISTS users (
id BIGINT PRIMARY KEY,
username VARCHAR(255),
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255),
group VARCHAR(255) DEFAULT 'other',
timeout INT DEFAULT 10,
allow_message BOOLEAN DEFAULT TRUE,
thread VARCHAR(255) DEFAULT 'General',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS schedule(
id BIGSERIAL PRIMARY KEY,
day_of_week VARCHAR(255),
begin_time VARCHAR(20),
end_time VARCHAR(20),
course VARCHAR(255),
lector VARCHAR(255),
is_lecture BOOLEAN DEFAULT FALSE,
room VARCHAR(20),
group VARCHAR(10),
parity VARCHAR(10),
);
-300
View File
@@ -1,300 +0,0 @@
#!/usr/bin/python3.3
import json, yaml, schedule
import schedules as bot
from datetime import datetime
path_users = "json/users.json" # Нужный путь до json файлов
path_schedule = "yaml_groups/{}.yaml"
path_students = "json/students.json"
path_groups = "json/groups.json"
allow_update = True
def save_user(infos):
for i in range(len(infos)):
if type(infos[i]) == type(None):
infos[i] = ""
infos[i] = str(infos[i])
infos = {
"id": infos[0],
"first_name": infos[1],
"last_name": infos[2],
"username": infos[3],
"group": infos[4],
}
data = {}
with open(path_users, "r", encoding="utf-8") as json_file:
data = json.load(json_file)
data[infos["id"]] = {
"username": infos["username"],
"first_name": infos["first_name"],
"last_name": infos["last_name"],
"group": infos["group"],
"timeout": "10",
"allow_message": "yes",
"thread": "General",
}
with open(path_users, "w", encoding="utf-8") as json_file:
json.dump(data, json_file, ensure_ascii=False, indent=4)
create_schedule_tasks()
return
def change_user_param(id, key, value):
id = str(id)
data = {}
with open(path_users, "r", encoding="utf-8") as json_file:
data = json.load(json_file)
data[id][key] = value
with open(path_users, "w", encoding="utf-8") as json_file:
json.dump(data, json_file, ensure_ascii=False, indent=4)
create_schedule_tasks()
# bot.send_admin_message(
# "Изменения:\
# \n<i>user:</i> @{}\
# \n<i>id:</i> <code>{}</code>\
# \n<i>key:</i> <code>{}</code>\
# \n<i>value:</i> <code>{}</code>".format(
# data[id]["username"], id, key, value
# )
# )
return
def parse_lesson(lesson: dict[str, str]) -> str:
text = ""
start, end = lesson["begin"], lesson["end"]
if not lesson["name"]:
return ""
if len(lesson["teacher.room"]) == 1:
text = "{}-{} | {}\
\n<b>{}</b>\
\n<i>{}</i>".format(
start,
end,
lesson["teacher.room"][0]["r"],
lesson["name"],
lesson["teacher.room"][0]["t"],
)
else:
text = "{}-{}\
\n<b>{}</b>".format(
start,
end,
lesson["name"],
)
for tr in lesson["teacher.room"]:
if not tr["t"] and not tr["r"]:
continue
text += "\n({}) <i>{}</i>".format(tr["r"], tr["t"])
return text
def create_schedule_tasks(manual=False):
global allow_update
if not manual:
if not allow_update:
return
# bot.send_admin_message("Произошёл auto-update")
allow_update = True
schedule.clear()
users = {}
schdl = {}
with open(path_users, "r", encoding="utf-8") as json_file:
users = json.load(json_file)
for id, params in users.items():
if params["allow_message"] != "yes":
continue
group = params["group"]
if group == "other":
continue
with open(path_schedule.format(group), "r", encoding="utf-8") as json_file:
# schdl = json.load(json_file)["schedule"]
schdl = yaml.safe_load(json_file)["schedule"]
for day in schdl:
for lesson in schdl[day]:
if not lesson["name"]: # Неправильный формат schedule
continue
start = lesson["begin"]
text = parse_lesson(lesson)
if not text:
continue
thread_id = params["thread"]
delta = "00:" + params["timeout"]
format = "%H:%M"
start_task = datetime.strptime(start, format) - datetime.strptime(
delta, format
)
tmp = ""
for i in str(start_task).split(":"):
tmp += (("0" + i) if len(i) < 2 else i) + ":"
start_task = tmp[:-1]
if day == "mon":
schedule.every().monday.at(start_task).do(
bot.send_message, id, text, thread_id, lesson["parity"]
)
elif day == "tue":
schedule.every().tuesday.at(start_task).do(
bot.send_message, id, text, thread_id, lesson["parity"]
)
elif day == "wed":
schedule.every().wednesday.at(start_task).do(
bot.send_message, id, text, thread_id, lesson["parity"]
)
elif day == "thu":
schedule.every().thursday.at(start_task).do(
bot.send_message, id, text, thread_id, lesson["parity"]
)
elif day == "fri":
schedule.every().friday.at(start_task).do(
bot.send_message, id, text, thread_id, lesson["parity"]
)
elif day == "sat":
schedule.every().saturday.at(start_task).do(
bot.send_message, id, text, thread_id, lesson["parity"]
)
return
def groups_in_json() -> list[str]:
groups = {}
with open(path_groups, "r", encoding="utf-8") as json_file:
groups = json.load(json_file)
return groups
def students_in_group(group) -> list[str]:
group = str(group)
data = {}
with open(path_users, "r", encoding="utf-8") as json_file:
data = json.load(json_file)
students = []
for i in data.keys():
if data[i]["group"] == group:
students.append(i)
return students
def students_in_json(id: str, key: str = "") -> list[str] | None:
id = str(id)
group = {}
with open(path_users, "r", encoding="utf-8") as json_file:
group = json.load(json_file)
group = group[id]["group"]
stud = {}
with open(path_students, "r", encoding="utf-8") as json_file:
stud = json.load(json_file)
stud = stud.get(group)
if stud == None:
return None
if key == "":
return list(stud.keys())
return stud[key]
def return_infos(id) -> dict[str, str] | None:
id = str(id)
data = {}
with open(path_users, "r", encoding="utf-8") as json_file:
data = json.load(json_file)
data = data.get(id)
return data
def pause_bot():
global allow_update
if allow_update:
schedule.clear()
allow_update = False
bot.send_admin_message("Бот больше не отправляет расписание")
else:
create_schedule_tasks(True)
allow_update = True
bot.send_admin_message("Бот возобновил рассылку!")
return
def get_schedule(id: str, day: str) -> str:
id = str(id)
if day == "sun":
day = "mon"
days = ["mon", "tue", "wed", "thu", "fri", "sat"]
russian_days = ["понедельник", "вторник", "среду", "четверг", "пятницу", "субботу"]
text = "<u>Расписание на {}</u>:\n\n".format(russian_days[days.index(day)])
schdl_today = {}
group = ""
with open(path_users, "r", encoding="utf-8") as user_file:
group = json.load(user_file)[id]["group"]
if group == "other":
return "У тебя не выбрана группа для рассылки сообщений"
try:
with open(path_schedule.format(group), "r", encoding="utf-8") as schedule_file:
try:
# schdl_today = json.load(schedule_file)["schedule"][day]
schdl_today = yaml.safe_load(schedule_file)["schedule"][day]
except:
return "Расписания твоей группы на {} нет".format(
russian_days[days.index(day)]
)
for lesson in schdl_today:
str_lesson = parse_lesson(lesson)
if str_lesson:
text += "" + str_lesson + "\n\n"
except:
return "Расписания твоей группы нет"
return text
if __name__ == "__main__":
print(groups_in_json())
pass
-46
View File
@@ -1,46 +0,0 @@
[
"101",
"102",
"103",
"104",
"105",
"106",
"107",
"108",
"109",
"110",
"111",
"112",
"113",
"114",
"115",
"116",
"117",
"118",
"119",
"120",
"141",
"142",
"201",
"202",
"203",
"204",
"205",
"206",
"219",
"207",
"208",
"209",
"210",
"211",
"212",
"213",
"214",
"215",
"216",
"217",
"218",
"220",
"241",
"242"
]
-11
View File
@@ -1,11 +0,0 @@
{
"856850518": {
"username": "kr0sh_512",
"first_name": "Дмитрий",
"last_name": "",
"group": "117",
"timeout": "10",
"allow_message": "yes",
"thread": "General"
}
}
+6 -4
View File
@@ -1,4 +1,6 @@
python-telegram-bot==13.7 pytelegrambotapi
requests==2.26.0 coloredlogs
python-dotenv==0.19.0 psycopg2-binary
schedule==1.1.0 sshtunnel
python-dotenv
schedule
+63
View File
@@ -0,0 +1,63 @@
CREATE TABLE IF NOT EXISTS schedule(
id BIGSERIAL PRIMARY KEY,
day_of_week VARCHAR(255),
begin_time VARCHAR(20),
end_time VARCHAR(20),
course VARCHAR(255),
lector VARCHAR(255),
is_lecture BOOLEAN DEFAULT FALSE,
room VARCHAR(20),
group VARCHAR(10),
parity INT,
);
-- ('mon', '101', '08:45', '10:20', '', '', ''),
-- ('mon', '101', '10:30', '12:05', '', '', ''),
-- ('mon', '101', '12:50', '14:25', '', '', ''),
-- ('mon', '101', '14:35', '16:10', '', '', ''),
-- ('mon', '101', '16:50', '18:20', '', '', ''),
INSERT INTO schedule(day_of_week, group, begin_time, end_time, course, lector, room) VALUES
('mon', '101', '10:30', '12:05', 'Практикум на ЭВМ', 'Кузьменкова Е.А.', '731'),
('mon', '101', '10:30', '12:05', 'Практикум на ЭВМ', 'Малышев Н.Е.', '778'),
('mon', '101', '12:50', '14:25', 'Английский язык', 'Бим М.М.', '735'),
('mon', '101', '12:50', '14:25', 'Английский язык', 'Шубина Ю.В.', '790'),
('mon', '101', '14:35', '16:10', 'Русский язык и культура речи', 'Рождественская Ольга Юрьевна', 'П-6'),
('mon', '102', '14:35', '16:10', 'Русский язык и культура речи', 'Рождественская Ольга Юрьевна', 'П-6'),
('mon', '103', '14:35', '16:10', 'Русский язык и культура речи', 'Рождественская Ольга Юрьевна', 'П-6'),
('mon', '104', '14:35', '16:10', 'Русский язык и культура речи', 'Рождественская Ольга Юрьевна', 'П-6'),
('mon', '105', '14:35', '16:10', 'Русский язык и культура речи', 'Рождественская Ольга Юрьевна', 'П-6'),
('mon', '106', '14:35', '16:10', 'Русский язык и культура речи', 'Рождественская Ольга Юрьевна', 'П-6'),
('mon', '107', '14:35', '16:10', 'Русский язык и культура речи', 'Рождественская Ольга Юрьевна', 'П-6'),
('mon', '101', '16:50', '18:20', 'Физическая культура', '', ''),
('mon', '102', '16:50', '18:20', 'Физическая культура', '', ''),
('mon', '103', '16:50', '18:20', 'Физическая культура', '', ''),
('mon', '104', '16:50', '18:20', 'Физическая культура', '', ''),
('mon', '105', '16:50', '18:20', 'Физическая культура', '', ''),
('mon', '106', '16:50', '18:20', 'Физическая культура', '', ''),
('mon', '107', '16:50', '18:20', 'Физическая культура', '', ''),
('mon', '102', '10:30', '12:05', 'Английский язык', 'Бим М.М.', '735'),
('mon', '102', '10:30', '12:05', 'Английский язык', 'Шубина Ю.В.', '790'),
('mon', '102', '12:50', '14:25', 'Математический анализ', 'Лапонин В.С.', '682'),
('mon', '103', '08:45', '10:20', 'Английский язык', 'Лаптинова А.И.', '73'),
('mon', '103', '08:45', '10:20', 'Английский язык', 'Каришева П.В.', '72'),
('mon', '103', '10:30', '12:05', 'Математический анализ', 'Зайцева Н.В.', '71'),
('mon', '103', '12:50', '14:25', 'Практикум на ЭВМ', 'Кузьменкова Е.А.', '731'),
('mon', '103', '12:50', '14:25', 'Практикум на ЭВМ', 'Малышев Н.Е.', '778'),
('mon', '104', '10:30', '12:05', 'Математический анализ', 'Лапонин В.С.', '682'),
('mon', '104', '12:50', '14:25', 'Английский язык', 'Каришева П.В.', '72'),
('mon', '105', '10:30', '12:05', 'Математический анализ', 'Никитин А.А.', '72'),
('mon', '105', '12:50', '14:25', 'Алгебра и геометрия', 'Лебедева О.С.', '503'),
('mon', '106', '10:30', '12:05', 'Практикум на ЭВМ', 'Корухова Л.С.', '779'),
('mon', '106', '10:30', '12:05', 'Практикум на ЭВМ', 'Соловьев М.А.', '780'),
('mon', '106', '12:15', '13:50', 'Алгебра и геометрия', 'Есикова Н.Б.', '504'),
('mon', '107', '08:45', '10:20', 'Математический анализ', 'Зайцева Н.В.', '71'),
('mon', '107', '10:30', '12:05', 'Алгебра и геометрия', 'Есикова Н.Б.', '504'),
('mon', '107', '12:50', '14:25', 'Практикум на ЭВМ', 'Журихин Д.М.', 'МЗ-3'),
('mon', '107', '12:50', '14:25', 'Практикум на ЭВМ', 'Гуляев Д.А.', 'МЗ-2'),
-201
View File
@@ -1,201 +0,0 @@
studies.begin: {y: 2024, m: 9, d: 1}
studies.end: null
schedule:
mon:
- begin: '8:45'
end: '10:20'
name: Английский язык
teacher.room:
- {t: Бим М.М., r: '735'}
- {t: ' Шубина Ю.В.', r: '73'}
type: seminar
parity: null
- begin: '10:30'
end: '12:05'
name: Практикум на ЭВМ
teacher.room:
- {t: Тюляева В.В., r: МЗ-4}
- {t: '', r: null}
type: seminar
parity: null
- begin: '12:15'
end: '13:50'
name: Классическая механика
teacher.room:
- {t: Брандт Н.Н., r: '659'}
type: seminar
parity: null
- begin: '15:00'
end: '16:30'
name: Физическая культура
teacher.room: []
type: null
parity: null
tue:
- begin: '8:45'
end: '10:20'
name: Теория вероятностей и математическая статистика
teacher.room:
- {t: профессор Королёв Виктор Юрьевич, r: П-13}
type: lecture
parity: null
- begin: '10:30'
end: '12:05'
name: Математический анализ
teacher.room:
- {t: доцент Крицков Леонид Владимирович, r: П-11}
type: lecture
parity: null
- begin: '12:15'
end: '13:50'
name: Математический анализ
teacher.room:
- {t: Сычугов Д.Ю., r: '606'}
type: seminar
parity: null
- begin: '14:35'
end: '16:10'
name: Обыкновенные дифференциальные ур-я
teacher.room:
- {t: Дмитриева И.В., r: '607'}
type: seminar
parity: null
- begin: '16:20'
end: '17:55'
name: null
teacher.room: []
type: seminar
parity: null
wed:
- begin: '8:45'
end: '10:20'
name: Практикум на ЭВМ
teacher.room:
- {t: Тюляева В.В., r: '507'}
type: seminar
parity: null
- begin: '10:30'
end: '12:05'
name: Математический анализ
teacher.room:
- {t: доцент Крицков Леонид Владимирович, r: П-6}
type: lecture
parity: null
- begin: '12:15'
end: '13:50'
name: Обыкновенные дифференциальные уравнения
teacher.room:
- {t: профессор Денисов Александр Михайлович, r: П-6}
type: lecture
parity: null
- begin: '15:10'
end: '18:50'
name: Межфакультетские курсы
teacher.room: []
type: seminar
parity: null
thu:
- begin: '8:45'
end: '10:20'
name: Операционные системы
teacher.room:
- {t: профессор Машечкин Игорь Валерьевич, r: П-13}
type: lecture
parity: null
- begin: '10:30'
end: '12:05'
name: Операционные системы
teacher.room:
- {t: профессор Машечкин Игорь Валерьевич, r: П-13}
type: lecture
parity: null
- begin: '12:15'
end: '13:50'
name: Английский язык
teacher.room:
- {t: Бим М.М., r: '71'}
- {t: '', r: null}
type: seminar
parity: null
- begin: '14:35'
end: '16:10'
name: Введение в численные методы
teacher.room:
- {t: доцент Нефёдов Владимир Вадимович, r: П-5}
type: lecture
parity: null
- begin: '16:20'
end: '17:55'
name: null
teacher.room: []
type: seminar
parity: null
fri:
- begin: '8:45'
end: '10:20'
name: Английский язык
teacher.room:
- {t: Шубина Ю.В., r: '506'}
type: seminar
parity: null
- begin: '10:50'
end: '12:20'
name: Физическая культура
teacher.room: []
type: null
parity: null
- begin: '12:50'
end: '14:25'
name: Теория вероятностей и математич. статистика
teacher.room:
- {t: Шевцова И.Г., r: '504'}
type: seminar
parity: null
- begin: '14:35'
end: '16:10'
name: Математический анализ
teacher.room:
- {t: Сычугов Д.Ю., r: '615'}
type: seminar
parity: null
- begin: '16:20'
end: '17:55'
name: null
teacher.room: []
type: seminar
parity: null
sat:
- begin: '8:45'
end: '10:20'
name: Философия
teacher.room:
- {t: профессор Девятова Светлана Владимировна, r: П-14}
type: lecture
parity: null
- begin: '10:30'
end: '12:05'
name: Философия Девятова С.В.
teacher.room:
- {t: null, r: '508'}
type: seminar
parity: null
- begin: '12:15'
end: '13:50'
name: null
teacher.room: []
type: seminar
parity: null
- begin: '14:35'
end: '16:10'
name: Операционные системы
teacher.room:
- {t: профессор Машечкин Игорь Валерьевич, r: П-13}
type: lecture
parity: null
- begin: '16:20'
end: '17:55'
name: Операционные системы
teacher.room:
- {t: профессор Машечкин Игорь Валерьевич, r: П-13}
type: lecture
parity: null