diff --git a/.env.example b/.env.example index 257630fa..7705ffe8 100644 --- a/.env.example +++ b/.env.example @@ -35,6 +35,11 @@ SENDGRID_SMTP_HOST=smtp.sendgrid.net SENDGRID_USER= SENDGRID_PW=5ecr3t +ETHEREAL_SMTP_HOST=smtp.ethereal.email +ETHEREAL_PORT=587 +ETHEREAL_USER= +ETHEREAL_PW= + ELASTIC_HOST=https://example.com:9243 ELASTIC_USER=5ecr3t ELASTIC_PASSWORD=5ecr3t @@ -78,6 +83,8 @@ SUBDOMAIN_SURVEY=survey. SUBDOMAIN_UPMONITOR=ciao. SUBDOMAIN_STATPING=ping. SUBDOMAIN_APIDOCS=apidocs. +SUBDOMAIN_RABBITMQ=rabbit. +SUBDOMAIN_MAILHOG=mailhog. SUBDOMAIN_REDISADMIN=redis. # Paths in the filesystem @@ -94,6 +101,7 @@ PATH_KNOWLEDGE=knowledge/docker/ PATH_PASS_MANAGER=pass-manager/docker/ PATH_MONITOR=monitor/docker/ PATH_GSUITE_WRAPPER=gsuite-wrapper/docker/ +PATH_DISPATCHER=dispatcher/docker/ PATH_OMS_ELASTIC=oms-elastic/docker/ # Other variables diff --git a/dispatcher/Makefile b/dispatcher/Makefile new file mode 100644 index 00000000..a834fbc3 --- /dev/null +++ b/dispatcher/Makefile @@ -0,0 +1,17 @@ + +default: build-dev + +build-dev: + BASE_URL=appserver.vgr SUBDOMAIN_RABBITMQ=rabbit. SUBDOMAIN_MAILHOG=mailhog. PATH_DISPATCHER=./ COMPOSE_PROJECT_NAME=myaegee docker-compose -f ./docker/docker-compose.yml -f ./docker/docker-compose.dev.yml up -d + +build: + BASE_URL=appserver.vgr SUBDOMAIN_RABBITMQ=rabbit. COMPOSE_PROJECT_NAME=myaegee docker-compose -f ./docker/docker-compose.yml up -d + +down: + COMPOSE_PROJECT_NAME=myaegee docker-compose -f ./docker/docker-compose.yml -f ./docker/docker-compose.dev.yml down + +test: build-dev + @sleep 5; sed -i "s/'172.18.0.X'/$$(docker inspect myaegee_rabbit_1 | jq .[0].NetworkSettings.Networks.OMS.IPAddress)/" helpers/send.py ; cd helpers && python send.py + +rabbit: + @DOCKER_BUILDKIT=0 docker build -t aegee/rabbit:latest -f docker/Dockerfile.rabbit . && docker push aegee/rabbit diff --git a/dispatcher/README.md b/dispatcher/README.md new file mode 100644 index 00000000..8c56bf73 --- /dev/null +++ b/dispatcher/README.md @@ -0,0 +1,58 @@ +# Dispatcher + +Polls rabbitMQ and takes action (sends mail). + +## How to run it +### Pre-requisites +In the Vagrant's appserver VM, the packages are installed globally: +1. faker +1. jinja2 +1. pika + +Otherwise use poetry to install dependencies and launch the virtual environment. + +### Launching it and testing it +In a console, run `python3 dispatcher/main.py`. +In another console, run `python3 helpers/send.py`. +Control on `appserver.test:8025` the emails sent. It is also possible to control rabbit's stats on `appserver.test:8080` + +This method is now dockerised. Using the docker way is useful for the 'DNS' feature of docker (i.e. not hardcoding the IP address of the rabbit host) + +### Rationale +We do not need a web service for this, only a worker. Doing it this way only means it cannot be scaled (unless precautions are taken for the ack of the message, but pika should already give this out of the box). +In order to add templates, one can work on the filesystem: as the template file is read from memory at the time a message is received, there is basically a mechanism of hot-reload ready to be used. + +We do not need a web service, because we do not need to pilot anything. + +## Queues + +Current queues: +1. email + +Queues envisioned: +1. email +1. telegram +1. slack (If EBs have enabled it) +1. webgui (handled by vue, NOT by this program) + +## TODOs and next steps +rather in order: + +1. [x] (not on this project): run core with the email as 'inserting in the queue' instead of 'API request to mailer' +1. [?] (not on this project): run core with the email as 'exchange' instead of 'inserting in the queue' +1. [x] include traefik configuration to have the mailhog and rabbit on a subdomain instead of `domain:port` +1. [x] When RabbitMQ quits or crashes it will forget the queues and messages unless you tell it not to: we need to mark both the queue and messages as durable +1. [ ] Add auto-retry (DLQ). rabbit is smart and doesn't let me process a message again unless i force it.. https://devcorner.digitalpress.blog/rabbitmq-retries-the-new-full-story/ +1. [ ] add the telegram queue +1. investigate the massmailer queue: a queue which picks every message, and creates a list of "bcc" to send only one email? (danger: queue needs something like batch ack..) - OR it is not feasible at all because "mass"mailer is still "personalised" mailer? + +1. why do we even have a `<`title`>` (which is dynamic), why not using directly the subject? (re: the body of the email) +1. remove extension Jinja2 (into jinja) +1. make it such that templates list is read from fs (for dynamic tests) + + + +https://www.rabbitmq.com/publishers.html#unroutable + + +Each consumer (subscription) has an identifier called a consumer tag. It can be used to unsubscribe from messages. Consumer tags are just strings. diff --git a/dispatcher/dispatcher/__init__.py b/dispatcher/dispatcher/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dispatcher/dispatcher/main.py b/dispatcher/dispatcher/main.py new file mode 100644 index 00000000..cd4a8e30 --- /dev/null +++ b/dispatcher/dispatcher/main.py @@ -0,0 +1,283 @@ +import pika +import json +import smtplib +from email.message import EmailMessage +from jinja2 import Environment, FileSystemLoader, exceptions +import os +import sys +import logging +from notify import slack_alert + +""" +continuously polls(*) the email queue and renders+sends the template on every acked message +(*) = waits for the queue to push a message onto the app +""" + +ONCALL_HANDLER = "@grasshopper" + +def connect_to_smtp(): + global smtpObj + + EMAIL_HOST='mailhog' + EMAIL_PORT=1025 + EMAIL_ADDRESS=None + EMAIL_PASSWORD=None + + if env == 'production': + EMAIL_HOST= os.environ.get("EMAIL_HOST") + EMAIL_PORT= os.environ.get("EMAIL_PORT") + EMAIL_ADDRESS= os.environ.get("EMAIL_ADDRESS") + EMAIL_PASSWORD= os.environ.get("EMAIL_PASSWORD") + + try: + smtpObj = smtplib.SMTP( EMAIL_HOST, EMAIL_PORT ) + + if env == 'production': + # we have to upgrade the connection and login + # (At least with ethereal.email.. didn't try with gmail!) #TODO + smtpObj.starttls() + smtpObj.login(EMAIL_ADDRESS, EMAIL_PASSWORD) + logging.info(" -> Connected") + except smtplib.SMTPConnectError: + logging.error("Could not connect to the SMTP server.") + except smtplib.SMTPAuthenticationError: + logging.error("Failed to authenticate with given credentials.") + except Exception as e: + logging.error(f"Could not connect to SMTP server for generic reason: {e}") + +def requeue_wait(ch, method, properties, body, reason): + REQUEUE_DELAY_DURATIONS = [ + 5 * 60000, # 5 mins + 50 * 60000, # 50 mins + 5*60 * 60000, # 5 hrs + 5*60*10 * 60000, # 50 hrs + 5*60*20 * 60000, # 100 hrs + ] + + current_delay = properties.headers.get("x-delay") if properties.headers else 0 + try: + index = REQUEUE_DELAY_DURATIONS.index(int(current_delay)) + except ValueError: + index = -1 + + next_index = index + 1 + + if next_index >= len(REQUEUE_DELAY_DURATIONS): + logging.warning('Max retry time hit, dropping message') + slack_alert(f"Time over, a message was dropped ({reason})", submessage = ":poop:") + ch.basic_ack(delivery_tag=method.delivery_tag) + return + + wait = REQUEUE_DELAY_DURATIONS[next_index] + retry_message = f'Retry attempt {next_index + 1}/{len(REQUEUE_DELAY_DURATIONS)} will happen in {int(wait/1000)} sec' + logging.info(retry_message) + last_chance = '' + if next_index + 1 == len(REQUEUE_DELAY_DURATIONS): + last_chance = f'-- LAST ATTEMPT TO FIX: within {int(wait/1000)} sec' + f' {ONCALL_HANDLER}' + logging.error(last_chance) + slack_alert(f"A template is missing! ({reason})", + submessage = retry_message + " " + last_chance + ) + + headers = { + 'reason': reason, + 'x-delay': wait, + } + prop = pika.BasicProperties( + headers=headers, + delivery_mode = pika.spec.PERSISTENT_DELIVERY_MODE, + ) + channel.basic_publish(exchange='wait_exchange', + routing_key='wait', + body=body, + properties=prop) #NOTE it completely ignores the previous properties (and it's fine) + ch.basic_ack(delivery_tag=method.delivery_tag) + +def send_email(ch, method, properties, body): + """ + Callback for the NORMAL MESSAGE + Output: send an email + OR + Output: Wait-exchange + """ + msg = json.loads(body) + + try: + template = tpl_environment.get_template(f"{msg['template']}.jinja2") + except exceptions.TemplateNotFound: + logging.error(f"Template {msg['template']}.jinja2 not found") + # NOTE: this is a requeuable message + requeue_wait(ch, method, properties, body, reason=f"template_not_found-{msg['template']}") + return + + try: + rendered = template.render(msg['parameters'], altro=msg['subject']) + except exceptions.UndefinedError as e: + logging.error(f"Error in rendering: some parameter is undefined (error: {e}; message: {msg})") + # NOTE: this is a NON-requeuable message + requeue_wait(ch, method, properties, body, reason="parameter_undefined") + return + except exceptions.TemplateNotFound: + logging.error(f"A sub-template in {msg['template']}.jinja2 was not found") + # NOTE: this is a requeuable message + requeue_wait(ch, method, properties, body, reason=f"subtemplate_not_found-{msg['template']}") + return + + try: + email = EmailMessage() + email.set_content(rendered, subtype='html') + email['From'] = msg['from'] + email['Reply-To'] = msg['reply_to'] + email['To'] = msg['to'] + email['Subject'] = msg['subject'] + smtpObj.send_message(email) + ch.basic_ack(delivery_tag = method.delivery_tag) + except smtplib.SMTPServerDisconnected: + logging.error("Server unexpectedly disconnected. Attempting to reconnect") + connect_to_smtp() + except smtplib.SMTPResponseException as e: + logging.error(f"SMTP error occurred: {e.smtp_code} - {e.smtp_error}") + except Exception as e: + logging.error(f"An unexpected error occurred: {e}") + +def process_dead_letter_messages(ch, method, properties, body): + """ + Callback for the ERROR MESSAGE + Output: none yet. I don't expect for messages to fall here, I keep the DLQ for safety + + @see https://stackoverflow.com/a/58500336 + "The way to do this is not to use NACK at all but to generate and return a 'new' message + (which is simply the current message you are handling, but adding new headers to it). + It appears that a NACK is basically doing this anyway according to the AMQP spec." + """ + REQUEUE_DELAY_DURATIONS = [ + 5 * 60000, # 5 mins + 50 * 60000, # 50 mins + 5*60 * 60000, # 5 hrs + 5*60*10 * 60000, # 50 hrs + 5*60*20 * 60000, # 100 hrs + ] #TODO: why is this here again? + wait_for = REQUEUE_DELAY_DURATIONS[-1] + + logging.error("For some reason there's the DLQ handler that was triggered!") + slack_alert("For some reason there's the DLQ handler that was triggered!") + + headers = { + 'x-delay': wait_for, + } + fullheaders = {**properties.headers, **headers} + prop = pika.BasicProperties( + headers=fullheaders, + delivery_mode = pika.spec.PERSISTENT_DELIVERY_MODE, + ) + channel.basic_publish(exchange='wait_exchange', + routing_key='wait', + body=body, + properties=prop) + + ch.basic_ack(delivery_tag = method.delivery_tag) + +def process_requeue(ch, method, properties, body): + """ + Callback for the WAITING MESSAGES + Output: Requeue on normal exchange (if error about missing template) + OR + Output: Remove (if unfixable error) + """ + + if (properties.headers["reason"] == 'parameter_undefined'): + logging.warning('Impossible to fix error, dropping message') + #TODO output something/notify to leave a trail for better debugging on what was missing + ch.basic_ack(delivery_tag = method.delivery_tag) + return + + channel.basic_publish(exchange='eml', + routing_key='mail', + body=body, + properties=pika.BasicProperties( + headers = properties.headers, # propagation to avoid endless loop + delivery_mode = pika.spec.PERSISTENT_DELIVERY_MODE, + )) + ch.basic_ack(delivery_tag = method.delivery_tag) + +def main(): + global smtpObj + global tpl_environment + global env + global channel + + # Configure logging for this app, and remove "info" of pika + logging.basicConfig(level=logging.INFO) + logging.getLogger('pika').setLevel(logging.WARNING) + + tpl_environment = Environment(loader=FileSystemLoader("../templates/")) + env = os.environ.get("ENV") or 'development' + + RABBIT_HOST='rabbit' + connection = pika.BlockingConnection(pika.ConnectionParameters(RABBIT_HOST)) + channel = connection.channel() + + channel.exchange_declare(exchange='eml', + exchange_type='direct', + durable=True) + channel.queue_declare(queue='email', + arguments={ + 'x-dead-letter-exchange': "dead_letter_exchange", + 'x-dead-letter-routing-key': "dead_letter_routing_key", + 'x-death-header': True, + }, + durable=True) + channel.queue_bind(exchange='eml', + queue='email', + routing_key='mail') + + #channel.basic_qos(prefetch_count=1) #TODO: notice that with this enabled, an error processing a message will BLOCK the others from being processed + + channel.exchange_declare(exchange="dead_letter_exchange", + exchange_type='direct', + durable=True) + channel.queue_declare(queue='error_queue', + durable=True) + channel.queue_bind(exchange='dead_letter_exchange', + queue='error_queue', + routing_key='dead_letter_routing_key') + + channel.exchange_declare(exchange="wait_exchange", + exchange_type='x-delayed-message', + durable=True, + arguments={"x-delayed-type": "direct"} + ) + channel.queue_declare(queue='requeue_queue', + durable=True) + channel.queue_bind(exchange='wait_exchange', + queue='requeue_queue', + routing_key='wait') + + channel.basic_consume(queue='email', + auto_ack=False, + on_message_callback=send_email) + + channel.basic_consume(queue='error_queue', + auto_ack=False, + on_message_callback=process_dead_letter_messages) + + channel.basic_consume(queue='requeue_queue', + auto_ack=False, + on_message_callback=process_requeue) + + logging.info(' [*] Connecting to smtp') + connect_to_smtp() + logging.info(' [*] Waiting for messages. To exit press CTRL+C') + channel.start_consuming() + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + logging.error('Interrupted') + smtpObj.quit() + try: + sys.exit(0) + except SystemExit: + os._exit(0) + diff --git a/dispatcher/dispatcher/notify.py b/dispatcher/dispatcher/notify.py new file mode 100755 index 00000000..1f9551c4 --- /dev/null +++ b/dispatcher/dispatcher/notify.py @@ -0,0 +1,65 @@ +import os +import json +import datetime +import slack_notifications as slack + + +#the ?? notification channels: +# - slack +# - telegram (wip) + +# example of tokens.json: +# { +# "slack": "xoxb-123", # a single token +# "telegram": ["456", "789"], # an array of tokens +# } + +tokens = None +NOTIFY_CHANNEL = os.environ.get("NOTIFY_CHANNEL") or "----monitoring" + +payload_nice = { + "title": 'Something going on with the dispatcher', + # "author_name": f'Host: {os.uname()[1]}', + #"text": "REPLACEME", + "footer": f'Error happened SOMETIME', + "color": '#FF5A36', + # "fields": [ + # slack.Attachment.Field( + # title='Error happened at:', + # value=f'{datetime.datetime.now().strftime("%d %b %Y, %H:%M:%S %Z")}', + # short=False + # ), + # ] + } + +def open_credentials_file(): + token_file = f'{os.path.realpath(os.path.dirname(__file__))}/../tokens.json' + with open(token_file, encoding="utf-8") as tokens_json: + return json.load(tokens_json) + + +def slack_alert(message_title, submessage = None): + global tokens + if tokens is None: + tokens = open_credentials_file() + api_key = tokens["slack"] + + slack.ACCESS_TOKEN = api_key + + payload_nice["title"] = f"Dispatcher says: {message_title}" + payload_nice["footer"] = f'Error happened {datetime.datetime.now().strftime("%d %b %Y, %H:%M:%S %Z")}' + if submessage: + payload_nice["text"] = submessage + + attachment = slack.Attachment( + **payload_nice + ) + + slack.send_notify(NOTIFY_CHANNEL, icon_emoji=':hole:', username='Dispatcher template notifier', + attachments=[attachment]) + +if __name__ == "__main__": + import sys + tokens = open_credentials_file() + EVENT = sys.argv[1] + slack_alert(EVENT) diff --git a/dispatcher/docker/Dockerfile b/dispatcher/docker/Dockerfile new file mode 100644 index 00000000..3c819a10 --- /dev/null +++ b/dispatcher/docker/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.9-slim + +WORKDIR /usr/app/src + +COPY ./requirements.txt /usr/app/src/requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +COPY ./templates /usr/app/templates +COPY ./dispatcher/main.py /usr/app/src/main.py + +RUN useradd -s /bin/bash python +USER python + +ENTRYPOINT [ "python", "-u" ] +CMD ["main.py"] diff --git a/dispatcher/docker/Dockerfile.rabbit b/dispatcher/docker/Dockerfile.rabbit new file mode 100644 index 00000000..7901d4d1 --- /dev/null +++ b/dispatcher/docker/Dockerfile.rabbit @@ -0,0 +1,6 @@ +FROM rabbitmq:3.11-management + +ADD https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/3.11.1/rabbitmq_delayed_message_exchange-3.11.1.ez /opt/rabbitmq/plugins +RUN chown rabbitmq:rabbitmq /opt/rabbitmq/plugins/rabbitmq_delayed_message_exchange-3.11.1.ez \ + && chmod 755 /opt/rabbitmq/plugins/rabbitmq_delayed_message_exchange-3.11.1.ez \ + && rabbitmq-plugins enable rabbitmq_delayed_message_exchange --offline diff --git a/dispatcher/docker/docker-compose.dev.yml b/dispatcher/docker/docker-compose.dev.yml new file mode 100644 index 00000000..af8f6075 --- /dev/null +++ b/dispatcher/docker/docker-compose.dev.yml @@ -0,0 +1,30 @@ +version: "3.4" + +services: + + rabbit: + build: + context: ./${PATH_DISPATCHER}/.. + dockerfile: ./docker/Dockerfile.rabbit + image: aegee/rabbit:3.11-mgmt-delayxch + + dispatcher: + build: + context: ./${PATH_DISPATCHER}/.. + dockerfile: ./docker/Dockerfile + image: aegee/dispatcher:dev + volumes: + - ./${PATH_DISPATCHER}/../templates:/usr/app/templates:ro + - ./${PATH_DISPATCHER}/../dispatcher:/usr/app/src:ro + + mailhog: + image: mailhog/mailhog:v1.0.1 + restart: always + ports: + - 8025:8025 + - 1025:1025 + labels: + - "traefik.frontend.rule=Host:${SUBDOMAIN_MAILHOG}${BASE_URL};" + - "traefik.backend=mailhog" + - "traefik.port=8025" + - "traefik.enable=true" diff --git a/dispatcher/docker/docker-compose.yml b/dispatcher/docker/docker-compose.yml new file mode 100644 index 00000000..3c953f83 --- /dev/null +++ b/dispatcher/docker/docker-compose.yml @@ -0,0 +1,38 @@ +version: "3.4" + +services: + + rabbit: # login with guest:guest + image: aegee/rabbit:3.11-mgmt-delayxch + restart: always + expose: + - 5672 + - 15672 + # environment: + # RABBITMQ_DEFAULT_USER: "admin" + # RABBITMQ_DEFAULT_PASS: "5ecr3t" + labels: + - "traefik.frontend.rule=Host:${SUBDOMAIN_RABBITMQ}${BASE_URL};PathPrefix:/" + - "traefik.backend=rabbit" + - "traefik.port=15672" + - "traefik.enable=true" + + # Not a webservice, but a worker service + dispatcher: + image: aegee/dispatcher + restart: always + environment: + ENV: "${MYAEGEE_ENV}" + EMAIL_HOST: "${ETHEREAL_SMTP_HOST}" + EMAIL_PORT: "${ETHEREAL_PORT}" + EMAIL_ADDRESS: "${ETHEREAL_USER}" + EMAIL_PASSWORD: "${ETHEREAL_PW}" + NOTIFY_CHANNEL: "slack-tests-programmatic-stuff-other" + volumes: + - ./secrets/tokens.json:/usr/app/tokens.json + + +networks: + default: + external: + name: OMS diff --git a/dispatcher/helpers/send.py b/dispatcher/helpers/send.py new file mode 100644 index 00000000..5092a2e0 --- /dev/null +++ b/dispatcher/helpers/send.py @@ -0,0 +1,276 @@ +import pika +import json +import random +import os + +""" +creates between 1 and 8 fake emails and puts in the queue +OR +tests all templates it finds in the folder +""" + +from faker import Faker +faker = Faker() + +ERROR_TEST=True +RANDOM_AMOUNT_TEST=False +MIN_MSG=1 +MAX_MSG=8 +ALL_TEMPLATES_TEST=True + +BODIES_LIST = [ "ITC", "HRC", "CC", "SomeCommission", "JC", "DPC", "MedCom" ] +su_words_list = ['summer', 'awesome', 'your', 'now', 'sustainability', 'culture', 'europe', 'balkan', 'russia', 'adventure', 'ukraine', 'capital', 'montenegro', 'ireland', 'serbia', 'crimea', 'amazing', 'slavaukraini', 'heroiamslava'] +def su_sentence(): + return faker.sentence(nb_words=5, ext_word_list=su_words_list) +def agora_sentence(): + return f"Agora {faker.sentence(nb_words=1)}" + +# from constants.js (at least for core..) +MAIL_SUBJECTS = { + "CORE": { + "MAIL_CONFIRMATION": 'MyAEGEE: Please confirm your account', + "MAIL_CHANGE": 'MyAEGEE: Email change', + "PASSWORD_RESET": 'MyAEGEE: Password reset request', + "NEW_JOIN_REQUEST": 'MyAEGEE: New join request for your body', + "NEW_MEMBER": 'MyAEGEE: Welcome to AEGEE' + }, + "EVENTS": { + "MAIL_APPLIED": f"You've successfully applied for {su_sentence()}", + "MAIL_UPDATED": f"Your application for {su_sentence()} was updated", + "MAIL_EVENT_CREATED": "The event was created", + "MAIL_EVENT_UPDATED": "The event was updated", + "MAIL_EVENT_STATUS_CHANGED": "Your event's status was changed", + "MAIL_EVENT_SUBMITTED": "An event was submitted", + }, + "STATUTORY": { + "MAIL_APPLIED": f"You've successfully applied for {agora_sentence()}", + "MAIL_UPDATED": f"Your application for {agora_sentence()} was updated", + "MAIL_APPLIED_BOARD": f"One of your body members has applied to {agora_sentence()}", + "MAIL_UPDATED_BOARD": f"One of your body members has updated their application to {agora_sentence()}", + "MAIL_CANDIDATE_APPLIED": "A new application was submitted", + "MAIL_MEMBERSLIST_SUBMITTED": "The event was updated", + "MAIL_MEMBERSLIST_EDITED": f"A memberslist has been edited for {agora_sentence()}", + }, + "SUMMERUNIVERSITIES": { + "MAIL_APPLIED_MEMBER": f"You've successfully applied for {su_sentence()}", + "MAIL_APPLIED_ORGANISERS": f"Somebody has applied for {su_sentence()}", + "MAIL_APPLIED_BOARD": f"One of your body members has applied to {su_sentence()}", + "MAIL_UPDATED_MEMBER": f"Your application for {su_sentence()} was updated", + "MAIL_UPDATED_ORGANISERS": f"Somebody has updated their application for {su_sentence()}", + "MAIL_UPDATED_BOARD": f"One of your body members has updated their application to {su_sentence()}", + "MAIL_APPL_STATUS_CHANGED": f"Your application status for {su_sentence()} was updated", + "MAIL_SU_CREATED": "The event was created", + "MAIL_SU_UPDATED": "The event was updated", + "MAIL_SU_STATUS_CHANGED": "Your event's status was changed", + "MAIL_SU_SUBMITTED": "An event was submitted", + }, + "NETWORK": { + "MAIL_NEW_BOARD": f'A new board was added for { random.choice(BODIES_LIST) }', + }, + "OTHER": { + "MAIL_EXPIRED_MEMBERSHIP": 'member_expired', + "MAIL_CUSTOM": 'custom', + }, +} +# should exist in constants.js but it does not yet. +# anyway here one could make it in a smarter way: look into the filesystem +# but then you miss the correspondence between the subject and the template. +# So we only look in the fs to cross-check if we were thorough +MAIL_TEMPLATES = { + "CORE": { + "MAIL_CONFIRMATION": 'confirm_email', + "MAIL_CHANGE": 'mail_change', + "PASSWORD_RESET": 'password_reset', + "NEW_JOIN_REQUEST": 'member_joined', + "NEW_MEMBER": 'new_member' + }, + "EVENTS": { + "MAIL_APPLIED": "events_applied", + "MAIL_UPDATED": "events_edited", + "MAIL_EVENT_CREATED": "events_event_created", + "MAIL_EVENT_UPDATED": "events_event_updated", + "MAIL_EVENT_STATUS_CHANGED": "events_status_changed", + "MAIL_EVENT_SUBMITTED": "events_submitted", + }, + "STATUTORY": { + "MAIL_APPLIED": "statutory_applied", + "MAIL_UPDATED": "statutory_edited", + "MAIL_APPLIED_BOARD": "statutory_board_applied", + "MAIL_UPDATED_BOARD": "statutory_board_edited", + "MAIL_CANDIDATE_APPLIED": "candidate_applied", + "MAIL_MEMBERSLIST_SUBMITTED": "statutory_memberslist_submitted", + "MAIL_MEMBERSLIST_EDITED": "statutory_memberslist_edited", + }, + "SUMMERUNIVERSITIES": { + "MAIL_APPLIED_MEMBER": "summeruniversity_applied", + "MAIL_APPLIED_ORGANISERS": "summeruniversity_organizer_applied", + "MAIL_APPLIED_BOARD": "summeruniversity_board_applied", + "MAIL_UPDATED_MEMBER": "summeruniversity_application_edited", + "MAIL_UPDATED_ORGANISERS": "summeruniversity_organizer_edited", + "MAIL_UPDATED_BOARD": "summeruniversity_board_edited", + "MAIL_APPL_STATUS_CHANGED": "summeruniversity_application_status_updated", + "MAIL_SU_CREATED": "summeruniversity_event_created", + "MAIL_SU_UPDATED": "summeruniversity_event_updated", + "MAIL_SU_STATUS_CHANGED": "summeruniversity_status_changed", + "MAIL_SU_SUBMITTED": "summeruniversity_submitted", + }, + "NETWORK": { + "MAIL_NEW_BOARD": 'network_new_board', + }, + "OTHER": { + "MAIL_EXPIRED_MEMBERSHIP": 'member_expired', + "MAIL_CUSTOM": 'custom', + }, +} + +RABBIT_HOST="172.18.0.X" #FIXME (as this is a python script launched on host we cant use docker's dns) +connection = pika.BlockingConnection(pika.ConnectionParameters(RABBIT_HOST)) +channel = connection.channel() + +channel.exchange_declare(exchange='eml', + exchange_type='direct', + durable=True) +channel.queue_declare(queue='email', + arguments={ + 'x-dead-letter-exchange': "dead_letter_exchange", + 'x-dead-letter-routing-key': "dead_letter_routing_key", + 'x-death-header': True, + }, + durable=True) +channel.queue_bind(exchange='eml', + queue='email', + routing_key='mail') + +def generate_fake_payload(subj="", template="", return_malformed_mail=False): + email = { + "from": "mailer@aegee.eu", + "to": [faker.email() for _ in range(random.randrange(1,3))], + "reply_to": "noreply@aegee.eu", + "subject": subj or faker.sentence(), + "template": template or "new_member_dynamic", #TODO remove the or/make it fail + "parameters": { # Not all will be used at the same time but this is not important, it's a test + "member_firstname": faker.first_name(), + "member_lastname": faker.last_name(), + "body": f"AEGEE-{faker.city()}", + "last_payment": faker.date(), + "body_name": random.choice(BODIES_LIST), #note: discrepancy in the microservices on the use of body vs body_name + "body_id": random.choice(range(random.randrange(10,70))), + "place": faker.city(), + "token": faker.md5(), + "old_status": "snafu", + "event_name": agora_sentence(), + "membership_fee": "15⎊", + "board": { + "elected_date": "yesteeeerday", + "start_date": "tomooorrow", + "end_date": "next weeeeek", + "message": "chattanooga", + }, + "event": { + "name": su_sentence(), + "location": faker.city(), + "url": "example.org", + "status": "damned", + "questions": [ + { "description": "Who are you?" }, + { "description": "What is the answer to life ...etc?" }, + ], + }, + "application": { + "first_name": faker.first_name(), + "last_name": faker.last_name(), + "body_name": random.choice(BODIES_LIST), + "created_at": "yesterday or whatever", + "updated_at": "now more or less", + "aegee_experience": "I suck", + "ideal_su": "I get to suck", + "motivation": "I wanna suck", + "status": "totally snafu", + "email": "someone@example.org", + "gender": "Robot", + "date_of_birth": "1985-04-16", + "nationality": "Europe", + "meals": "Steak", + "allergies": "wallopers", + "number_of_events_visited": "over 9000", + "visa_required": "No", + "answers": [ + "ho-hoo, ho-hoo", + "42", + ], + }, + "candidate": { + "first_name": faker.first_name(), + "last_name": faker.last_name(), + }, + "position": { + "event_id": "42", + "name": "This gran C commissioner", + }, + "body": """ + + """ + } + } + if(return_malformed_mail): + email["template"] = MAIL_TEMPLATES["EVENTS"]["MAIL_EVENT_UPDATED"] + del email["parameters"]["event"] + return email + + +if(RANDOM_AMOUNT_TEST): + amount = random.randrange(MIN_MSG,MAX_MSG) + for _ in range(amount): + email = generate_fake_payload() + channel.basic_publish(exchange='eml', + routing_key='mail', + body=json.dumps(email), + properties=pika.BasicProperties( + delivery_mode = pika.spec.PERSISTENT_DELIVERY_MODE + )) + print(f" [x] Sent {email['subject']} (to {email['to']})") + print(f" Gee, I sent all {amount} ") + +if(ERROR_TEST): + #ERROR 1: no template + email = generate_fake_payload(template="notexisting", subj="will never be delivered") + channel.basic_publish(exchange='eml', + routing_key='mail', + body=json.dumps(email), + properties=pika.BasicProperties( + delivery_mode = pika.spec.PERSISTENT_DELIVERY_MODE + )) + print(f" [x] Sent {email['subject']} (to {email['to']})") + #ERROR 2: missing field + email = generate_fake_payload(return_malformed_mail=True, subj="will never be delivered") + channel.basic_publish(exchange='eml', + routing_key='mail', + body=json.dumps(email), + properties=pika.BasicProperties( + delivery_mode = pika.spec.PERSISTENT_DELIVERY_MODE + )) + print(f" [x] Sent {email['subject']} (to {email['to']})") + +if(ALL_TEMPLATES_TEST): + templates_tested=0 + for ms in MAIL_TEMPLATES: + for case in MAIL_TEMPLATES[ms]: + email = generate_fake_payload(MAIL_SUBJECTS[ms][case], MAIL_TEMPLATES[ms][case]) + channel.basic_publish(exchange='eml', + routing_key='mail', + body=json.dumps(email), + properties=pika.BasicProperties( + delivery_mode = pika.spec.PERSISTENT_DELIVERY_MODE + )) + print(f" [x] Sent {email['subject']}") + templates_tested+=1 + file_count = len([f for f in os.listdir("../templates/") if os.path.isfile(os.path.join("../templates/", f))]) + if(file_count != templates_tested): + print() + print() + print(f"Tested {templates_tested} templates but the folder contains {file_count}") + +connection.close() diff --git a/dispatcher/poetry.lock b/dispatcher/poetry.lock new file mode 100644 index 00000000..3f03e702 --- /dev/null +++ b/dispatcher/poetry.lock @@ -0,0 +1,305 @@ +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. + +[[package]] +name = "certifi" +version = "2023.7.22" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.2.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, + {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, +] + +[[package]] +name = "faker" +version = "18.13.0" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Faker-18.13.0-py3-none-any.whl", hash = "sha256:801d1a2d71f1fc54d332de2ab19de7452454309937233ea2f7485402882d67b3"}, + {file = "Faker-18.13.0.tar.gz", hash = "sha256:84bcf92bb725dd7341336eea4685df9a364f16f2470c4d29c1d7e6c5fd5a457d"}, +] + +[package.dependencies] +python-dateutil = ">=2.4" + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, +] + +[[package]] +name = "pika" +version = "1.3.2" +description = "Pika Python AMQP Client Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pika-1.3.2-py3-none-any.whl", hash = "sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f"}, + {file = "pika-1.3.2.tar.gz", hash = "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f"}, +] + +[package.extras] +gevent = ["gevent"] +tornado = ["tornado"] +twisted = ["twisted"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "slack-notifications" +version = "0.2.2" +description = "Send notifications to slack channel with supporting attachments and fields" +optional = false +python-versions = "*" +files = [ + {file = "slack-notifications-0.2.2.tar.gz", hash = "sha256:cb3930c01f7625b93fa3da239942161a2a9407894314688f4fde6a7634543df2"}, + {file = "slack_notifications-0.2.2-py3-none-any.whl", hash = "sha256:1a0e2fd382b43ef3a0f9828df35e2a69ce711f84c8fda7f8ab3c159861c75cf1"}, +] + +[package.dependencies] +requests = "*" + +[[package]] +name = "urllib3" +version = "2.0.5" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.7" +files = [ + {file = "urllib3-2.0.5-py3-none-any.whl", hash = "sha256:ef16afa8ba34a1f989db38e1dbbe0c302e4289a47856990d0682e374563ce35e"}, + {file = "urllib3-2.0.5.tar.gz", hash = "sha256:13abf37382ea2ce6fb744d4dad67838eec857c9f4f57009891805e0b5e123594"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.9" +content-hash = "cfd3ce33c11517121dc4dad8f62cb06ef74a82d92be8f9408b3e4237f1059938" diff --git a/dispatcher/pyproject.toml b/dispatcher/pyproject.toml new file mode 100644 index 00000000..08d928cd --- /dev/null +++ b/dispatcher/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "dispatcher" +version = "0.1.0" +description = "Polls rabbitMQ and takes action" +authors = ["linuxbandit "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.9" +pika = "^1.3.1" +jinja2 = "^3.1.2" +slack-notifications = "^0.2.2" + + +[tool.poetry.group.dev.dependencies] +faker = "^18.4.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/dispatcher/requirements.txt b/dispatcher/requirements.txt new file mode 100644 index 00000000..5310b7be --- /dev/null +++ b/dispatcher/requirements.txt @@ -0,0 +1,6 @@ +urllib3==1.22 +requests==2.18.4 +pika==1.3.1 +Jinja2==3.0.3 +Faker==14.2.1 +slack-notifications==0.2.2 diff --git a/dispatcher/templates/candidate_applied.jinja2 b/dispatcher/templates/candidate_applied.jinja2 new file mode 100644 index 00000000..e2c92d07 --- /dev/null +++ b/dispatcher/templates/candidate_applied.jinja2 @@ -0,0 +1,82 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: New candidature submitted") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ Logo +
+
A new candidature is submitted
+
+ A new candidature to position + {{ position["name"] }} + is submitted by + {{ candidate["first_name"] }} {{candidate["last_name"] }}. +
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/confirm_email.jinja2 b/dispatcher/templates/confirm_email.jinja2 new file mode 100644 index 00000000..2be2dbb9 --- /dev/null +++ b/dispatcher/templates/confirm_email.jinja2 @@ -0,0 +1,83 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Confirm your registation") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + + + + +
+ AEGEE-Europe +
+
Hello {{member_firstname}} {{member_surname}}
+
You've recently registered on MyAEGEE. To confirm your account, follow this link
+
+ +
+
If the link does not work for you, go to this page: https://my.aegee.eu/confirm_signup and paste this token into it:
+
{{token}}
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/custom.jinja2 b/dispatcher/templates/custom.jinja2 new file mode 100644 index 00000000..1e2dccc9 --- /dev/null +++ b/dispatcher/templates/custom.jinja2 @@ -0,0 +1,2 @@ + +{{ body }} diff --git a/dispatcher/templates/events_applied.jinja2 b/dispatcher/templates/events_applied.jinja2 new file mode 100644 index 00000000..2071f38c --- /dev/null +++ b/dispatcher/templates/events_applied.jinja2 @@ -0,0 +1,76 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Your application for VAR"|replace("VAR",event["name"])) }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ AEGEE-Europe +
+
You've successfully applied to {{ event["name"] }}
+
+
+ Here are your application details: + {% include "snippets/events_application_info.jinja2" %} +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/events_edited.jinja2 b/dispatcher/templates/events_edited.jinja2 new file mode 100644 index 00000000..d78e7d5f --- /dev/null +++ b/dispatcher/templates/events_edited.jinja2 @@ -0,0 +1,76 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Your application for VAR"|replace("VAR",event["name"])) }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ AEGEE-Europe +
+
Your application for {{ event["name"] }} was successfully updated.
+
+
+ Here are your application details: + {% include "snippets/events_application_info.jinja2" %} +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/events_event_created.jinja2 b/dispatcher/templates/events_event_created.jinja2 new file mode 100644 index 00000000..0a2def3d --- /dev/null +++ b/dispatcher/templates/events_event_created.jinja2 @@ -0,0 +1,81 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: The event has been created") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ Logo +
+
+ The event "{{ event["name"] }}" has been created. +
+
+ You are receiving this as one of the organizers. +
+
+
+ See event +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/events_event_updated.jinja2 b/dispatcher/templates/events_event_updated.jinja2 new file mode 100644 index 00000000..0f77b5d0 --- /dev/null +++ b/dispatcher/templates/events_event_updated.jinja2 @@ -0,0 +1,81 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: The event has been updated") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ Logo +
+
+ The event "{{ event["name"] }}" has been updated. +
+
+ You are receiving this as one of the organizers. +
+
+
+ See event +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/events_status_changed.jinja2 b/dispatcher/templates/events_status_changed.jinja2 new file mode 100644 index 00000000..043e767f --- /dev/null +++ b/dispatcher/templates/events_status_changed.jinja2 @@ -0,0 +1,82 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Event status changed") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ Logo +
+
Your event's status was changed
+
+ The status of your event, + {{ event["name"] }}, + has changed from {{ old_status }} + to {{ event["status"] }}. +
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/events_submitted.jinja2 b/dispatcher/templates/events_submitted.jinja2 new file mode 100644 index 00000000..365f9df6 --- /dev/null +++ b/dispatcher/templates/events_submitted.jinja2 @@ -0,0 +1,81 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: New event submitted") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ Logo +
+
A new event is submitted
+
+ A new event + "{{ event["name"] }}" + is submitted. +
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/mail_change.jinja2 b/dispatcher/templates/mail_change.jinja2 new file mode 100644 index 00000000..070fdf4b --- /dev/null +++ b/dispatcher/templates/mail_change.jinja2 @@ -0,0 +1,89 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Mail change") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + + + + + + + +
+ +
+
Hello {{member_firstname}} {{member_surname}}
+
You've asked to change your email. To do so, follow this link:
+
+ +
+
If the link does not work for you, go to this page: https://my.aegee.eu/mail-change and paste this token into it:
+
{{token}}
+
+
+ If you didn't request a mail change, just ignore this email. +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/member_expired.jinja2 b/dispatcher/templates/member_expired.jinja2 new file mode 100644 index 00000000..cfcb80f0 --- /dev/null +++ b/dispatcher/templates/member_expired.jinja2 @@ -0,0 +1,71 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Membership expired") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ AEGEE-Europe +
+
Membership expired
+
Your membership in {{ body }} has expired! Contact your local board in case you believe this is a mistake!
+
+
Your last fee payment was on {{ last_payment }}. You will not be able to apply to statutory events until your membership is reactivated.
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + diff --git a/dispatcher/templates/member_joined.jinja2 b/dispatcher/templates/member_joined.jinja2 new file mode 100644 index 00000000..c8767a64 --- /dev/null +++ b/dispatcher/templates/member_joined.jinja2 @@ -0,0 +1,77 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: New Join Request") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ Logo +
+
You might have a new member soon!
+
{{member_firstname}} {{member_surname}} requested to join {{body_name}}
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/network_new_board.jinja2 b/dispatcher/templates/network_new_board.jinja2 new file mode 100644 index 00000000..b1953ba2 --- /dev/null +++ b/dispatcher/templates/network_new_board.jinja2 @@ -0,0 +1,76 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: New board added for VAR"|replace("VAR",body_name)) }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ AEGEE-Europe +
+
A new board has successfully been added for {{ body_name }}
+
+
+ Here are their details: + {% include "snippets/board_info.jinja2" %} +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/new_member.jinja2 b/dispatcher/templates/new_member.jinja2 new file mode 100644 index 00000000..63842d3a --- /dev/null +++ b/dispatcher/templates/new_member.jinja2 @@ -0,0 +1,93 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Welcome to AEGEE") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + + + + + + + + + + +
+ AEGEE-Europe +
+
Hello, {{member_firstname}}
+
Welcome to AEGEE! You have taken the first steps to discover the AEGEE Universe. In this mail you can find some further information to help you further along in your journey.
+
+
For quick information and communication the following links will help you out:
+ AEGEE Wiki (the knowledge backbone of AEGEE)
+ Mailing lists (official communication channels of AEGEE-Europe which keep you updated)
+ AEGEEans (Facebook group with the latest news in AEGEE)
+ Telegram channel (updates from AEGEE-Europe)
+
+
MyAEGEE offers most information. Ranging from Events listing to all the locals and internal bodies of AEGEE-Europe. In the left menu under ‘Resources’ one can find quick access to the most important information.
+
+
In order to find out what’s happening in your local, get in touch with members and the board of {{ body_name }}.
+
+
Enjoy your time in AEGEE!
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/password_reset.jinja2 b/dispatcher/templates/password_reset.jinja2 new file mode 100644 index 00000000..593630e6 --- /dev/null +++ b/dispatcher/templates/password_reset.jinja2 @@ -0,0 +1,84 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Password reset") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + + + + +
+ AEGEE Logo +
+
Hello
+
You requested a password reset. To finish the password reset, follow this link:
+
+ +
+
If the link does not work for you, go to this page: https://my.aegee.eu/password_confirm and paste this token into it:
+
{{token}}
+
Also notice that password resets expire after 30 minutes
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/snippets/board_info.jinja2 b/dispatcher/templates/snippets/board_info.jinja2 new file mode 100644 index 00000000..53642360 --- /dev/null +++ b/dispatcher/templates/snippets/board_info.jinja2 @@ -0,0 +1,12 @@ + diff --git a/dispatcher/templates/snippets/css.jinja2 b/dispatcher/templates/snippets/css.jinja2 new file mode 100644 index 00000000..f4a3ce3e --- /dev/null +++ b/dispatcher/templates/snippets/css.jinja2 @@ -0,0 +1,202 @@ + diff --git a/dispatcher/templates/snippets/events_application_info.jinja2 b/dispatcher/templates/snippets/events_application_info.jinja2 new file mode 100644 index 00000000..f425a425 --- /dev/null +++ b/dispatcher/templates/snippets/events_application_info.jinja2 @@ -0,0 +1,12 @@ + diff --git a/dispatcher/templates/snippets/macros.jinja2 b/dispatcher/templates/snippets/macros.jinja2 new file mode 100644 index 00000000..4a07a4e9 --- /dev/null +++ b/dispatcher/templates/snippets/macros.jinja2 @@ -0,0 +1,10 @@ +{% macro head(title) %} + + + {{ title }} + + + + {% include "snippets/css.jinja2" %} + +{% endmacro %} diff --git a/dispatcher/templates/snippets/statutory_application_info.jinja2 b/dispatcher/templates/snippets/statutory_application_info.jinja2 new file mode 100644 index 00000000..90782211 --- /dev/null +++ b/dispatcher/templates/snippets/statutory_application_info.jinja2 @@ -0,0 +1,30 @@ + diff --git a/dispatcher/templates/snippets/summeruniversity_application_info.jinja2 b/dispatcher/templates/snippets/summeruniversity_application_info.jinja2 new file mode 100644 index 00000000..914e4673 --- /dev/null +++ b/dispatcher/templates/snippets/summeruniversity_application_info.jinja2 @@ -0,0 +1,15 @@ + diff --git a/dispatcher/templates/statutory_applied.jinja2 b/dispatcher/templates/statutory_applied.jinja2 new file mode 100644 index 00000000..9c754e9c --- /dev/null +++ b/dispatcher/templates/statutory_applied.jinja2 @@ -0,0 +1,83 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Your application for VAR"|replace("VAR", event["name"])) }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + + + + +
+ AEGEE-Europe +
+
You've successfully applied to {{ event["name"] }}
+
+
+ Here are your application details: + {% include "snippets/statutory_application_info.jinja2" %} +
+
+
+ Have you already subscribed to our internal communication channels? Don’t miss out on any AEGEE news or announcements, subscribe to our mailing lists! +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/statutory_board_applied.jinja2 b/dispatcher/templates/statutory_board_applied.jinja2 new file mode 100644 index 00000000..095ac010 --- /dev/null +++ b/dispatcher/templates/statutory_board_applied.jinja2 @@ -0,0 +1,84 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: A member of your body applied for VAR"|replace("VAR", event["name"])) }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + + + + +
+ +
+
A member of your body ({{ application["body_name"] }}) applied for {{ event["name"] }}
+
+
+ Here are the application details: + {% include "snippets/statutory_application_info.jinja2" %} +
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/statutory_board_edited.jinja2 b/dispatcher/templates/statutory_board_edited.jinja2 new file mode 100644 index 00000000..12312b98 --- /dev/null +++ b/dispatcher/templates/statutory_board_edited.jinja2 @@ -0,0 +1,84 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: A member of your body edited the application for VAR"|replace("VAR", event["name"])) }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + + + + +
+ +
+
A member of your body ({{ application["body_name"] }}) edited the application for {{ event["name"] }}
+
+
+ Here are the application details: + {% include "snippets/statutory_application_info.jinja2" %} +
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/statutory_edited.jinja2 b/dispatcher/templates/statutory_edited.jinja2 new file mode 100644 index 00000000..31fbf47b --- /dev/null +++ b/dispatcher/templates/statutory_edited.jinja2 @@ -0,0 +1,76 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Your application for VAR"|replace("VAR", event["name"])) }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ AEGEE-Europe +
+
Your application for {{ event["name"] }} was successfully updated.
+
+
+ Here are your application details: + {% include "snippets/statutory_application_info.jinja2" %} +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/statutory_memberslist_edited.jinja2 b/dispatcher/templates/statutory_memberslist_edited.jinja2 new file mode 100644 index 00000000..0ab859d1 --- /dev/null +++ b/dispatcher/templates/statutory_memberslist_edited.jinja2 @@ -0,0 +1,71 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: A memberslist has been edited for VAR"|replace("VAR", event_name)) }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + +
+ Logo +
+
+ "{{ body_name }}" has edited their memberslist for "{{ event_name }}". + Their new membership fee is "{{ membership_fee }}". +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/statutory_memberslist_submitted.jinja2 b/dispatcher/templates/statutory_memberslist_submitted.jinja2 new file mode 100644 index 00000000..7dfd0d3e --- /dev/null +++ b/dispatcher/templates/statutory_memberslist_submitted.jinja2 @@ -0,0 +1,71 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: A memberslist has been submitted for VAR"|replace("VAR", event_name)) }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + +
+ Logo +
+
+ "{{ body_name }}" has submitted their memberslist for "{{ event_name }}". + Their membership fee is "{{ membership_fee }}". +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/summeruniversity_application_edited.jinja2 b/dispatcher/templates/summeruniversity_application_edited.jinja2 new file mode 100644 index 00000000..370987ac --- /dev/null +++ b/dispatcher/templates/summeruniversity_application_edited.jinja2 @@ -0,0 +1,76 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Your application for VAR"|replace("VAR", event["name"])) }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ AEGEE-Europe +
+
Your application for {{event["name"]}} was successfully updated.
+
+
+ Here are your application details: + {% include "snippets/summeruniversity_application_info.jinja2" %} +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/summeruniversity_application_status_updated.jinja2 b/dispatcher/templates/summeruniversity_application_status_updated.jinja2 new file mode 100644 index 00000000..ab054e60 --- /dev/null +++ b/dispatcher/templates/summeruniversity_application_status_updated.jinja2 @@ -0,0 +1,78 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Updated status of your application for VAR"|replace("VAR", event["name"])) }} + + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ AEGEE-Europe +
+
The status of your application for {{event["name"]}} was changed.
+
+
+ The status of your application for {{event["name"]}} is {{application["status"]}}. + If you have been accepted, the organizers of the Summer University will be in touch with you soon and provide you with more information. + If you have been rejected, there might be other Summer Universities with open calls. If there are any at this moment you can find them on MyAEGEE. +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/summeruniversity_applied.jinja2 b/dispatcher/templates/summeruniversity_applied.jinja2 new file mode 100644 index 00000000..20443d0b --- /dev/null +++ b/dispatcher/templates/summeruniversity_applied.jinja2 @@ -0,0 +1,77 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Your application for VAR"|replace("VAR", event["name"])) }} + + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ AEGEE-Europe +
+
You've successfully applied to {{event["name"]}}
+
+
+ Here are your application details: + {% include "snippets/summeruniversity_application_info.jinja2" %} +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/summeruniversity_board_applied.jinja2 b/dispatcher/templates/summeruniversity_board_applied.jinja2 new file mode 100644 index 00000000..91ff6ffd --- /dev/null +++ b/dispatcher/templates/summeruniversity_board_applied.jinja2 @@ -0,0 +1,84 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: A member of your body applied for VAR"|replace("VAR", event["name"])) }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + + + + +
+ +
+
A member of your body ({{application["body_name"]}}) applied for {{event["name"]}}
+
+
+ Here are the application details: + {% include "snippets/summeruniversity_application_info.jinja2" %} +
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/summeruniversity_board_edited.jinja2 b/dispatcher/templates/summeruniversity_board_edited.jinja2 new file mode 100644 index 00000000..7f7ec9d0 --- /dev/null +++ b/dispatcher/templates/summeruniversity_board_edited.jinja2 @@ -0,0 +1,85 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: A member of your body edited the application for VAR"|replace("VAR", event["name"])) }} + + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + + + + +
+ +
+
A member of your body ({{application["body_name"]}}) edited the application for {{event["name"]}}
+
+
+ Here are the application details: + {% include "snippets/summeruniversity_application_info.jinja2" %} +
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/summeruniversity_event_created.jinja2 b/dispatcher/templates/summeruniversity_event_created.jinja2 new file mode 100644 index 00000000..b8c4b1e9 --- /dev/null +++ b/dispatcher/templates/summeruniversity_event_created.jinja2 @@ -0,0 +1,82 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: The event has been created") }} + + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ Logo +
+
+ The event "{{event["name"]}}" has been created. +
+
+ You are receiving this as one of the organizers. +
+
+
+ See event +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/summeruniversity_event_updated.jinja2 b/dispatcher/templates/summeruniversity_event_updated.jinja2 new file mode 100644 index 00000000..54f25363 --- /dev/null +++ b/dispatcher/templates/summeruniversity_event_updated.jinja2 @@ -0,0 +1,82 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: The event has been updated") }} + + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ Logo +
+
+ The event "{{event["name"]}}" has been updated. +
+
+ You are receiving this as one of the organizers. +
+
+
+ See event +
+
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/summeruniversity_organizer_applied.jinja2 b/dispatcher/templates/summeruniversity_organizer_applied.jinja2 new file mode 100644 index 00000000..f71f247f --- /dev/null +++ b/dispatcher/templates/summeruniversity_organizer_applied.jinja2 @@ -0,0 +1,85 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Somebody has applied for VAR"|replace("VAR", event["name"])) }} + + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + + + + +
+ +
+
{{application["first_name"]}} {{application["last_name"]}} applied for {{event["name"]}}
+
+
+ Here are the application details: + {% include "snippets/summeruniversity_application_info.jinja2" %} +
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/summeruniversity_organizer_edited.jinja2 b/dispatcher/templates/summeruniversity_organizer_edited.jinja2 new file mode 100644 index 00000000..d4b2ba93 --- /dev/null +++ b/dispatcher/templates/summeruniversity_organizer_edited.jinja2 @@ -0,0 +1,85 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Somebody has updated their application for VAR"|replace("VAR", event["name"])) }} + + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + + + + +
+ +
+
{{application["first_name"]}} {{application["last_name"]}} edited their application for {{event["name"]}}
+
+
+ Here are the application details: + {% include "snippets/summeruniversity_application_info.jinja2" %} +
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/summeruniversity_status_changed.jinja2 b/dispatcher/templates/summeruniversity_status_changed.jinja2 new file mode 100644 index 00000000..2545fe3e --- /dev/null +++ b/dispatcher/templates/summeruniversity_status_changed.jinja2 @@ -0,0 +1,82 @@ + + + + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: Summer University status changed") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ Logo +
+
Your Summer University's status was changed
+
+ The status of your Summer University, + {{event["name"]}}, + has changed from {{old_status}} + to {{event["status"]}}. +
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/templates/summeruniversity_submitted.jinja2 b/dispatcher/templates/summeruniversity_submitted.jinja2 new file mode 100644 index 00000000..98e09dc1 --- /dev/null +++ b/dispatcher/templates/summeruniversity_submitted.jinja2 @@ -0,0 +1,87 @@ + + + + +{# +{% with title="MyAEGEE: New event submitted" %} + {% include "snippets/head.jinja2" %} +{% endwith %} +#} + +{% import "snippets/macros.jinja2" as macros %} +{{ macros.head("MyAEGEE: New event submitted") }} + + + + + + + + + +
  + + + + + + +
+ + + + + + +
+
 
+
+ + + + + + + + + + + + +
+ Logo +
+
A new event is submitted for {{event["status"]}}
+
+ A new event + {{event["name"]}} + is submitted as {{event["status"]}}. +
+
+ +
+ + + + + + +
+
This email was autogenerated by MyAEGEE mailer.
+
+
+
+ + + + + + diff --git a/dispatcher/tests/__init__.py b/dispatcher/tests/__init__.py new file mode 100644 index 00000000..e69de29b