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
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,3 +5,6 @@ SpreadsheetsService/tests/__pycache__
|
||||
SpreadsheetsService/manager/__pycache__
|
||||
SpreadsheetsService/manager/client_secret.json
|
||||
SpreadsheetsService/manager/token.json
|
||||
TelegramBot/.secrets
|
||||
SpreadsheetsService/.secrets
|
||||
TelegramBot/messages/__pycache__
|
||||
|
||||
@ -1,9 +1,34 @@
|
||||
import flask
|
||||
import time
|
||||
import models
|
||||
from functools import wraps
|
||||
from manager import *
|
||||
import flask
|
||||
import logging
|
||||
import models
|
||||
import time
|
||||
|
||||
LOG = logging.getLogger(__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("/")
|
||||
@ -12,13 +37,32 @@ def hello_world():
|
||||
|
||||
|
||||
@app.route("/auth")
|
||||
@availability
|
||||
def auth():
|
||||
# TODO: return json text instead of text/html.
|
||||
token = flask.request.args.get("token")
|
||||
tg_id = flask.request.args.get("tg_id")
|
||||
manager = Manager()
|
||||
student = manager.get_student_by_name("Дзюба")
|
||||
return str(student)
|
||||
student = app.db.get_student_by_name("Дзюба")
|
||||
student = Student(1, "Фарид Михайлов", 5374)
|
||||
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__":
|
||||
|
||||
@ -20,8 +20,8 @@ ASSIGNMENT_SUBJECT = 3
|
||||
ASSIGNMENT_WEIGHTS = 4
|
||||
ASSIGNMENT_RANGES = 5
|
||||
ASSIGNMENT_ALLOWS = 6
|
||||
TOKEN_FILE = pathlib.Path() / "manager" / "token.json"
|
||||
CLIENT_SECRET_FILE = pathlib.Path() / "manager" / "client_secret.json"
|
||||
TOKEN_FILE = pathlib.Path() / ".secrets" / "token.json"
|
||||
CLIENT_SECRET_FILE = pathlib.Path() / ".secrets" / "client_secret.json"
|
||||
|
||||
|
||||
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.weight = weight
|
||||
self.values = values
|
||||
self.points = points
|
||||
dict.__init__(
|
||||
self,
|
||||
name=self.name,
|
||||
weight=self.weight,
|
||||
points=self.points,
|
||||
total=self.total,
|
||||
)
|
||||
|
||||
@property
|
||||
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
|
||||
import json
|
||||
|
||||
|
||||
class Student:
|
||||
class Student(dict):
|
||||
"""Позволяет выполнять операции над студентами."""
|
||||
|
||||
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.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):
|
||||
self.assignments.append(assignment)
|
||||
|
||||
@ -24,13 +32,3 @@ class Student:
|
||||
def score(self):
|
||||
"""Количество баллов у студента."""
|
||||
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
|
||||
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
|
||||
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
|
||||
# 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
|
||||
|
||||
@ -1,58 +1,97 @@
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import time
|
||||
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(
|
||||
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)
|
||||
dispatcher = updater.dispatcher
|
||||
|
||||
# ================================================================ 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
|
||||
|
||||
if p is None:
|
||||
context.bot.send_message(
|
||||
chat_id=update.effective_chat.id,
|
||||
text="Нет доступа к боту",
|
||||
)
|
||||
update.message.reply_markdown_v2(text=MESSAGES.auth.NO_AUTH)
|
||||
return
|
||||
|
||||
data = requests.get(
|
||||
"http://127.0.0.1:5000/auth",
|
||||
params={"token": p, "tg_id": update.effective_chat.id},
|
||||
).json()
|
||||
update.message.reply_text(text=MESSAGES.auth.START)
|
||||
|
||||
LOG.info(f"Authenticanting {user_id}")
|
||||
|
||||
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(
|
||||
chat_id=update.effective_chat.id,
|
||||
text=f"Привет, {data}",
|
||||
text=MESSAGES.auth.hello(data),
|
||||
)
|
||||
|
||||
|
||||
# Ответ на не командное сообщение (отвечает тем же сообщением)
|
||||
def echo(update, context):
|
||||
def grades(update: Update, context: CallbackContext):
|
||||
"""Команда: Получить оценки студента."""
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# ==================================================================== HANDLERS
|
||||
|
||||
start_handler = CommandHandler("start", start)
|
||||
grades_handler = CommandHandler("grades", grades)
|
||||
|
||||
echo_handler = MessageHandler(Filters.text & (~Filters.command), echo)
|
||||
|
||||
dispatcher.add_handler(start_handler)
|
||||
dispatcher.add_handler(grades_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"
|
||||
|
||||
services:
|
||||
spreadsheets:
|
||||
container_name: "carolyn-spreadsheets"
|
||||
build: ./SpreadsheetsService
|
||||
ports:
|
||||
- "5000:5000"
|
||||
environment:
|
||||
SECRETKEY: "HELLO-WORLD-@21XX 15AF"
|
||||
volumes:
|
||||
- ./SpreadsheetsService:/app
|
||||
telegrambot:
|
||||
container_name: "carolyn-telegrambot"
|
||||
build: ./TelegramBot
|
||||
environment:
|
||||
TOKEN: "1903048555:AAFK-d7prKvOvwwXo0IPzeRwJ1ZcuOT5ty8"
|
||||
volumes:
|
||||
- ./TelegramBot:/app
|
||||
spreadsheets:
|
||||
container_name: "carolyn-spreadsheets"
|
||||
build: ./SpreadsheetsService
|
||||
ports:
|
||||
- "5000:5000"
|
||||
environment:
|
||||
SECRETKEY: "HELLO-WORLD-@21XX 15AF"
|
||||
volumes:
|
||||
- ./SpreadsheetsService:/app
|
||||
telegrambot:
|
||||
container_name: "carolyn-telegrambot"
|
||||
build: ./TelegramBot
|
||||
volumes:
|
||||
- ./TelegramBot:/app
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user