rendering of custom emojis, etc

This commit is contained in:
mst 2024-10-19 23:57:10 +03:00
parent b24e3ced2d
commit ee40d1b73f

View file

@ -1,13 +1,14 @@
from flask import url_for, request
from flask import url_for, request, jsonify
from markupsafe import Markup
from bleach.sanitizer import Cleaner
from datetime import datetime
from datetime import datetime, timezone
from pathlib import Path
from mistune import HTMLRenderer, escape
from PIL import Image
import mistune
import humanize
import mysql.connector
import re
import os
import random
import json
@ -40,6 +41,31 @@ def formatRelativeTime(date_str):
return humanize.naturaltime(time_difference)
def formatRelativeTime2(date_str):
date_format = "%Y-%m-%dT%H:%M:%SZ"
past_date = None
try:
if date_str:
past_date = datetime.strptime(date_str, date_format)
else:
pass
except ValueError:
pass
if past_date is None:
return ''
# raise ValueError("Date string does not match any supported format.")
if past_date.tzinfo is None:
past_date = past_date.replace(tzinfo=timezone.utc)
now = datetime.now(timezone.utc)
time_difference = now - past_date
return humanize.naturaltime(time_difference)
dbHost = os.environ.get("DB_HOST")
dbUser = os.environ.get("DB_USER")
dbPass = os.environ.get("DB_PASS")
@ -95,6 +121,10 @@ def readPlainFile(file, split=False):
else:
return []
def savePlainFile(file, contents):
with open(file, 'w') as file:
file.write(contents)
def getRandomWord():
items = readPlainFile(const.antiSpamFile, split=True)
return random.choice(items)
@ -117,18 +147,203 @@ def parse_inline_button(inline, m, state):
return m.end()
def render_inline_button(renderer, text):
return f"<button class='btn btn-outline-secondary'>{text}</button>"
return f"<button class='btn btn-secondary' type='button'>{text}</button>"
def button(md):
md.inline.register('inline_button', inlineBtnPattern, parse_inline_button, before='link')
if md.renderer and md.renderer.NAME == 'html':
md.renderer.register('inline_button', render_inline_button)
# Base directory where emoji packs are stored
EMOJI_BASE_PATH = Path.cwd() / 'static' / 'emojis'
emoji_cache = {}
def find_emoji_path(emoji_name):
head, sep, tail = emoji_name.partition('_')
if any(Path(EMOJI_BASE_PATH).glob(f'{head}.json')):
for json_file in Path(EMOJI_BASE_PATH).glob('*.json'):
print("\n[CatAsk/functions/find_emoji_path] Using JSON meta file\n")
pack_data = loadJSON(json_file)
emojis = pack_data.get('emojis', [])
for emoji in emojis:
if emoji['name'] == emoji_name:
rel_dir = json_file.stem
emoji_path = os.path.join('static/emojis', rel_dir, emoji['file_name'])
emoji_cache[emoji_name] = emoji_path
return emoji_path
else:
for address, dirs, files in os.walk(EMOJI_BASE_PATH):
print("\n[CatAsk/functions/find_emoji_path] Falling back to scanning directories\n")
if f"{emoji_name}.png" in files:
rel_dir = os.path.relpath(address, EMOJI_BASE_PATH)
emoji_path = os.path.join("static/emojis", rel_dir, f"{emoji_name}.png")
emoji_cache[emoji_name] = emoji_path
return emoji_path
return None
emojiPattern = r':(?P<emoji_name>[a-zA-Z0-9_]+):'
def parse_emoji(inline, m, state):
emoji_name = m.group("emoji_name")
state.append_token({"type": "emoji", "raw": emoji_name})
return m.end()
def render_emoji(renderer, emoji_name):
emoji_path = find_emoji_path(emoji_name)
if emoji_path:
absolute_emoji_path = url_for('static', filename=emoji_path.replace('static/', ''))
return f"<img src='{absolute_emoji_path}' alt=':{emoji_name}:' title=':{emoji_name}:' class='emoji' loading='lazy' width='28' height='28' />"
return f":{emoji_name}:"
def emoji(md):
md.inline.register('emoji', emojiPattern, parse_emoji, before='link')
if md.renderer and md.renderer.NAME == 'html':
md.renderer.register('emoji', render_emoji)
def listEmojis():
emojis = []
emoji_base_path = Path.cwd() / 'static' / 'emojis'
# Iterate over files that are directly in the emoji base path (not in subdirectories)
for file in emoji_base_path.iterdir():
# Only include files, not directories
if file.is_file() and file.suffix in {'.png', '.jpg', '.jpeg', '.webp'}:
# Get the relative path and name for the emoji
relative_path = os.path.relpath(file, emoji_base_path)
emojis.append({
'name': file.stem, # Get the file name without the extension
'image': os.path.join('static/emojis', relative_path), # Full relative path for image
'relative_path': relative_path
})
return emojis
"""
def listEmojiPacks():
emoji_packs = []
emoji_base_path = Path.cwd() / 'static' / 'emojis'
for pack_dir in emoji_base_path.iterdir():
if pack_dir.is_dir():
relative_path = os.path.relpath(pack_dir, emoji_base_path)
json_file_path = const.emojiPath / f"{pack_dir.name}.json"
if json_file_path.exists():
pack_data = loadJSON(json_file_path)
pack_info = {
'name': pack_data['name'],
'exportedAt': pack_data['exportedAt'],
# 'website': pack_data['website'],
'emojis': pack_data['emojis']
}
print(f"pack info: {pack_info}")
print(f"pack name: {pack_info['name']}")
return pack_info
else:
preview_image = None
for file in pack_dir.iterdir():
if file.suffix in {'.png', '.jpg', '.jpeg', '.webp'}:
preview_image = os.path.join('static/emojis', relative_path, file.name)
break
emoji_packs.append({
'name': pack_dir.name,
'preview_image': preview_image,
'relative_path': f'static/emojis/{relative_path}'
})
return emoji_packs
"""
def listEmojiPacks():
emoji_packs = []
emoji_base_path = const.emojiPath
# Iterate through all directories in the emoji base path
for pack_dir in emoji_base_path.iterdir():
if pack_dir.is_dir():
relative_path = os.path.relpath(pack_dir, emoji_base_path)
# Check if a meta.json file exists in the directory
meta_json_path = const.emojiPath / f"{pack_dir}.json"
if meta_json_path.exists():
print(f"[CatAsk/functions/listEmojiPacks] Using meta.json file ({meta_json_path})")
# Load data from the meta.json file
pack_data = loadJSON(meta_json_path)
emoji_packs.append({
'name': pack_data.get('name', pack_dir.name).capitalize(),
'exportedAt': pack_data.get('exportedAt', 'Unknown'),
'preview_image': pack_data.get('preview_image', ''),
'website': pack_data.get('website', ''),
'relative_path': f'static/emojis/{relative_path}',
'emojis': pack_data.get('emojis', [])
})
else:
print(f"[CatAsk/functions/listEmojiPacks] Falling back to directory scan ({pack_dir})")
# If no meta.json is found, fall back to directory scan
preview_image = None
# Find the first image in the directory for preview
for file in pack_dir.iterdir():
if file.suffix in {'.png', '.jpg', '.jpeg', '.webp'}:
preview_image = os.path.join('static/emojis', relative_path, file.name)
break
# Append pack info without meta.json
emoji_packs.append({
'name': pack_dir.name.capitalize(),
'preview_image': preview_image,
'relative_path': f'static/emojis/{relative_path}'
})
return emoji_packs
def processEmojis(meta_json_path):
emoji_metadata = loadJSON(meta_json_path)
emojis = emoji_metadata.get('emojis', [])
pack_name = emoji_metadata['emojis'][0]['emoji']['category'].capitalize()
exported_at = emoji_metadata.get('exportedAt', 'Unknown')
website = emoji_metadata.get('host', '')
preview_image = os.path.join('static/emojis', pack_name.lower(), emoji_metadata['emojis'][0]['fileName'])
relative_path = os.path.join('static/emojis', pack_name.lower())
processed_emojis = []
for emoji in emojis:
emoji_info = {
'name': emoji['emoji']['name'],
'file_name': emoji['fileName'],
}
processed_emojis.append(emoji_info)
print(f"[CatAsk/API/upload_emoji_pack] Processed emoji: {emoji_info['name']}\t(File: {emoji_info['file_name']})")
# Create the pack info structure
pack_info = {
'name': pack_name,
'exportedAt': exported_at,
'preview_image': preview_image,
'relative_path': relative_path,
'website': website,
'emojis': processed_emojis
}
# Save the combined pack info to <pack_name>.json
pack_json_name = const.emojiPath / f"{pack_name.lower()}.json"
saveJSON(pack_info, pack_json_name)
return processed_emojis
def renderMarkdown(text):
plugins = [
'strikethrough',
button
button,
emoji
]
allowed_tags = [
'p',
@ -143,11 +358,13 @@ def renderMarkdown(text):
'button',
'ol',
'li',
'hr'
'hr',
'img'
]
allowed_attrs = {
'a': 'href',
'button': 'class'
'button': 'class',
'img': ['src', 'width', 'height', 'alt', 'class', 'loading', 'title']
}
# hard_wrap=True means that newlines will be
# converted into <br> tags
@ -187,10 +404,21 @@ def generateMetadata(question=None, answer=None):
return metadata
allowedFileExtensions = {'png', 'jpg', 'jpeg', 'webp', 'bmp', 'jxl'}
allowedArchiveExtensions = {'zip', 'tar', 'gz', 'bz2', 'xz'}
def allowedFile(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowedFileExtensions
def allowedArchive(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowedArchiveExtensions
def stripArchExtension(filename):
if filename.endswith(('.tar.gz', '.tar.bz2', '.tar.xz')):
filename = filename.rsplit('.', 2)[0]
else:
filename = filename.rsplit('.', 1)[0]
return filename
def generateFavicon(file_name):
sizes = {
'apple-touch-icon.png': (180, 180),
@ -210,3 +438,16 @@ def generateFavicon(file_name):
resized_img = img.resize(size)
resized_img_absolute_path = const.faviconDir / filename
resized_img.save(resized_img_absolute_path)
# reserved for 1.7.0 or later
"""
def getUserIp():
if request.environ.get('HTTP_X_FORWARDED_FOR') is None:
return request.environ['REMOTE_ADDR']
else:
return request.environ['HTTP_X_FORWARDED_FOR']
def isIpBlacklisted(user_ip):
blacklist = readPlainFile(const.ipBlacklistFile, split=True)
return user_ip in blacklist
"""