mirror of
https://github.com/Faridik/Carolyn.git
synced 2026-07-08 05:11:52 +00:00
Текст сообщений вынесен в отдельный класс. Перемещены секретные файлы. Налажено развертывание контейнеров.
This commit is contained in:
parent
815bb0e471
commit
b0bca6433f
5
.gitignore
vendored
5
.gitignore
vendored
@ -4,4 +4,7 @@ SpreadsheetsService/utils/__pycache__
|
|||||||
SpreadsheetsService/tests/__pycache__
|
SpreadsheetsService/tests/__pycache__
|
||||||
SpreadsheetsService/manager/__pycache__
|
SpreadsheetsService/manager/__pycache__
|
||||||
SpreadsheetsService/manager/client_secret.json
|
SpreadsheetsService/manager/client_secret.json
|
||||||
SpreadsheetsService/manager/token.json
|
SpreadsheetsService/manager/token.json
|
||||||
|
TelegramBot/.secrets
|
||||||
|
SpreadsheetsService/.secrets
|
||||||
|
TelegramBot/messages/__pycache__
|
||||||
|
|||||||
@ -1,9 +1,34 @@
|
|||||||
import flask
|
from functools import wraps
|
||||||
import time
|
|
||||||
import models
|
|
||||||
from manager import *
|
from manager import *
|
||||||
|
import flask
|
||||||
|
import logging
|
||||||
|
import models
|
||||||
|
import time
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
|
app.db = None
|
||||||
|
try:
|
||||||
|
app.db = Manager()
|
||||||
|
except Exception as e:
|
||||||
|
app.db_error = e.args
|
||||||
|
LOG.exception("Cannot init manager")
|
||||||
|
|
||||||
|
|
||||||
|
def availability(func):
|
||||||
|
"""Декоратор, который проверяет доступность менеджера по работе с Google
|
||||||
|
Spreadsheets.
|
||||||
|
|
||||||
|
Если инициализация менеджера обвалилась, то сервер выдает ошибку 500 и ее
|
||||||
|
содержимое в JSON."""
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
if app.db is None:
|
||||||
|
return flask.jsonify(dict(error=True, message=app.db_error)), 500
|
||||||
|
func(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
@ -12,13 +37,32 @@ def hello_world():
|
|||||||
|
|
||||||
|
|
||||||
@app.route("/auth")
|
@app.route("/auth")
|
||||||
|
@availability
|
||||||
def auth():
|
def auth():
|
||||||
# TODO: return json text instead of text/html.
|
|
||||||
token = flask.request.args.get("token")
|
token = flask.request.args.get("token")
|
||||||
tg_id = flask.request.args.get("tg_id")
|
tg_id = flask.request.args.get("tg_id")
|
||||||
manager = Manager()
|
student = app.db.get_student_by_name("Дзюба")
|
||||||
student = manager.get_student_by_name("Дзюба")
|
student = Student(1, "Фарид Михайлов", 5374)
|
||||||
return str(student)
|
return flask.jsonify(student)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/grades")
|
||||||
|
@availability
|
||||||
|
def grades():
|
||||||
|
"""Возвращает оценки по id пользователя из телеграма."""
|
||||||
|
tg_id = flask.request.args.get("tg_id")
|
||||||
|
student = models.Student(1, "Фарид Михайлов", 5374)
|
||||||
|
student.add_assignment(models.Assignment("ИДЗ 1", [1, 1, 0, 0, 1]))
|
||||||
|
student.add_assignment(models.Assignment("КР 1", [0.4, 0.8, 1, 0.5, 1], 5))
|
||||||
|
student.add_assignment(models.Assignment("ИДЗ 2", [1, 1, 1, 0, 1]))
|
||||||
|
|
||||||
|
return flask.jsonify(
|
||||||
|
dict(
|
||||||
|
score=student.score,
|
||||||
|
grade=student.grade,
|
||||||
|
assignments=student.assignments,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@ -20,8 +20,8 @@ ASSIGNMENT_SUBJECT = 3
|
|||||||
ASSIGNMENT_WEIGHTS = 4
|
ASSIGNMENT_WEIGHTS = 4
|
||||||
ASSIGNMENT_RANGES = 5
|
ASSIGNMENT_RANGES = 5
|
||||||
ASSIGNMENT_ALLOWS = 6
|
ASSIGNMENT_ALLOWS = 6
|
||||||
TOKEN_FILE = pathlib.Path() / "manager" / "token.json"
|
TOKEN_FILE = pathlib.Path() / ".secrets" / "token.json"
|
||||||
CLIENT_SECRET_FILE = pathlib.Path() / "manager" / "client_secret.json"
|
CLIENT_SECRET_FILE = pathlib.Path() / ".secrets" / "client_secret.json"
|
||||||
|
|
||||||
|
|
||||||
class Manager:
|
class Manager:
|
||||||
|
|||||||
@ -1,10 +1,17 @@
|
|||||||
class Assignment:
|
class Assignment(dict):
|
||||||
"""Задание."""
|
"""Задание."""
|
||||||
|
|
||||||
def __init__(self, name: str, values: list, weight: float=1):
|
def __init__(self, name: str, points: list, weight: float = 1):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.weight = weight
|
self.weight = weight
|
||||||
self.values = values
|
self.points = points
|
||||||
|
dict.__init__(
|
||||||
|
self,
|
||||||
|
name=self.name,
|
||||||
|
weight=self.weight,
|
||||||
|
points=self.points,
|
||||||
|
total=self.total,
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total(self) -> float:
|
def total(self) -> float:
|
||||||
@ -12,4 +19,4 @@ class Assignment:
|
|||||||
|
|
||||||
`вес*СУММ(рез-ты)`
|
`вес*СУММ(рез-ты)`
|
||||||
"""
|
"""
|
||||||
return sum(self.values) * self.weight
|
return sum(self.points) * self.weight
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
from .assignment import Assignment
|
from .assignment import Assignment
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class Student:
|
class Student(dict):
|
||||||
"""Позволяет выполнять операции над студентами."""
|
"""Позволяет выполнять операции над студентами."""
|
||||||
|
|
||||||
def __init__(self, number: int, name: str, group_id: int, tg_id: int = None):
|
def __init__(self, number: int, name: str, group_id: int, tg_id: int = None):
|
||||||
@ -12,6 +11,15 @@ class Student:
|
|||||||
self.tg_id: int = tg_id
|
self.tg_id: int = tg_id
|
||||||
self.assignments = []
|
self.assignments = []
|
||||||
|
|
||||||
|
dict.__init__(
|
||||||
|
self,
|
||||||
|
number=self.number,
|
||||||
|
name=self.name,
|
||||||
|
group_id=self.group_id,
|
||||||
|
tg_id=self.tg_id,
|
||||||
|
assignments=self.assignments,
|
||||||
|
)
|
||||||
|
|
||||||
def add_assignment(self, assignment: Assignment):
|
def add_assignment(self, assignment: Assignment):
|
||||||
self.assignments.append(assignment)
|
self.assignments.append(assignment)
|
||||||
|
|
||||||
@ -24,13 +32,3 @@ class Student:
|
|||||||
def score(self):
|
def score(self):
|
||||||
"""Количество баллов у студента."""
|
"""Количество баллов у студента."""
|
||||||
return sum([a.total for a in self.assignments])
|
return sum([a.total for a in self.assignments])
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return json.dumps(
|
|
||||||
{
|
|
||||||
"number": self.number,
|
|
||||||
"name": self.name,
|
|
||||||
"group_id": self.group_id,
|
|
||||||
"tg_id": self.tg_id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|||||||
@ -1 +1,3 @@
|
|||||||
Flask==2.0.0
|
Flask==2.0.0
|
||||||
|
google-api-python-client==2.15.0
|
||||||
|
google-auth-oauthlib==0.4.5
|
||||||
@ -12,7 +12,6 @@ COPY requirements.txt .
|
|||||||
RUN python -m pip install -r requirements.txt
|
RUN python -m pip install -r requirements.txt
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . /app
|
|
||||||
|
|
||||||
# Creates a non-root user with an explicit UID and adds permission to access the /app folder
|
# Creates a non-root user with an explicit UID and adds permission to access the /app folder
|
||||||
# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers
|
# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers
|
||||||
|
|||||||
@ -1,58 +1,97 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import time
|
|
||||||
import requests
|
import requests
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from telegram.ext import Updater
|
|
||||||
from telegram.ext import CommandHandler
|
|
||||||
from telegram.ext import MessageHandler, Filters
|
|
||||||
|
|
||||||
TOKEN = "1903048555:AAFK-d7prKvOvwwXo0IPzeRwJ1ZcuOT5ty8" # os.environ["TOKEN"]
|
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
|
||||||
|
from telegram.ext import (
|
||||||
|
Updater,
|
||||||
|
CommandHandler,
|
||||||
|
MessageHandler,
|
||||||
|
Filters,
|
||||||
|
ConversationHandler,
|
||||||
|
CallbackContext,
|
||||||
|
)
|
||||||
|
|
||||||
|
from messages import Messages
|
||||||
|
|
||||||
|
TOKEN = Path(".secrets/bot_token.txt").read_text()
|
||||||
|
MESSAGES = Messages()
|
||||||
|
HOST = "http://carolyn-spreadsheets:5000"
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
|
||||||
)
|
)
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
LOG.debug("Token is %s", TOKEN)
|
||||||
updater = Updater(token=TOKEN, use_context=True)
|
updater = Updater(token=TOKEN, use_context=True)
|
||||||
dispatcher = updater.dispatcher
|
dispatcher = updater.dispatcher
|
||||||
|
|
||||||
# ================================================================ BOT COMMANDS
|
# ================================================================ BOT COMMANDS
|
||||||
|
|
||||||
# Стартовое сообщение (к команде /start)
|
|
||||||
def start(update, context):
|
def start(update: Update, context: CallbackContext):
|
||||||
|
"""Стартовое сообщение (к команде /start)"""
|
||||||
|
|
||||||
|
user_id = update.message.from_user.id
|
||||||
|
|
||||||
p = context.args[0] if len(context.args) > 0 else None
|
p = context.args[0] if len(context.args) > 0 else None
|
||||||
|
|
||||||
if p is None:
|
if p is None:
|
||||||
context.bot.send_message(
|
update.message.reply_markdown_v2(text=MESSAGES.auth.NO_AUTH)
|
||||||
chat_id=update.effective_chat.id,
|
|
||||||
text="Нет доступа к боту",
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
data = requests.get(
|
update.message.reply_text(text=MESSAGES.auth.START)
|
||||||
"http://127.0.0.1:5000/auth",
|
|
||||||
params={"token": p, "tg_id": update.effective_chat.id},
|
LOG.info(f"Authenticanting {user_id}")
|
||||||
).json()
|
|
||||||
|
try:
|
||||||
|
data = requests.get(
|
||||||
|
f"{HOST}/auth",
|
||||||
|
params={"token": p, "tg_id": user_id},
|
||||||
|
).json()
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
update.message.reply_sticker(MESSAGES.stickers.DEAD)
|
||||||
|
return
|
||||||
|
|
||||||
context.bot.send_message(
|
context.bot.send_message(
|
||||||
chat_id=update.effective_chat.id,
|
text=MESSAGES.auth.hello(data),
|
||||||
text=f"Привет, {data}",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Ответ на не командное сообщение (отвечает тем же сообщением)
|
def grades(update: Update, context: CallbackContext):
|
||||||
def echo(update, context):
|
"""Команда: Получить оценки студента."""
|
||||||
|
|
||||||
|
user_id = update.message.from_user.id
|
||||||
|
try:
|
||||||
|
data = requests.get(f"{HOST}/grades", params={"tg_id": user_id}).json()
|
||||||
|
score = data["score"]
|
||||||
|
grade = data["grade"]
|
||||||
|
n_of_assignments = len(data["assignments"])
|
||||||
|
|
||||||
|
score_message = MESSAGES.score.get(grade, score, n_of_assignments)
|
||||||
|
update.message.reply_html(score_message)
|
||||||
|
update.message.reply_sticker(MESSAGES.stickers.bad())
|
||||||
|
except:
|
||||||
|
LOG.exception("Failed to get grades.")
|
||||||
|
update.message.reply_sticker(MESSAGES.stickers.DEAD)
|
||||||
|
|
||||||
|
|
||||||
|
def echo(update: Update, context: CallbackContext):
|
||||||
|
"""Ответ на не командное сообщение (отвечает тем же сообщением)"""
|
||||||
context.bot.send_message(chat_id=update.effective_chat.id, text=update.message.text)
|
context.bot.send_message(chat_id=update.effective_chat.id, text=update.message.text)
|
||||||
|
|
||||||
|
|
||||||
# ==================================================================== HANDLERS
|
# ==================================================================== HANDLERS
|
||||||
|
|
||||||
start_handler = CommandHandler("start", start)
|
start_handler = CommandHandler("start", start)
|
||||||
|
grades_handler = CommandHandler("grades", grades)
|
||||||
|
|
||||||
echo_handler = MessageHandler(Filters.text & (~Filters.command), echo)
|
echo_handler = MessageHandler(Filters.text & (~Filters.command), echo)
|
||||||
|
|
||||||
dispatcher.add_handler(start_handler)
|
dispatcher.add_handler(start_handler)
|
||||||
|
dispatcher.add_handler(grades_handler)
|
||||||
dispatcher.add_handler(echo_handler)
|
dispatcher.add_handler(echo_handler)
|
||||||
|
|
||||||
# Начало работы бота
|
# Начало работы бота
|
||||||
|
|||||||
1
TelegramBot/messages/__init__.py
Normal file
1
TelegramBot/messages/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .messages import Messages
|
||||||
84
TelegramBot/messages/messages.py
Normal file
84
TelegramBot/messages/messages.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
class Messages:
|
||||||
|
class Auth:
|
||||||
|
def __init__(self):
|
||||||
|
self.NO_AUTH = "[Правила авторизации](https://github.com/Faridik/Carolyn/wiki/%F0%9F%A6%B8%E2%80%8D%E2%99%82%EF%B8%8F-%D0%90%D0%B2%D1%82%D0%BE%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F)"
|
||||||
|
self.START = "🔑 Авторизация..."
|
||||||
|
self.NO_ACCESS = "⛔ Нет доступа"
|
||||||
|
|
||||||
|
def hello(self, data):
|
||||||
|
return f"Привет, {data} 👋"
|
||||||
|
|
||||||
|
class Score:
|
||||||
|
def get(self, grade, score, n_of_assignments):
|
||||||
|
summary = (
|
||||||
|
f"Ранг: <b>{score:.2f}</b> {n_of_assignments} зад.\n"
|
||||||
|
+ f"Оценка: <b>{grade:.2f}</b>"
|
||||||
|
)
|
||||||
|
return summary
|
||||||
|
|
||||||
|
class Spreadsheets:
|
||||||
|
def __init__(self):
|
||||||
|
self.DEAD = "⚰ Сервис «таблицы» недоступен"
|
||||||
|
|
||||||
|
class Stickers:
|
||||||
|
def __init__(self):
|
||||||
|
# Воробей павлик с колокольчиком и надписью "позор"
|
||||||
|
self.POZOR = "CAACAgIAAxkBAAEm1RhhBZg-2D6NypUeTsnbToD8_LMFTAACuUcAAulVBRjfnofQTkeMnCAE"
|
||||||
|
# кот Сима в петле плачет и показывает палец вверх
|
||||||
|
self.ROPE = "CAACAgIAAxkBAAEm1SFhBZjLaZgFIkvyxfBmAplwUWQFwwACBS0AAulVBRiIaSQ42WLoyyAE"
|
||||||
|
# Акио смотрит на брокколи
|
||||||
|
self.BROCCOLI = "CAACAgIAAxkBAAEm1SVhBZkd24Dy2B_wYSCi6UrNuUjgwQACEUkAAulVBRiykDzrzywbmiAE"
|
||||||
|
# Ян показывает палец вниз
|
||||||
|
self.THUMB_DOWN = "CAACAgIAAxkBAAEm1SthBZlcos0_TbJo8W7mAcqGLNAh2wACJ5cAAp7OCwABDddMclD7Tf8gBA"
|
||||||
|
# Тигр закатывает глаза
|
||||||
|
self.EYES_UP = "CAACAgEAAxkBAAEm1TBhBZmIZTrwzhGSzC47rNFbewQe6QAC4gADOA6CEeTgk4YIcTeWIAQ"
|
||||||
|
# Нутри большими глазами и надписью "пипец"
|
||||||
|
self.PPTS = "CAACAgIAAxkBAAEm1TZhBZnNVwLXmrbZwvdGjxiSwefzDAACn0sAAulVBRhECmBPSGCXHSAE"
|
||||||
|
# Попугай Кент с недовольной мордахой
|
||||||
|
self.GRUMPY_KENT = "CAACAgIAAxkBAAEm1UBhBZn1gXmRfxaK_RqFTj1aFxLY-gACy0EAAulVBRhgQdlH2rd0uyAE"
|
||||||
|
# wojak
|
||||||
|
self.WOJAK = "CAACAgIAAxkBAAEm1UJhBZpfHNYVT_ZLrxUpyi8qRLWJGgACPAADB7lDCi2YrzAEyxJeIAQ"
|
||||||
|
# Лэтишный котик и надпись "ОЙ"
|
||||||
|
self.OOPS = "CAACAgIAAxkBAAEm1URhBZp2li7w2E1M6PirG7UnqLOc2QACSxEAAlTE4UoR1Olz8RANZCAE"
|
||||||
|
# Мем "оп ахах неловко вышло"
|
||||||
|
self.NELOVKO = "CAACAgIAAxkBAAEm1UZhBZqZ94SFa1e6d-Zy5I-6_SC_fQACgwgAAh8MqEiLbHl9nugoYSAE"
|
||||||
|
# Попугай смотрит в зеркало "посмотри что с тобой стало"
|
||||||
|
self.MIRROR = "CAACAgIAAxkBAAEm1VFhBZudma_FmFhybGgXikz3csc0BQAC2UEAAulVBRgkdje995ir7iAE"
|
||||||
|
# Попугай с сигаретой
|
||||||
|
self.CIG = "CAACAgIAAxkBAAEm1VZhBZv6p7GsHS6wcyt2mCXoHJCo-wAC0UEAAulVBRghLLSQAcU6rSAE"
|
||||||
|
# Попугай и песок
|
||||||
|
self.SAND = "CAACAgIAAxkBAAEm1VhhBZwbw83_UVS8A5Crsf3EVAABvWIAAshBAALpVQUYl51K7ci-ClIgBA"
|
||||||
|
# Акио падает в обморок
|
||||||
|
self.PANIC = "CAACAgIAAxkBAAEm1VxhBZxwqRUJmVXDtzmsPJUIIGJgegACB0kAAulVBRiMI-hzK3GcKiAE"
|
||||||
|
# Эникей надпись "умер"
|
||||||
|
self.DEAD = "CAACAgIAAxkBAAEm1aRhBaWXRb_tcI03O1onx0jW0bHmuAACejcAAulVBRi1oSUXeBATmiAE"
|
||||||
|
|
||||||
|
def bad(self):
|
||||||
|
"""Возвращает стикер с грустным смыслом."""
|
||||||
|
return random.choice(
|
||||||
|
[
|
||||||
|
self.POZOR,
|
||||||
|
self.ROPE,
|
||||||
|
self.BROCCOLI,
|
||||||
|
self.THUMB_DOWN,
|
||||||
|
self.EYES_UP,
|
||||||
|
self.PPTS,
|
||||||
|
self.GRUMPY_KENT,
|
||||||
|
self.WOJAK,
|
||||||
|
self.OOPS,
|
||||||
|
self.NELOVKO,
|
||||||
|
self.MIRROR,
|
||||||
|
self.CIG,
|
||||||
|
self.SAND,
|
||||||
|
self.PANIC,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.auth = self.Auth()
|
||||||
|
self.score = self.Score()
|
||||||
|
self.spreadsheets = self.Spreadsheets()
|
||||||
|
self.stickers = self.Stickers()
|
||||||
@ -1,19 +1,17 @@
|
|||||||
version: "3.9"
|
version: "3.9"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
spreadsheets:
|
spreadsheets:
|
||||||
container_name: "carolyn-spreadsheets"
|
container_name: "carolyn-spreadsheets"
|
||||||
build: ./SpreadsheetsService
|
build: ./SpreadsheetsService
|
||||||
ports:
|
ports:
|
||||||
- "5000:5000"
|
- "5000:5000"
|
||||||
environment:
|
environment:
|
||||||
SECRETKEY: "HELLO-WORLD-@21XX 15AF"
|
SECRETKEY: "HELLO-WORLD-@21XX 15AF"
|
||||||
volumes:
|
volumes:
|
||||||
- ./SpreadsheetsService:/app
|
- ./SpreadsheetsService:/app
|
||||||
telegrambot:
|
telegrambot:
|
||||||
container_name: "carolyn-telegrambot"
|
container_name: "carolyn-telegrambot"
|
||||||
build: ./TelegramBot
|
build: ./TelegramBot
|
||||||
environment:
|
volumes:
|
||||||
TOKEN: "1903048555:AAFK-d7prKvOvwwXo0IPzeRwJ1ZcuOT5ty8"
|
- ./TelegramBot:/app
|
||||||
volumes:
|
|
||||||
- ./TelegramBot:/app
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user