mirror of
https://github.com/xbgmsharp/postgsail.git
synced 2025-09-17 19:27:49 +00:00
Split public schame in file by type tables,functions and functions in python
This commit is contained in:
210
initdb/02_3_1_signalk_public_tables.sql
Normal file
210
initdb/02_3_1_signalk_public_tables.sql
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- singalk db public schema tables
|
||||||
|
--
|
||||||
|
|
||||||
|
-- List current database
|
||||||
|
select current_database();
|
||||||
|
|
||||||
|
-- connect to the DB
|
||||||
|
\c signalk
|
||||||
|
|
||||||
|
CREATE SCHEMA IF NOT EXISTS public;
|
||||||
|
COMMENT ON SCHEMA public IS 'backend functions';
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- Table geocoders
|
||||||
|
--
|
||||||
|
-- https://github.com/CartoDB/labs-postgresql/blob/master/workshop/plpython.md
|
||||||
|
--
|
||||||
|
CREATE TABLE IF NOT EXISTS geocoders(
|
||||||
|
name TEXT UNIQUE,
|
||||||
|
url TEXT,
|
||||||
|
reverse_url TEXT
|
||||||
|
);
|
||||||
|
-- Description
|
||||||
|
COMMENT ON TABLE
|
||||||
|
public.geocoders
|
||||||
|
IS 'geo service nominatim url';
|
||||||
|
|
||||||
|
INSERT INTO geocoders VALUES
|
||||||
|
('nominatim',
|
||||||
|
NULL,
|
||||||
|
'https://nominatim.openstreetmap.org/reverse');
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- Tables for message template email/pushover/telegram
|
||||||
|
--
|
||||||
|
CREATE TABLE IF NOT EXISTS email_templates(
|
||||||
|
name TEXT UNIQUE,
|
||||||
|
email_subject TEXT,
|
||||||
|
email_content TEXT,
|
||||||
|
pushover_title TEXT,
|
||||||
|
pushover_message TEXT
|
||||||
|
);
|
||||||
|
-- Description
|
||||||
|
COMMENT ON TABLE
|
||||||
|
public.email_templates
|
||||||
|
IS 'email/message templates for notifications';
|
||||||
|
|
||||||
|
-- with escape value, eg: E'A\nB\r\nC'
|
||||||
|
-- https://stackoverflow.com/questions/26638615/insert-line-break-in-postgresql-when-updating-text-field
|
||||||
|
-- TODO Update notification subject for log entry to 'logbook #NB ...'
|
||||||
|
INSERT INTO email_templates VALUES
|
||||||
|
('logbook',
|
||||||
|
'New Logbook Entry',
|
||||||
|
E'Hello __RECIPIENT__,\n\nWe just wanted to let you know that you have a new entry on openplotter.cloud: "__LOGBOOK_NAME__"\r\n\r\nSee more details at __APP_URL__/log/__LOGBOOK_LINK__\n\nHappy sailing!\nThe PostgSail Team',
|
||||||
|
'New Logbook Entry',
|
||||||
|
E'We just wanted to let you know that you have a new entry on openplotter.cloud: "__LOGBOOK_NAME__"\r\n\r\nSee more details at __APP_URL__/log/__LOGBOOK_LINK__\n\nHappy sailing!\nThe PostgSail Team'),
|
||||||
|
('user',
|
||||||
|
'Welcome',
|
||||||
|
E'Hello __RECIPIENT__,\nCongratulations!\nYou successfully created an account.\nKeep in mind to register your vessel.\nHappy sailing!',
|
||||||
|
'Welcome',
|
||||||
|
E'Hi!\nYou successfully created an account\nKeep in mind to register your vessel.\nHappy sailing!'),
|
||||||
|
('vessel',
|
||||||
|
'New vessel',
|
||||||
|
E'Hi!\nHow are you?\n__BOAT__ is now linked to your account.',
|
||||||
|
'New vessel',
|
||||||
|
E'Hi!\nHow are you?\n__BOAT__ is now linked to your account.'),
|
||||||
|
('monitor_offline',
|
||||||
|
'Offline',
|
||||||
|
E'__BOAT__ has been offline for more than an hour\r\nFind more details at __APP_URL__/boats/\n',
|
||||||
|
'Offline',
|
||||||
|
E'__BOAT__ has been offline for more than an hour\r\nFind more details at __APP_URL__/boats/\n'),
|
||||||
|
('monitor_online',
|
||||||
|
'Online',
|
||||||
|
E'__BOAT__ just came online\nFind more details at __APP_URL__/boats/\n',
|
||||||
|
'Online',
|
||||||
|
E'__BOAT__ just came online\nFind more details at __APP_URL__/boats/\n'),
|
||||||
|
('badge',
|
||||||
|
'New Badge!',
|
||||||
|
E'Hello __RECIPIENT__,\nCongratulations! You have just unlocked a new badge: __BADGE_NAME__\nSee more details at __APP_URL__/badges\nHappy sailing!\nThe PostgSail Team',
|
||||||
|
'New Badge!',
|
||||||
|
E'Congratulations!\nYou have just unlocked a new badge: __BADGE_NAME__\nSee more details at __APP_URL__/badges\nHappy sailing!\nThe PostgSail Team'),
|
||||||
|
('pushover',
|
||||||
|
'Pushover integration',
|
||||||
|
E'Hello __RECIPIENT__,\nCongratulations! You have just connect your account to pushover.\n\nThe PostgSail Team',
|
||||||
|
'Pushover integration!',
|
||||||
|
E'Congratulations!\nYou have just connect your account to pushover.\n\nThe PostgSail Team'),
|
||||||
|
('email_otp',
|
||||||
|
'Email verification',
|
||||||
|
E'Hello __RECIPIENT__,\nPlease active your account using the following code: __OTP_CODE__.\nThe code is valid 15 minutes.\nThe PostgSail Team',
|
||||||
|
'Email verification',
|
||||||
|
E'Congratulations!\nPlease validate your account. Check your email!'),
|
||||||
|
('telegram_otp',
|
||||||
|
'Telegram bot',
|
||||||
|
E'Hello __RECIPIENT__,\nTo connect your account to a @postgsail_bot. Please type this verification code __OTP_CODE__ back to the bot.\nThe code is valid 15 minutes.\nThe PostgSail Team',
|
||||||
|
'Telegram bot',
|
||||||
|
E'Congratulations!\nTo connect your account to a @postgsail_bot. Check your email!'),
|
||||||
|
('telegram_valid',
|
||||||
|
'Telegram bot',
|
||||||
|
E'Hello __RECIPIENT__,\nCongratulations! You have just connect your account to a @postgsail_bot.\n\nThe PostgSail Team',
|
||||||
|
'Telegram bot!',
|
||||||
|
E'Congratulations!\nYou have just connect your account to a @postgsail_bot.\n\nHappy sailing!\nThe PostgSail Team');
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- Queue handling
|
||||||
|
--
|
||||||
|
-- https://gist.github.com/kissgyorgy/beccba1291de962702ea9c237a900c79
|
||||||
|
-- https://www.depesz.com/2012/06/13/how-to-send-mail-from-database/
|
||||||
|
|
||||||
|
-- Listen/Notify way
|
||||||
|
--create function new_logbook_entry() returns trigger as $$
|
||||||
|
--begin
|
||||||
|
-- perform pg_notify('new_logbook_entry', NEW.id::text);
|
||||||
|
-- return NEW;
|
||||||
|
--END;
|
||||||
|
--$$ language plpgsql;
|
||||||
|
|
||||||
|
-- table way
|
||||||
|
CREATE TABLE IF NOT EXISTS public.process_queue (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
channel TEXT NOT NULL,
|
||||||
|
payload TEXT NOT NULL,
|
||||||
|
stored TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||||
|
processed TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL
|
||||||
|
);
|
||||||
|
-- Description
|
||||||
|
COMMENT ON TABLE
|
||||||
|
public.process_queue
|
||||||
|
IS 'process queue for async job';
|
||||||
|
-- Index
|
||||||
|
CREATE INDEX ON public.process_queue (channel);
|
||||||
|
CREATE INDEX ON public.process_queue (stored);
|
||||||
|
CREATE INDEX ON public.process_queue (processed);
|
||||||
|
|
||||||
|
-- Function process_queue helpers
|
||||||
|
create function new_account_entry_fn() returns trigger as $new_account_entry$
|
||||||
|
begin
|
||||||
|
insert into process_queue (channel, payload, stored) values ('new_account', NEW.email, now());
|
||||||
|
return NEW;
|
||||||
|
END;
|
||||||
|
$new_account_entry$ language plpgsql;
|
||||||
|
|
||||||
|
create function new_account_otp_validation_entry_fn() returns trigger as $new_account_otp_validation_entry$
|
||||||
|
begin
|
||||||
|
insert into process_queue (channel, payload, stored) values ('new_account_otp', NEW.email, now());
|
||||||
|
return NEW;
|
||||||
|
END;
|
||||||
|
$new_account_otp_validation_entry$ language plpgsql;
|
||||||
|
|
||||||
|
create function new_vessel_entry_fn() returns trigger as $new_vessel_entry$
|
||||||
|
begin
|
||||||
|
insert into process_queue (channel, payload, stored) values ('new_vessel', NEW.owner_email, now());
|
||||||
|
return NEW;
|
||||||
|
END;
|
||||||
|
$new_vessel_entry$ language plpgsql;
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- Tables Application Settings
|
||||||
|
-- https://dba.stackexchange.com/questions/27296/storing-application-settings-with-different-datatypes#27297
|
||||||
|
-- https://stackoverflow.com/questions/6893780/how-to-store-site-wide-settings-in-a-database
|
||||||
|
-- http://cvs.savannah.gnu.org/viewvc/*checkout*/gnumed/gnumed/gnumed/server/sql/gmconfiguration.sql
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.app_settings (
|
||||||
|
name TEXT NOT NULL UNIQUE,
|
||||||
|
value TEXT NOT NULL
|
||||||
|
);
|
||||||
|
-- Description
|
||||||
|
COMMENT ON TABLE public.app_settings IS 'application settings';
|
||||||
|
COMMENT ON COLUMN public.app_settings.name IS 'application settings name key';
|
||||||
|
COMMENT ON COLUMN public.app_settings.value IS 'application settings value';
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- Badges descriptions
|
||||||
|
-- TODO add contiditions
|
||||||
|
--
|
||||||
|
CREATE TABLE IF NOT EXISTS badges(
|
||||||
|
name TEXT UNIQUE,
|
||||||
|
description TEXT
|
||||||
|
);
|
||||||
|
-- Description
|
||||||
|
COMMENT ON TABLE
|
||||||
|
public.badges
|
||||||
|
IS 'Badges descriptions';
|
||||||
|
|
||||||
|
INSERT INTO badges VALUES
|
||||||
|
('Helmsman',
|
||||||
|
'Nice work logging your first sail! You are officially a helmsman now!'),
|
||||||
|
('Wake Maker',
|
||||||
|
'Yowzers! Welcome to the 15 knot+ club ya speed demon skipper!'),
|
||||||
|
('Explorer',
|
||||||
|
'It looks like home is where the helm is. Cheers to 10 days away from home port!'),
|
||||||
|
('Mooring Pro',
|
||||||
|
'It takes a lot of skill to "thread that floating needle" but seems like you have mastered mooring with 10 nights on buoy!'),
|
||||||
|
('Anchormaster',
|
||||||
|
'Hook, line and sinker, you have this anchoring thing down! 25 days on the hook for you!'),
|
||||||
|
('Traveler',
|
||||||
|
'Who needs to fly when one can sail! You are an international sailor. À votre santé!'),
|
||||||
|
('Stormtrooper',
|
||||||
|
'Just like the elite defenders of the Empire, here you are, our braving your own hydro-empire in windspeeds above 30kts. Nice work trooper! '),
|
||||||
|
('Club Alaska',
|
||||||
|
'Home to the bears, glaciers, midnight sun and high adventure. Welcome to the Club Alaska Captain!'),
|
||||||
|
('Tropical Traveler',
|
||||||
|
'Look at you with your suntan, tropical drink and southern latitude!'),
|
||||||
|
('Aloha Award',
|
||||||
|
'Ticking off over 2300 NM across the great blue Pacific makes you the rare recipient of the Aloha Award. Well done and Aloha sailor!'),
|
||||||
|
('Tyee',
|
||||||
|
'You made it to the Tyee Outstation, the friendliest dock in Pacific Northwest!'),
|
||||||
|
-- TODO the sea is big and the world is not limited to the US
|
||||||
|
('Mediterranean Traveler',
|
||||||
|
'You made it trought the Mediterranean!');
|
@@ -9,392 +9,10 @@ select current_database();
|
|||||||
\c signalk
|
\c signalk
|
||||||
|
|
||||||
CREATE SCHEMA IF NOT EXISTS public;
|
CREATE SCHEMA IF NOT EXISTS public;
|
||||||
COMMENT ON SCHEMA public IS 'backend functions';
|
|
||||||
|
|
||||||
---------------------------------------------------------------------------
|
|
||||||
-- python reverse_geocode
|
|
||||||
--
|
|
||||||
-- https://github.com/CartoDB/labs-postgresql/blob/master/workshop/plpython.md
|
|
||||||
--
|
|
||||||
CREATE TABLE IF NOT EXISTS geocoders(
|
|
||||||
name TEXT UNIQUE,
|
|
||||||
url TEXT,
|
|
||||||
reverse_url TEXT
|
|
||||||
);
|
|
||||||
-- Description
|
|
||||||
COMMENT ON TABLE
|
|
||||||
public.geocoders
|
|
||||||
IS 'geo service nominatim url';
|
|
||||||
|
|
||||||
INSERT INTO geocoders VALUES
|
|
||||||
('nominatim',
|
|
||||||
NULL,
|
|
||||||
'https://nominatim.openstreetmap.org/reverse');
|
|
||||||
|
|
||||||
DROP FUNCTION IF EXISTS reverse_geocode_py_fn;
|
|
||||||
CREATE OR REPLACE FUNCTION reverse_geocode_py_fn(IN geocoder TEXT, IN lon NUMERIC, IN lat NUMERIC,
|
|
||||||
OUT geo_name TEXT)
|
|
||||||
AS $reverse_geocode_py$
|
|
||||||
import requests
|
|
||||||
|
|
||||||
# Use the shared cache to avoid preparing the geocoder metadata
|
|
||||||
if geocoder in SD:
|
|
||||||
plan = SD[geocoder]
|
|
||||||
# A prepared statement from Python
|
|
||||||
else:
|
|
||||||
plan = plpy.prepare("SELECT reverse_url AS url FROM geocoders WHERE name = $1", ["text"])
|
|
||||||
SD[geocoder] = plan
|
|
||||||
|
|
||||||
# Execute the statement with the geocoder param and limit to 1 result
|
|
||||||
rv = plpy.execute(plan, [geocoder], 1)
|
|
||||||
url = rv[0]['url']
|
|
||||||
|
|
||||||
# Validate input
|
|
||||||
if not lon or not lat:
|
|
||||||
plpy.notice('reverse_geocode_py_fn Parameters [{}] [{}]'.format(lon, lat))
|
|
||||||
plpy.error('Error missing parameters')
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Make the request to the geocoder API
|
|
||||||
payload = {"lon": lon, "lat": lat, "format": "jsonv2", "zoom": 18}
|
|
||||||
r = requests.get(url, params=payload)
|
|
||||||
|
|
||||||
# Return the full address or nothing if not found
|
|
||||||
if r.status_code == 200 and "name" in r.json():
|
|
||||||
return r.json()["name"]
|
|
||||||
else:
|
|
||||||
plpy.error('Failed to received a geo full address %s', r.json())
|
|
||||||
return 'unknow'
|
|
||||||
$reverse_geocode_py$ LANGUAGE plpython3u;
|
|
||||||
-- Description
|
|
||||||
COMMENT ON FUNCTION
|
|
||||||
public.reverse_geocode_py_fn
|
|
||||||
IS 'query reverse geo service to return location name';
|
|
||||||
|
|
||||||
---------------------------------------------------------------------------
|
|
||||||
-- python template email/pushover
|
|
||||||
--
|
|
||||||
CREATE TABLE IF NOT EXISTS email_templates(
|
|
||||||
name TEXT UNIQUE,
|
|
||||||
email_subject TEXT,
|
|
||||||
email_content TEXT,
|
|
||||||
pushover_title TEXT,
|
|
||||||
pushover_message TEXT
|
|
||||||
);
|
|
||||||
-- Description
|
|
||||||
COMMENT ON TABLE
|
|
||||||
public.email_templates
|
|
||||||
IS 'email/message templates for notifications';
|
|
||||||
|
|
||||||
-- with escape value, eg: E'A\nB\r\nC'
|
|
||||||
-- https://stackoverflow.com/questions/26638615/insert-line-break-in-postgresql-when-updating-text-field
|
|
||||||
-- TODO Update notification subject for log entry to 'logbook #NB ...'
|
|
||||||
INSERT INTO email_templates VALUES
|
|
||||||
('logbook',
|
|
||||||
'New Logbook Entry',
|
|
||||||
E'Hello __RECIPIENT__,\n\nWe just wanted to let you know that you have a new entry on openplotter.cloud: "__LOGBOOK_NAME__"\r\n\r\nSee more details at __APP_URL__/log/__LOGBOOK_LINK__\n\nHappy sailing!\nThe PostgSail Team',
|
|
||||||
'New Logbook Entry',
|
|
||||||
E'We just wanted to let you know that you have a new entry on openplotter.cloud: "__LOGBOOK_NAME__"\r\n\r\nSee more details at __APP_URL__/log/__LOGBOOK_LINK__\n\nHappy sailing!\nThe PostgSail Team'),
|
|
||||||
('user',
|
|
||||||
'Welcome',
|
|
||||||
E'Hello __RECIPIENT__,\nCongratulations!\nYou successfully created an account.\nKeep in mind to register your vessel.\nHappy sailing!',
|
|
||||||
'Welcome',
|
|
||||||
E'Hi!\nYou successfully created an account\nKeep in mind to register your vessel.\nHappy sailing!'),
|
|
||||||
('vessel',
|
|
||||||
'New vessel',
|
|
||||||
E'Hi!\nHow are you?\n__BOAT__ is now linked to your account.',
|
|
||||||
'New vessel',
|
|
||||||
E'Hi!\nHow are you?\n__BOAT__ is now linked to your account.'),
|
|
||||||
('monitor_offline',
|
|
||||||
'Offline',
|
|
||||||
E'__BOAT__ has been offline for more than an hour\r\nFind more details at __APP_URL__/boats/\n',
|
|
||||||
'Offline',
|
|
||||||
E'__BOAT__ has been offline for more than an hour\r\nFind more details at __APP_URL__/boats/\n'),
|
|
||||||
('monitor_online',
|
|
||||||
'Online',
|
|
||||||
E'__BOAT__ just came online\nFind more details at __APP_URL__/boats/\n',
|
|
||||||
'Online',
|
|
||||||
E'__BOAT__ just came online\nFind more details at __APP_URL__/boats/\n'),
|
|
||||||
('badge',
|
|
||||||
'New Badge!',
|
|
||||||
E'Hello __RECIPIENT__,\nCongratulations! You have just unlocked a new badge: __BADGE_NAME__\nSee more details at __APP_URL__/badges\nHappy sailing!\nThe PostgSail Team',
|
|
||||||
'New Badge!',
|
|
||||||
E'Congratulations!\nYou have just unlocked a new badge: __BADGE_NAME__\nSee more details at __APP_URL__/badges\nHappy sailing!\nThe PostgSail Team'),
|
|
||||||
('pushover',
|
|
||||||
'Pushover integration',
|
|
||||||
E'Hello __RECIPIENT__,\nCongratulations! You have just connect your account to pushover.\n\nThe PostgSail Team',
|
|
||||||
'Pushover integration!',
|
|
||||||
E'Congratulations!\nYou have just connect your account to pushover.\n\nThe PostgSail Team'),
|
|
||||||
('email_otp',
|
|
||||||
'Email verification',
|
|
||||||
E'Hello __RECIPIENT__,\nPlease active your account using the following code: __OTP_CODE__.\nThe code is valid 15 minutes.\nThe PostgSail Team',
|
|
||||||
'Email verification',
|
|
||||||
E'Congratulations!\nPlease validate your account. Check your email!'),
|
|
||||||
('telegram_otp',
|
|
||||||
'Telegram bot',
|
|
||||||
E'Hello __RECIPIENT__,\nTo connect your account to a @postgsail_bot. Please type this verification code __OTP_CODE__ back to the bot.\nThe code is valid 15 minutes.\nThe PostgSail Team',
|
|
||||||
'Telegram bot',
|
|
||||||
E'Congratulations!\nTo connect your account to a @postgsail_bot. Check your email!'),
|
|
||||||
('telegram_valid',
|
|
||||||
'Telegram bot',
|
|
||||||
E'Hello __RECIPIENT__,\nCongratulations! You have just connect your account to a @postgsail_bot.\n\nThe PostgSail Team',
|
|
||||||
'Telegram bot!',
|
|
||||||
E'Congratulations!\nYou have just connect your account to a @postgsail_bot.\n\nHappy sailing!\nThe PostgSail Team');
|
|
||||||
|
|
||||||
---------------------------------------------------------------------------
|
|
||||||
-- python send email
|
|
||||||
--
|
|
||||||
-- https://www.programcreek.com/python/example/3684/email.utils.formatdate
|
|
||||||
DROP FUNCTION IF EXISTS send_email_py_fn;
|
|
||||||
CREATE OR REPLACE FUNCTION send_email_py_fn(IN email_type TEXT, IN _user JSONB, IN app JSONB) RETURNS void
|
|
||||||
AS $send_email_py$
|
|
||||||
# Import smtplib for the actual sending function
|
|
||||||
import smtplib
|
|
||||||
|
|
||||||
# Import the email modules we need
|
|
||||||
#from email.message import EmailMessage
|
|
||||||
from email.utils import formatdate,make_msgid
|
|
||||||
from email.mime.text import MIMEText
|
|
||||||
|
|
||||||
# Use the shared cache to avoid preparing the email metadata
|
|
||||||
if email_type in SD:
|
|
||||||
plan = SD[email_type]
|
|
||||||
# A prepared statement from Python
|
|
||||||
else:
|
|
||||||
plan = plpy.prepare("SELECT * FROM email_templates WHERE name = $1", ["text"])
|
|
||||||
SD[email_type] = plan
|
|
||||||
|
|
||||||
# Execute the statement with the email_type param and limit to 1 result
|
|
||||||
rv = plpy.execute(plan, [email_type], 1)
|
|
||||||
email_subject = rv[0]['email_subject']
|
|
||||||
email_content = rv[0]['email_content']
|
|
||||||
|
|
||||||
# Replace fields using input jsonb obj
|
|
||||||
if not _user or not app:
|
|
||||||
plpy.notice('send_email_py_fn Parameters [{}] [{}]'.format(_user, app))
|
|
||||||
plpy.error('Error missing parameters')
|
|
||||||
return None
|
|
||||||
if 'logbook_name' in _user and _user['logbook_name']:
|
|
||||||
email_content = email_content.replace('__LOGBOOK_NAME__', _user['logbook_name'])
|
|
||||||
if 'logbook_link' in _user and _user['logbook_link']:
|
|
||||||
email_content = email_content.replace('__LOGBOOK_LINK__', str(_user['logbook_link']))
|
|
||||||
if 'recipient' in _user and _user['recipient']:
|
|
||||||
email_content = email_content.replace('__RECIPIENT__', _user['recipient'])
|
|
||||||
if 'boat' in _user and _user['boat']:
|
|
||||||
email_content = email_content.replace('__BOAT__', _user['boat'])
|
|
||||||
if 'badge' in _user and _user['badge']:
|
|
||||||
email_content = email_content.replace('__BADGE_NAME__', _user['badge'])
|
|
||||||
if 'otp_code' in _user and _user['otp_code']:
|
|
||||||
email_content = email_content.replace('__OTP_CODE__', _user['otp_code'])
|
|
||||||
|
|
||||||
if 'app.url' in app and app['app.url']:
|
|
||||||
email_content = email_content.replace('__APP_URL__', app['app.url'])
|
|
||||||
|
|
||||||
email_from = 'root@localhost'
|
|
||||||
if 'app.email_from' in app and app['app.email_from']:
|
|
||||||
email_from = 'PostgSail <' + app['app.email_from'] + '>'
|
|
||||||
#plpy.notice('Sending email from [{}] [{}]'.format(email_from, app['app.email_from']))
|
|
||||||
|
|
||||||
email_to = 'root@localhost'
|
|
||||||
if 'email' in _user and _user['email']:
|
|
||||||
email_to = _user['email']
|
|
||||||
#plpy.notice('Sending email to [{}] [{}]'.format(email_to, _user['email']))
|
|
||||||
else:
|
|
||||||
plpy.error('Error email to')
|
|
||||||
return None
|
|
||||||
|
|
||||||
msg = MIMEText(email_content, 'plain', 'utf-8')
|
|
||||||
msg["Subject"] = email_subject
|
|
||||||
msg["From"] = email_from
|
|
||||||
msg["To"] = email_to
|
|
||||||
msg["Date"] = formatdate()
|
|
||||||
msg["Message-ID"] = make_msgid()
|
|
||||||
|
|
||||||
server_smtp = 'localhost'
|
|
||||||
if 'app.email_server' in app and app['app.email_server']:
|
|
||||||
server_smtp = app['app.email_server']
|
|
||||||
|
|
||||||
# Send the message via our own SMTP server.
|
|
||||||
try:
|
|
||||||
# send your message with credentials specified above
|
|
||||||
with smtplib.SMTP(server_smtp, 25) as server:
|
|
||||||
if 'app.email_user' in app and app['app.email_user'] \
|
|
||||||
and 'app.email_pass' in app and app['app.email_pass']:
|
|
||||||
server.starttls()
|
|
||||||
server.login(app['app.email_user'], app['app.email_pass'])
|
|
||||||
#server.send_message(msg)
|
|
||||||
server.sendmail(msg["From"], msg["To"], msg.as_string())
|
|
||||||
server.quit()
|
|
||||||
# tell the script to report if your message was sent or which errors need to be fixed
|
|
||||||
plpy.notice('Sent email successfully to [{}] [{}]'.format(msg["To"], msg["Subject"]))
|
|
||||||
return None
|
|
||||||
except OSError as error:
|
|
||||||
plpy.error(error)
|
|
||||||
except smtplib.SMTPConnectError:
|
|
||||||
plpy.error('Failed to connect to the server. Bad connection settings?')
|
|
||||||
except smtplib.SMTPServerDisconnected:
|
|
||||||
plpy.error('Failed to connect to the server. Wrong user/password?')
|
|
||||||
except smtplib.SMTPException as e:
|
|
||||||
plpy.error('SMTP error occurred: ' + str(e))
|
|
||||||
$send_email_py$ TRANSFORM FOR TYPE jsonb LANGUAGE plpython3u;
|
|
||||||
-- Description
|
|
||||||
COMMENT ON FUNCTION
|
|
||||||
public.send_email_py_fn
|
|
||||||
IS 'Send email notification using plpython3u';
|
|
||||||
|
|
||||||
---------------------------------------------------------------------------
|
|
||||||
-- python send pushover message
|
|
||||||
-- https://pushover.net/
|
|
||||||
DROP FUNCTION IF EXISTS send_pushover_py_fn;
|
|
||||||
CREATE OR REPLACE FUNCTION send_pushover_py_fn(IN message_type TEXT, IN _user JSONB, IN app JSONB) RETURNS void
|
|
||||||
AS $send_pushover_py$
|
|
||||||
import requests
|
|
||||||
|
|
||||||
# Use the shared cache to avoid preparing the email metadata
|
|
||||||
if message_type in SD:
|
|
||||||
plan = SD[message_type]
|
|
||||||
# A prepared statement from Python
|
|
||||||
else:
|
|
||||||
plan = plpy.prepare("SELECT * FROM email_templates WHERE name = $1", ["text"])
|
|
||||||
SD[message_type] = plan
|
|
||||||
|
|
||||||
# Execute the statement with the message_type param and limit to 1 result
|
|
||||||
rv = plpy.execute(plan, [message_type], 1)
|
|
||||||
pushover_title = rv[0]['pushover_title']
|
|
||||||
pushover_message = rv[0]['pushover_message']
|
|
||||||
|
|
||||||
# Replace fields using input jsonb obj
|
|
||||||
if 'logbook_name' in _user and _user['logbook_name']:
|
|
||||||
pushover_message = pushover_message.replace('__LOGBOOK_NAME__', _user['logbook_name'])
|
|
||||||
if 'logbook_link' in _user and _user['logbook_link']:
|
|
||||||
pushover_message = pushover_message.replace('__LOGBOOK_LINK__', str(_user['logbook_link']))
|
|
||||||
if 'recipient' in _user and _user['recipient']:
|
|
||||||
pushover_message = pushover_message.replace('__RECIPIENT__', _user['recipient'])
|
|
||||||
if 'boat' in _user and _user['boat']:
|
|
||||||
pushover_message = pushover_message.replace('__BOAT__', _user['boat'])
|
|
||||||
if 'badge' in _user and _user['badge']:
|
|
||||||
pushover_message = pushover_message.replace('__BADGE_NAME__', _user['badge'])
|
|
||||||
|
|
||||||
if 'app.url' in app and app['app.url']:
|
|
||||||
pushover_message = pushover_message.replace('__APP_URL__', app['app.url'])
|
|
||||||
|
|
||||||
pushover_token = None
|
|
||||||
if 'app.pushover_app_token' in app and app['app.pushover_app_token']:
|
|
||||||
pushover_token = app['app.pushover_app_token']
|
|
||||||
else:
|
|
||||||
plpy.error('Error no pushover token defined, check app settings')
|
|
||||||
return None
|
|
||||||
pushover_user = None
|
|
||||||
if 'pushover_user_key' in _user and _user['pushover_user_key']:
|
|
||||||
pushover_user = _user['pushover_user_key']
|
|
||||||
else:
|
|
||||||
plpy.error('Error no pushover user token defined, check user settings')
|
|
||||||
return None
|
|
||||||
|
|
||||||
# requests
|
|
||||||
r = requests.post("https://api.pushover.net/1/messages.json", data = {
|
|
||||||
"token": pushover_token,
|
|
||||||
"user": pushover_user,
|
|
||||||
"title": pushover_title,
|
|
||||||
"message": pushover_message
|
|
||||||
})
|
|
||||||
|
|
||||||
#print(r.text)
|
|
||||||
# Return ?? or None if not found
|
|
||||||
plpy.notice('Sent pushover successfully to [{}] [{}]'.format(r.text, r.status_code))
|
|
||||||
if r.status_code == 200:
|
|
||||||
plpy.notice('Sent pushover successfully to [{}] [{}] [{}]'.format("__USER__", pushover_title, r.text))
|
|
||||||
else:
|
|
||||||
plpy.error('Failed to send pushover')
|
|
||||||
return None
|
|
||||||
$send_pushover_py$ TRANSFORM FOR TYPE jsonb LANGUAGE plpython3u;
|
|
||||||
-- Description
|
|
||||||
COMMENT ON FUNCTION
|
|
||||||
public.send_pushover_py_fn
|
|
||||||
IS 'Send pushover notification using plpython3u';
|
|
||||||
|
|
||||||
---------------------------------------------------------------------------
|
|
||||||
-- python send telegram message
|
|
||||||
-- https://core.telegram.org/
|
|
||||||
DROP FUNCTION IF EXISTS send_telegram_py_fn;
|
|
||||||
CREATE OR REPLACE FUNCTION send_telegram_py_fn(IN message_type TEXT, IN _user JSONB, IN app JSONB) RETURNS void
|
|
||||||
AS $send_telegram_py$
|
|
||||||
"""
|
|
||||||
Send a message to a telegram user or group specified on chatId
|
|
||||||
chat_id must be a number!
|
|
||||||
"""
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
|
|
||||||
# Use the shared cache to avoid preparing the email metadata
|
|
||||||
if message_type in SD:
|
|
||||||
plan = SD[message_type]
|
|
||||||
# A prepared statement from Python
|
|
||||||
else:
|
|
||||||
plan = plpy.prepare("SELECT * FROM email_templates WHERE name = $1", ["text"])
|
|
||||||
SD[message_type] = plan
|
|
||||||
|
|
||||||
# Execute the statement with the message_type param and limit to 1 result
|
|
||||||
rv = plpy.execute(plan, [message_type], 1)
|
|
||||||
telegram_title = rv[0]['pushover_title']
|
|
||||||
telegram_message = rv[0]['pushover_message']
|
|
||||||
|
|
||||||
# Replace fields using input jsonb obj
|
|
||||||
if 'logbook_name' in _user and _user['logbook_name']:
|
|
||||||
telegram_message = telegram_message.replace('__LOGBOOK_NAME__', _user['logbook_name'])
|
|
||||||
if 'logbook_link' in _user and _user['logbook_link']:
|
|
||||||
telegram_message = telegram_message.replace('__LOGBOOK_LINK__', str(_user['logbook_link']))
|
|
||||||
if 'recipient' in _user and _user['recipient']:
|
|
||||||
telegram_message = telegram_message.replace('__RECIPIENT__', _user['recipient'])
|
|
||||||
if 'boat' in _user and _user['boat']:
|
|
||||||
telegram_message = telegram_message.replace('__BOAT__', _user['boat'])
|
|
||||||
if 'badge' in _user and _user['badge']:
|
|
||||||
telegram_message = telegram_message.replace('__BADGE_NAME__', _user['badge'])
|
|
||||||
|
|
||||||
if 'app.url' in app and app['app.url']:
|
|
||||||
telegram_message = telegram_message.replace('__APP_URL__', app['app.url'])
|
|
||||||
|
|
||||||
telegram_token = None
|
|
||||||
if 'app.telegram_bot_token' in app and app['app.telegram_bot_token']:
|
|
||||||
telegram_token = app['app.telegram_bot_token']
|
|
||||||
else:
|
|
||||||
plpy.error('Error no telegram token defined, check app settings')
|
|
||||||
return None
|
|
||||||
telegram_chat_id = None
|
|
||||||
if 'telegram_chat_id' in _user and _user['telegram_chat_id']:
|
|
||||||
telegram_chat_id = _user['telegram_chat_id']
|
|
||||||
else:
|
|
||||||
plpy.error('Error no telegram user token defined, check user settings')
|
|
||||||
return None
|
|
||||||
|
|
||||||
# requests
|
|
||||||
headers = {'Content-Type': 'application/json',
|
|
||||||
'Proxy-Authorization': 'Basic base64'}
|
|
||||||
data_dict = {'chat_id': telegram_chat_id,
|
|
||||||
'text': telegram_message,
|
|
||||||
'parse_mode': 'HTML',
|
|
||||||
'disable_notification': False}
|
|
||||||
data = json.dumps(data_dict)
|
|
||||||
url = f'https://api.telegram.org/bot{telegram_token}/sendMessage'
|
|
||||||
r = requests.post(url,
|
|
||||||
data=data,
|
|
||||||
headers=headers)
|
|
||||||
print(r.text)
|
|
||||||
# Return the full address or None if not found
|
|
||||||
plpy.notice('Sent telegram successfully to [{}] [{}]'.format(r.text, r.status_code))
|
|
||||||
if r.status_code == 200:
|
|
||||||
plpy.notice('Sent telegram successfully to [{}] [{}] [{}]'.format("__USER__", telegram_title, r.text))
|
|
||||||
else:
|
|
||||||
plpy.error('Failed to send telegram')
|
|
||||||
return None
|
|
||||||
$send_telegram_py$ TRANSFORM FOR TYPE jsonb LANGUAGE plpython3u;
|
|
||||||
-- Description
|
|
||||||
COMMENT ON FUNCTION
|
|
||||||
public.send_telegram_py_fn
|
|
||||||
IS 'Send a message to a telegram user or group specified on chatId using plpython3u';
|
|
||||||
|
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
-- Functions public schema
|
-- Functions public schema
|
||||||
|
-- process single cron event, process_[logbook|stay|moorage|badge]_queue_fn()
|
||||||
--
|
--
|
||||||
|
|
||||||
-- Update a logbook with avg data
|
-- Update a logbook with avg data
|
||||||
@@ -986,114 +604,6 @@ COMMENT ON FUNCTION
|
|||||||
public.set_vessel_settings_from_clientid_fn
|
public.set_vessel_settings_from_clientid_fn
|
||||||
IS 'set_vessel settings details from a clientid, initiate for process queue functions';
|
IS 'set_vessel settings details from a clientid, initiate for process queue functions';
|
||||||
|
|
||||||
---------------------------------------------------------------------------
|
|
||||||
-- Queue handling
|
|
||||||
--
|
|
||||||
-- https://gist.github.com/kissgyorgy/beccba1291de962702ea9c237a900c79
|
|
||||||
-- https://www.depesz.com/2012/06/13/how-to-send-mail-from-database/
|
|
||||||
|
|
||||||
-- Listen/Notify way
|
|
||||||
--create function new_logbook_entry() returns trigger as $$
|
|
||||||
--begin
|
|
||||||
-- perform pg_notify('new_logbook_entry', NEW.id::text);
|
|
||||||
-- return NEW;
|
|
||||||
--END;
|
|
||||||
--$$ language plpgsql;
|
|
||||||
|
|
||||||
-- table way
|
|
||||||
CREATE TABLE IF NOT EXISTS public.process_queue (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
channel TEXT NOT NULL,
|
|
||||||
payload TEXT NOT NULL,
|
|
||||||
stored TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
|
||||||
processed TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL
|
|
||||||
);
|
|
||||||
-- Description
|
|
||||||
COMMENT ON TABLE
|
|
||||||
public.process_queue
|
|
||||||
IS 'process queue for async job';
|
|
||||||
-- Index
|
|
||||||
CREATE INDEX ON public.process_queue (channel);
|
|
||||||
CREATE INDEX ON public.process_queue (stored);
|
|
||||||
CREATE INDEX ON public.process_queue (processed);
|
|
||||||
|
|
||||||
-- Function process_queue helpers
|
|
||||||
create function new_account_entry_fn() returns trigger as $new_account_entry$
|
|
||||||
begin
|
|
||||||
insert into process_queue (channel, payload, stored) values ('new_account', NEW.email, now());
|
|
||||||
return NEW;
|
|
||||||
END;
|
|
||||||
$new_account_entry$ language plpgsql;
|
|
||||||
|
|
||||||
create function new_account_otp_validation_entry_fn() returns trigger as $new_account_otp_validation_entry$
|
|
||||||
begin
|
|
||||||
insert into process_queue (channel, payload, stored) values ('new_account_otp', NEW.email, now());
|
|
||||||
return NEW;
|
|
||||||
END;
|
|
||||||
$new_account_otp_validation_entry$ language plpgsql;
|
|
||||||
|
|
||||||
create function new_vessel_entry_fn() returns trigger as $new_vessel_entry$
|
|
||||||
begin
|
|
||||||
insert into process_queue (channel, payload, stored) values ('new_vessel', NEW.owner_email, now());
|
|
||||||
return NEW;
|
|
||||||
END;
|
|
||||||
$new_vessel_entry$ language plpgsql;
|
|
||||||
|
|
||||||
---------------------------------------------------------------------------
|
|
||||||
-- App settings
|
|
||||||
-- https://dba.stackexchange.com/questions/27296/storing-application-settings-with-different-datatypes#27297
|
|
||||||
-- https://stackoverflow.com/questions/6893780/how-to-store-site-wide-settings-in-a-database
|
|
||||||
-- http://cvs.savannah.gnu.org/viewvc/*checkout*/gnumed/gnumed/gnumed/server/sql/gmconfiguration.sql
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.app_settings (
|
|
||||||
name TEXT NOT NULL UNIQUE,
|
|
||||||
value TEXT NOT NULL
|
|
||||||
);
|
|
||||||
-- Description
|
|
||||||
COMMENT ON TABLE public.app_settings IS 'application settings';
|
|
||||||
COMMENT ON COLUMN public.app_settings.name IS 'application settings name key';
|
|
||||||
COMMENT ON COLUMN public.app_settings.value IS 'application settings value';
|
|
||||||
|
|
||||||
---------------------------------------------------------------------------
|
|
||||||
-- Badges descriptions
|
|
||||||
-- TODO add contiditions
|
|
||||||
--
|
|
||||||
CREATE TABLE IF NOT EXISTS badges(
|
|
||||||
name TEXT UNIQUE,
|
|
||||||
description TEXT
|
|
||||||
);
|
|
||||||
-- Description
|
|
||||||
COMMENT ON TABLE
|
|
||||||
public.badges
|
|
||||||
IS 'Badges descriptions';
|
|
||||||
|
|
||||||
INSERT INTO badges VALUES
|
|
||||||
('Helmsman',
|
|
||||||
'Nice work logging your first sail! You are officially a helmsman now!'),
|
|
||||||
('Wake Maker',
|
|
||||||
'Yowzers! Welcome to the 15 knot+ club ya speed demon skipper!'),
|
|
||||||
('Explorer',
|
|
||||||
'It looks like home is where the helm is. Cheers to 10 days away from home port!'),
|
|
||||||
('Mooring Pro',
|
|
||||||
'It takes a lot of skill to "thread that floating needle" but seems like you have mastered mooring with 10 nights on buoy!'),
|
|
||||||
('Anchormaster',
|
|
||||||
'Hook, line and sinker, you have this anchoring thing down! 25 days on the hook for you!'),
|
|
||||||
('Traveler',
|
|
||||||
'Who needs to fly when one can sail! You are an international sailor. À votre santé!'),
|
|
||||||
('Stormtrooper',
|
|
||||||
'Just like the elite defenders of the Empire, here you are, our braving your own hydro-empire in windspeeds above 30kts. Nice work trooper! '),
|
|
||||||
('Club Alaska',
|
|
||||||
'Home to the bears, glaciers, midnight sun and high adventure. Welcome to the Club Alaska Captain!'),
|
|
||||||
('Tropical Traveler',
|
|
||||||
'Look at you with your suntan, tropical drink and southern latitude!'),
|
|
||||||
('Aloha Award',
|
|
||||||
'Ticking off over 2300 NM across the great blue Pacific makes you the rare recipient of the Aloha Award. Well done and Aloha sailor!'),
|
|
||||||
('Tyee',
|
|
||||||
'You made it to the Tyee Outstation, the friendliest dock in Pacific Northwest!'),
|
|
||||||
-- TODO the sea is big and the world is not limited to the US
|
|
||||||
('Mediterranean Traveler',
|
|
||||||
'You made it trought the Mediterranean!');
|
|
||||||
|
|
||||||
create function public.process_badge_queue_fn() RETURNS void AS $process_badge_queue$
|
create function public.process_badge_queue_fn() RETURNS void AS $process_badge_queue$
|
||||||
declare
|
declare
|
||||||
badge_rec record;
|
badge_rec record;
|
||||||
@@ -1139,6 +649,7 @@ $process_badge_queue$ language plpgsql;
|
|||||||
-- TODO add alert monitoring for Battery
|
-- TODO add alert monitoring for Battery
|
||||||
|
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
-- PostgREST API pre-request check
|
||||||
-- TODO db-pre-request = "public.check_jwt"
|
-- TODO db-pre-request = "public.check_jwt"
|
||||||
-- Prevent unregister user or unregister vessel access
|
-- Prevent unregister user or unregister vessel access
|
||||||
CREATE OR REPLACE FUNCTION public.check_jwt() RETURNS void AS $$
|
CREATE OR REPLACE FUNCTION public.check_jwt() RETURNS void AS $$
|
||||||
@@ -1243,6 +754,7 @@ BEGIN
|
|||||||
END
|
END
|
||||||
$$ language plpgsql security definer;
|
$$ language plpgsql security definer;
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
-- Function to trigger cron_jobs using API for tests.
|
-- Function to trigger cron_jobs using API for tests.
|
||||||
-- Todo limit access and permision
|
-- Todo limit access and permision
|
||||||
-- Run con jobs
|
-- Run con jobs
|
308
initdb/02_3_3_signalk_public_functions_py.sql
Normal file
308
initdb/02_3_3_signalk_public_functions_py.sql
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- singalk db public schema
|
||||||
|
--
|
||||||
|
|
||||||
|
-- List current database
|
||||||
|
select current_database();
|
||||||
|
|
||||||
|
-- connect to the DB
|
||||||
|
\c signalk
|
||||||
|
|
||||||
|
CREATE SCHEMA IF NOT EXISTS public;
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- python reverse_geocode
|
||||||
|
--
|
||||||
|
-- https://github.com/CartoDB/labs-postgresql/blob/master/workshop/plpython.md
|
||||||
|
--
|
||||||
|
DROP FUNCTION IF EXISTS reverse_geocode_py_fn;
|
||||||
|
CREATE OR REPLACE FUNCTION reverse_geocode_py_fn(IN geocoder TEXT, IN lon NUMERIC, IN lat NUMERIC,
|
||||||
|
OUT geo_name TEXT)
|
||||||
|
AS $reverse_geocode_py$
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# Use the shared cache to avoid preparing the geocoder metadata
|
||||||
|
if geocoder in SD:
|
||||||
|
plan = SD[geocoder]
|
||||||
|
# A prepared statement from Python
|
||||||
|
else:
|
||||||
|
plan = plpy.prepare("SELECT reverse_url AS url FROM geocoders WHERE name = $1", ["text"])
|
||||||
|
SD[geocoder] = plan
|
||||||
|
|
||||||
|
# Execute the statement with the geocoder param and limit to 1 result
|
||||||
|
rv = plpy.execute(plan, [geocoder], 1)
|
||||||
|
url = rv[0]['url']
|
||||||
|
|
||||||
|
# Validate input
|
||||||
|
if not lon or not lat:
|
||||||
|
plpy.notice('reverse_geocode_py_fn Parameters [{}] [{}]'.format(lon, lat))
|
||||||
|
plpy.error('Error missing parameters')
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Make the request to the geocoder API
|
||||||
|
payload = {"lon": lon, "lat": lat, "format": "jsonv2", "zoom": 18}
|
||||||
|
r = requests.get(url, params=payload)
|
||||||
|
|
||||||
|
# Return the full address or nothing if not found
|
||||||
|
if r.status_code == 200 and "name" in r.json():
|
||||||
|
return r.json()["name"]
|
||||||
|
else:
|
||||||
|
plpy.error('Failed to received a geo full address %s', r.json())
|
||||||
|
return 'unknow'
|
||||||
|
$reverse_geocode_py$ LANGUAGE plpython3u;
|
||||||
|
-- Description
|
||||||
|
COMMENT ON FUNCTION
|
||||||
|
public.reverse_geocode_py_fn
|
||||||
|
IS 'query reverse geo service to return location name using plpython3u';
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- python send email
|
||||||
|
--
|
||||||
|
-- https://www.programcreek.com/python/example/3684/email.utils.formatdate
|
||||||
|
DROP FUNCTION IF EXISTS send_email_py_fn;
|
||||||
|
CREATE OR REPLACE FUNCTION send_email_py_fn(IN email_type TEXT, IN _user JSONB, IN app JSONB) RETURNS void
|
||||||
|
AS $send_email_py$
|
||||||
|
# Import smtplib for the actual sending function
|
||||||
|
import smtplib
|
||||||
|
|
||||||
|
# Import the email modules we need
|
||||||
|
#from email.message import EmailMessage
|
||||||
|
from email.utils import formatdate,make_msgid
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
|
# Use the shared cache to avoid preparing the email metadata
|
||||||
|
if email_type in SD:
|
||||||
|
plan = SD[email_type]
|
||||||
|
# A prepared statement from Python
|
||||||
|
else:
|
||||||
|
plan = plpy.prepare("SELECT * FROM email_templates WHERE name = $1", ["text"])
|
||||||
|
SD[email_type] = plan
|
||||||
|
|
||||||
|
# Execute the statement with the email_type param and limit to 1 result
|
||||||
|
rv = plpy.execute(plan, [email_type], 1)
|
||||||
|
email_subject = rv[0]['email_subject']
|
||||||
|
email_content = rv[0]['email_content']
|
||||||
|
|
||||||
|
# Replace fields using input jsonb obj
|
||||||
|
if not _user or not app:
|
||||||
|
plpy.notice('send_email_py_fn Parameters [{}] [{}]'.format(_user, app))
|
||||||
|
plpy.error('Error missing parameters')
|
||||||
|
return None
|
||||||
|
if 'logbook_name' in _user and _user['logbook_name']:
|
||||||
|
email_content = email_content.replace('__LOGBOOK_NAME__', _user['logbook_name'])
|
||||||
|
if 'logbook_link' in _user and _user['logbook_link']:
|
||||||
|
email_content = email_content.replace('__LOGBOOK_LINK__', str(_user['logbook_link']))
|
||||||
|
if 'recipient' in _user and _user['recipient']:
|
||||||
|
email_content = email_content.replace('__RECIPIENT__', _user['recipient'])
|
||||||
|
if 'boat' in _user and _user['boat']:
|
||||||
|
email_content = email_content.replace('__BOAT__', _user['boat'])
|
||||||
|
if 'badge' in _user and _user['badge']:
|
||||||
|
email_content = email_content.replace('__BADGE_NAME__', _user['badge'])
|
||||||
|
if 'otp_code' in _user and _user['otp_code']:
|
||||||
|
email_content = email_content.replace('__OTP_CODE__', _user['otp_code'])
|
||||||
|
|
||||||
|
if 'app.url' in app and app['app.url']:
|
||||||
|
email_content = email_content.replace('__APP_URL__', app['app.url'])
|
||||||
|
|
||||||
|
email_from = 'root@localhost'
|
||||||
|
if 'app.email_from' in app and app['app.email_from']:
|
||||||
|
email_from = 'PostgSail <' + app['app.email_from'] + '>'
|
||||||
|
#plpy.notice('Sending email from [{}] [{}]'.format(email_from, app['app.email_from']))
|
||||||
|
|
||||||
|
email_to = 'root@localhost'
|
||||||
|
if 'email' in _user and _user['email']:
|
||||||
|
email_to = _user['email']
|
||||||
|
#plpy.notice('Sending email to [{}] [{}]'.format(email_to, _user['email']))
|
||||||
|
else:
|
||||||
|
plpy.error('Error email to')
|
||||||
|
return None
|
||||||
|
|
||||||
|
msg = MIMEText(email_content, 'plain', 'utf-8')
|
||||||
|
msg["Subject"] = email_subject
|
||||||
|
msg["From"] = email_from
|
||||||
|
msg["To"] = email_to
|
||||||
|
msg["Date"] = formatdate()
|
||||||
|
msg["Message-ID"] = make_msgid()
|
||||||
|
|
||||||
|
server_smtp = 'localhost'
|
||||||
|
if 'app.email_server' in app and app['app.email_server']:
|
||||||
|
server_smtp = app['app.email_server']
|
||||||
|
|
||||||
|
# Send the message via our own SMTP server.
|
||||||
|
try:
|
||||||
|
# send your message with credentials specified above
|
||||||
|
with smtplib.SMTP(server_smtp, 25) as server:
|
||||||
|
if 'app.email_user' in app and app['app.email_user'] \
|
||||||
|
and 'app.email_pass' in app and app['app.email_pass']:
|
||||||
|
server.starttls()
|
||||||
|
server.login(app['app.email_user'], app['app.email_pass'])
|
||||||
|
#server.send_message(msg)
|
||||||
|
server.sendmail(msg["From"], msg["To"], msg.as_string())
|
||||||
|
server.quit()
|
||||||
|
# tell the script to report if your message was sent or which errors need to be fixed
|
||||||
|
plpy.notice('Sent email successfully to [{}] [{}]'.format(msg["To"], msg["Subject"]))
|
||||||
|
return None
|
||||||
|
except OSError as error:
|
||||||
|
plpy.error(error)
|
||||||
|
except smtplib.SMTPConnectError:
|
||||||
|
plpy.error('Failed to connect to the server. Bad connection settings?')
|
||||||
|
except smtplib.SMTPServerDisconnected:
|
||||||
|
plpy.error('Failed to connect to the server. Wrong user/password?')
|
||||||
|
except smtplib.SMTPException as e:
|
||||||
|
plpy.error('SMTP error occurred: ' + str(e))
|
||||||
|
$send_email_py$ TRANSFORM FOR TYPE jsonb LANGUAGE plpython3u;
|
||||||
|
-- Description
|
||||||
|
COMMENT ON FUNCTION
|
||||||
|
public.send_email_py_fn
|
||||||
|
IS 'Send email notification using plpython3u';
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- python send pushover message
|
||||||
|
-- https://pushover.net/
|
||||||
|
DROP FUNCTION IF EXISTS send_pushover_py_fn;
|
||||||
|
CREATE OR REPLACE FUNCTION send_pushover_py_fn(IN message_type TEXT, IN _user JSONB, IN app JSONB) RETURNS void
|
||||||
|
AS $send_pushover_py$
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# Use the shared cache to avoid preparing the email metadata
|
||||||
|
if message_type in SD:
|
||||||
|
plan = SD[message_type]
|
||||||
|
# A prepared statement from Python
|
||||||
|
else:
|
||||||
|
plan = plpy.prepare("SELECT * FROM email_templates WHERE name = $1", ["text"])
|
||||||
|
SD[message_type] = plan
|
||||||
|
|
||||||
|
# Execute the statement with the message_type param and limit to 1 result
|
||||||
|
rv = plpy.execute(plan, [message_type], 1)
|
||||||
|
pushover_title = rv[0]['pushover_title']
|
||||||
|
pushover_message = rv[0]['pushover_message']
|
||||||
|
|
||||||
|
# Replace fields using input jsonb obj
|
||||||
|
if 'logbook_name' in _user and _user['logbook_name']:
|
||||||
|
pushover_message = pushover_message.replace('__LOGBOOK_NAME__', _user['logbook_name'])
|
||||||
|
if 'logbook_link' in _user and _user['logbook_link']:
|
||||||
|
pushover_message = pushover_message.replace('__LOGBOOK_LINK__', str(_user['logbook_link']))
|
||||||
|
if 'recipient' in _user and _user['recipient']:
|
||||||
|
pushover_message = pushover_message.replace('__RECIPIENT__', _user['recipient'])
|
||||||
|
if 'boat' in _user and _user['boat']:
|
||||||
|
pushover_message = pushover_message.replace('__BOAT__', _user['boat'])
|
||||||
|
if 'badge' in _user and _user['badge']:
|
||||||
|
pushover_message = pushover_message.replace('__BADGE_NAME__', _user['badge'])
|
||||||
|
|
||||||
|
if 'app.url' in app and app['app.url']:
|
||||||
|
pushover_message = pushover_message.replace('__APP_URL__', app['app.url'])
|
||||||
|
|
||||||
|
pushover_token = None
|
||||||
|
if 'app.pushover_app_token' in app and app['app.pushover_app_token']:
|
||||||
|
pushover_token = app['app.pushover_app_token']
|
||||||
|
else:
|
||||||
|
plpy.error('Error no pushover token defined, check app settings')
|
||||||
|
return None
|
||||||
|
pushover_user = None
|
||||||
|
if 'pushover_user_key' in _user and _user['pushover_user_key']:
|
||||||
|
pushover_user = _user['pushover_user_key']
|
||||||
|
else:
|
||||||
|
plpy.error('Error no pushover user token defined, check user settings')
|
||||||
|
return None
|
||||||
|
|
||||||
|
# requests
|
||||||
|
r = requests.post("https://api.pushover.net/1/messages.json", data = {
|
||||||
|
"token": pushover_token,
|
||||||
|
"user": pushover_user,
|
||||||
|
"title": pushover_title,
|
||||||
|
"message": pushover_message
|
||||||
|
})
|
||||||
|
|
||||||
|
#print(r.text)
|
||||||
|
# Return ?? or None if not found
|
||||||
|
plpy.notice('Sent pushover successfully to [{}] [{}]'.format(r.text, r.status_code))
|
||||||
|
if r.status_code == 200:
|
||||||
|
plpy.notice('Sent pushover successfully to [{}] [{}] [{}]'.format("__USER__", pushover_title, r.text))
|
||||||
|
else:
|
||||||
|
plpy.error('Failed to send pushover')
|
||||||
|
return None
|
||||||
|
$send_pushover_py$ TRANSFORM FOR TYPE jsonb LANGUAGE plpython3u;
|
||||||
|
-- Description
|
||||||
|
COMMENT ON FUNCTION
|
||||||
|
public.send_pushover_py_fn
|
||||||
|
IS 'Send pushover notification using plpython3u';
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- python send telegram message
|
||||||
|
-- https://core.telegram.org/
|
||||||
|
DROP FUNCTION IF EXISTS send_telegram_py_fn;
|
||||||
|
CREATE OR REPLACE FUNCTION send_telegram_py_fn(IN message_type TEXT, IN _user JSONB, IN app JSONB) RETURNS void
|
||||||
|
AS $send_telegram_py$
|
||||||
|
"""
|
||||||
|
Send a message to a telegram user or group specified on chatId
|
||||||
|
chat_id must be a number!
|
||||||
|
"""
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Use the shared cache to avoid preparing the email metadata
|
||||||
|
if message_type in SD:
|
||||||
|
plan = SD[message_type]
|
||||||
|
# A prepared statement from Python
|
||||||
|
else:
|
||||||
|
plan = plpy.prepare("SELECT * FROM email_templates WHERE name = $1", ["text"])
|
||||||
|
SD[message_type] = plan
|
||||||
|
|
||||||
|
# Execute the statement with the message_type param and limit to 1 result
|
||||||
|
rv = plpy.execute(plan, [message_type], 1)
|
||||||
|
telegram_title = rv[0]['pushover_title']
|
||||||
|
telegram_message = rv[0]['pushover_message']
|
||||||
|
|
||||||
|
# Replace fields using input jsonb obj
|
||||||
|
if 'logbook_name' in _user and _user['logbook_name']:
|
||||||
|
telegram_message = telegram_message.replace('__LOGBOOK_NAME__', _user['logbook_name'])
|
||||||
|
if 'logbook_link' in _user and _user['logbook_link']:
|
||||||
|
telegram_message = telegram_message.replace('__LOGBOOK_LINK__', str(_user['logbook_link']))
|
||||||
|
if 'recipient' in _user and _user['recipient']:
|
||||||
|
telegram_message = telegram_message.replace('__RECIPIENT__', _user['recipient'])
|
||||||
|
if 'boat' in _user and _user['boat']:
|
||||||
|
telegram_message = telegram_message.replace('__BOAT__', _user['boat'])
|
||||||
|
if 'badge' in _user and _user['badge']:
|
||||||
|
telegram_message = telegram_message.replace('__BADGE_NAME__', _user['badge'])
|
||||||
|
|
||||||
|
if 'app.url' in app and app['app.url']:
|
||||||
|
telegram_message = telegram_message.replace('__APP_URL__', app['app.url'])
|
||||||
|
|
||||||
|
telegram_token = None
|
||||||
|
if 'app.telegram_bot_token' in app and app['app.telegram_bot_token']:
|
||||||
|
telegram_token = app['app.telegram_bot_token']
|
||||||
|
else:
|
||||||
|
plpy.error('Error no telegram token defined, check app settings')
|
||||||
|
return None
|
||||||
|
telegram_chat_id = None
|
||||||
|
if 'telegram_chat_id' in _user and _user['telegram_chat_id']:
|
||||||
|
telegram_chat_id = _user['telegram_chat_id']
|
||||||
|
else:
|
||||||
|
plpy.error('Error no telegram user token defined, check user settings')
|
||||||
|
return None
|
||||||
|
|
||||||
|
# requests
|
||||||
|
headers = {'Content-Type': 'application/json',
|
||||||
|
'Proxy-Authorization': 'Basic base64'}
|
||||||
|
data_dict = {'chat_id': telegram_chat_id,
|
||||||
|
'text': telegram_message,
|
||||||
|
'parse_mode': 'HTML',
|
||||||
|
'disable_notification': False}
|
||||||
|
data = json.dumps(data_dict)
|
||||||
|
url = f'https://api.telegram.org/bot{telegram_token}/sendMessage'
|
||||||
|
r = requests.post(url,
|
||||||
|
data=data,
|
||||||
|
headers=headers)
|
||||||
|
print(r.text)
|
||||||
|
# Return the full address or None if not found
|
||||||
|
plpy.notice('Sent telegram successfully to [{}] [{}]'.format(r.text, r.status_code))
|
||||||
|
if r.status_code == 200:
|
||||||
|
plpy.notice('Sent telegram successfully to [{}] [{}] [{}]'.format("__USER__", telegram_title, r.text))
|
||||||
|
else:
|
||||||
|
plpy.error('Failed to send telegram')
|
||||||
|
return None
|
||||||
|
$send_telegram_py$ TRANSFORM FOR TYPE jsonb LANGUAGE plpython3u;
|
||||||
|
-- Description
|
||||||
|
COMMENT ON FUNCTION
|
||||||
|
public.send_telegram_py_fn
|
||||||
|
IS 'Send a message to a telegram user or group specified on chatId using plpython3u';
|
Reference in New Issue
Block a user