mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-18 15:14:18 +02:00
Commited backup
This commit is contained in:
533
hikariatama/ftg/flash_cards.py
Normal file
533
hikariatama/ftg/flash_cards.py
Normal file
@@ -0,0 +1,533 @@
|
||||
# █ █ ▀ █▄▀ ▄▀█ █▀█ ▀
|
||||
# █▀█ █ █ █ █▀█ █▀▄ █
|
||||
# © Copyright 2022
|
||||
# https://t.me/hikariatama
|
||||
#
|
||||
# 🔒 Licensed under the GNU AGPLv3
|
||||
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
|
||||
|
||||
# scope: hikka_min 1.2.10
|
||||
|
||||
# meta pic: https://img.icons8.com/stickers/500/000000/cards.png
|
||||
# meta banner: https://mods.hikariatama.ru/badges/flash_cards.jpg
|
||||
# meta developer: @hikarimods
|
||||
|
||||
import asyncio
|
||||
import io
|
||||
import json
|
||||
import re
|
||||
from random import randint
|
||||
|
||||
from telethon.tl.types import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
TEMPLATE = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title>Testing ^title_deck_name^</title>
|
||||
<style type="text/css">
|
||||
@import url('https://fonts.googleapis.com/css2?family=Exo+2&display=swap');
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
transition: all .3s ease;
|
||||
}
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: #121212;
|
||||
color: #fff;
|
||||
font-family: 'Exo 2';
|
||||
}
|
||||
|
||||
.cards, .testing {
|
||||
width: 94%;
|
||||
margin-left: 3%;
|
||||
min-height: 30vh;
|
||||
background: #121212;
|
||||
border-radius: 10px;
|
||||
box-shadow: inset 9.31px 9.31px 19px #0B0B0B, inset -9.31px -9.31px 19px #161616;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 94%;
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
margin-left: 3%;
|
||||
background: #121212;
|
||||
border-radius: 10px;
|
||||
margin-top: 10px;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
box-shadow: inset 9.31px 9.31px 19px #0B0B0B, inset -9.31px -9.31px 19px #161616;
|
||||
}
|
||||
|
||||
.back {
|
||||
width: 94%;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 10px 0;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
margin-left: 3%;
|
||||
border-radius: 5px;
|
||||
margin-top: 10px;
|
||||
|
||||
background: linear-gradient(145deg , #d9d9d9, #C8C8C8);
|
||||
box-shadow: rgb(117 117 117) 0px 1px 20px 0px inset;
|
||||
}
|
||||
|
||||
.back::placeholder {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 20px;
|
||||
text-align: center;
|
||||
font-size: 25px;
|
||||
padding: 0;
|
||||
margin-left: 5%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 736px) {
|
||||
body {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 25px;
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.testing {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.front {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>^deck_name^</h1>
|
||||
<div class="cards">
|
||||
Loading...
|
||||
</div>
|
||||
<div class="testing">
|
||||
<h1 class="front"></h1>
|
||||
<input class="back" type="text" placeholder="Ответ">
|
||||
</div>
|
||||
<div class="begin button">Start test</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
cards = JSON.parse("^json_cards^");
|
||||
var cards_html = "";
|
||||
for (var front in cards) {
|
||||
cards_html += front + " - " + cards[front] + "<br>\\n";
|
||||
}
|
||||
|
||||
document.querySelector('.cards').innerHTML = cards_html;
|
||||
|
||||
function getRndInteger(min, max) {return Math.floor(Math.random() * (max - min) ) + min;}
|
||||
|
||||
function render_next_one() {
|
||||
var keys = Object.keys(cards);
|
||||
var front = keys[getRndInteger(0,keys.length)];
|
||||
var back = cards[front];
|
||||
document.querySelector('.front').innerHTML = front;
|
||||
document.querySelector('.back').setAttribute('answer', back);
|
||||
}
|
||||
|
||||
function check_answer() {
|
||||
var el = document.querySelector('.back');
|
||||
if(el.getAttribute('answer') == el.value){
|
||||
document.querySelector('.front').innerHTML = "Yup!";
|
||||
document.querySelector('.testing').style.background = '#26681e';
|
||||
} else {
|
||||
document.querySelector('.testing').style.background = '#611a1a';
|
||||
document.querySelector('.front').innerHTML = "Nope. Right answer: " + el.getAttribute('answer');
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
document.querySelector('.testing').style.background = '#121212';
|
||||
render_next_one();
|
||||
}, 1000);
|
||||
el.value = "";
|
||||
}
|
||||
|
||||
document.querySelector('.begin').onclick = function() {
|
||||
document.querySelector('.cards').style.opacity = 0;
|
||||
setTimeout(() => {document.querySelector('.cards').style.display = 'none'; document.querySelector('.testing').style.display = 'block';}, 300);
|
||||
this.classList.remove('begin');
|
||||
this.classList.add('next');
|
||||
this.innerHTML = "Check answer";
|
||||
render_next_one();
|
||||
document.querySelector('.next').onclick = function() {
|
||||
check_answer();
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector('.back').onkeyup = function(e) {
|
||||
if (e.key === 'Enter' || e.keyCode === 13) {
|
||||
check_answer();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
@loader.tds
|
||||
class FlashCardsMod(loader.Module):
|
||||
"""Flash cards for learning"""
|
||||
|
||||
strings = {
|
||||
"name": "FlashCards",
|
||||
"deck_not_found": "<b>🚫 Deck not found</b",
|
||||
"no_deck_name": "<b>You haven't provided deck name</b>",
|
||||
"deck_created": "#Deck <code>#{}</code> <b>{}</b> successfully created!",
|
||||
"deck_removed": "<b>🚫 Deck removed</b>",
|
||||
"save_deck_no_reply": (
|
||||
"<b>🚫 This command should be used in reply to message with deck items.</b>"
|
||||
),
|
||||
"deck_saved": "✅ <b>Deck saved!</b>",
|
||||
"generating_page": "<b>⚙️ Generating page, please wait ...</b>",
|
||||
"offline_testing": "<b>📖 Offline testing, based on deck {}</b>",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"deck_not_found": "<b>🚫 Дека не найдена</b",
|
||||
"no_deck_name": "<b>Ты не указал имя деки</b>",
|
||||
"deck_created": "#Deck <code>#{}</code> <b>{}</b> успешно создана!",
|
||||
"deck_removed": "<b>🚫 Дека удалена</b>",
|
||||
"save_deck_no_reply": (
|
||||
"<b>🚫 Эта команда должна выполняться в ответ на измененную деку.</b>"
|
||||
),
|
||||
"deck_saved": "✅ <b>Дека сохранена!</b>",
|
||||
"generating_page": "<b>⚙️ Генерирую страницу, секунду...</b>",
|
||||
"offline_testing": "<b>📖 Оффлайн тестирование на основе деки {}</b>",
|
||||
"_cmd_doc_newdeck": "<name> - Создать новую деку",
|
||||
"_cmd_doc_decks": "Показать деки",
|
||||
"_cmd_doc_deletedeck": "<id> - Удалить деку",
|
||||
"_cmd_doc_listdeck": "<id> - Показать деку",
|
||||
"_cmd_doc_editdeck": "<id> - Редактировать деку",
|
||||
"_cmd_doc_savedeck": "<reply> - Сохранить деку",
|
||||
"_cmd_doc_htmldeck": "<id> - Сгенерировать оффлайн-тестирование по деке",
|
||||
"_cls_doc": "Флеш-карты для обучения",
|
||||
}
|
||||
|
||||
strings_de = {
|
||||
"deck_not_found": "<b>🚫 Deck nicht gefunden</b",
|
||||
"no_deck_name": "<b>Du hast keinen Decknamen angegeben</b>",
|
||||
"deck_created": "#Deck <code>#{}</code> <b>{}</b> erfolgreich erstellt!",
|
||||
"deck_removed": "<b>🚫 Deck entfernt</b>",
|
||||
"save_deck_no_reply": (
|
||||
"<b>🚫 Dieser Befehl sollte in Antwort auf eine Nachricht mit"
|
||||
" Deck-Elementen"
|
||||
" verwendet werden.</b>"
|
||||
),
|
||||
"deck_saved": "✅ <b>Deck gespeichert!</b>",
|
||||
"generating_page": "<b>⚙️ Seite wird generiert, bitte warten ...</b>",
|
||||
"offline_testing": "<b>📖 Offline-Testing basierend auf dem Deck {}</b>",
|
||||
"_cmd_doc_newdeck": "<name> - Erstelle ein neues Deck",
|
||||
"_cmd_doc_decks": "Zeige Decks",
|
||||
"_cmd_doc_deletedeck": "<id> - Deck löschen",
|
||||
"_cmd_doc_listdeck": "<id> - Deck anzeigen",
|
||||
"_cmd_doc_editdeck": "<id> - Deck bearbeiten",
|
||||
"_cmd_doc_savedeck": "<reply> - Deck speichern",
|
||||
"_cmd_doc_htmldeck": "<id> - Offline-Testing basierend auf dem Deck",
|
||||
"_cls_doc": "Flash-Karten für das Lernen",
|
||||
}
|
||||
|
||||
strings_tr = {
|
||||
"deck_not_found": "<b>🚫 Deck bulunamadı</b",
|
||||
"no_deck_name": "<b>Deck adı belirtmedin</b>",
|
||||
"deck_created": "#Deck <code>#{}</code> <b>{}</b> başarıyla oluşturuldu!",
|
||||
"deck_removed": "<b>🚫 Deck kaldırıldı</b>",
|
||||
"save_deck_no_reply": "<b>🚫 Bu komut, deck öğeleriyle yanıtlanmalıdır.</b>",
|
||||
"deck_saved": "✅ <b>Deck kaydedildi!</b>",
|
||||
"generating_page": "<b>⚙️ Sayfa oluşturuluyor, lütfen bekleyin ...</b>",
|
||||
"offline_testing": "<b>📖 {} deckine dayalı çevrimdışı test</b>",
|
||||
"_cmd_doc_newdeck": "<isim> - Yeni bir deck oluştur",
|
||||
"_cmd_doc_decks": "Deckleri göster",
|
||||
"_cmd_doc_deletedeck": "<id> - Deck sil",
|
||||
"_cmd_doc_listdeck": "<id> - Decki göster",
|
||||
"_cmd_doc_editdeck": "<id> - Decki düzenle",
|
||||
"_cmd_doc_savedeck": "<reply> - Decki kaydet",
|
||||
"_cmd_doc_htmldeck": "<id> - Decke dayalı çevrimdışı test oluştur",
|
||||
"_cls_doc": "Öğrenmek için flaş kartlar",
|
||||
}
|
||||
|
||||
strings_hi = {
|
||||
"deck_not_found": "<b>🚫 डेक नहीं मिला</b",
|
||||
"no_deck_name": "<b>आपने डेक का नाम नहीं दिया</b>",
|
||||
"deck_created": "#Deck <code>#{}</code> <b>{}</b> सफलतापूर्वक बनाया गया!",
|
||||
"deck_removed": "<b>🚫 डेक हटा दिया गया</b>",
|
||||
"save_deck_no_reply": (
|
||||
"<b>🚫 यह कमांड डेक आइटम के साथ उत्तर देने के लिए उपयोग किया जाना चाहिए।</b>"
|
||||
),
|
||||
"deck_saved": "✅ <b>डेक सहेज लिया गया!</b>",
|
||||
"generating_page": "<b>⚙️ पेज उत्पन्न किया जा रहा है, कृपया प्रतीक्षा करें ...</b>",
|
||||
"offline_testing": "<b>📖 {} डेक पर आधारित ऑफ़लाइन परीक्षण</b>",
|
||||
"_cmd_doc_newdeck": "<नाम> - एक नया डेक बनाएं",
|
||||
"_cmd_doc_decks": "डेक दिखाएं",
|
||||
"_cmd_doc_deletedeck": "<आईडी> - डेक हटाएं",
|
||||
"_cmd_doc_listdeck": "<आईडी> - डेक दिखाएं",
|
||||
"_cmd_doc_editdeck": "<आईडी> - डेक संपादित करें",
|
||||
"_cmd_doc_savedeck": "<उत्तर> - डेक सहेजें",
|
||||
"_cmd_doc_htmldeck": "<आईडी> - डेक पर आधारित ऑफ़लाइन परीक्षण बनाएं",
|
||||
"_cls_doc": "फ्लैश कार्ड अध्ययन के लिए",
|
||||
}
|
||||
|
||||
strings_uz = {
|
||||
"deck_not_found": "<b>🚫 Deck topilmadi</b",
|
||||
"no_deck_name": "<b>Deck nomini kiritmadingiz</b>",
|
||||
"deck_created": "#Deck <code>#{}</code> <b>{}</b> muvaffaqiyatli yaratildi!",
|
||||
"deck_removed": "<b>🚫 Deck o'chirildi</b>",
|
||||
"save_deck_no_reply": (
|
||||
"<b>🚫 Bu buyruq deck elementlari bilan javob berilishi kerak.</b>"
|
||||
),
|
||||
"deck_saved": "✅ <b>Deck saqlandi!</b>",
|
||||
"generating_page": "<b>⚙️ Sahifa yaratilmoqda, iltimos kuting ...</b>",
|
||||
"offline_testing": "<b>📖 {} deckiga asoslangan oflayn test</b>",
|
||||
"_cmd_doc_newdeck": "<nom> - Yangi deck yaratish",
|
||||
"_cmd_doc_decks": "Decklarni ko'rsatish",
|
||||
"_cmd_doc_deletedeck": "<id> - Deckni o'chirish",
|
||||
"_cmd_doc_listdeck": "<id> - Deckni ko'rsatish",
|
||||
"_cmd_doc_editdeck": "<id> - Deckni tahrirlash",
|
||||
"_cmd_doc_savedeck": "<javob> - Deckni saqlash",
|
||||
"_cmd_doc_htmldeck": "<id> - Deckiga asoslangan oflayn test yaratish",
|
||||
"_cls_doc": "O'rganish uchun flash kartalar",
|
||||
}
|
||||
|
||||
async def client_ready(self):
|
||||
self.decks = self.get("decks", {})
|
||||
|
||||
def get_deck_from_reply(self, reply, limit=None):
|
||||
if reply is None:
|
||||
return False
|
||||
|
||||
if "#Deck" in reply.text:
|
||||
counter = 1
|
||||
|
||||
for line in reply.text.split("\n"):
|
||||
line = line.split()
|
||||
if len(line) > 1:
|
||||
deck = (
|
||||
line[1]
|
||||
.replace("<code>", "")
|
||||
.replace("</code>", "")
|
||||
.replace("#", "")
|
||||
)
|
||||
try:
|
||||
int(deck)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if deck in self.decks:
|
||||
if (
|
||||
limit is None
|
||||
or not limit
|
||||
and "#Decks" not in reply.text
|
||||
or counter == limit
|
||||
):
|
||||
return deck
|
||||
else:
|
||||
counter += 1
|
||||
|
||||
return False
|
||||
|
||||
async def get_from_message(self, message: Message):
|
||||
args = utils.get_args_raw(message)
|
||||
try:
|
||||
args = args.split()[0]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if args.startswith("#"):
|
||||
args = args[1:]
|
||||
|
||||
try:
|
||||
int_args = int(args)
|
||||
except Exception:
|
||||
args = False
|
||||
int_args = False
|
||||
|
||||
if int(int_args) < 1000:
|
||||
args = self.get_deck_from_reply(await message.get_reply_message(), int_args)
|
||||
|
||||
if not args or args not in self.decks:
|
||||
await utils.answer(message, self.strings("deck_not_found"))
|
||||
await asyncio.sleep(2)
|
||||
await message.delete()
|
||||
return False
|
||||
|
||||
return args
|
||||
|
||||
async def newdeckcmd(self, message: Message):
|
||||
"""<name> - New deck of cards"""
|
||||
|
||||
args = utils.get_args_raw(message)
|
||||
if args == "":
|
||||
await utils.answer(message, self.strings("no_deck_name"))
|
||||
await asyncio.sleep(2)
|
||||
await message.delete()
|
||||
return
|
||||
|
||||
random_id = str(randint(10000, 99999))
|
||||
|
||||
self.decks[random_id] = {"name": args, "cards": [("sample", "sample")]}
|
||||
|
||||
self.set("decks", self.decks)
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings("deck_created").format(random_id, args),
|
||||
)
|
||||
|
||||
async def deckscmd(self, message: Message):
|
||||
"""List decks"""
|
||||
res = "<b>#Decks:</b>\n\n"
|
||||
for counter, (item_id, item) in enumerate(self.decks.items(), start=1):
|
||||
if len(item["cards"]) == 0:
|
||||
items = "No cards"
|
||||
else:
|
||||
items = "".join(
|
||||
f"\n {front} - {back}" for front, back in item["cards"][:2]
|
||||
)
|
||||
if len(item["cards"]) > 2:
|
||||
items += "\n <...>"
|
||||
res += (
|
||||
f"🔸<b>{counter}.</b> <code>{item_id}</code> |"
|
||||
f" {item['name']}<code>{items}</code>\n\n"
|
||||
)
|
||||
await utils.answer(message, res)
|
||||
|
||||
async def deletedeckcmd(self, message: Message):
|
||||
"""<id> - Delete deck"""
|
||||
deck_id = await self.get_from_message(message)
|
||||
if not deck_id:
|
||||
return
|
||||
|
||||
del self.decks[deck_id]
|
||||
self.set("decks", self.decks)
|
||||
reply = await message.get_reply_message()
|
||||
if reply:
|
||||
if "#Decks" in reply.text:
|
||||
await self.deckscmd(reply)
|
||||
elif "#Deck" in reply.text:
|
||||
await reply.edit(reply.text + "\n" + self.strings("deck_removed"))
|
||||
await utils.answer(message, self.strings("deck_removed"))
|
||||
|
||||
async def listdeckcmd(self, message: Message):
|
||||
"""<id> - List deck items"""
|
||||
deck_id = await self.get_from_message(message)
|
||||
if not deck_id:
|
||||
return
|
||||
|
||||
deck = self.decks[deck_id]
|
||||
res = f"📋#Deck #{deck_id} <b>{deck['name']}</b>:\n➖➖➖➖➖➖➖➖➖➖"
|
||||
for i, (front, back) in enumerate(deck["cards"], start=1):
|
||||
res += f"\n<b>{i}. {front} - {back}</b>"
|
||||
await utils.answer(message, res)
|
||||
|
||||
async def editdeckcmd(self, message: Message):
|
||||
"""<id> - Edit deck items"""
|
||||
deck_id = await self.get_from_message(message)
|
||||
if not deck_id:
|
||||
return
|
||||
|
||||
deck = self.decks[deck_id]
|
||||
res = f"📋#Deck #{deck_id} \"<b>{deck['name']}</b>\":\n➖➖➖➖➖➖➖➖➖➖"
|
||||
for front, back in deck["cards"]:
|
||||
res += f"\n<b>{front} - {back}</b>"
|
||||
|
||||
res += (
|
||||
"\n➖➖➖➖➖➖➖➖➖➖\nEdit and type <code>.savedeck</code> in reply to"
|
||||
" this"
|
||||
" message\n<i>Note: you can edit title and cards, but other message should"
|
||||
" stay untouched, otherwise it can be saved incorrectly!</i> #Editing"
|
||||
)
|
||||
|
||||
await utils.answer(message, res)
|
||||
|
||||
def remove_html(self, text):
|
||||
return re.sub(r"<.*?>", "", text)
|
||||
|
||||
async def savedeckcmd(self, message: Message):
|
||||
"""<reply> - Save deck. Do not use if you don't know what is this"""
|
||||
reply = await message.get_reply_message()
|
||||
if not reply or "#Editing" not in reply.text:
|
||||
await utils.answer(message, self.strings("save_deck_no_reply"))
|
||||
await asyncio.sleep(2)
|
||||
await message.delete()
|
||||
return False
|
||||
|
||||
deck_id = await self.get_from_message(message)
|
||||
if not deck_id:
|
||||
return
|
||||
|
||||
deck = self.decks[deck_id]
|
||||
self.decks[deck_id]["cards"] = []
|
||||
items = reply.text.split("\n")
|
||||
for item in items[2:-3]:
|
||||
self.decks[deck_id]["cards"].append(
|
||||
(
|
||||
self.remove_html(item.split(" - ")[0]),
|
||||
self.remove_html(item.split(" - ")[1]),
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
self.decks[deck_id]["name"] = self.remove_html(
|
||||
re.search(r""(.+?)"", items[0]).group(1)
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.set("decks", self.decks)
|
||||
|
||||
res = f"📋#Deck #{deck_id} <b>{deck['name']}</b>:\n➖➖➖➖➖➖➖➖➖➖"
|
||||
for i, (front, back) in enumerate(deck["cards"], start=1):
|
||||
res += f"\n<b>{i}. {front} - {back}</b>"
|
||||
res += "\n➖➖➖➖➖➖➖➖➖➖\n" + self.strings("deck_saved")
|
||||
|
||||
await utils.answer(reply, res)
|
||||
await message.delete()
|
||||
|
||||
async def htmldeckcmd(self, message: Message):
|
||||
"""<id> - Generates the page with specified deck"""
|
||||
deck_id = await self.get_from_message(message)
|
||||
if not deck_id:
|
||||
return
|
||||
|
||||
deck = self.decks[deck_id]
|
||||
await utils.answer(message, self.strings("generating_page"))
|
||||
deck_name = deck["name"]
|
||||
loc_cards = deck["cards"].copy()
|
||||
cards = dict(loc_cards)
|
||||
json_cards = json.dumps(cards).replace('"', '\\"')
|
||||
txt = io.BytesIO(
|
||||
TEMPLATE.replace("^title_deck_name^", deck_name)
|
||||
.replace("^deck_name^", deck_name)
|
||||
.replace("^json_cards^", json_cards)
|
||||
.encode("utf-8")
|
||||
)
|
||||
txt.name = "testing.html"
|
||||
await message.delete()
|
||||
await message.client.send_file(
|
||||
message.to_id,
|
||||
txt,
|
||||
caption=self.strings("offline_testing").format(deck_name),
|
||||
)
|
||||
Reference in New Issue
Block a user