def send_daily_report(hours_of_day: list): audience = get_all_humans_for_telegram_notifications( hours_of_day=hours_of_day) # create bot bot = get_telegram_bot_instance() # send to audience notify_admin = True count = 0 for member in audience: try: human = Human(human_id=member[HumanProperties.ID.value]) # get most recent coordinates most_recent_location = human.get_most_recent_location() if most_recent_location is None: return "NOTHING TO SHOW. SHARE YOUR LOCATION FIRST." lat, lng = most_recent_location human.send_proximity_alert(lat=lat, lng=lng) count += 1 except Exception as e: if notify_admin: bot.send_message( chat_id=int( CONFIG.get('telegram-credentials-telegram-admin-id')), text="[ADMIN] `send_daily_report` exception : {}".format( e)) notify_admin = False # alert admin bot.send_message( chat_id=int(CONFIG.get('telegram-credentials-telegram-admin-id')), text="[ADMIN] Sent proximity report to {}/{} humans.".format( count, len(audience)))
def get_proximity_alert(lat: float, lng: float) -> str: km_radius = int(CONFIG.get('km_radius')) days_window = int(CONFIG.get('days_window')) risky_humans = get_risky_humans(lat=lat, lng=lng, days_window=days_window, km_radius=km_radius) if len(risky_humans) == 0: alert_emoji = '🔔' alert_info = "*0 individuals* who self-reported symptoms (fever, cough, shortness of breath) were found" else: alert_emoji = '❗' alert_info = "*{} individual(s)* who self-reported symptoms (fever, cough, shortness of breath) were found. ".format( len(risky_humans) ) alert_info += "\n\nSee specific locations on the map below ⬇️ and *please exercise additional caution* ⚠️." alert_message = """ {} During the last {} days and within a {} km ({} miles) radius from your current location, {} DISCLAIMER: This only represents the data *Opendemic* users have shared and might not be complete. Always be cautious and follow your local public health authority's guidelines. """.format( alert_emoji, days_window, km_radius, round(km_radius / 1.609, 1), alert_info ) return alert_message
def global_map(token): if token != CONFIG.get('global-map-token'): abort(403) lat = request.args.get(CoordinateType.LATITUDE.value) lng = request.args.get(CoordinateType.LONGITUDE.value) if lat is None or lng is None: lat, lng = DEFAULT_LAT, DEFAULT_LNG risky_humans_geojson = get_risky_humans_geojson( lat=lat, lng=lng, days_window=int(CONFIG.get('days_window')), km_radius=None ) return render_template( MAP_TEMPLATE, self_geojson_feature=None, self_lat_lng=[lng, lat], risky_humans_geojson=risky_humans_geojson, km_radius=0, include_legend=True, zoom_level=1 )
def dynamic_map(): risky_humans_geojson = get_risky_humans_geojson( lat=request.args.get('lat', None), lng=request.args.get('lng', None), days_window=int(CONFIG.get('days_window')), km_radius=int(CONFIG.get('km_radius')) ) return json.dumps(risky_humans_geojson)
def location(): # Fetch or create human, log its location, and return nearby risky humans params = create_parameters() validation_error_response = validate_location_url_params(params=params) if len(validation_error_response) > 0: return_invalid_response(validation_error_response) human = model.get_human_from_fingerprint(fingerprint=params[LocationResourceFields.FINGERPRINT.value]) if human is None: human, err = model.create_human(fingerprint=params[LocationResourceFields.FINGERPRINT.value]) if err is not None: logger.error(err) response = Response( response=json.dumps({ "error": "Error creating human with fingerprint {}".format(params[LocationResourceFields.FINGERPRINT.value]) }), status=403, mimetype='application/json' ) response.headers.add('Access-Control-Allow-Origin', '*') return response human.log_location( latitude=float(params[LocationResourceFields.LATITUDE.value]), longitude=float(params[LocationResourceFields.LONGITUDE.value]), send_alert=False) self_lat_lng = [params[LocationResourceFields.LONGITUDE.value], params[LocationResourceFields.LATITUDE.value]] self_geojson_feature = { 'type': "Point", 'coordinates': self_lat_lng } risky_humans_geojson = model.get_risky_humans_geojson( lat=params[LocationResourceFields.LATITUDE.value], lng=params[LocationResourceFields.LONGITUDE.value], days_window=int(CONFIG.get('days_window')), km_radius=int(CONFIG.get('km_radius')) ) return render_template( 'map.html', self_geojson_feature=self_geojson_feature, self_lat_lng=self_lat_lng, risky_humans_geojson=risky_humans_geojson, km_radius=int(CONFIG.get('km_radius')), include_legend=params[LocationResourceFields.INCLUDE_LEGEND.value], zoom_level=9 )
def send_email(sender: str, recipient: str, subject: str, body_text: str) -> bool: charset = "UTF-8" client = boto3.client('ses', region_name=CONFIG.get('aws-region')) try: response = client.send_email(Destination={ 'ToAddresses': [recipient], }, Message={ 'Body': { 'Text': { 'Charset': charset, 'Data': body_text }, }, 'Subject': { 'Charset': charset, 'Data': subject }, }, Source=sender) except ClientError as e: logger.error(e.response['Error']['Message']) return False else: logger.debug("Email sent! Message ID : {}".format( response['MessageId'])) return True
def telegram(token): # authenticate webhook if token != CONFIG.get('webhook_token'): abort(404) # fetch and validate request content type if request.headers.get('content-type') == 'application/json': return process_telegram_update(update=get_webhook_update(request=request)) else: abort(403)
def create(cls, name, i2c_address): """ This method creates a desk on both the API side and the local config side. :param name: Name of desk :param i2c_address: Address of desk on the I2C bus :return: A tuple containing a status flag and the json response from our API """ response = ApiService.create_desk(name) json_response = response.json() if type(i2c_address) is str: i2c_address = int(i2c_address, 16) if response.status_code == 201: CONFIG.add_desk(json_response['id'], name, hex(i2c_address)) return cls.SUCCESS_STATUS, json_response else: return cls.FAILURE_STATUS, json_response
def getLogger(cls): if not cls.__logger: logger = logging.getLogger(CONFIG.get("APP_NAME")) flhdlr = logging.FileHandler(CONFIG["LOGS_PATH"] + CONFIG["LOGS_FILE_NAME"]) console_handler = logging.StreamHandler() formatter = logging.Formatter( "%(asctime)s %(levelname)s %(message)s") flhdlr.setFormatter(formatter) logger.addHandler(console_handler) logger.addHandler(flhdlr) logger.setLevel(CONFIG.get("LOGGING_LEVEL")) cls.__logger = logger return cls.__logger
def send_feedback_request(hours_of_day: list): audience = get_all_humans_for_telegram_notifications( hours_of_day=hours_of_day) # create bot bot = get_telegram_bot_instance() # send to audience notify_admin = True count = 0 for member in audience: try: bot.send_message( chat_id=member[HumanProperties.TELEGRAM_HUMAN_ID.value], text= "*[🤙 Feedback Request]* Please share your feedback on the product by clicking here: @OpendemicTeam", parse_mode='markdown', reply_markup=get_telegram_menu()) except Exception as e: if notify_admin: bot.send_message( chat_id=int( CONFIG.get('telegram-credentials-telegram-admin-id')), text="[ADMIN] `send_feedback_request` exception : {}". format(e)) notify_admin = False # try to unsubscribe try: human = Human(human_id=member[HumanProperties.ID.value]) human.unsubscribe() except Exception as unsb_e: pass else: count += 1 # alert admin bot.send_message( chat_id=int(CONFIG.get('telegram-credentials-telegram-admin-id')), text="[ADMIN] Sent feedback request to {}/{} humans.".format( count, len(audience)))
def post(self): nem = NEMConnect() data = json.loads(request.data) transation = data.get("transaction") if transation is not None: timestamp = utils.get_timestamp() transation["timeStamp"] = timestamp transation["deadline"] = utils.get_deadline(timestamp) transation["fee"] = CONFIG["CreateMosaicTransfer"].get("fee") transation["type"] = CONFIG["CreateMosaicTransfer"].get("type") transation["version"] = CONFIG.get("version") response = nem.initiate_transaction(data) return response
def send_reminders(hours_of_day: list): audience = get_all_humans_for_telegram_notifications( hours_of_day=hours_of_day) # create bot bot = get_telegram_bot_instance() # send to audience notify_admin = True count = 0 for member in audience: try: bot.send_message( chat_id=member[HumanProperties.TELEGRAM_HUMAN_ID.value], text= "👇 Remember to report your location (always) and symptoms (if any) 👇", reply_markup=get_telegram_menu()) except Exception as e: if notify_admin: bot.send_message( chat_id=int( CONFIG.get('telegram-credentials-telegram-admin-id')), text="[ADMIN] `send_reminders` exception : {}".format(e)) notify_admin = False # try to unsubscribe try: human = Human(human_id=member[HumanProperties.ID.value]) human.unsubscribe() except Exception as unsb_e: pass else: count += 1 # alert admin bot.send_message(chat_id=int( CONFIG.get('telegram-credentials-telegram-admin-id')), text="[ADMIN] Sent reminder to {}/{} humans.".format( count, len(audience)))
def __create_objects(server, idx): """ Cria os objetos no servidor OPC-UA """ # node de todos os objetos (parent) objects = server.get_objects_node() # node dos tipos uatypes = server.get_base_objectType_node() # lista de objetos a serem criados obj_list = CONFIG.get_objects() try: for name_obj in obj_list: config = CONFIG(entity=name_obj) path = [ ":".join([str(idx), config.INHERIT]), ":".join([str(idx), config.OPC_TYPE]) ] type = uatypes.get_child(path) # cria no servidor opc os objetos objects.add_object(idx, name_obj, objecttype=type.nodeid) logger.info( "Criado com sucesso objeto {} do tipo {} em {}".format( name_obj, type, objects)) except: logger.error( "Parametros Nome do objeo {} Path {} incorretos para criação dos objetos" .format(name_obj, path))
def fulfill_my_map_request(human: Human) -> bool: bot = get_telegram_bot_instance() try: bot.send_message( chat_id=human.telegram_human_id, text="See who's around you 👇", parse_mode='markdown', reply_markup=make_reply_keyboard_markup(markup_map=[ {'text': "🌍 See Map", 'url': CONFIG.get('client-url')}, ]) ) except Exception as e: logger.error(e) return False return True
def local_map(human_id): try: human = Human(human_id=human_id) except Exception as e: logger.error(e) abort(403) # get most recent coordinates most_recent_location = human.get_most_recent_location() if most_recent_location is None: return "Nothing to show. Share your location first.", 200 else: lat, lng = most_recent_location self_lat_lng = [lng, lat] self_geojson_feature = { 'type': "Point", 'coordinates': self_lat_lng } risky_humans_geojson = get_risky_humans_geojson( lat=lat, lng=lng, days_window=int(CONFIG.get('days_window')), km_radius=int(CONFIG.get('km_radius')) ) return render_template( MAP_TEMPLATE, self_geojson_feature=self_geojson_feature, self_lat_lng=self_lat_lng, risky_humans_geojson=risky_humans_geojson, km_radius=int(CONFIG.get('km_radius')), include_legend=True, zoom_level=9 )
def __init__(self, reader=False): if reader: host = CONFIG.get('rds-aurora-mysql-opendemic-host-reader') else: host = CONFIG.get('rds-aurora-mysql-opendemic-host') self.connection = pymysql.connect( host, port=CONFIG.getint('rds-aurora-mysql-opendemic-port'), user=CONFIG.get('rds-aurora-mysql-opendemic-username'), password=CONFIG.get('rds-aurora-mysql-opendemic-password'), db=CONFIG.get('rds-aurora-mysql-opendemic-database'), charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor)
def post(self): nem = NEMConnect() validator_obj = Validator(namespace_schema) data = json.loads(request.data) validate = validator_obj.validate(data) response = {} if (validate): transation = data.get("transaction") if transation is not None: timestamp = utils.get_timestamp() transation["timeStamp"] = timestamp transation["deadline"] = utils.get_deadline(timestamp) transation["rentalFee"] = CONFIG["CreateNameSpace"].get( "rentalFee") transation["fee"] = CONFIG["CreateNameSpace"].get("fee") transation["type"] = CONFIG["CreateNameSpace"].get("type") transation["version"] = CONFIG.get("version") response = nem.initiate_transaction(data) else: response['Errors'] = validator_obj.errors return response
def send_proximity_alert(self, lat: float, lng: float) -> bool: if self.telegram_human_id is None: return False alert_message = get_proximity_alert(lat=lat, lng=lng) bot = get_telegram_bot_instance() try: bot.send_message( chat_id=self.telegram_human_id, text=alert_message, parse_mode='markdown', reply_markup=make_reply_keyboard_markup(markup_map=[ {'text': "🌍 See Map", 'url': CONFIG.get('client-url')}, ]) ) except Exception as e: logger.error(e) self.unsubscribe() return False else: return True
def device(type, idx, name): """ Inicia a execução de um dispositivo """ try: uaClient.connect() class_type = CONFIG.choice_to_classe(type) Factory.create_device(idx, name, class_type) except: #print(e) logger.error("Erro ao tentar criar se conectar no servidor opcua !") uaClient.disconnect() click.echo("Pressione [Ctrl+c] para interronper a execução\n") uaDevice.run_forever() uaClient.disconnect()
def process_telegram_update(update: Update): bot = get_telegram_bot_instance() # get payload attributes if update.callback_query is not None: telegram_human_id = int(update.callback_query.from_user.id) telegram_message_timestamp = datetime.datetime.fromtimestamp(update.callback_query.message.date) telegram_message_id = int(update.callback_query.message.message_id) try: bot.delete_message(chat_id=telegram_human_id, message_id=telegram_message_id) except Exception as e: pass elif update.message is not None: telegram_human_id = int(update.message.from_user.id) telegram_message_timestamp = datetime.datetime.fromtimestamp(update.message.date) telegram_message_id = int(update.message.message_id) else: return '', 204 # typing animation try: bot.send_chat_action( chat_id=telegram_human_id, action='typing' ) except Exception as e: return '', 204 else: time.sleep(0.1) # detect action type is_message = False is_callback = False callback_data = None message_content_type = None message_is_command = False message_text = None telegram_command = None is_reply = False reply_to_message_id = None if update.callback_query is not None: # fetch callback data callback_data = update.callback_query.data is_callback = True if update.message is not None: # set flag is_message = True is_reply = update.message.reply_to_message is not None if is_reply: reply_to_message_id = update.message.reply_to_message.message_id # fetch message content type message_content_type = update.message.content_type # case message is text if message_content_type == 'text': message_text = update.message.text # case message is Telegram command if TelegramCommand.is_telegram_command(data=message_text): telegram_command = TelegramCommand(data=message_text) message_is_command = True logger.debug("[TELEGRAM WEBHOOK REQUEST] human : {}".format(telegram_human_id)) logger.debug("[TELEGRAM WEBHOOK REQUEST] timestamp : {}".format(telegram_message_timestamp)) logger.debug("[TELEGRAM WEBHOOK REQUEST] message : {}".format(telegram_message_id)) # authenticate human human_exists, human_id = verify_telegram_id_exists(telegram_human_id=telegram_human_id) # log message if is_callback: action_type = CONFIG.get('INTENT-ACTION') action_value = callback_data elif is_message: if message_is_command: action_type = CONFIG.get('TELEGRAM-COMMAND') action_value = telegram_command.command else: action_type = message_content_type action_value = message_text log_action( human_id=human_id, telegram_human_id=telegram_human_id, from_human=True, action_type=action_type, action_value=action_value, local_timestamp=telegram_message_timestamp, message_id=telegram_message_id ) # redirect to landing unless if human_exists: # fetch human human = Human(human_id=human_id) else: try: log_sent_message(bot.send_message( chat_id=telegram_human_id, text="Welcome to Opendemic!", reply_markup=get_telegram_menu() ), human_id=human_id) except Exception as e: return '', 204 else: human, err = create_human(telegram_human_id=telegram_human_id) if err is not None: logger.error(err) return '', 204 log_sent_message(bot.send_message( chat_id=telegram_human_id, text=""" *Opendemic* is an non-profit anonymous COVID-19 proximity alert system. Here is how it works ⬇️: 1. Anonymously share your location and symptoms as much as you can (we'll send you reminders to prompt you) 2. You'll get an alert if you've been in close proximity to a potential COVID-19 case 3. *Opendemic* will only share anonymous data with public health authorities """, parse_mode='markdown', reply_markup=get_telegram_menu() ), human_id=human_id) return '', 204 # case callback if is_callback: # process callback fulfill_intent( intent=callback_data, human_id=human_id ) elif is_message: # case text if message_content_type == 'text': # case command if message_is_command: # process command fulfill_telegram_command( telegram_command=telegram_command, human_id=human_id ) # case free-form message else: if is_reply: log_action( human_id=human_id, telegram_human_id=telegram_human_id, from_human=True, action_type="REPLY_TO[{}]".format(reply_to_message_id), action_value=message_text, local_timestamp=telegram_message_timestamp, message_id=telegram_message_id, tag=CONFIG.get('REFER-REPLY-ACTION') ) else: valid_intent = fulfill_intent( intent=message_text, human_id=human_id ) if not valid_intent: try: log_sent_message(bot.send_message( chat_id=telegram_human_id, text="Please use '/' commands to communicate with *Opendemic*.", parse_mode="Markdown" ), human_id=human_id) except Exception as e: return '', 204 # case location elif message_content_type == 'location': try: log_sent_message(bot.reply_to( message=update.message, text=random.choice([ "Thanks for sharing your location 🙏", "Great, got it!", "Thanks! We'll keep that secure." ]) ), human_id=human_id) except Exception as e: return '', 204 else: human.log_location( latitude=update.message.location.latitude, longitude=update.message.location.longitude ) # case photo elif message_content_type == 'photo': try: log_sent_message(bot.reply_to( message=update.message, text=random.choice([ "Thanks for the pic! But we can't process it just yet.", "Cannot process photos just yet, though we are sure that's a great pic!" ]) ), human_id=human_id) log_sent_message(bot.send_message( chat_id=telegram_human_id, text="Please use '/' commands to communicate with *Opendemic*.", parse_mode="Markdown" ), human_id=human_id) except Exception: return '', 204 # case documents elif message_content_type == 'document': try: log_sent_message(bot.reply_to( message=update.message, text=random.choice([ "Thanks for the document! But we can't process it just yet.", "Cannot process photos just yet, though we are sure that's a great document!" ]) ), human_id=human_id) log_sent_message(bot.send_message( chat_id=telegram_human_id, text="Please use '/' commands to communicate with *Opendemic*.", parse_mode="Markdown" ), human_id=human_id) except Exception as e: return '', 204 # case sticker elif message_content_type == 'sticker': try: log_sent_message(bot.reply_to( message=update.message, text=random.choice([ "Thanks for the sticker! But we can't process it just yet.", "Cannot process stickers just yet, though that looks like a great sticker!" ]) ), human_id=human_id) log_sent_message(bot.send_message( chat_id=telegram_human_id, text="Please use '/' commands to communicate with *Opendemic*.", parse_mode="Markdown" ), human_id=human_id) except Exception: return '', 204 return '', 204
uaClient.connect() # cria um objeto e le o valor obj = uaObject(None, idx, name) var = uaObject(obj.node, idx, prop) # imprime a variavel click.echo("{}->{} = {}".format(name, prop, var.value)) uaClient.disconnect() @cli.command() @click.option('--type', type=click.Choice(CONFIG.get_name_devices_choice()), default=None, help="Tipo do dispositivo a ser criado") @click.option('--name', type=click.STRING, default=None, help="Nome do dispositvo") @click.option('--idx', type=click.INT, default=1, help="Index do namespace") def device(type, idx, name): """ Inicia a execução de um dispositivo """ try: uaClient.connect()
def create_app(): app = Flask(__name__, instance_relative_config=True) app.config.from_mapping(SECRET_KEY=CONFIG.get("app-secret-key-value")) # add CORS cors = CORS(app) init(autoreset=True) # register routes import opendemic.webhook.telegram.controller as telegram_controller from opendemic.contact import controller as contact_controller from opendemic.map import controller as map_controller from opendemic.subscription import controller as subscription_controller from opendemic.human.location import controller as location_controller from opendemic.human.symptom import controller as symptom_controller from opendemic.human.alert import controller as alert_controller from opendemic.estimation.rt import controller as rt_controller app.register_blueprint(blueprint=telegram_controller.blueprint, url_prefix='/webhook') app.register_blueprint(blueprint=location_controller.blueprint, url_prefix='/human') app.register_blueprint(blueprint=symptom_controller.blueprint, url_prefix='/human') app.register_blueprint(blueprint=alert_controller.blueprint, url_prefix='/human') app.register_blueprint(blueprint=subscription_controller.blueprint) app.register_blueprint(blueprint=map_controller.blueprint) app.register_blueprint(blueprint=contact_controller.blueprint) app.register_blueprint(blueprint=rt_controller.blueprint, url_prefix='/estimation') # TODO - move Telegram webhook registration to worker try: register_webhook_url() except Exception as exception: logger.error( "An exception occurred while registering the telegram webhook: ", exception) @app.before_request def start_timer(): g.start = time.time() @app.after_request def log_request(response): if request.path == "/metrics/" or request.path == "/metrics": return response now = time.time() duration = round(now - g.start, 2) REQUEST_LATENCY.labels('opendemic', request.path).observe(duration) dt = datetime.datetime.fromtimestamp(now) timestamp = rfc3339(dt, utc=True) REQUEST_COUNT.labels('opendemic', request.method, request.path, response.status_code).inc() ip = request.headers.get('X-Forwarded-For', request.remote_addr) host = request.host.split(':', 1)[0] args = dict(request.args) log_params = [('method', request.method, 'blue'), ('path', request.path, 'blue'), ('status', response.status_code, 'yellow'), ('duration', duration, 'green'), ('time', timestamp, 'magenta'), ('ip', ip, 'red'), ('host', host, 'red'), ('params', args, 'blue')] request_id = request.headers.get('X-Request-ID') if request_id: log_params.append(('request_id', request_id, 'yellow')) parts = [] for name, value, color in log_params: part = (Fore.BLUE + name + " = " + Fore.GREEN + str(value)) parts.append(part) line = " ".join(parts) app.logger.info(line) return response @app.route('/') def index(): return "success", 200 @app.route('/debug-sentry') def trigger_error(): division_by_zero = 1 / 0 @app.route('/metrics/') def metrics(): return Response(prometheus_client.generate_latest(), mimetype=CONTENT_TYPE_LATEST) return app
def generate_db_uri(): return 'mysql://' + CONFIG.get('rds-aurora-mysql-opendemic-username') + ':' + \ CONFIG.get('rds-aurora-mysql-opendemic-password') + '@' + \ CONFIG.get('rds-aurora-mysql-opendemic-host') + ':' + \ CONFIG.get('rds-aurora-mysql-opendemic-port') + '/' + \ CONFIG.get('rds-aurora-mysql-opendemic-database')