[
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# poetry\n#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control\n#poetry.lock\n\n# pdm\n#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.\n#pdm.lock\n#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it\n#   in version control.\n#   https://pdm.fming.dev/#use-with-ide\n.pdm.toml\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# PyCharm\n#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can\n#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore\n#  and can be added to the global gitignore or merged into this file.  For a more nuclear\n#  option (not recommended) you can uncomment the following to ignore the entire idea folder.\n#.idea/\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM python:3.9\n\nRUN apt-get update && apt-get -y install cron\n\nWORKDIR /app\n\nCOPY . .\n\nRUN pip install -r requirements.txt\n\nCOPY crontab /etc/cron.d/crontab\n\nRUN touch /var/log/cron.log\n\nRUN chmod 0644 /etc/cron.d/crontab\n\nRUN crontab /etc/cron.d/crontab\n\nCMD cron && tail -f /var/log/cron.log"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Ali Khalkhali\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<h2 align=\"center\" >\n  Table of Contents \n</h2>\n<p align=\"center\">\n  <a href=\"#Installation\">Installation</a> •\n  <a href=\"#Configuring-Programs-Watcher\">Configuring Programs Watcher</a> •\n  <a href=\"#Contributing\">Contributing</a> •\n  <a href=\"#License\">License</a> •\n   <a href=\"#Contact\">Contact</a> •\n  </p>\n  \n# Programs Watcher\nPrograms Watcher is a Python program that monitors and notifies you of new updates from various bug bounty platforms. It uses MongoDB for data storage and Discord webhooks for notifications.\n<br>\n<br>\n## Youtube Video\n\n[![Video Thumbnail](https://github.com/Alikhalkhali/programs-watcher/blob/main/img/Thumbnail.jpg)](https://www.youtube.com/watch?v=V6d6_YVUSR8)\n# Installation\n\nTo install Programs Watcher, you have two options depending on your needs.\n\n## Option 1: Docker Installation (Recommended for server environments)\n\n1. Clone the repository to your local machine:\n\n```bash\ngit clone https://github.com/Alikhalkhali/programs-watcher.git\n```\n2. Change directory to the project folder:\n```bash\ncd programs-watcher\n```\n3. In the `config.yml` file, replace `<YOUR DISCORD WEBHOOK>` with your Discord webhook URL.\n4. Run:\n ```bash\ndocker-compose up -d\n```\n## Option 2: Manual Installation (Recommended for desktop and laptop usage)\n\nThis option is suitable for users who want to run Programs Watcher on their local machines and manually trigger updates and notifications.\n\n1. Install MongoDB:\n   - Download and install MongoDB from the official website: [MongoDB Download](https://www.mongodb.com/try/download/community)\n   - Follow the installation instructions for your operating system.\n\n2. Clone the repository to your local machine:\n‍‍‍\n```bash\ngit clone https://github.com/Alikhalkhali/programs-watcher.git\n```\n\n3. Change directory to the project folder:\n```bash\ncd programs-watcher\n```\n4. In the `config.yml` file, replace `<YOUR DISCORD WEBHOOK>` with your Discord webhook URL and update the MongoDB connection URL to `mongodb://localhost:27017/`.\n\n5. Install the required dependencies:\n```bash\npip3 install -r requirements.txt\n```\n6. Run the program:\n```bash\npython3 main.py\n```\n\nRemember to manually run the program whenever you want to check for updates and receive notifications. This option is recommended for desktop and laptop installations.\n\n# Configuring Programs Watcher\n\nThe Programs Watcher program uses a configuration file named `config.yml` to store information about the bug bounty platforms to monitor and the notification options to use.\n\n## Discord Webhook\n\nTo use the Discord webhook, replace `<YOUR DISCORD WEBHOOK>` with the actual URL of your webhook in the following line:\n\n```yaml\ndiscordWebhook:\nprograms_watcher: <YOUR DISCORD WEBHOOK>\n```\n\n## MongoDB\n\n- `uri`: This is the URI of the MongoDB database that the application will use to store data.\n- `database`: This is the name of the database that the application will use to store data.\n\n## Platforms\n\nThis section contains a list of bug bounty platforms to monitor. For each platform, provide the name, URL, and a set of notification options. The notification options specify which types of changes should trigger notifications.\n\n### Monitor:\n\nSpecify the monitoring options for the platform.\n\n- `rdp`: Set to true if you want to monitor RDP programs.\n- `vdp`: Set to true if you want to monitor VDP programs.\n- `excluded_programs`: List the URLs of the programs you wish to exclude from monitoring. Please note that this applies only to public programs.\n- `specific_programs`:  List the specific program URLs you want to monitor. Note that it only monitors public programs. For example:\n  - bugcrowd:\n    ```yml\n      specific_programs: \n        - https://bugcrowd.com/rarible-ogmbb\n        - https://bugcrowd.com/smartthings\n    ```\n  - hackerone:\n    ```yml\n      specific_programs: \n        - https://hackerone.com/phabricator?type=team\n        - https://hackerone.com/yahoo?type=team\n    ```\n  - intigriti:\n    ```yml\n      specific_programs: \n        - https://app.intigriti.com/programs/portofantwerp/nxtport\n        - https://app.intigriti.com/programs/intergamma/intergamma\n    ```\n  - yeswehack:\n    ```yml\n      specific_programs: \n        - https://yeswehack.com/programs/swiss-post-evoting\n        - https://yeswehack.com/programs/dana-bug-bounty-programd\n    ```\n\n### Notifications\n\nTo receive notifications for a specific type of change, set the corresponding notification option to `true`. The available notification options are:\n\n- `new_program`: Notify when a new program is added.\n- `removed_program`: Notify when a program is removed.\n- `new_inscope`: Notify when a new scope is added.\n- `removed_inscope`: Notify when a scope is removed.\n- `new_out_of_scope`: Notify when a new out-of-scope item is added.\n- `removed_out_of_scope`: Notify when an out-of-scope item is removed.\n- `new_scope`: Notify when a scope is added or removed.\n- `changed_scope`: Notify when a scope is modified.\n- `new_type`: Notify when a new program type is added.\n- `new_bounty_table`: Notify when a new bounty table is added.\n\nFill in the necessary information for each platform that you want to monitor.\n\n# Contributing\n\nIf you would like to contribute to this project, please fork the repository and submit a pull request.\n\n# License\n\nThis project is licensed under the MIT license. See the LICENSE file for details.\n\n# Contact\n\nIf you have any questions or concerns, please feel free to contact me directly on social media:\n\n- Twitter: [ali_khalkhali0](https://twitter.com/ali_khalkhali0)\n- Instagram: [ali_khalkhali0](https://instagram.com/ali_khalkhali0)\n\nI am always happy to hear from you and will do my best to respond to your questions as soon as possible.\n<br>\n#### For Support:\n\n[![\"Buy Me A Coffee\"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/alikhalkhali)\n"
  },
  {
    "path": "config.yml",
    "content": "discordWebhook:\n  programs_watcher: <discord-webhook>\nmongoDB:\n  uri: mongodb://db:27017/\n  database: programs_watcher\nplatforms:\n  - name: bugcrowd\n    url: https://raw.githubusercontent.com/Osb0rn3/bugbounty-targets/main/programs/bugcrowd.json\n    monitor:\n      rdp: true\n      vdp: true\n      specific_programs: []\n      excluded_programs: []\n    notifications:\n      new_program: true\n      removed_program: true\n      new_inscope: true\n      removed_inscope: true\n      new_out_of_scope: true\n      removed_out_of_scope: true\n      new_type: true\n      new_bounty_table: true\n  - name: hackerone\n    url: https://raw.githubusercontent.com/Osb0rn3/bugbounty-targets/main/programs/hackerone.json\n    monitor:\n      rdp: true\n      vdp: true\n      specific_programs: []\n      excluded_programs: \n        - https://hackerone.com/alshaya?type=team\n        - https://hackerone.com/gsa_vdp?type=team\n        - https://hackerone.com/clarivate?type=team\n    notifications:\n      new_program: true\n      removed_program: true\n      new_scope: true\n      removed_scope: true\n      changed_scope: true\n      new_type: true\n  - name: intigriti\n    url: https://raw.githubusercontent.com/Osb0rn3/bugbounty-targets/main/programs/intigriti.json\n    monitor:\n      rdp: true\n      vdp: true\n      specific_programs: []\n      excluded_programs: [] \n    notifications:\n      new_program: true\n      removed_program: true\n      new_scope: true\n      removed_scope: true\n      changed_scope: true\n      new_type: true\n      new_bounty_table: true\n  - name: yeswehack\n    url: https://raw.githubusercontent.com/Osb0rn3/bugbounty-targets/main/programs/yeswehack.json\n    monitor:\n      rdp: true\n      vdp: true\n      specific_programs: []\n      excluded_programs: []\n    notifications:\n      new_program: true\n      removed_program: true\n      new_inscope: true\n      removed_inscope: true\n      new_type: true\n      new_bounty_table: true"
  },
  {
    "path": "crontab",
    "content": "*/30 * * * * root cd /app && /usr/local/bin/python main.py >> /var/log/cron.log 2>&1\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: \"3\"\n\nservices:\n  app:\n    build: .\n    volumes:\n      - ./config.yml:/app/config.yml\n  db:\n    image: mongo:4.0-xenial\n    volumes:\n      - mongo_data:/data/db\n\nvolumes:\n  mongo_data:\n"
  },
  {
    "path": "main.py",
    "content": "import yaml\nfrom pymongo import MongoClient\nimport os\nimport shutil\nfrom modules.platforms.bugcrowd import check_bugcrowd\nfrom modules.platforms.hackerone import check_hackerone\nfrom modules.platforms.intigriti import check_intigriti\nfrom modules.platforms.yeswehack import check_yeswehack\nfrom modules.notifier.discord import send_startup_message\n\n\n# load config file\nwith open(\"config.yml\", \"r\") as ymlfile:\n    cfg = yaml.full_load(ymlfile)\ndiscord_webhook = cfg['discordWebhook']['programs_watcher']\nplatforms = {}\nfor platform in cfg['platforms']:\n    platforms[platform['name']] = {\n        'url': platform['url'],\n        'notifications': platform['notifications'],\n        'monitor': platform['monitor']\n    }\n\n# connect to MongoDB\nclient = MongoClient(cfg['mongoDB']['uri'])\ndbName = cfg['mongoDB']['database']\ndb = client[dbName]\nfirst_time = False\nif dbName not in client.list_database_names():\n    first_time = True\n\n# Check ./tmp directory exists\ntmp_dir = f\"./tmp/\"\nif os.path.exists(tmp_dir):\n    shutil.rmtree(tmp_dir)\n    os.mkdir(tmp_dir)\nelse:\n    os.mkdir(tmp_dir)\n\n# checking bugcrowd\ncheck_bugcrowd(tmp_dir, discord_webhook, first_time, db, platforms['bugcrowd'])\n\n# checking hackerone\ncheck_hackerone(tmp_dir, discord_webhook, first_time, db, platforms['hackerone'])\n\n# checking intigriti\ncheck_intigriti(tmp_dir, discord_webhook, first_time, db, platforms['intigriti'])\n\n# checking yeswehack\ncheck_yeswehack(tmp_dir, discord_webhook, first_time, db, platforms['yeswehack'])\n\n# Clean up resources and remove tmp_dir\nclient.close()\nshutil.rmtree(tmp_dir)\n\nif first_time:\n    send_startup_message(discord_webhook)"
  },
  {
    "path": "modules/notifier/discord.py",
    "content": "from discord_webhook import DiscordWebhook, DiscordEmbed\nfrom modules.notifier.functions import generate_diff, split_text, get_platform_profile, shorten_string\n\n\ndef add_field(embed, data, message, diff=False):\n    texts = split_text(data)\n    isFirstTime = 0\n    type = \"\"\n    if diff:\n        type = \"diff\\n\"\n    for text in texts:\n        if isFirstTime == 0:\n            embed.add_embed_field(\n                name=message, value=f\"```{type}{text}```\", inline=False)\n            isFirstTime = 1\n        else:\n            embed.add_embed_field(\n                name=\" \", value=f\"```{type}{text}```\", inline=False)\n\n\ndef changed_program_message(data):\n    embed = DiscordEmbed(title=f\"{data['programName']}\",\n                         description=f\"** {data['programName']} ** has changed!\\n** Program type: ** {data['programType']}\\n\\n** Program page: ** [Click here]({data['programURL']})\", color=data['color'])\n    embed.set_thumbnail(url=data['logo'])\n    embed.set_footer(text='Powered by Ali Khalkhali',\n                     icon_url='https://cdn.discordapp.com/avatars/941457826662985808/488f3bcab0de041de57860b4e05e2e9f.webp')\n    if data[\"platformName\"] in [\"HackerOne\", \"Intigriti\"]:\n        if data[\"newProgramType\"]:\n            embed.add_embed_field(name=\"Program type changed:\",\n                                  value=f\"```changed to {data['newProgramType']}```\", inline=False)\n        if data[\"platformName\"] == \"Intigriti\":\n            if data['newReward']:\n                embed.add_embed_field(\n                    name=\"Bounty Table changed:\", value=f\"```{data['newReward']['min']} -- {data['newReward']['max']}```\", inline=False)\n        if data[\"newScope\"]:\n            scope = '\\n'.join(data['newScope'])\n            add_field(embed, scope, \"Following scope added:\")\n        if data[\"removedScope\"]:\n            removedScope = '\\n'.join(data['removedScope'])\n            add_field(embed, removedScope, \"Following Scope removed:\")\n        if data[\"changedScope\"]:\n            for scope in data[\"changedScope\"]:\n                old = scope[\"old\"]\n                new = scope[\"new\"]\n                diff = generate_diff(old, new)\n                add_field(embed, diff, \"Following scope changed:\", True)\n    if data[\"platformName\"] in [\"Bugcrowd\", \"YesWeHack\"]:\n        if data['newType']:\n            embed.add_embed_field(\n                name=\"Program Type changed\", value=f\"```New Type: {data['newType']}```\", inline=False)\n        if data['reward']:\n            embed.add_embed_field(name=\"Bounty Table changed:\",\n                                  value=f\"```{data['reward']['min']} -- {data['reward']['max']}```\", inline=False)\n        if data['newInScope']:\n            inscope = '\\n'.join(data['newInScope'])\n            add_field(embed, inscope, \"Following inScope added:\")\n        if data['removeInScope']:\n            removeInScope = '\\n'.join(data['removeInScope'])\n            add_field(embed, removeInScope, \"Following Inscope removed:\")\n\n        if data[\"platformName\"] == \"Bugcrowd\":\n            if data['newOutOfScope']:\n                newOutOfScope = '\\n'.join(data['newOutOfScope'])\n                add_field(embed, newOutOfScope,\n                          \"Following out of scope added:\")\n            if data['removeOutOfScope']:\n                removeOutOfScope = '\\n'.join(data['removeOutOfScope'])\n                add_field(embed, removeOutOfScope,\n                          \"Following out of scope removed:\")\n    embed.add_embed_field(name=\" \", value= \"[Click here to Buy me a coffee ☕](https://www.buymeacoffee.com/alikhalkhali)\", inline=False)\n    return embed\n\n\ndef new_program_message(data):\n    embed = DiscordEmbed(title=f\"{data['programName']}\",\n                         description=f\"** New program ** named** {data['programName']} ** added to platform!\\n** Program type: ** {data['programType']}\\n\\n** Program page: ** [Click here]({data['programURL']})\", color=data['color'])\n    embed.set_thumbnail(url=data['logo'])\n    embed.set_footer(text='Powered by Ali Khalkhali',\n                     icon_url='https://cdn.discordapp.com/avatars/941457826662985808/488f3bcab0de041de57860b4e05e2e9f.webp')\n    if data[\"platformName\"] in [\"HackerOne\", \"Intigriti\"]:\n        if data[\"platformName\"] == \"Intigriti\":\n            if data['newReward']:\n                embed.add_embed_field(\n                    name=\"Bounty Table:\", value=f\"```{data['newReward']['min']} -- {data['newReward']['max']}```\", inline=False)\n        if data[\"newScope\"]:\n            scope = \"\"\n            for newScope in data[\"newScope\"]:\n                scope += f\"{shorten_string(newScope)}\\n\"\n            add_field(embed, scope, \"Scope:\")\n    if data[\"platformName\"] in [\"Bugcrowd\", \"YesWeHack\"]:\n        if data['reward']:\n            embed.add_embed_field(\n                name=\"Bounty Table:\", value=f\"```{data['reward']['min']} -- {data['reward']['max']}```\", inline=False)\n        if data['newInScope']:\n            inscope = '\\n'.join(data['newInScope'])\n            add_field(embed, inscope, \"InScope:\")\n        if data[\"platformName\"] == \"Bugcrowd\":\n            if data['newOutOfScope']:\n                newOutOfScope = '\\n'.join(data['newOutOfScope'])\n                add_field(embed, newOutOfScope, \"Out of scope:\")\n\n    embed.add_embed_field(name=\" \", value= \"[Click here to Buy me a coffee ☕](https://www.buymeacoffee.com/alikhalkhali)\", inline=False)\n    return embed\n\ndef removed_program_message(data):\n    embed = DiscordEmbed(title=f\"{data['programName']}\",\n                         description=f\"Program named ** {data['programName']} ** has removed from platform!\\n** Program type: ** {data['programType']}\", color=data['color'])\n    embed.set_thumbnail(url=data['logo'])\n    embed.set_footer(text='Powered by Ali Khalkhali',\n                     icon_url='https://cdn.discordapp.com/avatars/941457826662985808/488f3bcab0de041de57860b4e05e2e9f.webp')\n\n    embed.add_embed_field(name=\" \", value= \"[Click here to Buy me a coffee ☕](https://www.buymeacoffee.com/alikhalkhali)\", inline=False)\n    return embed\n\ndef send_notification(data, webhook_url):\n\n    webhook = DiscordWebhook(\n        url=webhook_url, username=data['platformName'], avatar_url=get_platform_profile(data['platformName']))\n    if data[\"isRemoved\"]:\n        embed = removed_program_message(data)\n    elif data[\"isNewProgram\"]:\n        embed = new_program_message(data)\n    else:\n        embed = changed_program_message(data)\n    webhook.add_embed(embed)\n    response = webhook.execute()\n    if response.status_code != 200:\n        print(data[\"programName\"])\n        print(\"Error sending message:\", response.content)\n\ndef send_startup_message(webhook_url):\n    webhook = DiscordWebhook(\n        url=webhook_url, username=\"Programs Watcher\", avatar_url=\"https://t3.ftcdn.net/jpg/00/91/64/62/360_F_91646202_b3K6ELfgM2E8QIwuNTlzco7K1r3mOJvp.jpg\")\n    embed = DiscordEmbed(title=f\"🎉 Successful Start of Programs Watcher 🎉\",\n                         description= \"Hi, welcome to ** Programs Watcher **! 🎉\\nThe program has started successfully and is now waiting for a change. 🗼✨\\n** Github page: ** https://github.com/Alikhalkhali/programs-watcher\", color=\"23ff1f\")\n    embed.set_footer(text='Powered by Ali Khalkhali',\n                     icon_url='https://cdn.discordapp.com/avatars/941457826662985808/488f3bcab0de041de57860b4e05e2e9f.webp')\n    embed.add_embed_field(name=\" \", value= \"[Click here to Buy me a coffee ☕](https://www.buymeacoffee.com/alikhalkhali)\", inline=False)\n\n    webhook.add_embed(embed)\n    response = webhook.execute()\n\n    if response.status_code != 200:\n       print(\"There was an error sending the Discord message\")\n"
  },
  {
    "path": "modules/notifier/functions.py",
    "content": "import difflib\n\n\ndef get_platform_profile(platform_name):\n    if platform_name == \"HackerOne\":\n        profile_url = \"https://profile-photos.hackerone-user-content.com/variants/000/000/013/fa942b9b1cbf4faf37482bf68458e1195aab9c02_original.png/e60fe2d979b041d2254f8a36a3d2d7a24d7c4a8ad33ea024d13fc56668c7c4f6\"\n    elif platform_name == \"Bugcrowd\":\n        profile_url = \"https://logos.bugcrowdusercontent.com/logos/ef74/d1fa/62a5b64c/3809e0af42850a579f02c3434743e3ca_bugcrowd__1_.png\"\n    elif platform_name == \"Intigriti\":\n        profile_url = \"https://api.intigriti.com/file/api/file/public_bucket_d23a1f29-c2fe-4d03-8daf-df24d1e076ea-c2449aa2-3a08-4bf5-a430-441a11020851\"\n    elif platform_name == \"YesWeHack\":\n        profile_url = \"https://avatars.githubusercontent.com/u/16663829?s=280&v=4\"\n    return profile_url\n\n\ndef shorten_string(string):\n    if len(string) > 30:\n        return string[:30] + \"...\\n\"\n    else:\n        return string\n\n\ndef split_text(huge_text, max_chunk_size=1000):\n    sentences = huge_text.split('\\n')\n    chunks = []\n    current_chunk = \"\"\n\n    for sentence in sentences:\n        if len(current_chunk) + len(sentence) + 1 <= max_chunk_size:\n            current_chunk += (sentence + '\\n')\n        else:\n            chunks.append(current_chunk.strip())\n            current_chunk = sentence + '\\n'\n\n    if current_chunk:\n        chunks.append(current_chunk.strip())\n\n    return chunks\n\n\ndef generate_diff(old_str, new_str):\n    diff = difflib.ndiff(old_str.splitlines(), new_str.splitlines())\n    result = []\n    for line in diff:\n        if line.startswith('+'):\n            result.append(f'+{line[1:]}')\n        elif line.startswith('-'):\n            result.append(f'-{line[1:]}')\n        elif line.startswith(' '):\n            result.append(line)\n    return '\\n'.join(result)\n"
  },
  {
    "path": "modules/platforms/bugcrowd.py",
    "content": "import json\nfrom modules.platforms.functions import find_program, generate_program_key, get_resource, remove_elements, save_data, check_send_notification\nfrom modules.notifier.discord import send_notification\n\n\n# checking bugcrowd\ndef check_bugcrowd(tmp_dir, mUrl, first_time, db, config):\n    json_programs_key = []\n    notifications = config['notifications']\n    monitor = config['monitor']\n    get_resource(tmp_dir, config['url'], \"bugcrowd\")\n    bugcrowdFile = open(f\"{tmp_dir}bugcrowd.json\")\n    bugcrowd = json.load(bugcrowdFile)\n    bugcrowdFile.close()\n    for program in bugcrowd:\n        programName = program[\"name\"]\n        programURL = \"https://bugcrowd.com\"+program[\"briefUrl\"]\n        logo = program[\"logoUrl\"]\n        data = {\"programName\": programName, \"reward\": {},\"isRemoved\": False, \"newType\": \"\", \"newInScope\": [], \"removeInScope\": [], \"newOutOfScope\": [], \"removeOutOfScope\": [], \"programURL\": programURL,\n                \"logo\": logo, \"platformName\": \"Bugcrowd\", \"isNewProgram\": False, \"color\": 14584064}\n        dataJson = {\"programName\": programName, \"programURL\": programURL, \"programType\": \"\",\n                    \"outOfScope\": [], \"inScope\": [], \"reward\": {}}\n        programKey = generate_program_key(programName, programURL)\n        json_programs_key.append(programKey)\n        watcherData = find_program(db, 'bugcrowd', programKey)\n        if watcherData is None:\n            data[\"isNewProgram\"] = True\n            watcherData = {\"programKey\": programKey, \"programName\": programName, \"programURL\": programURL, \"programType\": \"\",\n                           \"outOfScope\": [], \"inScope\": [], \"reward\": {}}\n        for target in program[\"target_groups\"]:\n            if target[\"in_scope\"] == False:\n                for item in target[\"targets\"]:\n                    dataJson[\"outOfScope\"].append(item[\"name\"])\n\n            else:\n                for item in target[\"targets\"]:\n                    dataJson[\"inScope\"].append((item[\"name\"]))\n            bounty = {\n                \"min\": \"\",\n                \"max\": \"\"\n            }\n            if \"rewardSummary\" in program and program[\"rewardSummary\"] != None:\n                dataJson[\"programType\"] = \"rdp\"\n                data[\"programType\"] = \"rdp\"\n                bounty[\"max\"] = program[\"rewardSummary\"][\"maxReward\"]\n                bounty[\"min\"] = program[\"rewardSummary\"][\"minReward\"]\n            else:\n                dataJson[\"programType\"] = \"vdp\"\n                data[\"programType\"] = \"vdp\"\n            dataJson[\"reward\"] = bounty\n        newInScope = [i for i in dataJson[\"inScope\"]\n                      if i not in watcherData[\"inScope\"]]\n        removeInScope = [i for i in watcherData[\"inScope\"]\n                         if i not in dataJson[\"inScope\"]]\n        removedOutOfScope = [i for i in watcherData[\"outOfScope\"]\n                             if i not in dataJson[\"outOfScope\"]]\n        newOutOfScope = [i for i in dataJson[\"outOfScope\"]\n                         if i not in watcherData[\"outOfScope\"]]\n        hasChanged = False\n        is_update = False\n        if newInScope:\n            watcherData[\"inScope\"].extend(newInScope)\n            notifi_status = notifications['new_inscope']\n            hasChanged = True\n            if notifi_status:\n                data[\"newInScope\"] = newInScope\n                is_update = True\n        if removeInScope:\n            remove_elements(watcherData[\"inScope\"], removeInScope)\n            hasChanged = True\n            notifi_status = notifications['removed_inscope']\n            if notifi_status:\n                data[\"removeInScope\"] = removeInScope\n                is_update = True\n        if newOutOfScope:\n            watcherData[\"outOfScope\"].extend(newOutOfScope)\n            hasChanged = True\n            notifi_status = notifications['new_out_of_scope']\n            if notifi_status:\n                data[\"newOutOfScope\"] = newOutOfScope\n                is_update = True\n        if removedOutOfScope:\n            remove_elements(watcherData[\"outOfScope\"], removedOutOfScope)\n            hasChanged = True\n            notifi_status = notifications['removed_out_of_scope']\n            if notifi_status:\n                data[\"removeOutOfScope\"] = removedOutOfScope\n                is_update = True\n        if dataJson[\"programType\"] != watcherData[\"programType\"]:\n            watcherData[\"programType\"] = dataJson[\"programType\"]\n            hasChanged = True\n            notifi_status = notifications['new_type']\n            if notifi_status:\n                data[\"newType\"] = dataJson[\"programType\"]\n                is_update = True\n        if dataJson[\"reward\"] != watcherData[\"reward\"]:\n            watcherData[\"reward\"] = bounty\n            hasChanged = True\n            notifi_status = notifications['new_bounty_table']\n            if notifi_status:\n                data[\"reward\"] = bounty\n                is_update = True\n        if hasChanged:\n            save_data(db, \"bugcrowd\", programKey, watcherData)\n            if check_send_notification(first_time, is_update, data,watcherData, monitor, notifications):\n                    send_notification(data, mUrl)\n        \n    db_programs_key = db['bugcrowd'].distinct(\"programKey\")\n    removed_programs_key = set(db_programs_key) - set(json_programs_key)\n    for program_key in removed_programs_key:\n        program = find_program(db,'bugcrowd', program_key)\n        data = {\n            \"color\": 14584064,\n            \"logo\": \"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTwToiI8YA0eLclDkd-vJ0xXs7bun5LdHfTrgJucvI&s\",\n            \"platformName\": \"Bugcrowd\",\n            \"isRemoved\": True, \n            \"programName\": program[\"programName\"],\n            \"programType\": program[\"programType\"]\n        }\n        if notifications['removed_program'] and not first_time:\n            send_notification(data,mUrl)\n        db['bugcrowd'].delete_many({\"programKey\": program_key})        "
  },
  {
    "path": "modules/platforms/functions.py",
    "content": "import requests\nfrom hashlib import md5\n\n\ndef get_resource(tmp_dir, url, platformName):\n    domainList = requests.get(url, allow_redirects=True)\n    open(F\"{tmp_dir}{platformName}.json\", 'wb').write(domainList.content)\n\n\ndef generate_program_key(programName, programURL):\n    program_key = md5(\n        f\"{programName}|{programURL}\".encode('utf-8')).hexdigest()\n    return program_key\n\n\ndef find_program(db, platformName, programKey):\n    data = db[platformName].find_one({'programKey': programKey})\n    return data\n\n\ndef remove_elements(array1, array2):\n    for element in array2:\n        array1.remove(element)\n\n\ndef save_data(db, platformName, programKey, dataJson):\n    db[platformName].update_one({'programKey': programKey}, {\n        '$set': dataJson}, upsert=True)\n\ndef check_send_notification(first_time,is_update,data,watcherData,monitor,notifications):\n    pt_notify_status = False\n    notify_status = False\n    if data['isNewProgram']:\n        if data['programType'] == \"rdp\" and monitor['rdp']:\n            pt_notify_status = True\n        elif data['programType'] == \"vdp\" and monitor[\"vdp\"]: \n            pt_notify_status = True\n    else:\n        if watcherData['programType'] == \"rdp\" and monitor['rdp']:\n            pt_notify_status = True\n        elif watcherData['programType'] == \"vdp\" and monitor[\"vdp\"]: \n            pt_notify_status = True   \n    if watcherData['programURL'] in monitor['specific_programs']:\n            pt_notify_status = True\n    if pt_notify_status:\n        if not first_time and data['isNewProgram'] and notifications['new_program']:\n            notify_status = True\n        elif not first_time and is_update and not data['isNewProgram']:\n            notify_status = True\n    if watcherData['programURL'] in monitor['excluded_programs']:\n        print(watcherData['programURL'])\n        notify_status = False \n    return notify_status  \n    "
  },
  {
    "path": "modules/platforms/hackerone.py",
    "content": "import json\nfrom modules.platforms.functions import find_program, generate_program_key, get_resource, save_data, check_send_notification\nfrom modules.notifier.discord import send_notification\n\n\n# checking hackerone\ndef check_hackerone(tmp_dir, mUrl, first_time, db, config):\n    json_programs_key = []\n    notifications = config['notifications']\n    monitor = config['monitor']\n    get_resource(tmp_dir, config['url'], \"hackerone\")\n    hackeroneFile = open(f\"{tmp_dir}hackerone.json\")\n    hackerone = json.load(hackeroneFile)\n    hackeroneFile.close()\n    for program in hackerone:\n        programName = program[\"attributes\"][\"name\"]\n        logo = program[\"attributes\"][\"profile_picture\"]\n        if logo.startswith(\"https://hackerone-us-west-2-p\"):\n            logo = \"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTBmCle7j7K48bRZz483rDz52Nc6w0au28ASw&usqp=CAU\"\n        programURL = \"https://hackerone.com/\" + \\\n            program[\"attributes\"][\"handle\"]+\"?type=team\"\n        data = {\"programName\": programName, \"programType\": \"\", \"programURL\": programURL,\n                \"logo\": logo, \"platformName\": \"HackerOne\", \"isRemoved\": False,\"isNewProgram\": False, \"color\": 16777215}\n        dataJson = {\"programName\": programName,\n                    \"programURL\": programURL, \"programType\": \"\", \"scope\": {}}\n        programKey = generate_program_key(programName, programURL)\n        json_programs_key.append(programKey)\n        watcherData = find_program(db, 'hackerone', programKey)\n\n        if watcherData is None:\n            data[\"isNewProgram\"] = True\n            watcherData = {\"programName\": programName,\n                           \"programURL\": programURL, \"programType\": \"\", \"scope\": {}}\n        if program[\"attributes\"][\"offers_bounties\"]:\n            dataJson[\"programType\"] = \"rdp\"\n            data[\"programType\"] = \"rdp\"\n        else:\n            dataJson[\"programType\"] = \"vdp\"\n            data[\"programType\"] = \"vdp\"\n        for target in program[\"relationships\"][\"structured_scopes\"][\"data\"]:\n            targetType = \"\"\n            if target['attributes']['eligible_for_submission']:\n                targetType = \"(InScope)\"\n            else:\n                targetType = \"(OutOfScope)\"\n            if target['attributes']['instruction'] is not None:\n                dataJson[\"scope\"][target[\"id\"]\n                                  ] = f\"{target['attributes']['asset_identifier']} {targetType}\\n{target['attributes']['instruction']}\\n\"\n            else:\n                dataJson[\"scope\"][target[\"id\"]\n                                  ] = f\"{target['attributes']['asset_identifier']} {targetType}\\n\"\n\n        hasChanged = False\n\n        scopeId = {prop for prop in dataJson[\"scope\"]}\n        dbScopeId = {prop for prop in watcherData[\"scope\"]}\n\n        newScope = scopeId - dbScopeId\n        removedScope = dbScopeId - scopeId\n        scopeId = scopeId - newScope\n        data[\"newScope\"] = []\n        data[\"changedScope\"] = []\n        data[\"removedScope\"] = []\n        data[\"newProgramType\"] = []\n        hasChanged = False\n        is_update = False\n        if newScope:\n            notifi_status = notifications['new_scope']\n            for i in newScope:\n                if notifi_status:\n                    data[\"newScope\"].append(dataJson[\"scope\"][i])\n                    is_update = True\n                watcherData[\"scope\"][i] = dataJson[\"scope\"][i]\n            hasChanged = True\n        if removedScope:\n            notifi_status = notifications['removed_scope']\n            for i in removedScope:\n                if notifi_status:\n                    data[\"removedScope\"].append(watcherData[\"scope\"][i])\n                    is_update = True\n                del watcherData[\"scope\"][i]\n                hasChanged = True\n        if dataJson[\"programType\"] != watcherData[\"programType\"]:\n            notifi_status = notifications['new_type']\n            if notifi_status:\n                data[\"newProgramType\"] = dataJson[\"programType\"]\n                is_update = True\n            watcherData[\"programType\"] = dataJson[\"programType\"]\n            hasChanged = True\n        notifi_status = notifications['changed_scope']\n        for id in scopeId:\n            if dataJson[\"scope\"][id] != watcherData[\"scope\"][id]:\n                if notifi_status:\n                    scope = {\n                        \"new\": dataJson[\"scope\"][id],\n                        \"old\": watcherData[\"scope\"][id],\n                    }\n                    data[\"changedScope\"].append(scope)\n                    is_update = True\n                watcherData[\"scope\"][id] = dataJson[\"scope\"][id]\n                hasChanged = True\n\n        if hasChanged:\n            save_data(db, \"hackerone\", programKey, watcherData)\n            if check_send_notification(first_time, is_update, data,watcherData, monitor, notifications):\n                    send_notification(data, mUrl)\n\n    db_programs_key = db['hackerone'].distinct(\"programKey\")\n    removed_programs_key = set(db_programs_key) - set(json_programs_key)\n    for program_key in removed_programs_key:\n        program = find_program(db,'hackerone', program_key)\n        data = {\n            \"color\": 16777215,\n            \"logo\": \"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTwToiI8YA0eLclDkd-vJ0xXs7bun5LdHfTrgJucvI&s\",\n            \"platformName\": \"HackerOne\",\n            \"isRemoved\": True, \n            \"programName\": program[\"programName\"],\n            \"programType\": program[\"programType\"]\n        }\n        if notifications['removed_program'] and not first_time:\n            send_notification(data,mUrl)\n        db['hackerone'].delete_many({\"programKey\": program_key})        \n"
  },
  {
    "path": "modules/platforms/intigriti.py",
    "content": "import json\nfrom modules.platforms.functions import find_program, generate_program_key, get_resource, save_data, check_send_notification\nfrom modules.notifier.discord import send_notification\n\n\n# checking intigriti\ndef check_intigriti(tmp_dir, mUrl, first_time, db, config):\n    json_programs_key = []\n    notifications = config['notifications']\n    monitor = config['monitor']\n    get_resource(tmp_dir, config['url'], \"intigriti\")\n    intigriti = open(f\"{tmp_dir}intigriti.json\")\n    intigriti = json.load(intigriti)\n    for program in intigriti:\n        programName = program[\"name\"]\n        logo = \"https://secure.gravatar.com/avatar/8609b9cc1f1333c3632b2afdc8607f4d?s=500&d=identicon&r=g\"\n        programURL = f\"https://app.intigriti.com/{program['webLinks']['detail']}\"\n        data = {\"programName\": programName, \"programType\": \"\", \"programURL\": programURL,\n                \"logo\": logo, \"platformName\": \"Intigriti\",\"isRemoved\": False, \"isNewProgram\": False, \"color\": 10858237}\n        dataJson = {\"programName\": programName,\n                    \"programURL\": programURL, \"programType\": \"\", \"scope\": {}, \"reward\": {}}\n        programKey = generate_program_key(programName, programURL)\n        json_programs_key.append(programKey)\n        watcherData = find_program(db, 'intigriti', programKey)\n\n        if watcherData is None:\n            data[\"isNewProgram\"] = True\n            watcherData = {\"programName\": programName,\n                           \"programURL\": programURL, \"programType\": \"\", \"scope\": {}, \"reward\": {}}\n        if \"domains\" in program:\n            for target in program['domains']:\n                if target['description'] is not None:\n                    dataJson['scope'][target['id']\n                                    ] = f\"{target['endpoint']}\\n{target['description']}\\n\"\n                else:\n                    dataJson['scope'][target['id']] = f\"{target['endpoint']}\\n\"\n        if program[\"maxBounty\"][\"value\"] > 0:\n            dataJson[\"programType\"] = \"rdp\"\n            bounty = {\n                \"min\": f\"{program['minBounty']['value']} {program['minBounty']['currency']}\",\n                \"max\": f\"{program['maxBounty']['value']} {program['maxBounty']['currency']}\"\n            }\n            dataJson[\"reward\"] = bounty\n            data[\"programType\"] = \"rdp\"\n\n        else:\n            dataJson[\"programType\"] = \"vdp\"\n            data[\"programType\"] = \"vdp\"\n        # Checking for changes\n        hasChanged = False\n\n        scopeId = {prop for prop in dataJson[\"scope\"]}\n        dbScopeId = {prop for prop in watcherData[\"scope\"]}\n        newScope = scopeId - dbScopeId\n        removedScope = dbScopeId - scopeId\n        scopeId = scopeId - newScope\n        data[\"newScope\"] = []\n        data[\"changedScope\"] = []\n        data[\"removedScope\"] = []\n        data[\"newProgramType\"] = []\n        data[\"newReward\"] = []\n        hasChanged = False\n        is_update = False\n        if newScope:\n            notifi_status = notifications['new_scope']\n            for i in newScope:\n                if notifi_status:\n                    data[\"newScope\"].append(dataJson[\"scope\"][i])\n                    is_update = True\n                watcherData[\"scope\"][i] = dataJson[\"scope\"][i]\n            hasChanged = True\n        if removedScope:\n            notifi_status = notifications['removed_scope']\n            for i in removedScope:\n                if notifi_status:\n                    data[\"removedScope\"].append(watcherData[\"scope\"][i])\n                    is_update = True\n                del watcherData[\"scope\"][i]\n            hasChanged = True\n        if dataJson[\"programType\"] != watcherData[\"programType\"]:\n            notifi_status = notifications['new_type']\n            if notifi_status:\n                data[\"newProgramType\"] = dataJson[\"programType\"]\n                is_update = True\n            watcherData[\"programType\"] = dataJson[\"programType\"]\n            hasChanged = True\n        if dataJson[\"reward\"] != watcherData[\"reward\"]:\n            notifi_status = notifications['new_bounty_table']\n            if notifi_status:\n                data[\"newReward\"] = dataJson[\"reward\"]\n                is_update = True\n            watcherData[\"reward\"] = dataJson[\"reward\"]\n            hasChanged = True\n\n        notifi_status = notifications['changed_scope']\n        for id in scopeId:\n            if dataJson[\"scope\"][id] != watcherData[\"scope\"][id]:\n                if notifi_status:\n                    scope = {\n                        \"new\": dataJson[\"scope\"][id],\n                        \"old\": watcherData[\"scope\"][id],\n                    }\n                    data[\"changedScope\"].append(scope)\n                    is_update = True\n                watcherData[\"scope\"][id] = dataJson[\"scope\"][id]\n                hasChanged = True\n        if hasChanged:\n            save_data(db, \"intigriti\", programKey, watcherData)\n            if check_send_notification(first_time, is_update, data,watcherData, monitor, notifications):\n                    send_notification(data, mUrl)\n    db_programs_key = db['intigriti'].distinct(\"programKey\")\n    removed_programs_key = set(db_programs_key) - set(json_programs_key)\n    for program_key in removed_programs_key:\n        program = find_program(db,'intigriti', program_key)\n        data = {\n            \"color\": 10858237,\n            \"logo\": \"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTwToiI8YA0eLclDkd-vJ0xXs7bun5LdHfTrgJucvI&s\",\n            \"platformName\": \"Intigriti\",\n            \"isRemoved\": True, \n            \"programName\": program[\"programName\"],\n            \"programType\": program[\"programType\"]\n        }\n        if notifications['removed_program'] and not first_time:\n            send_notification(data,mUrl)\n        db['intigriti'].delete_many({\"programKey\": program_key})        \n"
  },
  {
    "path": "modules/platforms/yeswehack.py",
    "content": "import json\nfrom modules.platforms.functions import find_program, generate_program_key, get_resource, remove_elements, save_data, check_send_notification\nfrom modules.notifier.discord import send_notification\n\n\n# checking yeswehack\ndef check_yeswehack(tmp_dir, mUrl, first_time, db, config):\n    json_programs_key = []\n    notifications = config['notifications']\n    monitor = config['monitor']\n    get_resource(tmp_dir, config['url'], \"yeswehack\")\n    yeswehack = open(f\"{tmp_dir}yeswehack.json\")\n    yeswehack = json.load(yeswehack)\n    for program in yeswehack:\n        programName = program['title']\n        logo = program['thumbnail']['url']\n        programURL = f\"https://yeswehack.com/programs/{program['slug']}\"\n        data = {\"programName\": programName, \"programType\": \"\", \"programURL\": programURL,\n                \"logo\": logo, \"platformName\": \"YesWeHack\",\"isRemoved\": False, \"isNewProgram\": False, \"color\": 16270147}\n        dataJson = {\"programName\": programName,\n                    \"programURL\": programURL, \"programType\": \"\", \"inScope\": [], \"reward\": {}}\n        programKey = generate_program_key(programName, programURL)\n        json_programs_key.append(programKey)\n        watcherData = find_program(db, 'yeswehack', programKey)\n\n        if watcherData is None:\n            data[\"isNewProgram\"] = True\n            watcherData = {\"programName\": programName,\n                           \"programURL\": programURL, \"programType\": \"\", \"inScope\": [], \"reward\": {}}\n        for target in program[\"scopes\"]:\n            dataJson['inScope'].append(target['scope'])\n        if program[\"bounty\"]:\n            dataJson['programType'] = \"rdp\"\n            data['programType'] = \"rdp\"\n            currency = program['business_unit']['currency']\n            bounty = {\n                \"min\": f\"{program['bounty_reward_min']} {currency}\",\n                \"max\": f\"{program['bounty_reward_max']} {currency}\"\n            }\n            dataJson['reward'] = bounty\n        else:\n            dataJson['programType'] = \"vdp\"\n            data['programType'] = \"vdp\"\n\n        newInScope = [i for i in dataJson[\"inScope\"]\n                      if i not in watcherData[\"inScope\"]]\n        removeInScope = [i for i in watcherData[\"inScope\"]\n                         if i not in dataJson[\"inScope\"]]\n\n        data[\"newType\"] = []\n        data[\"reward\"] = []\n        data[\"removeInScope\"] = []\n        data[\"newInScope\"] = []\n        hasChanged = False\n        is_update = False\n        if newInScope:\n            watcherData[\"inScope\"].extend(newInScope)\n            notifi_status = notifications['new_inscope']\n            if notifi_status:\n                data[\"newInScope\"] = newInScope\n                is_update = True\n            hasChanged = True\n        if removeInScope:\n            remove_elements(watcherData[\"inScope\"], removeInScope)\n            notifi_status = notifications['removed_inscope']\n            if notifi_status:\n                data[\"removeInScope\"] = removeInScope\n                is_update = True\n            hasChanged = True\n        if dataJson['reward'] != watcherData['reward']:\n            watcherData[\"reward\"] = dataJson['reward']\n            notifi_status = notifications['new_bounty_table']\n            if notifi_status:\n                data[\"reward\"] = dataJson['reward']\n                is_update = True\n            hasChanged = True\n        if dataJson[\"programType\"] != watcherData[\"programType\"]:\n            notifi_status = notifications['new_type']\n            if notifi_status:\n                data[\"newType\"] = dataJson[\"programType\"]\n                is_update = True\n            watcherData[\"programType\"] = dataJson[\"programType\"]\n            hasChanged = True\n        if hasChanged:\n            save_data(db, \"yeswehack\", programKey, watcherData)\n            if check_send_notification(first_time, is_update, data,watcherData, monitor, notifications):\n                    send_notification(data, mUrl)\n\n    db_programs_key = db['yeswehack'].distinct(\"programKey\")\n    removed_programs_key = set(db_programs_key) - set(json_programs_key)\n    for program_key in removed_programs_key:\n        program = find_program(db,'yeswehack', program_key)\n        data = {\n            \"color\": 16270147,\n            \"logo\": \"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTwToiI8YA0eLclDkd-vJ0xXs7bun5LdHfTrgJucvI&s\",\n            \"platformName\": \"YesWeHack\",\n            \"isRemoved\": True, \n            \"programName\": program[\"programName\"],\n            \"programType\": program[\"programType\"]\n        }\n        if notifications['removed_program'] and not first_time:\n            send_notification(data,mUrl)\n        db['yeswehack'].delete_many({\"programKey\": program_key})        \n"
  },
  {
    "path": "requirements.txt",
    "content": "certifi==2023.7.22\ncharset-normalizer==3.2.0\ndiscord-webhook==1.3.0\ndnspython==2.4.2\nidna==3.4\npymongo==4.5.0\nPyYAML==6.0.1\nrequests==2.31.0\nurllib3==2.0.5\n"
  }
]