{"id":19090133,"url":"https://github.com/destrangis/urlmonitor","last_synced_at":"2026-05-03T11:33:51.492Z","repository":{"id":62586681,"uuid":"185088850","full_name":"destrangis/urlmonitor","owner":"destrangis","description":"Check urls and run actions if changed","archived":false,"fork":false,"pushed_at":"2021-11-19T12:31:15.000Z","size":30,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-11-12T11:38:17.573Z","etag":null,"topics":["monitoring","monitoring-application","python","web","yaml"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/destrangis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-05T22:25:12.000Z","updated_at":"2021-11-19T12:31:18.000Z","dependencies_parsed_at":"2022-11-03T22:38:14.947Z","dependency_job_id":null,"html_url":"https://github.com/destrangis/urlmonitor","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/destrangis/urlmonitor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/destrangis%2Furlmonitor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/destrangis%2Furlmonitor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/destrangis%2Furlmonitor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/destrangis%2Furlmonitor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/destrangis","download_url":"https://codeload.github.com/destrangis/urlmonitor/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/destrangis%2Furlmonitor/sbom","scorecard":{"id":336322,"data":{"date":"2025-08-11","repo":{"name":"github.com/destrangis/urlmonitor","commit":"34382b139eb43c0986d74c3649dea8b9c67039e8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/22 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"16 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2022-42986 / GHSA-43fp-rhv2-5gv8","Warn: Project is vulnerable to: PYSEC-2023-135 / GHSA-xqr8-7jwr-rhp7","Warn: Project is vulnerable to: PYSEC-2024-60 / GHSA-jjg7-2v4v-x38h","Warn: Project is vulnerable to: PYSEC-2020-176 / GHSA-3pqx-4fqf-j49f","Warn: Project is vulnerable to: PYSEC-2020-96 / GHSA-6757-jp84-gxfx","Warn: Project is vulnerable to: PYSEC-2021-142 / GHSA-8q59-q68h-6hv4","Warn: Project is vulnerable to: GHSA-9hjg-9r4m-mvj7","Warn: Project is vulnerable to: GHSA-9wx4-h78v-vm56","Warn: Project is vulnerable to: PYSEC-2023-74 / GHSA-j8r2-6x86-q33q","Warn: Project is vulnerable to: GHSA-34jh-p97f-mpxf","Warn: Project is vulnerable to: PYSEC-2023-212 / GHSA-g4mx-q9vg-27p4","Warn: Project is vulnerable to: GHSA-pq67-6m6q-mj2v","Warn: Project is vulnerable to: PYSEC-2019-132 / GHSA-r64q-w8jr-g9qp","Warn: Project is vulnerable to: PYSEC-2023-192 / GHSA-v845-jxx5-vc9f","Warn: Project is vulnerable to: PYSEC-2020-148 / GHSA-wqvq-5m8c-6g24","Warn: Project is vulnerable to: PYSEC-2021-108"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T04:49:18.860Z","repository_id":62586681,"created_at":"2025-08-18T04:49:18.860Z","updated_at":"2025-08-18T04:49:18.860Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32567417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T06:36:36.687Z","status":"ssl_error","status_checked_at":"2026-05-03T06:36:09.306Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["monitoring","monitoring-application","python","web","yaml"],"created_at":"2024-11-09T03:02:05.148Z","updated_at":"2026-05-03T11:33:51.472Z","avatar_url":"https://github.com/destrangis.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"## UrlMonitor\n\nThis program is intended to be run periodically from cron or a systemd ``.timer`` service on GNU/Linux, a Periodic Task on Windows, or whatever you are using.\n\nWhen run, the program will check the URLs from its list, and, if they have changed since the last time they were checked, will run a series of actions as a result.\n\nThe list of URLs and actions to be run if changed are specified in a YAML file (by default called ``urllist.yml``). This file consist on a LIST of OBJECTS, each containing the fields name with a descriptive string, url with the url to be checked, and actions, which is a LIST of OBJECTS, where the key is the action name and the value are the arguments, which can be a STRING or a LIST of strings.\n\nAn example of urllist.yml file:\n\n```yaml\n---\n- name: Check for new versions of my favourite software\n  url: https://www.coolproject.org/download/files\n  actions:\n    - email_notify:\n        - joe@domain.com\n        - jill@somewhere.else.com\n\n- name: Get latest version of the software\n  url: https://www.coolproject.org/latest\n  actions:\n    - re_match:\n        - '[Ss]ource [Tt]arball.*\u003ca href=\\\"([^\"]+)'\n    - run_script: download_and_build https://www.coolproject.org/{re_match[1]}\n```\n\nAs you can see on the last line, the arguments to the actions are expanded with the variables from previous actions, the url, the contents, the HTTP status code obtained while fetching the URL, etc.\n\n### Installation\n\nJust run the provided ``setup.py`` program:\n\n```\n$ python3 setup.py install\n```\n\n### Running\n\nYou can call the program with ``--help`` for the program options::\n\n\n    $ urlmonitor --help\n    usage: urlmonitor [-h] [--config FILE] [--persist-file FILE]\n                      [ymlfile [ymlfile ...]]\n\n    positional arguments:\n      ymlfile               Url check specification.\n\n    optional arguments:\n      -h, --help            show this help message and exit\n      --config FILE, -c FILE\n                            Configuration file.\n      --persist-file FILE, -p FILE\n                            Persistence file to store URL status. Default\n                            ./persistence.sqlite\n\n\n* The ``yml`` file(s) contain the URLs to check, as discussed above.\n* The ``--persist-file`` is a database that will be created in order to track whether the URLs have changed and when.\n* The ``--config`` file is a YAML file that specifies where the options are located. It is discussed on the next section.\n\n### Configuration\n\nUrlMonitor is configured using a YAML file containing an OBJECT with the different sections where you can specify:\n\n* Global variables (``set_vars``): available to all the actions.\n* Actions (``actions``): Each additional action individually, specifying its name, the Python module where it is located, and perhaps a set of configuration parameters.\n* Action directories (``action_dir``): Specify a directory containing Python files. Each Python file contains an action named like the file.\n* Action configurations (``action_config``): Contains an OBJECT whose entries are the names of the actions and the values are the configuration parameters for that action. Any action can be configured in this way, whether it has been specified using ``action`` or an ``action_dir``. Some predefined actions also need to be configured this way, for example the ``email_notify`` predefined action needs the parameter ``smtp_server`` to be set.\n* Logging configuration (``logging_config``): Contains the settings needed to call `logging.config.dictConfig()` as per the [Python standard library documentation](https://docs.python.org/3/library/logging.config.html#logging-config-dictschema). The logger name that will be used is `URLMONITOR`. If this section is not present, sensible defaults will be used.\n\nAn example of the configuration follows::\n\n~~~yaml\n---\n\n# define global variables for all the actions\nset_vars:\n    var1: 5.4\n    var2:\n        - list\n        - of\n        - \"strings\"\n    var3:\n        keys: of\n        a: dict\n\n# load all the actions in a directory\n#   all the .py files in the directory will be loaded\n#   if the module is action_name.py, the action name will be 'action_name'\n#   and will be initialised with the objects in its entry in an\n#   an 'actions_config' section\n#\naction_dir: /path/to/dir1\n\naction_dir:\n    # specify a list of paths\n    - /path/to/dir2\n    - /another/path/dir3\n\n# actions and configuration objects:\n# configuration objects are mappings that the actions need to initialise\n# themselves, like the smtp host in a 'mail_send' action or a database name\n# and connection parameters to look up/store stuff etc.\n#\n# The actions will be called (or not) at runtime with the url that's changed,\n# the contents of that url, and the variables with their values at that point.\n\nactions_config:\n        # configuration objects for actions defined on action_dir\n        # as opposed to actions defined in actions:\n        action_in_dir1:\n            update_url: https://www.someserver.com\n            something_or_another: 34\n\n        some_action_in_dir3:\n            parameter: value\n            another_parameter: another_value\n\n\nactions:\n    # always a list containing mappings of actions and configuration values\n\n                # should always have a name and module\n    - name: add_to_database\n      module: /path/to/module.py\n                # ad_hoc configuration parameters, defined and expected by\n                # each particular action\n      dbhost: host1.mynetwork\n      dbname: documents\n      dbuser: albert\n      dbpassword: hello0011\n\n    - name: another_action:\n      module: /the/path/to/another_action.py\n      foo: bar\n\nlogging_config:\n     # the following is passed directly to logging.config.dictConfig()\n     loggers:\n        URLMONITOR:\n            level: DEBUG\n            handlers:\n                - console\n     handlers:\n         console:\n             class: logging.StreamHandler\n             stream: ext://sys.stdout\n             level: DEBUG\n~~~\n\n### Predefined Actions\n\nThe following actions are available out of the box:\n\n#### set_vars\nSet global variables. The variables defined using this action are global and can be used by all the URL objects and their actions.\n\nExample:\n```yaml\nset_vars:\n   value_1: 34\n   list_var:\n      - item 1\n      - item 2\n      - another item\n```\n\n#### list_vars\nThis action lists all the variables and their values to the log. The log level must be debug.\n\n#### no_action\nThis is a dummy action. It does nothing.\n\n#### re_match\nSearch the content of the URL using one or more regular expressions passed as arguments. The regular expressions are applied using ``re.MULTILINE`` (see [Python's ``re`` module documentation](https://docs.python.org/3/library/re.html#re.MULTILINE) ) i.e. '^' matches the beginning of every line and '$' matches every end of line.\n\nAs a result, it creates two variables:\n\n* ``re_match``: A list of all the group matches in the first regular expression.\n* ``re_match_all``: A list containing the list of all group matches of all the regular expressions in the argument list, in the same order as they appear.\n\nExample:\n\n```yaml\n  - name: Get amount and code\n    url: http://www.interesting.com/amounts_codes/\n    actions:\n       - re_match:\n             - 'The amount is:\\s*([0-9]+)'\n             - 'Code\\s*(\\w+)'\n       - set_vars:\n            amount: {re_match[1]}\n            code: {re_match_all[1][1]}\n```\n\n#### email_notify\nSend an email with a notification of the URL change to the recipient email addresses specified in the argument list.\n\nExample:\n```yaml\n- name: Check for new versions of my favourite software\n  url: https://www.coolproject.org/download/files\n  actions:\n    - email_notify:\n        - joe@domain.com\n        - jill@somewhere.else.com\n```\n\nBefore this action can be used it must be configured in the yaml configuration file with the following parameters:\n\nName            | Value                   | Default value\n----------------|-------------------------|-------------\nsmtp_server     | server name             |\nfrom_address    | email address           | urlmonitor@hostname\nsmtp_port       | port number             | 25 for SMTP, 587 for SSL\nsmtp_encryption | SSL or STARTTLS         | empty, no encryption\nsmtp_user       | username                | empty, no smtp authentication\nsmtp_password   | password base64 encoded |\n\nConfiguration example:\n\n```yaml\n- actions_config:\n     email_notify:\n         smtp_server: mailhost.server.com\n         from_address: urlbot@mydomain.com\n         smtp_encryption: STARTTLS\n         smtp_port: 465\n         smtp_user: phil\n         smtp_password: aGVsbG8wMDExMjI=\n```\n\n#### run_script\nThis action runs a program whose command line is its argument list.\n\nExample:\n\n```yaml\n- name: Download the new file\n  url: https://www.filesite.net/targetfiles.html\n  actions:\n      - re_match: '\u003ca href=\\\"(.*)\\\"\u003eThe file'\n      - run_script: wget {re_match[1]}\n```\nThis action sets the following variables:\n\nVariable    | Value\n------------|-----------------------------------\nreturn_code | The exit code of the program\nerror       | The error encountered, if any\nstdout      | Whatever the program has written to its standard output\nstderr      | Whatever the program has written to its error output\n\n\n### Writing Custom Actions\nYou can write a custom action easily. You just need to write a Python module that exports a callable called ``action_object`` with the following signature:\n\n```python\ndef action_object(name, arglst, url, content, variables, log):\n    pass\n```\n\nWhere the arguments are:\n\nArgument  | Description\n----------|--------\nname      | The action name\narglst    | The list of arguments.\nurl       | The url that changed\ncontent   | The content (possibly HTML) of the url\nvariables | A mapping containing the variables and their values\nlog       | A logging object\n\nIf the arguments in ``arglst`` will be expanded by the calling routine and should contain the final value.\n\nIf your action requires configuration parameters, you can create a class that has a ``__call__`` method and instantiate into ``action_object``. At configuration time, the program will call the ``initialise`` method with a dictionary containing the configuration parameters as argument.\n\nIn order to simplify the process, the module ``actionbase`` provides the class ``Action`` from which your action can inherit. This already provides an ``initialise`` method which will install the configuration parameters, which you can access as instance attributes of ``action_object``. In addition, you should provide a list of expected parameters and a default value for those that can have one in the class variables ``check_cfg_vars`` and ``default_vars``. An exception is raised if a parameter that has been specified in ``check_cfg_vars`` is not found and there is no default value.\n\nAs an example, you can check the code for the ``email_notify`` action.\n\n```python\nfrom urlmonitor.actionbase import Action\n\nclass _EmailAction(Action):\n\n    check_cfg_vars = [ \"smtp_server\", \"from_address\", \"smtp_encryption\",\n            \"smtp_user\", \"smtp_password\", \"smtp_port\"]\n    default_vars = {\n        \"from_address\": \"urlmonitor@{}\".format(_NODE),\n        \"smtp_encryption\": None,\n        \"smtp_user\": None,\n        \"smtp_password\": \"\",\n        \"smtp_port\": 0,\n        }\n\n    ...\n\n    def __call__(self, name, arglst, url, content, variables, log):\n        ...\n        \naction_object = _EmailAction()\n```\n\n\n### License\n\nThis software is released under the **MIT License**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdestrangis%2Furlmonitor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdestrangis%2Furlmonitor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdestrangis%2Furlmonitor/lists"}