mirror of
				https://github.com/dgtlmoon/changedetection.io.git
				synced 2025-11-04 00:27:48 +00:00 
			
		
		
		
	
		
			Some checks failed
		
		
	
	Build and push containers / metadata (push) Has been cancelled
				
			Build and push containers / build-push-containers (push) Has been cancelled
				
			Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
				
			ChangeDetection.io App Test / lint-code (push) Has been cancelled
				
			Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built 📦 package works basically. (push) Has been cancelled
				
			Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
				
			ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
				
			ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
				
			ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
				
			ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
				
			CodeQL / Analyze (javascript) (push) Has been cancelled
				
			CodeQL / Analyze (python) (push) Has been cancelled
				
			
		
			
				
	
	
		
			84 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			84 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import re
 | 
						|
 | 
						|
import pluggy
 | 
						|
from price_parser import Price
 | 
						|
from loguru import logger
 | 
						|
 | 
						|
hookimpl = pluggy.HookimplMarker("changedetectionio_conditions")
 | 
						|
 | 
						|
 | 
						|
@hookimpl
 | 
						|
def register_operators():
 | 
						|
    def starts_with(_, text, prefix):
 | 
						|
        return text.lower().strip().startswith(str(prefix).strip().lower())
 | 
						|
 | 
						|
    def ends_with(_, text, suffix):
 | 
						|
        return text.lower().strip().endswith(str(suffix).strip().lower())
 | 
						|
 | 
						|
    def length_min(_, text, strlen):
 | 
						|
        return len(text) >= int(strlen)
 | 
						|
 | 
						|
    def length_max(_, text, strlen):
 | 
						|
        return len(text) <= int(strlen)
 | 
						|
 | 
						|
    # Custom function for case-insensitive regex matching
 | 
						|
    def contains_regex(_, text, pattern):
 | 
						|
        """Returns True if `text` contains `pattern` (case-insensitive regex match)."""
 | 
						|
        return bool(re.search(pattern, str(text), re.IGNORECASE))
 | 
						|
 | 
						|
    # Custom function for NOT matching case-insensitive regex
 | 
						|
    def not_contains_regex(_, text, pattern):
 | 
						|
        """Returns True if `text` does NOT contain `pattern` (case-insensitive regex match)."""
 | 
						|
        return not bool(re.search(pattern, str(text), re.IGNORECASE))
 | 
						|
 | 
						|
    def not_contains(_, text, pattern):
 | 
						|
        return not pattern in text
 | 
						|
 | 
						|
    return {
 | 
						|
        "!in": not_contains,
 | 
						|
        "!contains_regex": not_contains_regex,
 | 
						|
        "contains_regex": contains_regex,
 | 
						|
        "ends_with": ends_with,
 | 
						|
        "length_max": length_max,
 | 
						|
        "length_min": length_min,
 | 
						|
        "starts_with": starts_with,
 | 
						|
    }
 | 
						|
 | 
						|
@hookimpl
 | 
						|
def register_operator_choices():
 | 
						|
    return [
 | 
						|
        ("!in", "Does NOT Contain"),
 | 
						|
        ("starts_with", "Text Starts With"),
 | 
						|
        ("ends_with", "Text Ends With"),
 | 
						|
        ("length_min", "Length minimum"),
 | 
						|
        ("length_max", "Length maximum"),
 | 
						|
        ("contains_regex", "Text Matches Regex"),
 | 
						|
        ("!contains_regex", "Text Does NOT Match Regex"),
 | 
						|
    ]
 | 
						|
 | 
						|
@hookimpl
 | 
						|
def register_field_choices():
 | 
						|
    return [
 | 
						|
        ("extracted_number", "Extracted number after 'Filters & Triggers'"),
 | 
						|
#        ("meta_description", "Meta Description"),
 | 
						|
#        ("meta_keywords", "Meta Keywords"),
 | 
						|
        ("page_filtered_text", "Page text after 'Filters & Triggers'"),
 | 
						|
        #("page_title", "Page <title>"), # actual page title <title>
 | 
						|
    ]
 | 
						|
 | 
						|
@hookimpl
 | 
						|
def add_data(current_watch_uuid, application_datastruct, ephemeral_data):
 | 
						|
 | 
						|
    res = {}
 | 
						|
    if 'text' in ephemeral_data:
 | 
						|
        res['page_filtered_text'] = ephemeral_data['text']
 | 
						|
 | 
						|
        # Better to not wrap this in try/except so that the UI can see any errors
 | 
						|
        price = Price.fromstring(ephemeral_data.get('text'))
 | 
						|
        if price and price.amount != None:
 | 
						|
            # This is slightly misleading, it's extracting a PRICE not a Number..
 | 
						|
            res['extracted_number'] = float(price.amount)
 | 
						|
            logger.debug(f"Extracted number result: '{price}' - returning float({res['extracted_number']})")
 | 
						|
 | 
						|
    return res
 |