Skip to content

Commit

Permalink
Merge pull request #4 from Geek-MD/develop
Browse files Browse the repository at this point in the history
v0.4.0
  • Loading branch information
Geek-MD authored Jul 23, 2023
2 parents 8eb7053 + e06b6de commit 445afea
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 65 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
76 changes: 55 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,92 @@

Python script that checks for Apple software updates, and notifies via Telegram Bot.

## Basic installation and configuration
## Previous steps

This script relies on a previously created Telegram bot. If you don't have one follow [this steps](https://www.alphr.com/telegram-create-bot/) to create one. You need your bot token at hand in order to run this script.

This script relies on a previously created Telegram bot. If you don't have one follow [this steps](https://www.alphr.com/telegram-create-bot/) to create one.
## Basic installation and configuration

Clone this repo with...

```
git clone https://github.com/Geek-MD/apple-security-updates-notifier.git
```

Now open ***config.json*** with the editor of your preference, and change *"timezone"*, *"bot_token"* and *"chat_id_n"* with their corresponding values.
To run the script you must add *-b* or *--bot-token* option followed by the bot token itself. Bot token is mandatory, if you don't provide it, the script will exit with an error. Timezone or country arguments are optional. The default timezone is UTC.
The basic syntax to run the script is as the following examples.

```
nano config.json
python3 config.py -b <bot_token>
python3 config.py --bot-token <bot_token>
```

Now, you have to create a service at ***systemd*** which will execute the script and restart it on failure.
You can check the script help by using *-h* or *--help* option.

```
sudo nano /etc/systemd/system/apple-security-updates.service
python3 config.py -h
python3 config.py --help
```

Paste the content of *apple-security-updates.service* file. Be sure to modify *ExecStart* with the path to the python script, and *Environment* with the route for python libraries.
You can check the script version by using *-v* or *--version* option.

```
python3 config.py -v
python3 config.py --version
```

## Advanced configuration

There are two options, independent one from the other, to define the time zone of the bot. You can use timezone or country options. Remember that this is optional.
Additionally, you can define the chat ids where Telegram notifications will be sent.

### Timezone or country configuration

To get PYTHONPATH, first you need to know which version of python you have installed.
If you don't provide timezone or country at startup, the script will display a dialog indicating that the default timezone is *"UTC"* and asking if you're OK with that.
If you answer *"no"*, the script will try to identify your country based on the IP address and will ask if that guess is OK or not.
If you answer *"yes"*, the script will display a list with all the timezones associated to that country, and will ask for a selection. Type in the option of your choice. If you want to switchback to the default timezone *(UTC)*, type *"0"* in answer to that dialog.

For timezone configuration you can use *-t* or *--timezone* followed by the timezone according to IANA Zone ID list (http://nodatime.org/TimeZones). Alternatively you may use *'x'* or *'X'* as argument, so the script will try to identify your country based on the IP address.

```
python3 config.py -b <bot_token> -t <timezone>
python3 config.py --bot-token <bot_token> --timezone x
```

For timezone configuration through country code, you can use *-c* or *--country* followed by the country code according to ISO Alpha-2 codes (http://iban.com/country-codes). Country code is a 2-letter code in upper case, but this script accepts it in lower case. Alternatively you may use *'x'* or *'X'* as argument, so the script will try to identify your country based on the IP address.

```
python3 --version
python3 config.py -b <bot_token> -c <country_code>
python3 config.py --bot-token <bot_token> --country x
```

In my case is *python 3.9.2*. With that info you can search for the path of your python libraries. Get into python interpreter and run the following commands.
### Chat ids configuration

For chat ids configuration you can use *-i* or *--chat-ids* followed by chat ids separated by a space between them. Don't forget to add the minus sign before the numeric code of the chat id.
If you don't provide chat ids at startup, the script will ask for them one by one, and you will have to type "0" to finish the input.

```
python3
import sys
sys.path
python3 config.py -b <bot_token> -i <chat_id_1>
python3 config.py --bot-token <bot_token> --chat-ids <chat_id_1> <chat_id_2>
```

You will obtain a series of paths. Focus on the one that has a structure similar to *'/home/<user>/.local/lib/python3.9/site-packages'*. Note that it makes mention to *python3.9* which is the same version that is running in your system. Paste the path replacing <path_to_your_python_packages>. Do not include quotation marks.
## Functionality

Save with Ctrl-O and exit with Ctrl X.
This piece of software comprehends 2 *.py* files, 1 *.json* file and 1 *.sh* file. Python files are *config.py* which runs just once at start, and *apple_security_updates.py* which is the persistent script. The JSON file is *asu-notifier.json* which contains some basic information for *config.py*. Finally, the bash file is *asu-notifier.sh* which contains shell commands in order to run the persistent Python script as a systemd service.
Once the script is run, it will automatically recreate 2 files using the information you gave it, a *asu-notifier.service* file used to start a *systemd* service, and a *config.json* file with the configuration needed by the persistent script.
Finally, the script will execute the bash file in order to enable and start the systemd service. This will take some time to start because of *ExecStartPre* section which will wait for 60 seconds. This is due to a recommendation found on several forums which intention is to ensure that the script executes only when the system is fully loaded.
When you see the system prompt, if you haven't received an error message, the script will be running in background, and you'll receive a Telegram Notification with the latest Apple Security Updates.

Now you have to reload *systemd* daemons, enable and start the service
## Troubleshooting

Eventually, the systemd service may need to be restarted due to a system restart or by any other reason. You can do it manually using the following shell commands.

```
sudo systemctl daemon-reload
sudo systemctl enable apple-security-updates.service
sudo systemctl start apple-security-updates.service
sudo systemctl enable asu-notifier.service
sudo systemctl start asu-notifier.service
```

The service will take some time to start because of *ExecStartPre* section which will wait for 60 seconds. This is due to a recomendation found on several forums which intention is to ensure that the script executes only when the system is fully loaded.

## Note

This is a first workaround attempt for a persistent bot in the near future.
This is a first workaround attempt for a permanent bot in the near future, instead of a self-hosted bot.
17 changes: 0 additions & 17 deletions apple-security-updates.service

This file was deleted.

37 changes: 13 additions & 24 deletions apple_security_updates.py → asu-bot.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env python3

# Apple Updates v0.3.0
# Apple Security Updates Notifier v0.4.0
# Python script that checks for Apple software updates, and notifies via Telegram Bot.
# This is a first workaround attempt for a persistent bot in the near future.
# This is a first workaround attempt for a permanent bot in the near future.

import contextlib
import datetime
Expand Down Expand Up @@ -30,9 +30,9 @@

# SQL queries
sql_check_empty_database: str = """ SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='main' """
sql_create_main_table: str = """ CREATE TABLE main ( main_id integer PRIMARY KEY AUTOINCREMENT, log_date text NOT
sql_create_main_table: str = """ CREATE TABLE IF NOT EXISTS main ( main_id integer PRIMARY KEY AUTOINCREMENT, log_date text NOT
NULL, file_hash text NOT NULL, log_message text NOT NULL ); """
sql_create_updates_table: str = """ CREATE TABLE updates ( update_id integer PRIMARY KEY AUTOINCREMENT, update_date
sql_create_updates_table: str = """ CREATE TABLE IF NOT EXISTS updates ( update_id integer PRIMARY KEY AUTOINCREMENT, update_date
text NOT NULL, update_product text NOT NULL, update_target text NOT NULL, update_link text, file_hash text NOT NULL ); """
sql_main_table_hash_check: str = """ SELECT COUNT(*) FROM main WHERE file_hash = ? """
sql_main_table: str = """ INSERT INTO main (log_date, file_hash, log_message) VALUES (?, ?, ?); """
Expand All @@ -45,7 +45,6 @@
sql_get_update_dates: str = """ SELECT DISTINCT update_date FROM updates; """
sql_get_date_update: str = """ SELECT update_date, update_product, update_target, update_link FROM updates WHERE update_date = ?; """


def get_config():
global apple_file, db_file, log_file, localtime, bot_token, chat_ids
config = open('config.json', 'r')
Expand All @@ -58,7 +57,6 @@ def get_config():
bot_token = data['bot_token']
chat_ids = data['chat_ids']


def create_connection(file):
if not os.path.isfile(file):
logging.info(f'\'{file}\' database created.')
Expand All @@ -69,16 +67,14 @@ def create_connection(file):
logging.error(str(error))
return conn


def create_table(conn, sql_create_table, table_name):
def create_table(conn, sql_create_table, table_name, file):
with contextlib.suppress(Error):
try:
conn.cursor().execute(sql_create_table)
logging.info(f'\'{table_name}\' table created.')
logging.info(f'\'{file}\' - \'{table_name}\' table created.')
except Error as error:
logging.error(str(error))


def get_updates(conn, full_update):
cursor = conn.cursor()
response = requests.get(apple_file)
Expand All @@ -92,13 +88,11 @@ def get_updates(conn, full_update):
conn.commit()
conn.close()


def update_databases(conn, content, file_hash, full_update):
log_date = datetime.now(tz=localtime)
update_main_database(conn, log_date, file_hash, full_update)
update_updates_database(conn, file_hash, content, full_update)


def update_main_database(conn, log_date, file_hash, full_update):
cursor = conn.cursor()
if full_update:
Expand All @@ -109,7 +103,6 @@ def update_main_database(conn, log_date, file_hash, full_update):
logging.info(log_message)
conn.commit()


def update_updates_database(conn, file_hash, content, full_update):
cursor = conn.cursor()
soup = BeautifulSoup(content, 'html.parser')
Expand All @@ -128,7 +121,6 @@ def update_updates_database(conn, file_hash, content, full_update):
conn.commit()
apprise_notification(conn, recent_updates, full_update)


def formatted_content(content):
content_list = []
for i, row in enumerate(content):
Expand All @@ -149,7 +141,6 @@ def formatted_content(content):
content_list.reverse()
return content_list


def check_date(date_str):
pattern = r'(\d{1,2}) de (\w+) de (\d{4})'
try:
Expand All @@ -164,15 +155,15 @@ def check_date(date_str):
except Exception:
return date_str


def apprise_notification(conn, updates, full_update):
apprise_object = Apprise()
apprise_message = build_message(conn, updates, full_update)
apprise_syntax = f'tgram://{bot_token}/'
for chat_id in chat_ids:
apprise_syntax = f'tgram://{bot_token}/{chat_id}/?format=markdown'
apprise_object.add(apprise_syntax, tag='telegram')
apprise_object.notify(apprise_message, tag="telegram")

apprise_syntax += f'{chat_id}/'
apprise_syntax *= '?format=markdown'
apprise_object.add(apprise_syntax, tag='telegram')
apprise_object.notify(apprise_message, tag="telegram")

def build_message(conn, last_updates, full_update):
max_updates = 5
Expand Down Expand Up @@ -203,7 +194,6 @@ def build_message(conn, last_updates, full_update):
apprise_message += f' - {element[2]}\n'
return apprise_message


def main():
get_config()

Expand All @@ -217,8 +207,8 @@ def main():
empty_database = cursor.execute(sql_check_empty_database).fetchone()[0] == 0
if empty_database:
# create database tables and populate them
create_table(conn, sql_create_main_table, 'main')
create_table(conn, sql_create_updates_table, 'updates')
create_table(conn, sql_create_main_table, 'main', db_file)
create_table(conn, sql_create_updates_table, 'updates', db_file)

# run first database update
get_updates(conn, full_update=True)
Expand All @@ -231,6 +221,5 @@ def main():
schedule.run_pending()
time.sleep(1)


if __name__ == '__main__':
main()
6 changes: 6 additions & 0 deletions asu-notifier.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"prog_name_short": "asu-notifier",
"prog_name_long": "Apple Security Updates Notifier",
"version": "v0.4.0",
"apple_url": "https://support.apple.com/es-es/HT201222"
}
Loading

0 comments on commit 445afea

Please sign in to comment.