diff --git a/function_app.py b/function_app.py index b7b65a7..b382e3f 100644 --- a/function_app.py +++ b/function_app.py @@ -1,4 +1,8 @@ import azure.functions as func +from azure.data.tables import TableServiceClient +from azure.data.tables import UpdateMode +import json +import datetime import logging import requests import os @@ -19,10 +23,20 @@ def spotbot(req: func.HttpRequest) -> func.HttpResponse: return func.HttpResponse("Invalid JSON", status_code=400) content = create_content(req_body) - return call_target(content) + callsign = req_body.get('callsign') + + table = get_table() + entity = query_for_entity(table, callsign) + messageId = None + if is_entity_recent(entity): + messageId = entity.metadata['MessageId'] + response = call_target(content, messageId) + messageId = extract_message_id(response) + upsert_entity(table, callsign, messageId) + + return response def create_content(req_body): - fullCallsign = req_body.get('fullCallsign', 'Unknown') callsign = req_body.get('callsign', 'Unknown') source = req_body.get('source', 'Unknown') frequency = req_body.get('frequency', 'Unknown') @@ -33,12 +47,16 @@ def create_content(req_body): spot_deeplink = create_spot_deeplink(source, callsign, wwffRef) # flags = 4 means it will suppress embeds: https://discord.com/developers/docs/resources/message#message-object-message-flags - content = {"content": f"{fullCallsign} | {source} | freq: {frequency} | mode: {mode} | loc: {summitRef}{wwffRef} | {spot_deeplink}", "flags": 4} + content = {"content": f"{callsign} | {source} | freq: {frequency} | mode: {mode} | loc: {summitRef}{wwffRef} | {spot_deeplink}", "flags": 4} return content -def call_target(content): +def call_target(content, messageId=None): target_url = os.getenv('TARGET_URL') - response = requests.post(target_url, json=content) + verb = "POST" + if messageId is not None: + target_url = target_url + f"/messages/{messageId}" + verb = "PATCH" + response = requests.request(verb, url=target_url, params={"wait": "true"}, json=content) return func.HttpResponse(response.text, status_code=response.status_code) def create_spot_deeplink(source, callsign, wwffRef): @@ -48,4 +66,37 @@ def create_spot_deeplink(source, callsign, wwffRef): case "pota": return f"[See their latest spot](https://api.pota.app/spot/comments/{callsign}/{wwffRef})" case _: - return "" \ No newline at end of file + return "" + +def get_table(): + connection_string = os.getenv('AzureWebJobsStorage') + table_name = os.getenv('TABLE_NAME') + table_service_client = TableServiceClient.from_connection_string(conn_str=connection_string) + table_client = table_service_client.get_table_client(table_name=table_name) + return table_client + +def query_for_entity(table_client, callsign): + entities = [ent for ent in table_client.query_entities(f"PartitionKey eq '{callsign}' and RowKey eq '{callsign}'")] + if len(entities) > 0: + logging.info(f"Entity already exists for {callsign}") + return entities[0] if len(entities) > 0 else None + +def is_entity_recent(entity): + if entity is None: + return False + ent_time = entity.metadata['timestamp'] + cur_time = datetime.datetime.now(datetime.timezone.utc) + two_hours_in_seconds = 60 * 60 * 2 + return (cur_time - ent_time).total_seconds() < two_hours_in_seconds + +def upsert_entity(table_client, callsign, messageId): + entity = { + u'PartitionKey': callsign, + u'RowKey': callsign, + u'MessageId': messageId + } + table_client.upsert_entity(mode=UpdateMode.REPLACE, entity=entity) + +def extract_message_id(response): + resp = json.loads(response.get_body()) + return resp['id'] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index ce86af0..fa0f337 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ azure-functions +azure-data-tables requests \ No newline at end of file diff --git a/test.py b/test.py index 91a3967..e05df6a 100644 --- a/test.py +++ b/test.py @@ -4,15 +4,15 @@ class TestSpotBot(unittest.TestCase): def test_function_app_basic(self): - req_body = {"fullCallsign": "KI7HSG/P", "callsign":"KI7HSG", "source": "pota", "frequency": "14.074", "mode": "FT8", "wwffRef":"US-0052"} + req_body = {"callsign":"KI7HSG", "source": "pota", "frequency": "14.074", "mode": "FT8", "wwffRef":"US-0052"} content = function_app.create_content(req_body) - expected = {'content': 'KI7HSG/P | pota | freq: 14.074 | mode: FT8 | loc: US-0052 | [See their latest spot](https://api.pota.app/spot/comments/KI7HSG/US-0052)', 'flags': 4} + expected = {'content': 'KI7HSG | pota | freq: 14.074 | mode: FT8 | loc: US-0052 | [See their latest spot](https://api.pota.app/spot/comments/KI7HSG/US-0052)', 'flags': 4} self.assertDictEqual(content, expected) def test_function_app(self): - req_body = {"fullCallsign": "KI7HSG/P", "callsign":"KI7HSG", "source": "sotawatch", "frequency": "14.074", "mode": "FT8", "summitRef": "ABCD"} + req_body = {"callsign":"KI7HSG", "source": "sotawatch", "frequency": "14.074", "mode": "FT8", "summitRef": "ABCD"} content = function_app.create_content(req_body) - expected = {'content': 'KI7HSG/P | sotawatch | freq: 14.074 | mode: FT8 | loc: ABCD | [See their latest spot](https://sotl.as/activators/KI7HSG)', 'flags': 4} + expected = {'content': 'KI7HSG | sotawatch | freq: 14.074 | mode: FT8 | loc: ABCD | [See their latest spot](https://sotl.as/activators/KI7HSG)', 'flags': 4} self.assertDictEqual(content, expected) if __name__ == '__main__':