├── VERSION
├── .dockerignore
├── .gitignore
├── please_sign.jpg
├── No-Trespassing.gif
├── example
└── images
│ ├── Botvid-19.png
│ └── No-Trespassing.gif
├── supervisord.conf
├── docker-compose.yml
├── config.yml
├── .github
└── workflows
│ └── docker.yml
├── arm
├── helpers.py
└── Dockerfile
├── helpers.py
├── workers
├── Webtop_Health_Statements.py
├── Amdocs_Health_Statements.py
├── Infogan_Health_Statements.py
├── Mashov_Health_Statements.py
└── Health_Statements.py
├── README.md
├── Dockerfile
├── dockerbot.py
└── License
/VERSION:
--------------------------------------------------------------------------------
1 | 1.1.0
2 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | screenshots
2 | forms-automation-cli.py
3 | forms-demo.py
4 |
--------------------------------------------------------------------------------
/please_sign.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/t0mer/Botvid-19/HEAD/please_sign.jpg
--------------------------------------------------------------------------------
/No-Trespassing.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/t0mer/Botvid-19/HEAD/No-Trespassing.gif
--------------------------------------------------------------------------------
/example/images/Botvid-19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/t0mer/Botvid-19/HEAD/example/images/Botvid-19.png
--------------------------------------------------------------------------------
/example/images/No-Trespassing.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/t0mer/Botvid-19/HEAD/example/images/No-Trespassing.gif
--------------------------------------------------------------------------------
/supervisord.conf:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | nodaemon=true
3 |
4 | [program:dockerbot]
5 | command=python /opt/dockerbot/dockerbot.py
6 | stdout_logfile=/var/log/dockerbot
7 | stdout_logfile_maxbytes = 0
8 | stderr_logfile=/dev/stderr
9 | stderr_logfile_maxbytes = 0
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.7"
2 |
3 | services:
4 | botvid:
5 | image: techblog/botvid-19
6 | container_name: botvid
7 | restart: always
8 | labels:
9 | - "com.ouroboros.enable=true"
10 | environment:
11 | - API_KEY=
12 | - ALLOWED_IDS=
13 | volumes:
14 | - ./botvid/config/:/opt/dockerbot/config
15 |
--------------------------------------------------------------------------------
/config.yml:
--------------------------------------------------------------------------------
1 | edu:
2 | USER_ID:
3 | USER_KEY:
4 | mashov:
5 | #Add Kids Block as needed
6 | #UNused Kid Block should be left empty or removed from file
7 | kid1:
8 | MASHOV_USER_ID_KID:
9 | MASHOV_USER_PWD_KID:
10 | MASHOV_SCHOOL_ID_KID:
11 | kid2:
12 | MASHOV_USER_ID_KID:
13 | MASHOV_USER_PWD_KID:
14 | MASHOV_SCHOOL_ID_KID:
15 | infogan:
16 | BASE_URL:
17 | PARENT_NAME:
18 | PARENT_ID:
19 | KID_NAME:
20 | KID_ID:
21 | webtop:
22 | USER_ID:
23 | USER_KEY:
24 | amdocs:
25 | EMAIL:
26 | USER_ID:
27 | PASSWORD:
--------------------------------------------------------------------------------
/.github/workflows/docker.yml:
--------------------------------------------------------------------------------
1 | name: docker_builde
2 |
3 | on:
4 | release:
5 | types: [ published ]
6 |
7 |
8 | jobs:
9 | docker:
10 | runs-on: ubuntu-latest
11 | steps:
12 | -
13 | name: Checkout
14 | uses: actions/checkout@v2
15 | -
16 | name: Set up QEMU
17 | uses: docker/setup-qemu-action@v1
18 | -
19 | name: Set up Docker Buildx
20 | uses: docker/setup-buildx-action@v1
21 | -
22 | name: Login to DockerHub
23 | uses: docker/login-action@v1
24 | with:
25 | username: ${{ secrets.DOCKERHUB_USERNAME }}
26 | password: ${{ secrets.DOCKERHUB_TOKEN }}
27 |
28 | - name: Get current date
29 | id: getDate
30 | run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
31 |
32 | - name: Get semantic version from file
33 | id: getSemver
34 | run: echo "::set-output name=semver::$(cat VERSION | tr -d ' \t\n\r' )"
35 |
36 |
37 | -
38 | name: Build and push
39 | uses: docker/build-push-action@v2
40 | with:
41 | context: .
42 | platforms: linux/amd64,linux/arm64,linux/arm/v7
43 | push: true
44 | tags: |
45 | techblog/botvid-19:latest
46 | techblog/botvid-19:${{ steps.getSemver.outputs.semver }}
47 |
--------------------------------------------------------------------------------
/arm/helpers.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from selenium import webdriver
3 | from selenium.webdriver.chrome.options import Options
4 | from datetime import date
5 | from datetime import time
6 | from datetime import datetime
7 | import time
8 | import os
9 | from os import path
10 | from selenium.common.exceptions import InvalidSessionIdException
11 | from loguru import logger
12 |
13 | #### Setting ChromeOptions ####
14 | def GetBrowser():
15 | options = webdriver.ChromeOptions()
16 | options.add_argument("-incognito")
17 | options.add_argument("--headless")
18 | options.add_argument("disable-gpu")
19 | options.add_argument("--no-sandbox")
20 | options.add_argument('--start-maximized')
21 | options.add_argument("--disable-dev-shm-usage")
22 | options.add_argument("--window-size=360,640")
23 | options.add_argument('--ignore-certificate-errors')
24 | browser = webdriver.Chrome(executable_path='/opt/chromedriver-83.0.4103.122/chromedriver', options=options)
25 | return browser
26 |
27 | ##### Screenshot for mobile view - like webtop #####
28 | def mobile_screenshot(browser,Image):
29 | if path.exists(Image):
30 | os.remove(Image)
31 | logger.info(browser)
32 | browser.set_window_size(380, 660) #the trick
33 | time.sleep(2)
34 | browser.save_screenshot(Image)
35 | browser.close()
36 |
37 | #### Screenshot for regular view ####
38 | def fullpage_screenshot(browser,Image):
39 | if path.exists(Image):
40 | os.remove(Image)
41 | logger.info(browser)
42 | browser.set_window_size(800, 600) #the trick
43 | time.sleep(2)
44 | browser.save_screenshot(Image)
45 |
46 |
47 | #### Browser state logging ####
48 | def log_browser(browser):
49 | logger.debug(f"Opened page. Url: {browser.current_url}, size: {len(browser.page_source)}")
50 |
51 |
52 |
53 | def ping(browser, page):
54 | try:
55 | browser.get('https://bots.techblog.co.il/' + page + '.html')
56 | except:
57 | logger.info("Unable to ping")
--------------------------------------------------------------------------------
/helpers.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from selenium import webdriver
3 | from selenium.webdriver.chrome.options import Options
4 | from datetime import date
5 | from datetime import time
6 | from datetime import datetime
7 | import time
8 | import os
9 | from os import path
10 | from selenium.common.exceptions import InvalidSessionIdException
11 | from loguru import logger
12 |
13 | #### Setting ChromeOptions ####
14 | def GetBrowser():
15 | options = webdriver.ChromeOptions()
16 | options.add_argument("-incognito")
17 | options.add_argument("--headless")
18 | options.add_argument("disable-gpu")
19 | options.add_argument("--no-sandbox")
20 | options.add_argument('--start-maximized')
21 | options.add_argument("--disable-dev-shm-usage")
22 | options.add_argument("--window-size=360,640")
23 | options.add_argument('--ignore-certificate-errors')
24 | browser = webdriver.Chrome(executable_path='/opt/chromedriver-86.0.4240.22/chromedriver', options=options)
25 | return browser
26 |
27 | ##### Screenshot for mobile view - like webtop #####
28 | def mobile_screenshot(browser,Image):
29 | if path.exists(Image):
30 | os.remove(Image)
31 | logger.info(browser)
32 | browser.set_window_size(380, 900) #the trick
33 | time.sleep(2)
34 | browser.save_screenshot(Image)
35 | browser.close()
36 |
37 | #### Screenshot for regular view ####
38 | def fullpage_screenshot(browser,Image):
39 | if path.exists(Image):
40 | os.remove(Image)
41 | logger.info(browser)
42 | browser.set_window_size(800, 600) #the trick
43 | time.sleep(2)
44 | browser.save_screenshot(Image)
45 |
46 |
47 | #### Browser state logging ####
48 | def log_browser(browser):
49 | logger.debug(f"Opened page. Url: {browser.current_url}, size: {len(browser.page_source)}")
50 |
51 |
52 |
53 | def ping(browser, page):
54 | try:
55 | browser.get('https://bots.techblog.co.il/' + page + '.html')
56 | time.sleep(2)
57 | except:
58 | logger.info("Unable to ping")
59 |
--------------------------------------------------------------------------------
/workers/Webtop_Health_Statements.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from selenium import webdriver
3 | from selenium.webdriver.chrome.options import Options
4 | from datetime import date, time, datetime
5 | from selenium.common.exceptions import InvalidSessionIdException
6 | import os, time, helpers
7 | from loguru import logger
8 |
9 | def sign(userCode, sitePassword, Image):
10 | try:
11 | browser = helpers.GetBrowser()
12 | logger.info("Starting process")
13 |
14 | try:
15 | helpers.ping(browser, 'infogan')
16 | except:
17 | logger.debug('Unable to ping')
18 |
19 | browser.get("https://www.webtop.co.il/mobilev2/?")
20 | helpers.log_browser(browser)
21 | time.sleep(1)
22 |
23 | browser.find_element_by_xpath('//*[@id="misradHachinuch"]').click()
24 | time.sleep(2)
25 | helpers.log_browser(browser)
26 |
27 | browser.get('https://lgn.edu.gov.il/nidp/wsfed/ep?id=EduCombinedAuthUidPwd&sid=0&option=credential&sid=0')
28 | time.sleep(2)
29 | helpers.log_browser(browser)
30 | browser.find_element_by_xpath('//*[@id="HIN_USERID"]').send_keys(str(userCode))
31 | browser.find_element_by_xpath('//*[@id="Ecom_Password"]').send_keys(str(sitePassword))
32 | time.sleep(1)
33 | logger.info('logged in!')
34 | browser.find_element_by_xpath('//*[@id="loginButton2"]').click()
35 | time.sleep(2)
36 |
37 | browser.get("https://www.webtop.co.il/mobilev2/corona.aspx")
38 | time.sleep(2)
39 | sign_btn=browser.find_element_by_xpath('//*[@id="viewData"]')
40 | if 'disabled' not in sign_btn.get_attribute('class').split():
41 | logger.info("class: " + sign_btn.get_attribute('class'))
42 | sign_btn.click()
43 | time.sleep(1)
44 | browser.find_element_by_xpath('//*[@id="signForm"]').click()
45 | time.sleep(2)
46 | helpers.log_browser(browser)
47 | helpers.mobile_screenshot(browser,Image)
48 | return 1
49 | except Exception as ex:
50 | logger.error(str(ex))
51 | return 0
52 |
--------------------------------------------------------------------------------
/workers/Amdocs_Health_Statements.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from selenium import webdriver
3 | from selenium.webdriver.chrome.options import Options
4 | from datetime import date, datetime
5 | import time, os
6 | from selenium.common.exceptions import InvalidSessionIdException
7 | from loguru import logger
8 | import helpers
9 |
10 | def sign(email, user_id, password, Image):
11 |
12 | try:
13 | logger.info("Starting process")
14 | browser = helpers.GetBrowser()
15 | try:
16 | helpers.ping(browser, 'infogan')
17 | except:
18 | logger.debug('Unable to ping')
19 |
20 | browser.delete_all_cookies()
21 | browser.get("https://amdocsprodmain.service-now.com/one_portal?id=aop_sc_cat_item&sys_id=781f12f1db5cd8903780e1aa4b961903")
22 | helpers.log_browser(browser)
23 |
24 | time.sleep(4)
25 |
26 | # Email page
27 | xpath_email = browser.find_element_by_xpath('//*[@id="i0116"]')
28 | xpath_email_submit = browser.find_element_by_xpath('//*[@id="idSIButton9"]')
29 |
30 | xpath_email.send_keys(email)
31 | xpath_email_submit.click()
32 |
33 | time.sleep(4)
34 |
35 | # User and Password page
36 | xpath_user = browser.find_element_by_xpath('//*[@id="userNameInput"]')
37 | xpath_password = browser.find_element_by_xpath('//*[@id="passwordInput"]')
38 | xpath_sign_in = browser.find_element_by_xpath('//*[@id="submitButton"]')
39 |
40 | xpath_user.clear()
41 | xpath_user.send_keys("ntnet\\" + user_id)
42 | xpath_password.send_keys(password)
43 | xpath_sign_in.click()
44 |
45 | time.sleep(4)
46 |
47 | # Declaration
48 | xpath_approved = browser.find_element_by_xpath("//input[@name='u_approved']")
49 | xpath_submit = browser.find_element_by_xpath("//button[@name='submit']")
50 |
51 | xpath_approved.click()
52 | xpath_submit.click()
53 |
54 | helpers.log_browser(browser)
55 | helpers.fullpage_screenshot(browser,Image)
56 |
57 | return 1
58 | except Exception as ex:
59 | logger.error(str(ex))
60 | return 0
61 |
--------------------------------------------------------------------------------
/workers/Infogan_Health_Statements.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from selenium import webdriver
3 | from selenium.webdriver.chrome.options import Options
4 | from datetime import date, datetime
5 | import time, os
6 | from selenium.common.exceptions import InvalidSessionIdException
7 | from loguru import logger
8 | import helpers
9 |
10 |
11 |
12 | def sign(parentName, parentId, kidName, kidId, formUrl, Image):
13 |
14 | try:
15 | logger.info("Starting process")
16 | browser = helpers.GetBrowser()
17 | try:
18 | helpers.ping(browser, 'infogan')
19 | except:
20 | logger.debug('Unable to ping')
21 |
22 | browser.get(formUrl)
23 | helpers.log_browser(browser)
24 | #get needed elements
25 | kid_id = '//*[@id="form-field-email"]'
26 | kid_name = '//*[@id="form-field-name"]'
27 | parent_id = '//*[@id="form-field-field_4"]'
28 | parent_name = '//*[@id="form-field-field_3"]'
29 | form_date = '//*[@id="form-field-field_2"]'
30 |
31 | #Fill Date
32 | browser.find_element_by_xpath(form_date).send_keys(str(datetime.now().date()))
33 | # #UnFocus Date Fiels
34 | # browser.find_element_by_xpath(kid_id).click()
35 | #Fill Kid id
36 | browser.find_element_by_xpath(kid_id).send_keys(str(kidId))
37 | #Fill Kid Name
38 | browser.find_element_by_xpath(kid_name).send_keys(str(kidName))
39 | #Fill Parent ID
40 | browser.find_element_by_xpath(parent_id).send_keys(str(parentId))
41 | #Fill Parent NAme
42 | browser.find_element_by_xpath(parent_name).send_keys(str(parentName))
43 |
44 | #Set Checkbox
45 | browser.find_element_by_xpath('//*[@id="form-field-field_1-0"]').click()
46 | browser.find_element_by_xpath('//*[@id="form-field-field_1-1"]').click()
47 | browser.find_element_by_xpath('//*[@id="form-field-field_1-2"]').click()
48 | browser.find_element_by_xpath('//*[@id="form-field-field_5"]').click()
49 |
50 | #Send The Form
51 | browser.find_element_by_xpath('//*[@type="submit"]').submit()
52 |
53 | time.sleep(2)
54 | helpers.log_browser(browser)
55 | helpers.fullpage_screenshot(browser,Image)
56 | return 1
57 | except Exception as ex:
58 | logger.error(str(ex))
59 | return 0
--------------------------------------------------------------------------------
/workers/Mashov_Health_Statements.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from selenium import webdriver
3 | from selenium.webdriver.chrome.options import Options
4 | from selenium.webdriver.common.keys import Keys
5 | from datetime import date, time, datetime
6 | from selenium.common.exceptions import InvalidSessionIdException
7 | import os, time, helpers
8 | from loguru import logger
9 |
10 |
11 | def sign(user, password, schoolid, kidnum, Image):
12 | try:
13 | browser = helpers.GetBrowser()
14 | logger.info("Starting process")
15 |
16 | try:
17 | helpers.ping(browser, 'infogan')
18 | except:
19 | logger.debug('Unable to ping')
20 | logger.debug('----------------------------------------------------------------------')
21 | logger.info("Starting Mashov Sign process for kid number: " + kidnum)
22 |
23 | browser.get("https://web.mashov.info/students/login")
24 | time.sleep(3)
25 |
26 | #choose school:
27 | form_element_mashov_select_school = browser.find_element_by_xpath("//*[@id='mat-input-3']")
28 | form_element_mashov_user = browser.find_element_by_xpath("//*[@id='mat-input-0']")
29 | form_element_mashov_password = browser.find_element_by_xpath("//*[@id='mat-input-4']")
30 | form_element_mashov_login = browser.find_element_by_xpath("//*[@id='mat-tab-content-0-0']/div/div/button[1]")
31 |
32 |
33 | form_element_mashov_select_school.click()
34 | #form_element_mashov_select_school.send_keys(_convert(var_mashov_school_id)) # Moving to identify by school number instead of school name
35 | form_element_mashov_select_school.send_keys(schoolid)
36 | form_element_mashov_select_school.send_keys(Keys.ARROW_DOWN)
37 | form_element_mashov_select_school.send_keys(Keys.RETURN)
38 |
39 | form_element_mashov_user.click()
40 | form_element_mashov_user.send_keys(user)
41 | form_element_mashov_password.click()
42 | form_element_mashov_password.send_keys(password)
43 | form_element_mashov_login.click()
44 | time.sleep(3)
45 |
46 | logger.info(f"Logged in")
47 |
48 | form_element_mashov_select_daily_corona_report = browser.find_element_by_xpath("/html/body/mshv-root/mshv-main/mat-sidenav-container/mat-sidenav-content/mshv-student-covidsplash/mat-card/mat-card-content/div[3]/mat-card")
49 | form_element_mashov_select_daily_corona_report.click()
50 | time.sleep(2)
51 |
52 | form_element_mashov_submit_report = browser.find_element_by_xpath("/html/body/mshv-root/mshv-main/mat-sidenav-container/mat-sidenav-content/mshv-students-covid-clearance/mat-card/mat-card/mat-card-content[2]/mat-card-actions/button")
53 | if 'אני מצהיר' in form_element_mashov_submit_report.text:
54 | form_element_mashov_submit_report.click()
55 |
56 |
57 | logger.info(f"Submitted Report")
58 | time.sleep(1)
59 |
60 | helpers.log_browser(browser)
61 | helpers.fullpage_screenshot(browser,Image)
62 |
63 | logger.info(f"Screenshot Saved")
64 | logger.info("Finished Mashov Sign process for kid number: " + kidnum)
65 | logger.debug('----------------------------------------------------------------------')
66 |
67 | return 1
68 | except Exception as ex:
69 | logger.error(str(ex))
70 | return 0
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | *Please :star: this repo if you find it useful*
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | # Botvid-19
10 |
11 | Botvid-19 is a [Telepot](https://telepot.readthedocs.io/en/latest/) and selenium powered, easy to use Telegram bot for signing Covid-19 digital health statements.
12 |
13 |
14 | #### Credits:
15 | =======
16 |
17 | - [Adam Russak](https://github.com/AdamRussak) for working with me on this project and writing the selenium part
18 |
19 | ## Usage
20 |
21 | ### supported platforms:
22 | * [edu](https://parents.education.gov.il)
23 | * [mashov](https://web.mashov.info/students/login)
24 | * [infogan](https://https://campaign.infogan.co.il/)
25 | * [webtop](https://www.webtop.co.il/mobilev2/?)
26 |
27 | #### docker-compose from hub
28 | ```yaml
29 | version: "3.7"
30 |
31 | services:
32 | botvid:
33 | image: techblog/botvid-19
34 | container_name: botvid
35 | restart: always
36 | labels:
37 | - "com.ouroboros.enable=true"
38 | environment:
39 | - API_KEY=
40 | - ALLOWED_IDS=
41 | volumes:
42 | - ./botvid/config/:/opt/dockerbot/config
43 |
44 | ```
45 |
46 | Replace API_KEY with your bot token. if you do not have existing bot you can create one
47 | using the instruction in this article:
48 | [Bots: An introduction for developers](https://core.telegram.org/bots)
49 |
50 | In order to secure the bot and block unwanted calls from Unauthorized users add your allowd Id's with comma separated values into ALLOWED_IDS
51 | environmet. in order to get your id use @myidbot in telegram and send the /getid command. the result will be your ID.
52 |
53 | Run
54 | ```
55 | docker-compose up -d
56 | ```
57 | A config file will be created in ./botvid/config/config.yaml
58 |
59 | Please fill in the parameters in the file config.yml
60 | ```
61 | edu:
62 | USER_ID:
63 | USER_KEY:
64 | mashov:
65 | #Add Kids Block as needed
66 | #UNused Kid Block should be left empty or removed from file
67 | kid1:
68 | MASHOV_USER_ID_KID:
69 | MASHOV_USER_PWD_KID:
70 | MASHOV_SCHOOL_ID_KID:
71 | kid2:
72 | MASHOV_USER_ID_KID:
73 | MASHOV_USER_PWD_KID:
74 | MASHOV_SCHOOL_ID_KID:
75 | infogan:
76 | BASE_URL:
77 | PARENT_NAME:
78 | PARENT_ID:
79 | KID_NAME:
80 | KID_ID:
81 | webtop:
82 | USER_ID:
83 | USER_KEY:
84 | ```
85 |
86 | You may fill only the section that are relevant to you.
87 |
88 | Enter the bot in Telegram and run the relevant command:
89 | `/sign` - all configured commands
90 | `/sign_mashov` - only Mashov
91 | `/sign_infogan` - only InfoGan
92 | `/sign_webtop` - only WebTop
93 | `/?` or `/start` - show configured settings
94 |
95 | You will get the signed form after about 10 seconds.
96 |
97 | [](https://raw.githubusercontent.com/t0mer/Botvid-19/master/Botvid-19.png "Telegram Bot Integration")
98 |
99 |
100 |
101 |
102 | # Donation
103 |
104 | If you find this project helpful, you can give me a cup of coffee :)
105 |
106 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8CGLEHN2NDXDE)
107 |
--------------------------------------------------------------------------------
/workers/Health_Statements.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from selenium import webdriver
3 | from selenium.webdriver.chrome.options import Options
4 | from datetime import date
5 | from datetime import time
6 | from datetime import datetime
7 | import time
8 | from argparse import ArgumentParser
9 | import os
10 | from selenium.common.exceptions import InvalidSessionIdException
11 | from loguru import logger
12 | import helpers
13 |
14 |
15 | def sign(userCode, sitePassword, Image):
16 | try:
17 | #### Starting Sign Proc ####
18 | logger.info("Starting process")
19 | logger.debug('-----------------------0000000000-----------------------------------------------')
20 | logger.debug(str(Image))
21 | #### Initialize Browser ####
22 | browser = helpers.GetBrowser()
23 |
24 | try:
25 | helpers.ping(browser, 'edu')
26 | except:
27 | logger.debug('Unable to ping')
28 |
29 | browser.get("https://parents.education.gov.il/prhnet/parents/rights-obligations-regulations/health-statement-kindergarden")
30 | start = '/html/body/app-root/main/div/components-page/div/div[2]/section[1]/div/health-declaration/section[1]/div[1]/div[1]/div[4]/div/div/div/input'
31 | time.sleep(2)
32 | helpers.fullpage_screenshot(browser,Image)
33 | helpers.log_browser(browser)
34 | browser.find_element_by_xpath(start).click()
35 | time.sleep(2)
36 | helpers.log_browser(browser)
37 | browser.get('https://lgn.edu.gov.il/nidp/wsfed/ep?id=EduCombinedAuthUidPwd&sid=0&option=credential&sid=0')
38 | time.sleep(2)
39 |
40 | #### Logging In ####
41 | user = '//*[@id="HIN_USERID"]'
42 | siteAccess = '//*[@id="Ecom_Password"]'
43 | NextPhase = '//*[@id="loginButton2"]'
44 | browser.find_element_by_xpath(user).send_keys(userCode)
45 | browser.find_element_by_xpath(siteAccess).send_keys(sitePassword)
46 | browser.find_element_by_xpath(NextPhase).click()
47 | time.sleep(2)
48 | helpers.log_browser(browser)
49 | logger.info(f"Logged in")
50 | time.sleep(2)
51 |
52 | try:
53 | element = "//input[@value='מילוי הצהרת בריאות']"
54 | checkForButton = browser.find_elements_by_xpath(element)
55 | LenCheckForButton = len(checkForButton)
56 | logger.info(f"Starting sign... check buttons: {LenCheckForButton}")
57 | if LenCheckForButton == 0:
58 | logger.error("Not able to find the check buttons. Exit")
59 | helpers.fullpage_screenshot(browser, Image)
60 |
61 | elif LenCheckForButton > 0:
62 | for x in range(LenCheckForButton):
63 | logger.info(x + 1)
64 | browser.find_element_by_xpath(element).click()
65 | time.sleep(2)
66 | ToApprove = browser.find_elements_by_xpath("//input[@value='אישור']")
67 |
68 | for a in ToApprove:
69 | browser.execute_script("arguments[0].click()", a)
70 | time.sleep(2)
71 | except Exception as ex:
72 | logger.error(str(ex))
73 |
74 | helpers.fullpage_screenshot(browser, Image)
75 | browser.close()
76 | return 1
77 | except Exception as ex:
78 | logger.info('#################################################################################################')
79 | logger.error(str(ex))
80 | browser.close()
81 | return 0
82 |
--------------------------------------------------------------------------------
/arm/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM armhf/ubuntu
2 |
3 | ENV DEBIAN_FRONTEND noninteractive
4 | ENV DEBCONF_NONINTERACTIVE_SEEN true
5 | ENV API_KEY ""
6 | ENV PYTHONIOENCODING=utf-8
7 | ENV LANG=C.UTF-8
8 |
9 | # Set timezone
10 | #RUN echo "Asia/Jerusalem" > /etc/timezone && \
11 | # dpkg-reconfigure --frontend noninteractive tzdata
12 |
13 | # Create a default user
14 | RUN groupadd --system automation && \
15 | useradd --system --create-home --gid automation --groups audio,video automation && \
16 | mkdir --parents /home/automation/reports && \
17 | chown --recursive automation:automation /home/automation
18 |
19 | # Update the repositories
20 | # Install dependencies
21 | # Install utilities
22 | # Install XVFB and TinyWM
23 | # Install fonts
24 | RUN apt-get -yqq update && \
25 | apt-get -yqq install gnupg2 && \
26 | apt-get -yqq install supervisor libnss3-dev && \
27 | apt-get -yqq install curl unzip wget && \
28 | apt-get -yqq install xvfb tinywm && \
29 | apt update && \
30 | apt full-upgrade -y && \
31 | apt install chromium -y && \
32 | rm -rf /var/lib/apt/lists/*
33 |
34 | # Install Chrome WebDriver
35 | #CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`
36 | RUN CHROMEDRIVER_VERSION=83.0.4103.122 && \
37 | mkdir -p /opt/chromedriver-$CHROMEDRIVER_VERSION && \
38 | wget -O /tmp/chromedriver-v9.3.2-linux-armv7l.zip https://github.com/electron/electron/releases/download/v9.3.2/chromedriver-v9.3.2-linux-armv7l.zip && \
39 | unzip -qq /tmp/chromedriver-v9.3.2-linux-armv7l.zip -d /opt/chromedriver-$CHROMEDRIVER_VERSION && \
40 | rm /tmp/chromedriver-v9.3.2-linux-armv7l.zip && \
41 | chmod +x /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver && \
42 | ln -fs /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver /usr/local/bin/chromedriver
43 |
44 | RUN pip install selenium --no-cache-dir && \
45 | pip install telepot --no-cache-dir && \
46 | pip install pyyaml --no-cache-dir && \
47 | pip install python-dotenv --no-cache-dir && \
48 | pip install loguru --no-cache-dir
49 |
50 | RUN pip3 install --upgrade pip --no-cache-dir && \
51 | pip3 install --upgrade setuptools --no-cache-dir && \
52 | pip install selenium --no-cache-dir && \
53 | pip install pyyaml --no-cache-dir && \
54 | pip install flask --no-cache-dir && \
55 | pip install flask_restful --no-cache-dir && \
56 | pip install loguru --no-cache-dir && \
57 | pip install cryptography==2.6.1 --no-cache-dir
58 |
59 | COPY config.yml /opt/dockerbot/config
60 | COPY config.yml /etc
61 | COPY workers/Health_Statements.py /opt/dockerbot
62 | COPY workers/Mashov_Health_Statements.py /opt/dockerbot
63 | COPY workers/Webtop_Health_Statements.py /opt/dockerbot
64 | COPY workers/Infogan_Health_Statements.py /opt/dockerbot
65 | COPY workers/Amdocs_Health_Statements.py /opt/dockerbot
66 | COPY arm/helpers.py /opt/dockerbot
67 | COPY dockerbot.py /opt/dockerbot
68 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
69 | COPY please_sign.jpg /opt/dockerbot
70 | COPY No-Trespassing.gif /opt/dockerbot
71 |
72 | VOLUME [ "/opt/config" ]
73 |
74 | RUN CHROMEDRIVER_VERSION=83.0.4103.122 && \
75 | echo "export CHROME_VERSION=83.0.4103.122 " >> /root/.bashrc && \
76 | echo 'export PATH=/opt/chromedriver-${CHROME_VERSION}:$PATH' >> /root/.bashrc
77 |
78 | # Default configuration
79 | ENV DISPLAY :20.0
80 | ENV SCREEN_GEOMETRY "1440x900x24"
81 | ENV CHROMEDRIVER_PORT 4444
82 | ENV CHROMEDRIVER_WHITELISTED_IPS "127.0.0.1"
83 | ENV CHROMEDRIVER_URL_BASE ''
84 | ENV CHROMEDRIVER_EXTRA_ARGS ''
85 |
86 | CMD ["/usr/bin/supervisord"]
87 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.14-rc-slim-bookworm
2 |
3 | ENV DEBIAN_FRONTEND noninteractive
4 | ENV DEBCONF_NONINTERACTIVE_SEEN true
5 | ENV API_KEY ""
6 | ENV PYTHONIOENCODING=utf-8
7 | ENV LANG=C.UTF-8
8 |
9 | # Set timezone
10 | RUN echo "Asia/Jerusalem" > /etc/timezone && \
11 | dpkg-reconfigure --frontend noninteractive tzdata
12 |
13 | # Create a default user
14 | RUN groupadd --system automation && \
15 | useradd --system --create-home --gid automation --groups audio,video automation && \
16 | mkdir --parents /home/automation/reports && \
17 | chown --recursive automation:automation /home/automation
18 |
19 | # Update the repositories
20 | # Install dependencies
21 | # Install utilities
22 | # Install XVFB and TinyWM
23 | # Install fonts
24 | RUN apt-get -yqq update && \
25 | apt-get -yqq install gnupg2 && \
26 | apt-get -yqq install supervisor && \
27 | apt-get -yqq install curl unzip && \
28 | apt-get -yqq install xvfb tinywm && \
29 | rm -rf /var/lib/apt/lists/*
30 |
31 | # Install Chrome WebDriver
32 | #CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`
33 | RUN CHROMEDRIVER_VERSION=86.0.4240.22 && \
34 | mkdir -p /opt/chromedriver-$CHROMEDRIVER_VERSION && \
35 | curl -sS -o /tmp/chromedriver_linux64.zip http://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip && \
36 | unzip -qq /tmp/chromedriver_linux64.zip -d /opt/chromedriver-$CHROMEDRIVER_VERSION && \
37 | rm /tmp/chromedriver_linux64.zip && \
38 | chmod +x /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver && \
39 | ln -fs /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver /usr/local/bin/chromedriver
40 |
41 | # Install Google Chrome
42 | RUN curl -sS -o - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
43 | echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list && \
44 | apt-get -yqq update && \
45 | apt-get -yqq install google-chrome-stable && \
46 | rm -rf /var/lib/apt/lists/*
47 |
48 | # Default configuration
49 | ENV DISPLAY :20.0
50 | ENV SCREEN_GEOMETRY "1440x900x24"
51 | ENV CHROMEDRIVER_PORT 4444
52 | ENV CHROMEDRIVER_WHITELISTED_IPS "127.0.0.1"
53 | ENV CHROMEDRIVER_URL_BASE ''
54 | ENV CHROMEDRIVER_EXTRA_ARGS ''
55 |
56 | RUN pip install selenium --no-cache-dir && \
57 | pip install telepot --no-cache-dir && \
58 | pip install pyyaml --no-cache-dir && \
59 | pip install python-dotenv --no-cache-dir && \
60 | pip install loguru --no-cache-dir
61 |
62 | RUN mkdir -p /opt/dockerbot \
63 | mkdir -p /opt/dockerbot/config \
64 | mkdir -p /opt/dockerbot/images \
65 | mkdir -p /var/log/supervisor
66 |
67 | COPY config.yml /opt/dockerbot/config
68 | COPY config.yml /etc
69 | COPY workers/Health_Statements.py /opt/dockerbot
70 | COPY workers/Mashov_Health_Statements.py /opt/dockerbot
71 | COPY workers/Webtop_Health_Statements.py /opt/dockerbot
72 | COPY workers/Infogan_Health_Statements.py /opt/dockerbot
73 | COPY workers/Amdocs_Health_Statements.py /opt/dockerbot
74 | COPY helpers.py /opt/dockerbot
75 | COPY dockerbot.py /opt/dockerbot
76 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
77 | COPY please_sign.jpg /opt/dockerbot
78 | COPY No-Trespassing.gif /opt/dockerbot
79 |
80 | VOLUME [ "/opt/config" ]
81 |
82 | RUN CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \
83 | echo "export CHROME_VERSION=86.0.4240.22" >> /root/.bashrc && \
84 | echo 'export PATH=/opt/chromedriver-${CHROME_VERSION}:$PATH' >> /root/.bashrc
85 |
86 | CMD ["/usr/bin/supervisord"]
87 | #ENTRYPOINT ["/usr/bin/python", "/opt/dockerbot/dockerbot.py"]
88 |
--------------------------------------------------------------------------------
/dockerbot.py:
--------------------------------------------------------------------------------
1 | from loguru import logger
2 | import time, re, random, datetime, telepot, os, sys
3 | from subprocess import call
4 | import subprocess, yaml, shutil
5 | from os import path
6 | from telepot.loop import MessageLoop
7 | from dotenv import load_dotenv
8 |
9 | configfile="/opt/dockerbot/config/config.yml"
10 | original_configfile = r'/etc/config.yml'
11 |
12 |
13 | def copyConfig():
14 | if not os.path.exists(configfile):
15 | shutil.copyfile(original_configfile, configfile)
16 |
17 |
18 | copyConfig()
19 |
20 | # Load Configuration
21 | with open("/opt/dockerbot/config/config.yml", 'r') as stream:
22 | try:
23 | list = yaml.safe_load(stream)
24 | except yaml.YAMLError as exc:
25 | print(exc)
26 |
27 | # Vars For https://web.mashov.info
28 | v_MASHOV_NUMBER_OF_KIDS = len(list['mashov'])
29 | if v_MASHOV_NUMBER_OF_KIDS >= 1 and list['mashov']['kid1']['MASHOV_USER_ID_KID'] != None:
30 | v_MASHOV_NUMBER_OF_KIDS = v_MASHOV_NUMBER_OF_KIDS + 1
31 |
32 | def handle(msg):
33 | config_edu = 1
34 | config_mashov = 1
35 | message_id = str(msg['message_id'])
36 | chat_id = msg['chat']['id']
37 | _command = command = str(msg['text'])
38 | logger.info("{message_id} Got msg: {command}")
39 |
40 | # Reject unauthorized requests
41 | if str(chat_id) not in os.getenv('ALLOWED_IDS'):
42 | bot.sendPhoto(
43 | chat_id, "https://github.com/t0mer/dockerbot/raw/master/No-Trespassing.gif")
44 | logger.error(f"[{message_id}] Chat id not allowed: {chat_id}")
45 | return
46 | #need to fix: so only active parts will show and not all options
47 | if command == '/?' or command == '/start':
48 | bot.sendMessage(chat_id, "List of available commands: ")
49 | if list['edu']['USER_ID'] and list['edu']['USER_KEY'] != None:
50 | bot.sendMessage(
51 | chat_id, "/sign_edu or /sign- This command start the sign process at https://parents.education.gov.il ")
52 | if v_MASHOV_NUMBER_OF_KIDS >= 1 and list['mashov']['kid1']['MASHOV_USER_ID_KID'] != None:
53 | bot.sendMessage(
54 | chat_id, "/sign_mashov - This command start the sign process at https://web.mashov.info/students/login ")
55 | if list['infogan']['BASE_URL'] and list['infogan']['KID_ID'] and list['infogan']['PARENT_NAME'] and list['infogan']['KID_NAME'] and list['infogan']['PARENT_ID'] != None:
56 | bot.sendMessage(
57 | chat_id, "/sign_infogan - This command start the sign process at https://www.infogan.co.il/ ")
58 | if list['webtop']['USER_ID'] and list['webtop']['USER_KEY'] != None:
59 | bot.sendMessage(
60 | chat_id, "/sign_webtop - This command start the sign process at https://www.webtop.co.il/v2/? ")
61 | if list['amdocs']['EMAIL'] and list['amdocs']['USER_ID'] and list['amdocs']['PASSWORD'] != None:
62 | bot.sendMessage(
63 | chat_id, "/sign_amdocs - This command start the sign process at Amdocs ")
64 |
65 | if command == '/sign_edu' or command == '/sign':
66 | if list['edu']['USER_ID'] and list['edu']['USER_KEY'] != None:
67 | Image = '/opt/dockerbot/images/edu_approval.png'
68 | try:
69 | bot.sendMessage(
70 | chat_id, "Starting Sign process at https://parents.education.gov.il")
71 | import Health_Statements
72 | if Health_Statements.sign(str(list['edu']['USER_ID']), list['edu']['USER_KEY'], Image) == 1:
73 | time.sleep(1)
74 | bot.sendPhoto(chat_id=chat_id,
75 | photo=open(str(Image), 'rb'))
76 | bot.sendMessage(chat_id, "Signed")
77 | logger.info(
78 | f"[{message_id}] Return result to command {command}. Result image path: {Image}")
79 | else:
80 | bot.sendMessage(
81 | chat_id, "Well, Somthing went wrong, please check the logs for more info")
82 | except Exception as ex:
83 | logger.exception(
84 | f"[{message_id}] Failed to handle command. Msg: {command}")
85 | bot.sendMessage(chat_id, f"ERROR: {str(ex)}")
86 | else:
87 | bot.sendMessage(chat_id, "edu NOT configured")
88 |
89 | if command == '/sign_infogan':
90 | if list['infogan']['BASE_URL'] and list['infogan']['KID_ID'] and list['infogan']['PARENT_NAME'] and list['infogan']['KID_NAME'] and list['infogan']['PARENT_ID'] != None:
91 | Image = '/opt/dockerbot/images/infogan_approval.png'
92 | try:
93 | bot.sendMessage(
94 | chat_id, "Starting Sign process at https://https://campaign.infogan.co.il/")
95 | import Infogan_Health_Statements
96 | if Infogan_Health_Statements.sign(list['infogan']['PARENT_NAME'], str(list['infogan']['PARENT_ID']), list['infogan']['KID_NAME'], str(list['infogan']['KID_ID']), list['infogan']['BASE_URL'], Image) == 1:
97 | bot.sendPhoto(chat_id=chat_id, photo=open(str(Image), 'rb'))
98 | time.sleep(1)
99 | os.remove(str(Image))
100 | logger.info(f"[{message_id}] Return result to command {command}. Result image path: {Image}")
101 | bot.sendMessage(chat_id, "Signed")
102 | else:
103 | bot.sendMessage(chat_id, "Well, Somthing went wrong, please check the logs for more info")
104 | except Exception as ex:
105 | logger.exception(
106 | f"[{message_id}] Failed to handle command. Msg: {command}")
107 | bot.sendMessage(chat_id, f"ERROR: {str(ex)}")
108 | else:
109 | bot.sendMessage(chat_id, "infogan NOT configured")
110 |
111 | if command == '/sign_webtop':
112 | if list['webtop']['USER_ID'] and list['webtop']['USER_KEY'] != None:
113 | try:
114 | Image = '/opt/dockerbot/images/webtop_approval.png'
115 | bot.sendMessage(
116 | chat_id, "Starting Sign process at https://www.webtop.co.il/mobilev2/?")
117 | import Webtop_Health_Statements
118 | if Webtop_Health_Statements.sign(list['webtop']['USER_ID'], list['webtop']['USER_KEY'], Image) == 1:
119 | time.sleep(2)
120 | bot.sendPhoto(chat_id=chat_id, photo=open(str(Image), 'rb'))
121 | logger.info(f"[{message_id}] Return result to command {command}. Result image path: {Image}")
122 | bot.sendMessage(chat_id, "Signed")
123 | else:
124 | bot.sendMessage(chat_id, "Well, Somthing went wrong, please check the logs for more info")
125 | except Exception as ex:
126 | logger.exception(f"[{message_id}] Failed to handle command. Msg: {command}")
127 | bot.sendMessage(chat_id, f"ERROR: {str(ex)}")
128 | else:
129 | bot.sendMessage(chat_id, "webtop NOT configured")
130 | if command == '/sign_mashov':
131 | Image = '/opt/dockerbot/images/mashov_approval_'
132 | if v_MASHOV_NUMBER_OF_KIDS >= 1 and list['mashov']['kid1']['MASHOV_USER_ID_KID'] != None:
133 | try:
134 | import Mashov_Health_Statements
135 | if v_MASHOV_NUMBER_OF_KIDS >= 1 and list['mashov']['kid1']['MASHOV_USER_ID_KID'] != None:
136 | for Mashov_Kid_Number in range(1, v_MASHOV_NUMBER_OF_KIDS, 1):
137 | if list['mashov']['kid'+str(Mashov_Kid_Number)]['MASHOV_USER_ID_KID'] and list['mashov']['kid'+str(Mashov_Kid_Number)]['MASHOV_USER_PWD_KID'] and list['mashov']['kid'+str(Mashov_Kid_Number)]['MASHOV_SCHOOL_ID_KID'] != None:
138 | bot.sendMessage(chat_id,"Starting Sign process at https://web.mashov.info/students/login for Kid Number: " + str(Mashov_Kid_Number))
139 | Prep_Switch_MASHOV_USER_DICT_ID_KID = list['mashov']['kid'+str(Mashov_Kid_Number)]['MASHOV_USER_ID_KID']
140 | Prep_Switch_MASHOV_USER_DICT_ID_PWD = list['mashov']['kid'+str(Mashov_Kid_Number)]['MASHOV_USER_PWD_KID']
141 | Prep_Switch_MASHOV_USER_DICT_ID_SCHOOL_ID = list['mashov']['kid'+str(Mashov_Kid_Number)]['MASHOV_SCHOOL_ID_KID']
142 | Mashov_Health_Statements.sign(Prep_Switch_MASHOV_USER_DICT_ID_KID, Prep_Switch_MASHOV_USER_DICT_ID_PWD, Prep_Switch_MASHOV_USER_DICT_ID_SCHOOL_ID, str(Mashov_Kid_Number), Image + str(Mashov_Kid_Number) + ".png")
143 | else:
144 | bot.sendMessage(chat_id, "mashov NOT configured")
145 | config_mashov = 0
146 |
147 | for file in os.listdir("/opt/dockerbot/images"):
148 | if file.endswith(".png") and file.startswith("mashov"):
149 | Image = os.path.join("/opt/dockerbot/images", file)
150 | bot.sendPhoto(chat_id=chat_id, photo=open(str(Image), 'rb'))
151 | os.remove(str(Image))
152 | logger.info(f"[{message_id}] Return result to command {command}. Result image path: {Image}")
153 | if config_mashov != 0:
154 | bot.sendMessage(chat_id, "Signed")
155 | except Exception as ex:
156 | logger.exception(f"[{message_id}] Failed to handle command. Msg: {command}")
157 | bot.sendMessage(chat_id, f"ERROR: {str(ex)}")
158 | else:
159 | bot.sendMessage(chat_id, "mashov NOT configured")
160 |
161 | if command == '/sign_amdocs':
162 | if list['amdocs']['EMAIL'] and list['amdocs']['USER_ID'] and list['amdocs']['PASSWORD'] != None:
163 | Image = '/opt/dockerbot/images/amdocs_approval.png'
164 | try:
165 | bot.sendMessage(
166 | chat_id, "Starting Sign process at Amdocs")
167 | import Amdocs_Health_Statements
168 | if Amdocs_Health_Statements.sign(str(list['amdocs']['EMAIL']), str(list['amdocs']['USER_ID']), str(list['amdocs']['PASSWORD']), Image) == 1:
169 | bot.sendPhoto(chat_id=chat_id, photo=open(str(Image), 'rb'))
170 | time.sleep(1)
171 | os.remove(str(Image))
172 | logger.info(f"[{message_id}] Return result to command {command}. Result image path: {Image}")
173 | bot.sendMessage(chat_id, "Signed")
174 | else:
175 | bot.sendMessage(chat_id, "Well, Somthing went wrong, please check the logs for more info")
176 | except Exception as ex:
177 | logger.exception(
178 | f"[{message_id}] Failed to handle command. Msg: {command}")
179 | bot.sendMessage(chat_id, f"ERROR: {str(ex)}")
180 | else:
181 | bot.sendMessage(chat_id, "Amdocs NOT configured")
182 |
183 | msg = f"Done message handling: {command}"
184 | logger.info(f"[{message_id}] {msg}")
185 |
186 | bot = telepot.Bot(os.getenv('API_KEY'))
187 | MessageLoop(bot, handle).run_as_thread()
188 | logger.info('I am listening...')
189 |
190 |
191 | while 1:
192 | time.sleep(10)
193 | if os.path.isfile(configfile):
194 | continue
195 | else:
196 | copyConfig()
197 | logger.error("Recoverd Config to /opt/dockerbot/config/")
--------------------------------------------------------------------------------
/License:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------