Compare commits

...
11 Commits
Author SHA1 Message Date
krosh 859e2c123c test 2025-09-09 01:03:00 +03:00
Dmitrii Gudov 74ce952a81 fix parity problems 2025-09-08 17:12:00 +03:00
kroshandGitHub fae3e903a0 add 5 and 6 courses 2025-09-02 21:28:12 +03:00
Dmitrii GudovandGitHub bff8c951b8 Merge branch 'main' into google-sql 2025-02-20 12:47:27 +03:00
krosh eb272e218d add sort 2025-02-20 12:45:52 +03:00
krosh cb4300c801 some upd 2025-02-20 12:32:57 +03:00
kr0sh512 c22085eeb3 some updates 2025-02-12 00:21:38 +03:00
kr0sh512 f4476bc15d stable version 2025-02-12 00:10:07 +03:00
kr0sh512 ab1272df70 for work with google sheets 2025-02-11 11:08:50 +03:00
kr0sh512 b2db3a4b76 refactor for sql 2025-02-09 16:30:25 +03:00
Dmitrii GudovandGitHub 8f1d383960 Update README.md 2024-11-11 23:43:34 +03:00
17 changed files with 754 additions and 787 deletions
+12 -1
View File
@@ -10,4 +10,15 @@ 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
1 курс весна 2025.xlsx
2 курс весна 2025.xls
Новая таблица - 1 курс весна 2025 copy.csv
Новая таблица - 1 курс весна 2025.csv
Новая таблица - 2 курс весна 2025 copy.csv
Новая таблица - 2 курс весна 2025.csv
schedule copy.sql
tmp/
credentials.json
+16
View File
@@ -0,0 +1,16 @@
FROM python:3.9-slim
RUN apt-get update && apt-get install -y cron
WORKDIR /app
# anyway copy make pip install again
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY ./app .
RUN chmod +x install.sh
CMD ["./install.sh"]
+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):
+339
View File
@@ -0,0 +1,339 @@
#!/usr/bin/python3.3
import schedule
import schedules as bot
from datetime import datetime
import os
from dotenv import load_dotenv
from sshtunnel import SSHTunnelForwarder
from psycopg2 import pool
from typing import Optional, Dict
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, first_name, last_name, username, "group") VALUES (%s, %s, %s, %s, %s) ON CONFLICT (id) DO UPDATE SET first_name = EXCLUDED.first_name, last_name = EXCLUDED.last_name, username = EXCLUDED.username, "group" = EXCLUDED."group"',
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"] += " (нечёт.)"
if lesson["lector"]:
if "/" not in lesson["lector"]:
text = "{}-{} | {}\
\n{}<b>{}</b>\
\n<i>{}</i>".format(
start,
end,
lesson["room"],
"🎓 " if lesson["is_lecture"] else "",
lesson["course"],
lesson["lector"],
)
else:
lectors = lesson["lector"].split("/")
text = "{}-{} | {}\
\n<b>{}</b>\
\n<i>{}</i>\
\n<i>{}</i>".format(
start,
end,
lesson["room"].replace("/", ", "),
lesson["course"],
lectors[0],
lectors[1],
)
else:
text = "{}-{}\
\n{}<b>{}</b>".format(
start,
end,
"🎓 " if lesson["is_lecture"] else "",
lesson["course"],
)
# text += f"\n{lesson['info']}"
return text
def create_schedule_tasks(manual=False):
global allow_update
if not manual:
if not allow_update:
return
allow_update = True
schedule.clear()
def run_script():
os.system("python3 -u upload_sql.py")
schedule.every(6).minutes.do(run_script)
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 not user["allow_message"]:
continue
group = user["group"]
if group == "other":
continue
cursor.execute('SELECT * FROM schedule WHERE "group" = %s', (group,))
data = cursor.fetchall()
if not data:
continue
schdl = into_list(data, cursor.description)
schdl.sort(key=lambda lesson: datetime.strptime(lesson["begin_time"], "%H:%M"))
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:" + str(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) -> Optional[Dict[str, str]]:
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()
def run_script():
os.system("python3 -u upload_sql.py")
schedule.every(6).minutes.do(run_script)
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 "У тебя не выбрана группа для рассылки сообщений"
if group not in groups_in_json():
return f"Такой группы нет в базе. Проверь в <a href='https://docs.google.com/spreadsheets/d/{os.environ.get('TABLE_ID')}/edit?usp=sharing'>таблице</a> существование расписания для твоей группы"
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
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
# List of environment variables to check
# env_vars=("TG_ADMIN_ID" "TG_API_TOKEN" "DB_HOST" "DB_PORT" "DB_USER" "DB_PASSWORD" "DB_NAME" "TABLE_ID")
# for var in "${env_vars[@]}"; do
# if [ -z "${!var}" ]; then
# echo "Environment variable $var is not set or is empty."
# exit 1
# # else
# # echo "Environment variable $var is set to '${!var}'."
# fi
# done
# Add cron jobs to crontab
# (crontab -l 2>/dev/null; echo "0 * * * * python3 /app/upload_sql.py") | crontab -
# (crontab -l 2>/dev/null; echo "*/14 * * * * python3 /app/upload_sql.py") | crontab -
# Start the schedules.py script
python3 -u /app/schedules.py
+76 -141
View File
@@ -1,16 +1,25 @@
#!/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")
if os.environ.get("ENV") == "dev"
else os.environ.get("TG_API_TOKEN")
),
parse_mode=ParseMode.HTML,
)
PARITY_FIRST = 0
@bot.message_handler(commands=["help", "faq"]) @bot.message_handler(commands=["help", "faq"])
@@ -29,12 +38,16 @@ def help(message):
send_message(message.chat.id, help_msg) send_message(message.chat.id, help_msg)
help_msg2 = f"Бот берёт расписание из таблицы, которую можно посмотреть по ссылке:\
\n<a href='https://docs.google.com/spreadsheets/d/{os.environ.get('TABLE_ID')}/edit?usp=sharing'>Расписание</a>"
send_message(message.chat.id, help_msg2)
if is_admin(message): if is_admin(message):
admin_help_msg = "И команды только для админа:\ admin_help_msg = "И команды только для админа:\
\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 +60,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 +70,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 +86,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 +114,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 +154,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 +192,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 +217,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 +229,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 +264,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(
@@ -290,18 +274,18 @@ def start(message):
else: else:
send_message( send_message(
message.chat.id, message.chat.id,
"Похоже, что расписания для этой группы ещё не существует. \ f"Похоже, что расписания для этой группы ещё не существует. \
\nРазработчик уже пинается, но можете дополнительно написать ему: @Kr0sH_512", \nПроверьте расписание своей группы в <a href='https://docs.google.com/spreadsheets/d/{os.environ.get('TABLE_ID')}/edit?usp=sharing'>этой таблице</a>",
) )
send_admin_message( # send_admin_message(
"В группе {} был запрос на {} группу.\ # "В группе {} был запрос на {} группу.\
\nid: {}".format( # \nid: {}".format(
message.chat.title, temp, message.chat.id # message.chat.title, temp, message.chat.id
) # )
) # )
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,10 +298,14 @@ 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"))
markup.add(types.InlineKeyboardButton(text="3 курс", callback_data="3course"))
markup.add(types.InlineKeyboardButton(text="4 курс", callback_data="4course"))
markup.add(types.InlineKeyboardButton(text="5 курс", callback_data="5course"))
markup.add(types.InlineKeyboardButton(text="6 курс", callback_data="6course"))
bot.send_message( bot.send_message(
message.chat.id, message.chat.id,
@@ -330,15 +318,15 @@ 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", "3course", "4course", "5course", "6course"]
) )
def callback_inline(call): def callback_inline(call):
if "course" in call.data: if "course" in call.data:
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,14 +356,15 @@ 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(
chat_id=call.message.chat.id, chat_id=call.message.chat.id,
message_id=call.message.message_id, message_id=call.message.message_id,
text="Используй команду /request и напиши номер группы, которую хочешь добавить\ text=f"Пожалуйста, проверь расписание для своей группы в <a href='https://docs.google.com/spreadsheets/d/{os.environ.get('TABLE_ID')}/edit?usp=sharing'>этой таблице</a>",
\nПосле создания расписания для твоей группы, я пришлю тебе сообщение", # text="Используй команду /request и напиши номер группы, которую хочешь добавить\
# \nПосле создания расписания для твоей группы, я пришлю тебе сообщение",
parse_mode=ParseMode.HTML, parse_mode=ParseMode.HTML,
) )
else: else:
@@ -396,7 +385,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 +426,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 +445,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 +468,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 +483,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 == True:
for_json.change_user_param(str(message.chat.id), "allow_message", "no") db.change_user_param(str(message.chat.id), "allow_message", False)
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", True)
send_message(message.chat.id, "Рассылка сообщений возоблена!") send_message(message.chat.id, "Рассылка сообщений возоблена!")
return return
@@ -579,7 +508,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 +537,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 +622,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 and parity != "-":
parity = 0 if parity == "чёт" else 1 if parity == "нечёт" else None
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 +650,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 +674,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("-------------------------")
@@ -753,6 +683,11 @@ if __name__ == "__main__":
target=bot.infinity_polling, name="bot_infinity_polling", daemon=True target=bot.infinity_polling, name="bot_infinity_polling", daemon=True
).start() ).start()
def run_script():
os.system("python3 -u upload_sql.py")
schedule.every(6).minutes.do(run_script)
while True: while True:
try: try:
schedule.run_pending() schedule.run_pending()
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/python3.3
from datetime import datetime
import os
from dotenv import load_dotenv
from sshtunnel import SSHTunnelForwarder
from psycopg2 import pool
import json
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"),
)
# Load users from JSON file
with open("users.json", "r", encoding="utf-8") as file:
users = json.load(file)
# Insert users into the database
with db.getconn() as conn:
with conn.cursor() as cursor:
for user_id, user_data in users.items():
cursor.execute(
"""
INSERT INTO users (id, username, first_name, last_name, "group", timeout, allow_message, thread)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
ON CONFLICT (id) DO NOTHING
""",
(
user_id,
user_data.get("username"),
user_data.get("first_name"),
user_data.get("last_name"),
user_data.get("group", "other"),
user_data.get("timeout", 10),
user_data.get("allow_message", "yes") == "yes",
user_data.get("thread", "General"),
),
)
conn.commit()
+124
View File
@@ -0,0 +1,124 @@
#!/usr/bin/python3.3
import gspread, os, telebot
import pandas as pd
from dotenv import load_dotenv
from google.oauth2 import service_account
from sshtunnel import SSHTunnelForwarder
from psycopg2 import pool
from datetime import datetime
load_dotenv()
bot = telebot.TeleBot(os.environ.get("TG_TEST_TOKEN"))
SERVICE_ACCOUNT_FILE = "credentials.json"
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE,
scopes=[
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/documents",
],
)
gc = gspread.authorize(credentials)
table = gc.open_by_key(os.environ.get("TABLE_ID"))
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 f():
ws = table.worksheets()
for i in ws:
# print(i.title)
if not (i.title == "Пример" or i.title == "main") and i.title.isdigit():
df = pd.DataFrame(i.get_all_records())
if not df.empty:
with db.getconn() as conn:
with conn.cursor() as cursor:
cursor.execute(
'DELETE FROM schedule WHERE "group" = %s', (str(i.title),)
)
conn.commit()
for index, row in df.iterrows():
day_of_week_map = {
"Понедельник": "mon",
"Вторник": "tue",
"Среда": "wed",
"Четверг": "thu",
"Пятница": "fri",
"Суббота": "sat",
"Воскресенье": "sun",
}
row["День недели"] = day_of_week_map.get(
row["День недели"], row["День недели"]
)
if row["Название"] == "":
continue
end_time = (
pd.to_datetime(row["Время начала"], format="%H:%M")
+ pd.Timedelta(hours=1, minutes=35)
).strftime("%H:%M")
cursor.execute(
"""
INSERT INTO schedule (day_of_week, begin_time, end_time, course, lector, is_lecture, room, "group", parity)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
""",
(
row["День недели"],
row["Время начала"],
end_time,
row["Название"],
f'{row["Преподаватель"]}{"/" + str(row["2-ой преподаватель"]) if row["2-ой преподаватель"] else ""}',
row["Лекция?"],
f'{row["Кабинет"]}{"/" + str(row["Прочие кабинеты"]) if row["Прочие кабинеты"] else ""}',
i.title,
"чёт" if row["Чётность пары"] == "чётная" else "нечёт" if row["Чётность пары"] == "нечётная" else None,
),
)
conn.commit()
text = f"Last tg-bot update was at: {datetime.now().astimezone().strftime('%Y-%m-%d %H:%M %Z')}"
table.get_worksheet(0).update_acell("A25", text)
return
if __name__ == "__main__":
try:
f()
except Exception as e:
print(e)
bot.send_message(int(os.environ.get("ADMIN_ID")), "Check logs! upload error.")
bot.send_message(int(os.environ.get("ADMIN_ID")), e)
exit(0)
-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
+26
View File
@@ -0,0 +1,26 @@
CREATE DATABASE tg_schedule;
CREATE TABLE IF NOT EXISTS users (
id BIGINT PRIMARY KEY,
username VARCHAR(255),
first_name VARCHAR(255),
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)
);
+26
View File
@@ -0,0 +1,26 @@
services:
schedule_bot:
build:
context: .
dockerfile: Dockerfile
container_name: schedule_bot
environment:
- ENV=production
- TZ=Europe/Moscow
restart: unless-stopped
networks:
- postgres_postgres
volumes:
- "/etc/timezone:/etc/timezone:ro"
- "/etc/localtime:/etc/localtime:ro"
deploy:
resources:
limits:
cpus: '0.3'
memory: 300M # Set a hard memory limit
reservations:
memory: 200M # Reserve a soft memory limit
networks:
postgres_postgres:
external: true
-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"
}
}
+11 -4
View File
@@ -1,4 +1,11 @@
python-telegram-bot==13.7 pytelegrambotapi
requests==2.26.0 python-telegram-bot
python-dotenv==0.19.0 coloredlogs
schedule==1.1.0 psycopg2-binary
sshtunnel
python-dotenv
schedule
google-api-python-client
oauth2client
gspread
pandas
-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