import re from string import Template from typing import Callable, NamedTuple, Optional import warnings from urlmatch import urlmatch import modules.inline as inline import modules.metadata as metadata from modules.metadata import Metadata, Tag, Tag_boolean, Tag_string from modules.patterns import isIncludePattern, isMatchPattern, regexFromIncludePattern from modules.utilities import compose2, stripIndentation, strs REGEX_URL: re.Pattern = re.compile(r"^https?://") directive_name : str = "name" directive_version : str = "version" directive_run_at : str = "run-at" directive_match : str = "match" directive_include : str = "include" directive_exclude : str = "exclude" directive_noframes : str = "noframes" directive_downloadURL : str = "downloadURL" document_end : str = "document-end" document_start : str = "document-start" document_idle : str = "document-idle" tag_name: Tag_string = Tag_string( name = directive_name, unique = True, default = None, required = True, predicate = lambda val: val != "", ) tag_run_at: Tag_string = Tag_string( name = directive_run_at, unique = True, default = document_end, required = False, predicate = lambda val: val in [ document_end, document_start, document_idle ], ) tag_match: Tag_string = Tag_string( name = directive_match, unique = False, default = None, required = False, predicate = isMatchPattern, ) tag_include: Tag_string = Tag_string( name = directive_include, unique = False, default = None, required = False, predicate = isIncludePattern, ) tag_exclude: Tag_string = Tag_string( name = directive_exclude, unique = False, default = None, required = False, predicate = isIncludePattern, ) tag_noframes: Tag_boolean = Tag_boolean( name = directive_noframes, unique = True, default = False, required = False, predicate = None, ) tag_version: Tag_string = Tag_string( name = directive_version, unique = True, default = None, required = False, predicate = None, ) tag_downloadURL: Tag_string = Tag_string( name = directive_downloadURL, unique = True, default = None, required = False, predicate = lambda val: REGEX_URL.match(val) is not None, ) METADATA_TAGS: list[Tag] = [ tag_name, tag_run_at, tag_match, tag_noframes, tag_include, tag_exclude, tag_version, tag_downloadURL, ] validateMetadata: Callable[[Metadata], Metadata] = metadata.validator(METADATA_TAGS) STRING_WARNING_INVALID_REGEX: Template = Template(f"""{metadata.tag(directive_include)}/{metadata.tag(directive_exclude)} patterns starting and ending with `/` are interpreted as regular expressions, and this pattern is not a valid regex: $pattern The regex engine reported this error: $error """) class Userscript(NamedTuple): name: str version: Optional[str] content: str runAt: str noframes: bool matchPatterns: list[str] includePatternRegexes: list[re.Pattern] excludePatternRegexes: list[re.Pattern] downloadURL: Optional[str] unsafeSequences: list[str] # in