Skip to content

Commit

Permalink
Merge pull request #18 from TogetherCrew/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
cyri113 authored Oct 20, 2023
2 parents 6cf62ba + 0e2c95e commit d5dbde4
Show file tree
Hide file tree
Showing 84 changed files with 4,605 additions and 71 deletions.
24 changes: 23 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
GITHUB_TOKEN=daolytics_access_token
AUTOMATION_DB_COLLECTION=
AUTOMATION_DB_NAME=
MONGODB_HOST=
MONGODB_PASS=
MONGODB_PORT=
MONGODB_USER=
NEO4J_DB=
NEO4J_HOST=
NEO4J_PASSWORD=
NEO4J_PORT=
NEO4J_PROTOCOL=
NEO4J_USER=
RABBIT_HOST=
RABBIT_PASSWORD=
RABBIT_PORT=
RABBIT_USER=
REDIS_HOST=
REDIS_PASSWORD=
REDIS_PORT=
SAGA_DB_COLLECTION=
SAGA_DB_NAME=
SENTRY_DSN=
SENTRY_ENV=
8 changes: 2 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@
FROM python:3.10-bullseye AS base
WORKDIR /project
COPY . .
ARG GITHUB_TOKEN
RUN pip3 install -r requirements.txt

FROM base AS test
RUN chmod +x docker-entrypoint.sh
CMD ["./docker-entrypoint.sh"]

FROM base AS prod-server
CMD ["python3", "start_rabbit_mq.py"]

FROM base as prod-worker
CMD ["python3", "redis_worker.py"]
FROM base AS prod
CMD ["python3", "server.py"]
Empty file added automation/__init__.py
Empty file.
226 changes: 226 additions & 0 deletions automation/automation_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import logging
from typing import Any

from automation.utils.automation_base import AutomationBase
from automation.utils.model import AutomationDB
from pybars import Compiler
from tc_messageBroker.rabbit_mq.event import Event
from tc_messageBroker.rabbit_mq.queue import Queue


class AutomationWorkflow(AutomationBase):
def __init__(self) -> None:
super().__init__()
self.automation_db = AutomationDB()

def start(self, guild_id: str):
"""
start the automation workflow for a guild
Parameters
-----------
guild_id : str
to select the right automation
"""
log_prefix = f"GUILDID: {guild_id}: "
automations = self.automation_db.load_from_db(guild_id)
if len(automations) == 0:
logging.info(f"{log_prefix}No automation available for this guild!")
else:
msg = f"{log_prefix}Starting automation!"
logging.info(f"{msg} number of automation fetched: {len(automations)}")

for at in automations:
at_pre = f"{log_prefix}: automation id: {at.id}: "
if at.enabled:
members_by_category: dict[str, list[dict[str, str]]] = {}
for trigger in at.triggers:
if trigger.enabled:
category: str | None = trigger.options["category"]
if category is None or not isinstance(category, str):
logging.error(
f"{at_pre}No category specified for one of the triggers!"
)
logging.error(f"{at_pre}Skipping the trigger!")
break

members_by_category[category] = []

users1, users2 = self._get_users_from_memberactivities(
guild_id, category
)
users = self._subtract_users(users1, users2)

for action in at.actions:
if action.enabled:
type = self._get_handlebar_type(action.template)
prepared_id_name: list[tuple[str, str]]
if type is None:
logging.warning(
f"{at_pre}No type specified in the action template!"
)
logging.warning(
f"{at_pre}Sending raw action.template to users!"
)
# adding a dummy variable for user_name
prepared_id_name = list(zip(users, users))
else:
prepared_id_name = self.prepare_names(
guild_id, list(users), user_field=type
)

for user_id, user_name in prepared_id_name:
compiled_message: str
if type is not None:
compiled_message = self._compile_message(
data={type: user_name},
message=action.template,
)
else:
compiled_message = action.template

data = self._prepare_saga_data(
guild_id, user_id, compiled_message
)
saga_id = self._create_manual_saga(data)
logging.info(
f"{at_pre}Started to fire events for user {user_id}!"
)
# firing the event
self.fire_event(saga_id, data)

members_by_category[category].append(
{"user_id": user_id, "user_name": user_name}
)

if at.report.enabled:
# setting up the names to send message
# to avoid duplicate we used dictionary
report_users: dict[str, str] = {}
for member in members_by_category[category]: # type: ignore
if member["user_name"] is not None:
report_users[member["user_id"]] = member["user_name"]
else:
# in case of no user info was available just send the ids
report_users[member["user_id"]] = member["user_id"]

compiled_message = self._prepare_report_compiled_message(
list(report_users.values()), at.report.template
)

for recipent in at.report.recipientIds:
data = self._prepare_saga_data(
guild_id, recipent, compiled_message
)
saga_id = self._create_manual_saga(data)

# firing the event
self.fire_event(saga_id, data)

def _prepare_report_compiled_message(
self, user_names: list[str], template: str
) -> str:
"""
prepare the message for the report
Note: we're just hardcoding the template message with having the template of
each user being in one line. and we just support the `usernames`
"""
# hardcoding the template type for report!
# we have to change it in future to support more types.
type = "usernames"
users_prepared = [f"- {user}\n" for user in user_names]

compiled_message = self._compile_message(
data={type: users_prepared}, message=template # type: ignore
)

return compiled_message

def _get_handlebar_type(self, template: str) -> str | None:
"""
get the handlebar type.
for example the template would be
"hello {{username}}!"
and the output would be `username`
Note: for now it just support returning one handlebar.
Parameters
------------
template : str
the template message to extract the type
Returns
---------
type : str
the handlebar type to use
"""
start_index = template.find("{{") + 2
end_index = template.find("}}")
if start_index == -1 or end_index == -1:
return None
return template[start_index:end_index]

def _compile_message(self, data: dict[str, str], message: str) -> str:
"""
compile the message to be sent to the user
Parameters
-----------
data : dict[str, str]
the dictionary to be compiled for the handlebars
message : str
the string message that contain the handlebars
"""
compiler = Compiler()
template = compiler.compile(message)
compiled_message = template(data)

return compiled_message

def _prepare_saga_data(
self, guild_id: str, user_id: str, message: str
) -> dict[str, Any]:
"""
prepare the data needed for the saga
Parameters:
------------
guild_id : str
the guild_id having the user
user_id : str
the user_id to send message
message : str
the message to send the user
"""
data = {
"guildId": guild_id,
"created": False,
"discordId": user_id,
"message": message,
"userFallback": True,
}

return data

def fire_event(self, saga_id: str, data: dict[str, Any]) -> None:
"""
fire the event `SEND_MESSAGE` to the user of a guild
Parameters:
------------
saga_id : str
the saga_id having of the event
data : str
the data to fire
"""

self.rabbitmq.publish(
queue_name=Queue.DISCORD_BOT,
event=Event.DISCORD_BOT.SEND_MESSAGE,
content={
"uuid": saga_id,
"data": data,
},
)
Empty file added automation/utils/__init__.py
Empty file.
Loading

0 comments on commit d5dbde4

Please sign in to comment.