315 lines
9.0 KiB
Python
315 lines
9.0 KiB
Python
import copy
|
|
import re
|
|
from typing import Optional
|
|
from bs4 import BeautifulSoup, Tag
|
|
from files.helpers.config.const import SITE_FULL_IMAGES
|
|
from files.helpers.marseyfx.tokenizer import GroupToken, NumberLiteralToken, StringLiteralToken, Token, Tokenizer
|
|
import files.helpers.marseyfx.parser as parser
|
|
|
|
modifier_whitelist = []
|
|
|
|
class Modifier:
|
|
name: str
|
|
args: list[Token]
|
|
|
|
def __init__(self, name: str, args: list[Token]):
|
|
self.name = name
|
|
self.args = args
|
|
|
|
def modifier(fn):
|
|
modifier_whitelist.append(fn.__name__)
|
|
|
|
def wrapper(*args, **kwargs):
|
|
slf = args[0]
|
|
ctx = ModifierContextFrame(fn.__name__)
|
|
slf.context_frames.insert(0, ctx)
|
|
slf.child = slf.container
|
|
slf.container = slf.child.wrap(slf.soup.new_tag('div', attrs={'class': f'marseyfx-modifier marseyfx-modifier-{ctx.name}'}))
|
|
slf.add_child_class(f'marseyfx-modifier-{ctx.name}-self')
|
|
res = fn(*args, **kwargs)
|
|
slf.context_frames.pop(0)
|
|
return res
|
|
return wrapper
|
|
|
|
def heavy(fn):
|
|
def wrapper(*args, **kwargs):
|
|
slf = args[0]
|
|
slf.heavy_count += 1
|
|
return fn(*args, **kwargs)
|
|
return wrapper
|
|
|
|
class ModifierContextFrame:
|
|
name: str
|
|
def __init__(self, name: str):
|
|
self.name = name
|
|
|
|
class Modified:
|
|
soup: BeautifulSoup
|
|
container: Tag
|
|
child: Tag
|
|
tokenizer: Tokenizer
|
|
heavy_count = 0
|
|
context_frames: list[ModifierContextFrame]
|
|
|
|
def __init__(self, el, tokenizer):
|
|
self.soup = BeautifulSoup()
|
|
self.container = el
|
|
self.tokenizer = tokenizer
|
|
self.context_frames = []
|
|
|
|
def ctx(self):
|
|
return self.context_frames[0] if len(self.context_frames) > 0 else None
|
|
|
|
def add_class(self, class_: str):
|
|
if not 'class' in self.container.attrs:
|
|
self.container.attrs['class'] = ''
|
|
else:
|
|
self.container.attrs['class'].append(' ' + class_)
|
|
|
|
def add_child_class(self, class_: str):
|
|
if not 'class' in self.child.attrs:
|
|
self.child.attrs['class'] = ''
|
|
else:
|
|
self.child.attrs['class'].append(' ' + class_)
|
|
|
|
def apply_modifiers(self, modifiers: list[Modifier]):
|
|
for modifier in modifiers:
|
|
if modifier.name in modifier_whitelist:
|
|
getattr(self, modifier.name)(*map(GroupToken.unwrap, modifier.args))
|
|
|
|
# Using this instead of throwing everything in a string and then parsing it helps
|
|
# mitigate the risk of XSS attacks
|
|
def image(self, name: str):
|
|
|
|
filename = name
|
|
|
|
if not '.' in filename:
|
|
filename += '.webp'
|
|
|
|
image = self.soup.new_tag(
|
|
'img',
|
|
loading='lazy',
|
|
src=f'{SITE_FULL_IMAGES}/i/{filename}',
|
|
attrs={'class': f'marseyfx-image marseyfx-image-{name}'}
|
|
)
|
|
|
|
container = self.soup.new_tag(
|
|
'div',
|
|
attrs={'class': f'marseyfx-image-container marseyfx-image-container-{name}'}
|
|
)
|
|
|
|
container.append(image)
|
|
return container
|
|
|
|
def underlay(self, underlay: Tag):
|
|
self.container.insert(0, underlay)
|
|
|
|
def overlay(self, overlay: Tag):
|
|
self.container.append(overlay)
|
|
|
|
def add_style(self, style: str):
|
|
if 'style' in self.container.attrs:
|
|
style = self.container.attrs['style'] + style
|
|
|
|
self.container.attrs['style'] = style
|
|
|
|
def meme_text(self, text: str, class_: Optional[str] = None):
|
|
attrs = {}
|
|
if class_ is not None:
|
|
attrs = {'class': f'marseyfx-memetext-{class_}'}
|
|
|
|
tag = self.soup.new_tag(
|
|
'span',
|
|
attrs=attrs
|
|
)
|
|
|
|
tag.string = text
|
|
|
|
self.overlay(tag)
|
|
|
|
def create_other(self, other: GroupToken = None):
|
|
wrapper = self.soup.new_tag('div', attrs={'class': f'marseyfx-modifier-{self.ctx().name}-other'})
|
|
|
|
if other is None:
|
|
return wrapper
|
|
|
|
other = other.wrap()
|
|
other_emoji = parser.parse_from_token(self.tokenizer, other)
|
|
|
|
if other_emoji is None:
|
|
return wrapper
|
|
|
|
other_emoji.is_primary = False
|
|
|
|
return other_emoji.create_el(self.tokenizer).wrap(wrapper)
|
|
|
|
@modifier
|
|
def pat(self):
|
|
self.overlay(self.image('hand'))
|
|
|
|
@modifier
|
|
def love(self):
|
|
self.overlay(self.image('love-foreground'))
|
|
self.underlay(self.image('love-background'))
|
|
|
|
@modifier
|
|
def talking(self):
|
|
self.overlay(self.image('talking'))
|
|
|
|
@modifier
|
|
def genocide(self):
|
|
pass
|
|
|
|
@modifier
|
|
def party(self):
|
|
pass
|
|
|
|
@modifier
|
|
def says(self, msg):
|
|
if not isinstance(msg, StringLiteralToken):
|
|
return
|
|
|
|
container = self.soup.new_tag(
|
|
'div',
|
|
attrs={'class': 'marseyfx-modifier-says-container'}
|
|
)
|
|
self.container.append(container)
|
|
|
|
container.append(self.soup.new_tag(
|
|
'div',
|
|
attrs={'class': 'marseyfx-modifier-says-nub'}
|
|
))
|
|
|
|
tag = self.soup.new_tag(
|
|
'span',
|
|
attrs={'class': 'marseyfx-modifier-says-text'}
|
|
)
|
|
tag.string = msg.value
|
|
container.append(tag)
|
|
|
|
@modifier
|
|
def fallover(self):
|
|
self.container = self.container.wrap(self.soup.new_tag(
|
|
'div',
|
|
attrs={'class': 'marseyfx-modifier-fallover-container'}
|
|
))
|
|
|
|
@modifier
|
|
def transform(self, transformstyle: StringLiteralToken):
|
|
if not re.fullmatch(r'[\w()\s%\.,]*', transformstyle.value):
|
|
print(f'Evil transform detected: {transformstyle.value}')
|
|
return
|
|
|
|
self.add_style(f'transform: {transformstyle.value};')
|
|
|
|
@heavy
|
|
@modifier
|
|
def enraged(self):
|
|
self.underlay(self.soup.new_tag(
|
|
'div',
|
|
attrs={'class': 'marseyfx-modifier-enraged-underlay'}
|
|
))
|
|
|
|
@modifier
|
|
def meme(self, toptext: Optional[StringLiteralToken] = None, bottomtext: Optional[StringLiteralToken] = None):
|
|
if isinstance(toptext, StringLiteralToken):
|
|
self.meme_text(toptext.value, 'toptext')
|
|
|
|
if isinstance(bottomtext, StringLiteralToken):
|
|
self.meme_text(bottomtext.value, 'bottomtext')
|
|
|
|
def bottomtext(self, text: StringLiteralToken):
|
|
if not isinstance(text, StringLiteralToken):
|
|
return
|
|
|
|
tag = self.soup.new_tag(
|
|
'span',
|
|
attrs={'class': 'marseyfx-modifier-bottomtext-text'}
|
|
)
|
|
|
|
tag.string = text.value
|
|
|
|
self.overlay(tag)
|
|
|
|
@modifier
|
|
def spin(self, speed=None):
|
|
if not isinstance(speed, NumberLiteralToken):
|
|
return
|
|
|
|
self.add_style(f'animation-duration: {1/speed.value}s;')
|
|
|
|
@modifier
|
|
def triumphs(self, other: GroupToken):
|
|
other = other.wrap()
|
|
other_emoji = parser.parse_from_token(self.tokenizer, other)
|
|
print(f'Other emoji: {other_emoji} / Token: {other}')
|
|
|
|
if other_emoji is None:
|
|
return
|
|
|
|
self.add_child_class('marseyfx-modifier-triumphs-self')
|
|
|
|
other_emoji.is_primary = False
|
|
|
|
other = other_emoji.create_el(self.tokenizer).wrap(
|
|
self.soup.new_tag('div', attrs={'class': 'marseyfx-modifier-triumphs-other'})
|
|
)
|
|
self.underlay(other)
|
|
|
|
@modifier
|
|
def nested(self, inside: GroupToken):
|
|
inside = inside.wrap()
|
|
inside_emoji = parser.parse_from_token(self.tokenizer, inside)
|
|
|
|
if inside_emoji is None:
|
|
return
|
|
|
|
inside_emoji.is_primary = False
|
|
|
|
inside = inside_emoji.create_el(self.tokenizer).wrap(
|
|
self.soup.new_tag('div', attrs={'class': 'marseyfx-modifier-nested-other'})
|
|
)
|
|
|
|
self.underlay(inside)
|
|
|
|
self.add_child_class('marseyfx-modifier-nested-side')
|
|
child = self.child
|
|
self.child = child.wrap(self.soup.new_tag('div', attrs={'class': 'marseyfx-modifier-nested-outer-container'}))
|
|
other_side = copy.copy(child)
|
|
self.child.append(other_side)
|
|
|
|
@modifier
|
|
def morph(self, other: GroupToken):
|
|
self.add_child_class('marseyfx-modifier-morph-self')
|
|
|
|
other = other.wrap()
|
|
other_emoji = parser.parse_from_token(self.tokenizer, other)
|
|
|
|
if other_emoji is None:
|
|
return
|
|
|
|
other_emoji.is_primary = False
|
|
other = other_emoji.create_el(self.tokenizer).wrap(
|
|
self.soup.new_tag('div', attrs={'class': 'marseyfx-modifier-morph-other'})
|
|
)
|
|
|
|
self.container.append(other)
|
|
|
|
@heavy
|
|
@modifier
|
|
def bulge(self, strength: NumberLiteralToken = None):
|
|
self.child = self.child.wrap(self.soup.new_tag('svg', attrs={'class': 'marseyfx-modifier-bulge-container'}))
|
|
|
|
@modifier
|
|
def prohibition(self):
|
|
self.overlay(self.image('prohibition.svg'))
|
|
|
|
@modifier
|
|
def snipe(self):
|
|
self.overlay(self.image('scope.svg'))
|
|
self.add_child_class('marseyfx-modifier-snipe-target')
|
|
|
|
@modifier
|
|
def fucks(self, other: GroupToken):
|
|
other = self.create_other(other)
|
|
self.container.append(other) |