From f17d37bc53fa1540412b954552d52b057b44b15e Mon Sep 17 00:00:00 2001
From: Trey <73353716+TreyWW@users.noreply.github.com>
Date: Mon, 22 Jul 2024 14:34:35 +0100
Subject: [PATCH] added reminders + refactor
Signed-off-by: Trey <73353716+TreyWW@users.noreply.github.com>
---
.github/management_bot/pulumi/__main__.py | 180 +++++++++++++++++-
.../{src => reminder_handler}/__init__.py | 0
.../{src => reminder_handler}/helpers.py | 0
.../pulumi/reminder_handler/lambda_handler.py | 63 ++++++
.../pulumi/src/issues/handler.py | 106 -----------
.../pulumi/webhook_handler/__init__.py | 0
.../pulumi/{src => webhook_handler}/_types.py | 1 +
.../pulumi/webhook_handler/boto3_handler.py | 39 ++++
.../pulumi/webhook_handler/helpers.py | 19 ++
.../pulumi/webhook_handler/issues/__init__.py | 0
.../pulumi/webhook_handler/issues/handler.py | 165 ++++++++++++++++
.../webhook_handler/issues/reminders.py | 68 +++++++
.../lambda_handler.py | 5 +
.../pulumi/webhook_handler/prs/__init__.py | 0
.../{src => webhook_handler}/prs/actions.py | 0
.../{src => webhook_handler}/prs/handler.py | 0
16 files changed, 531 insertions(+), 115 deletions(-)
rename .github/management_bot/pulumi/{src => reminder_handler}/__init__.py (100%)
rename .github/management_bot/pulumi/{src => reminder_handler}/helpers.py (100%)
create mode 100644 .github/management_bot/pulumi/reminder_handler/lambda_handler.py
delete mode 100644 .github/management_bot/pulumi/src/issues/handler.py
create mode 100644 .github/management_bot/pulumi/webhook_handler/__init__.py
rename .github/management_bot/pulumi/{src => webhook_handler}/_types.py (99%)
create mode 100644 .github/management_bot/pulumi/webhook_handler/boto3_handler.py
create mode 100644 .github/management_bot/pulumi/webhook_handler/helpers.py
create mode 100644 .github/management_bot/pulumi/webhook_handler/issues/__init__.py
create mode 100644 .github/management_bot/pulumi/webhook_handler/issues/handler.py
create mode 100644 .github/management_bot/pulumi/webhook_handler/issues/reminders.py
rename .github/management_bot/pulumi/{src => webhook_handler}/lambda_handler.py (96%)
create mode 100644 .github/management_bot/pulumi/webhook_handler/prs/__init__.py
rename .github/management_bot/pulumi/{src => webhook_handler}/prs/actions.py (100%)
rename .github/management_bot/pulumi/{src => webhook_handler}/prs/handler.py (100%)
diff --git a/.github/management_bot/pulumi/__main__.py b/.github/management_bot/pulumi/__main__.py
index d25d32f38..c655af340 100644
--- a/.github/management_bot/pulumi/__main__.py
+++ b/.github/management_bot/pulumi/__main__.py
@@ -3,7 +3,7 @@
import json
import pulumi
-from pulumi_aws import apigateway, iam
+from pulumi_aws import apigateway, iam, scheduler
from pulumi_aws import lambda_
config = pulumi.Config()
@@ -13,9 +13,15 @@
region = config.get("region", "eu-west-2")
tags = {"app": site_name}
+# Reminders
+
+reminders_group = scheduler.ScheduleGroup("invoice_reminders_group", name=f"myfinances-github-bot-remind_me")
+
# lambda_layer
lambda_access_role = iam.Role(
"lambda_access_role",
+ name="lambda_role",
+ path="/myfinances/management_bot/",
assume_role_policy=json.dumps(
{
"Version": "2012-10-17",
@@ -33,23 +39,176 @@
),
)
+#
+# This goes on Lambda to send comment
+lambda_execution_policy = iam.Policy(
+ "lambda-execution-policy",
+ name=f"lambda-execution-policy",
+ path="/myfinances/management_bot/",
+ policy=json.dumps(
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "CreateLogDelivery",
+ "Effect": "Allow",
+ "Action": [
+ "kms:Decrypt",
+ "ssm:GetParametersByPath",
+ "ssm:GetParameters",
+ "ssm:GetParameter",
+ "logs:CreateLogGroup",
+ "logs:CreateLogStream",
+ "logs:PutLogEvents",
+ ],
+ "Resource": [
+ "arn:aws:ssm:*:*:parameter/myfinances/github_bot/*",
+ "arn:aws:kms:*:*:key/*",
+ "arn:aws:logs:*:*:log-group:/aws/lambda/myfinances_github_bot_webhooks:*",
+ "arn:aws:logs:*:*:log-group:/aws/lambda/myfinances_github_bot_reminders:*",
+ "arn:aws:logs:*:*:log-group:/aws/lambda/myfinances_github_bot_webhooks:*:log-stream:*",
+ "arn:aws:logs:*:*:log-group:/aws/lambda/myfinances_github_bot_reminders:*:log-stream:*",
+ ],
+ }
+ ],
+ }
+ ),
+)
+#
+
+#
+lambda_execution_policy_attachment = iam.RolePolicyAttachment(
+ "lambda-execution-policy-attach",
+ policy_arn=lambda_execution_policy.arn,
+ role=lambda_access_role.name,
+ opts=pulumi.ResourceOptions(depends_on=[lambda_access_role, lambda_execution_policy]),
+)
+#
+
+#
+
+scheduler_execution_policy = iam.Policy(
+ "scheduler-execution-policy",
+ name=f"reminder-scheduler-execution-policy",
+ path="/myfinances/management_bot/",
+ policy=json.dumps(
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "InvokeLambda",
+ "Effect": "Allow",
+ "Action": ["lambda:InvokeFunction"],
+ "Resource": "arn:aws:lambda:*:*:function:myfinances_github_bot_reminders",
+ },
+ {
+ "Sid": "AllowEventbridgeScheduler",
+ "Effect": "Allow",
+ "Action": ["scheduler:*"],
+ "Resource": "*",
+ },
+ ],
+ }
+ ),
+)
+#
+
+#
+scheduler_execution_role = iam.Role(
+ "scheduler-execution-role",
+ name="reminder-scheduler-execution-role",
+ path="/myfinances/management_bot/",
+ assume_role_policy=json.dumps(
+ {
+ "Version": "2012-10-17",
+ "Statement": [{"Effect": "Allow", "Principal": {"Service": "scheduler.amazonaws.com"}, "Action": "sts:AssumeRole"}],
+ }
+ ),
+)
+#
+
+#
+scheduler_execution_policy_attachment = iam.RolePolicyAttachment(
+ "scheduler-execution-policy-attach",
+ policy_arn=scheduler_execution_policy.arn,
+ role=scheduler_execution_role.name,
+ opts=pulumi.ResourceOptions(depends_on=[scheduler_execution_policy, scheduler_execution_role]),
+)
+#
+
+scheduler_user = iam.User("scheduler_user", name="myfinances-github-bot-scheduler")
+
+reminders_create_schedule_policy = iam.Policy(
+ "reminders_create_schedule_policy",
+ name="myfinances-github-bot-reminders-create-schedule-policy",
+ path="/myfinances/management_bot/",
+ policy=json.dumps(
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "AllowEventbridgeScheduler",
+ "Effect": "Allow",
+ "Action": ["scheduler:CreateSchedule"],
+ "Resource": "arn:aws:scheduler:*:*:schedule/myfinances-github-bot-remind_me/*",
+ },
+ {
+ "Sid": "AllowRolePass",
+ "Effect": "Allow",
+ "Action": ["iam:PassRole"],
+ "Resource": "arn:aws:iam::*:role/myfinances/management_bot/reminder-scheduler-execution-role",
+ },
+ ],
+ }
+ ),
+)
+
+iam.UserPolicyAttachment(
+ "scheduler_user_policy_attachment",
+ policy_arn=reminders_create_schedule_policy.arn,
+ user=scheduler_user.name,
+ opts=pulumi.ResourceOptions(depends_on=[reminders_create_schedule_policy, scheduler_user]),
+)
+
+scheduler_access_role_key = iam.AccessKey(
+ "lambda_access_role_key", user=scheduler_user.name, opts=pulumi.ResourceOptions(depends_on=[scheduler_user])
+)
+
lambda_layer = lambda_.LayerVersion(
"lambda_layer", code=pulumi.FileArchive(config.require("lambda_zip_path")), layer_name="PyGithub_for_myfinances_management_bot"
)
-# lambda_ssm_layer = lambda_.LayerVersion(
-# "lambda_ssm_layer", layer_name="AWS-Parameters-and-Secrets-Lambda-Extension",
-# )
+reminder_handler_lambda_func = lambda_.Function(
+ "reminder_lambda",
+ name="myfinances_github_bot_reminders",
+ role=lambda_access_role.arn,
+ code=pulumi.AssetArchive({".": pulumi.FileArchive("./reminder_handler")}),
+ handler="lambda_handler.lambda_handler",
+ timeout=8,
+ runtime=lambda_.Runtime.PYTHON3D12,
+ environment=lambda_.FunctionEnvironmentArgs(variables={"ssm_prefix": "/myfinances/github_bot/"}),
+ layers=[lambda_layer.arn, "arn:aws:lambda:eu-west-2:133256977650:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11"],
+ tags={"project": "MyFinancesBot"},
+)
-lambda_func = lambda_.Function(
+main_lambda_func = lambda_.Function(
"webhook_lambda",
name="myfinances_github_bot_webhooks",
role=lambda_access_role.arn,
- code=pulumi.AssetArchive({".": pulumi.FileArchive("./src")}),
+ code=pulumi.AssetArchive({".": pulumi.FileArchive("./webhook_handler")}),
handler="lambda_handler.lambda_handler",
timeout=8,
runtime=lambda_.Runtime.PYTHON3D12,
- environment=lambda_.FunctionEnvironmentArgs(variables={"ssm_prefix": "/myfinances/github_bot/"}),
+ environment=lambda_.FunctionEnvironmentArgs(
+ variables={
+ "ssm_prefix": "/myfinances/github_bot/",
+ "AWS_REMINDER_LAMBDA_ARN": reminder_handler_lambda_func.arn,
+ "AWS_REMINDER_LAMBDA_ROLE_ARN": scheduler_execution_role.arn,
+ "AWS_SCHEDULES_ACCESS_KEY_ID": scheduler_access_role_key.id,
+ "AWS_SCHEDULES_SECRET_ACCESS_KEY": scheduler_access_role_key.secret,
+ "AWS_SCHEDULES_REGION_NAME": region,
+ }
+ ),
layers=[lambda_layer.arn, "arn:aws:lambda:eu-west-2:133256977650:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11"],
tags={"project": "MyFinancesBot"},
)
@@ -82,7 +241,8 @@
integration_http_method="POST",
type="AWS",
timeout_milliseconds=8000,
- uri=lambda_func.invoke_arn,
+ content_handling="CONVERT_TO_TEXT",
+ uri=main_lambda_func.arn.apply(lambda arn: arn + ":${stageVariables.lambda_function_version}"), # main_lambda_func.invoke_arn,
)
api_gw_200_resp = apigateway.MethodResponse(
@@ -107,7 +267,7 @@
"apigw_lambda",
statement_id="AllowExecutionFromAPIGateway",
action="lambda:InvokeFunction",
- function=lambda_func.name,
+ function=main_lambda_func.name,
principal="apigateway.amazonaws.com",
source_arn=pulumi.Output.all(rest_api.id).apply(lambda id: f"arn:aws:execute-api:{region}:{account_id}:{id[0]}/*/POST/"),
)
@@ -123,3 +283,5 @@
)
pulumi.export("invoke_url", prod_stage.invoke_url)
+pulumi.export("scheduler_execution_role", scheduler_execution_role.arn)
+pulumi.export("reminder_handler_lambda_func", reminder_handler_lambda_func.arn)
diff --git a/.github/management_bot/pulumi/src/__init__.py b/.github/management_bot/pulumi/reminder_handler/__init__.py
similarity index 100%
rename from .github/management_bot/pulumi/src/__init__.py
rename to .github/management_bot/pulumi/reminder_handler/__init__.py
diff --git a/.github/management_bot/pulumi/src/helpers.py b/.github/management_bot/pulumi/reminder_handler/helpers.py
similarity index 100%
rename from .github/management_bot/pulumi/src/helpers.py
rename to .github/management_bot/pulumi/reminder_handler/helpers.py
diff --git a/.github/management_bot/pulumi/reminder_handler/lambda_handler.py b/.github/management_bot/pulumi/reminder_handler/lambda_handler.py
new file mode 100644
index 000000000..421a1cf91
--- /dev/null
+++ b/.github/management_bot/pulumi/reminder_handler/lambda_handler.py
@@ -0,0 +1,63 @@
+import json
+import os, base64
+import urllib.request
+from textwrap import dedent
+
+from github import Github, Issue, GithubIntegration, PullRequest
+from github import Auth
+
+import logging
+import helpers
+
+logging.basicConfig()
+logging.getLogger().setLevel(logging.DEBUG if os.environ.get("DEBUG") else logging.DEBUG) # todo go back to info
+logger = logging.getLogger(__name__)
+
+aws_session_token = os.environ.get("AWS_SESSION_TOKEN")
+
+REPOSITORY_NAME = "TreyWW/MyFinances"
+
+
+def lambda_handler(event: dict, lambda_context):
+ print(f"EVENT_DETAILS: {event}")
+ # https://docs.aws.amazon.com/systems-manager/latest/userguide/ps-integration-lambda-extensions.html
+ stage = "production"
+ req = urllib.request.Request(
+ f"http://localhost:2773/systemsmanager/parameters/get?withDecryption=true&name=%2Fmyfinances%2Fgithub_bot%2F{stage}"
+ )
+ req.add_header("X-Aws-Parameters-Secrets-Token", aws_session_token)
+ config = urllib.request.urlopen(req).read()
+
+ ssm_result: json = json.loads(config)
+ ssm_value: json = json.loads(ssm_result["Parameter"]["Value"])
+
+ PRIVATE_KEY = helpers.decode_private_key(ssm_value["private_key"])
+ APP_ID = ssm_value["app_id"]
+
+ auth = Auth.AppAuth(APP_ID, PRIVATE_KEY)
+ gi = GithubIntegration(auth=auth)
+ g: Github = gi.get_installations()[0].get_github_for_installation()
+
+ repository = g.get_repo(REPOSITORY_NAME)
+
+ target: Issue.Issue | PullRequest.PullRequest
+ message: str = event.get("message")
+
+ if issue_id := event.get("issue_id"):
+ target = repository.get_issue(issue_id)
+ elif pr_id := event.get("pr_id"):
+ target = repository.get_pull(pr_id)
+ else:
+ raise ValueError("No issue or pull request specified")
+
+ target.create_comment(
+ dedent(
+ f"""
+ :wave: @{event.get("user")}, {message if message else "you set a reminder for now!"}
+
+ {helpers.del_reply_comment()}
+ """
+ )
+ )
+
+ return {"statusCode": 200, "body": json.dumps({}), "headers": {"Content-Type": "application/json"}}
diff --git a/.github/management_bot/pulumi/src/issues/handler.py b/.github/management_bot/pulumi/src/issues/handler.py
deleted file mode 100644
index 52a7198c4..000000000
--- a/.github/management_bot/pulumi/src/issues/handler.py
+++ /dev/null
@@ -1,106 +0,0 @@
-import os
-import re
-from textwrap import dedent
-import logging
-import github.Issue
-
-import string
-
-import random
-
-logger = logging.getLogger(__name__)
-if os.environ.get("AWS_EXECUTION_ENV") is not None:
- import _types
- import helpers
-else:
- from .. import _types
- from .. import helpers
-
-
-def title_handler(context_dicts: _types.Context, context_objs: _types.Objects) -> list[str]:
- if not re.match(r"^(bug|idea|implement|cleanup):\s*\S.*", context_objs.issue.title) and context_objs.sender.type == "User":
- logger.info(f"Regex title doesn't match. {context_objs.issue.title} doesnt start with bug|idea|implement|cleanup:")
- logger.info(f"Commenting on {context_objs.issue.html_url}")
- context_objs.issue.create_comment(
- dedent(
- f"""
- Hi @{context_objs.sender.login},
-
- You have chosen a title that is slightly different to our title standards. Please, if possible, use the format:
- (bug, idea, implement or cleanup) : Title
-
- e.g. "bug: xyz page doesn't work"
-
- {helpers.del_reply_comment()}
- """
- )
- )
- return ["added_issue_comment (invalid title)"]
- return []
-
-
-def delete_reply_handler(context_dicts: _types.Context, context_objs: _types.Objects) -> list[str]:
- match = re.search(r"DELREPLY-(.{8})", context_dicts.comment.body)
-
- if not match or context_objs.sender.type != "User":
- return []
-
- logger.info("Deleting comment due to DELREPLY in body")
-
- reference_code = match.group(1)
-
- logger.debug(f"Deleting comment with reference code: {reference_code}")
-
- for comment in context_objs.issue.get_comments():
- if f"DELREPLY-{reference_code}" in comment.body.upper():
- # comment.delete() # delete users reply comment
- context_objs.issue.get_comment(context_dicts.comment.id).delete() # delete bots comment
- return ["deleted_issue_comment (DEL REPLY)"]
-
-
-def command_handler(context_dicts: _types.Context, context_objs: _types.Objects) -> list[str]:
- base_message = context_dicts.comment.body
- split = base_message.split()
- command = split[0]
-
- logger.info(f"Extracted Command: {command}")
-
- if command == "/project_info":
- context_objs.issue.create_comment(
- dedent(
- f"""
- You can view our documentation at\
- [docs.myfinances.cloud](https://docs.myfinances.cloud/?utm_source=issue_{context_objs.issue.number}).
-
- There you can find info such as:
- - setting up guides
- - code styles
- - changelogs
- - our discord server
- - (soon) user usage guide
-
- {f"> Mentioning @{split[1]}" if len(split) > 1 else ""}
-
- {helpers.del_reply_comment()}
- """
- )
- )
- return ["added_issue_comment (project info)"]
- return []
-
-
-def handler(context_dicts: _types.Context, context_objs: _types.Objects) -> list[str]:
- logger.info(f"action: {context_dicts.action}")
- responses = []
- match context_dicts.action:
- case "opened":
- logger.info("Using title handler due to opened issue")
- responses.extend(title_handler(context_dicts, context_objs))
- case "edited":
- if context_dicts.changes.title and context_dicts.changes.title["from"]:
- responses.extend(title_handler(context_dicts, context_objs))
- case "created":
- if context_dicts.comment:
- responses.extend(delete_reply_handler(context_dicts, context_objs))
- responses.extend(command_handler(context_dicts, context_objs))
- return responses
diff --git a/.github/management_bot/pulumi/webhook_handler/__init__.py b/.github/management_bot/pulumi/webhook_handler/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/.github/management_bot/pulumi/src/_types.py b/.github/management_bot/pulumi/webhook_handler/_types.py
similarity index 99%
rename from .github/management_bot/pulumi/src/_types.py
rename to .github/management_bot/pulumi/webhook_handler/_types.py
index b663a90d7..7163e01b9 100644
--- a/.github/management_bot/pulumi/src/_types.py
+++ b/.github/management_bot/pulumi/webhook_handler/_types.py
@@ -71,6 +71,7 @@ class Changes:
@dataclass
class Context:
event: dict
+ lambda_context: Any
action: str
sender: User
issue: Optional[Issue] = None
diff --git a/.github/management_bot/pulumi/webhook_handler/boto3_handler.py b/.github/management_bot/pulumi/webhook_handler/boto3_handler.py
new file mode 100644
index 000000000..bd77c796a
--- /dev/null
+++ b/.github/management_bot/pulumi/webhook_handler/boto3_handler.py
@@ -0,0 +1,39 @@
+import json
+import os
+import logging
+import uuid
+
+import boto3
+from botocore.config import Config
+
+config = Config(connect_timeout=5, retries={"max_attempts": 2})
+
+Boto3HandlerSession = boto3.session.Session(
+ aws_access_key_id=os.environ.get("AWS_SCHEDULES_ACCESS_KEY_ID"),
+ aws_secret_access_key=os.environ.get("AWS_SCHEDULES_SECRET_ACCESS_KEY"),
+ region_name=os.environ.get("AWS_SCHEDULES_REGION_NAME", default="eu-west-2"),
+)
+
+event_bridge_client = Boto3HandlerSession.client("events")
+event_bridge_scheduler = Boto3HandlerSession.client("scheduler", config=config)
+
+logger = logging.getLogger(__name__)
+
+
+def create_reminder(comment_id, date_time, pr_id, issue_id, message, user):
+ target = {
+ "Arn": os.environ.get("AWS_REMINDER_LAMBDA_ARN", ""),
+ "RoleArn": os.environ.get("AWS_REMINDER_LAMBDA_ROLE_ARN", ""),
+ "Input": json.dumps({"pr_id": pr_id, "issue_id": issue_id, "message": message, "user": user}),
+ }
+ logger.info(f"target: {target}")
+ logger.info(f"schedule_expression: at({date_time})")
+ logger.info(f"")
+ return event_bridge_scheduler.create_schedule(
+ Name=f"{str(uuid.uuid4())}",
+ GroupName="myfinances-github-bot-remind_me",
+ FlexibleTimeWindow={"Mode": "OFF"},
+ ScheduleExpression=f"at({date_time})",
+ Target=target,
+ ActionAfterCompletion="DELETE",
+ )
diff --git a/.github/management_bot/pulumi/webhook_handler/helpers.py b/.github/management_bot/pulumi/webhook_handler/helpers.py
new file mode 100644
index 000000000..751765bf6
--- /dev/null
+++ b/.github/management_bot/pulumi/webhook_handler/helpers.py
@@ -0,0 +1,19 @@
+import base64
+import random
+import string
+
+
+def encode_private_key(entire_key: str) -> str:
+ return base64.b64encode(entire_key.encode("ascii")).decode("ascii")
+
+
+def decode_private_key(raw_private_key) -> str:
+ return base64.b64decode(raw_private_key.encode("ascii")).decode("ascii")
+
+
+def del_reply_comment() -> str:
+ return (
+ "> If you would like to ignore this message, please reply with the reference `DELREPLY-"
+ + "".join(random.choices(string.ascii_uppercase + string.digits, k=8))
+ + "` (you may delete this reply afterwards)"
+ )
diff --git a/.github/management_bot/pulumi/webhook_handler/issues/__init__.py b/.github/management_bot/pulumi/webhook_handler/issues/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/.github/management_bot/pulumi/webhook_handler/issues/handler.py b/.github/management_bot/pulumi/webhook_handler/issues/handler.py
new file mode 100644
index 000000000..8122b4d4f
--- /dev/null
+++ b/.github/management_bot/pulumi/webhook_handler/issues/handler.py
@@ -0,0 +1,165 @@
+import os
+import re
+from datetime import datetime
+from textwrap import dedent
+import logging
+import github.Issue
+
+import string
+
+import random
+
+logger = logging.getLogger(__name__)
+if os.environ.get("AWS_EXECUTION_ENV") is not None:
+ import _types
+ import helpers
+ from .reminders import validate_reminder_command
+ import boto3_handler
+else:
+ from .. import _types
+ from .. import helpers
+ from ..issues.reminders import validate_reminder_command
+ from .. import boto3_handler
+
+
+def title_handler(context_dicts: _types.Context, context_objs: _types.Objects) -> list[str]:
+ if not re.match(r"^(bug|idea|implement|cleanup):\s*\S.*", context_objs.issue.title) and context_objs.sender.type == "User":
+ logger.info(f"Regex title doesn't match. {context_objs.issue.title} doesnt start with bug|idea|implement|cleanup:")
+ logger.info(f"Commenting on {context_objs.issue.html_url}")
+ context_objs.issue.create_comment(
+ dedent(
+ f"""
+ Hi @{context_objs.sender.login},
+
+ You have chosen a title that is slightly different to our title standards. Please, if possible, use the format:
+ (bug, idea, implement or cleanup) : Title
+
+ e.g. "bug: xyz page doesn't work"
+
+ {helpers.del_reply_comment()}
+ """
+ )
+ )
+ return ["added_issue_comment (invalid title)"]
+ return []
+
+
+def delete_reply_handler(context_dicts: _types.Context, context_objs: _types.Objects) -> list[str]:
+ match = re.search(r"DELREPLY-(.{8})", context_dicts.comment.body)
+
+ if not match or context_objs.sender.type != "User":
+ return []
+
+ logger.info("Deleting comment due to DELREPLY in body")
+
+ reference_code = match.group(1)
+
+ logger.debug(f"Deleting comment with reference code: {reference_code}")
+
+ for comment in context_objs.issue.get_comments():
+ if f"DELREPLY-{reference_code}" in comment.body.upper():
+ comment.delete() # delete users reply comment
+ # context_objs.issue.get_comment(context_dicts.comment.id).delete() # delete bots comment
+ return ["deleted_issue_comment (DEL REPLY)"]
+
+
+def command_handler(context_dicts: _types.Context, context_objs: _types.Objects) -> list[str]:
+ base_message = context_dicts.comment.body
+ split = base_message.split()
+ command = split[0]
+
+ logger.info(f"Extracted Command: {command}")
+
+ match command:
+ #
+ case "/project_info":
+ context_objs.issue.create_comment(
+ dedent(
+ f"""
+ You can view our documentation at\
+ [docs.myfinances.cloud](https://docs.myfinances.cloud/?utm_source=issue_{context_objs.issue.number}).
+
+ There you can find info such as:
+ - setting up guides
+ - code styles
+ - changelogs
+ - our discord server
+ - (soon) user usage guide
+
+ {f"> Mentioning @{split[1]}" if len(split) > 1 else ""}
+
+ {helpers.del_reply_comment()}
+ """
+ )
+ )
+ return ["added_issue_comment (project info)"]
+ #
+ #
+ case "/remind":
+ logger.info("Using /remind")
+ if len(split) < 2:
+ logger.info("Invalid usage")
+ context_objs.issue.create_comment(
+ dedent(
+ f"""
+ Invalid usage. Example usage:
+ - `/remind 2d`
+ - `/remind 1w revamp this`
+
+ {helpers.del_reply_comment()}
+ """
+ )
+ )
+ return ["added_issue_comment (invalid /remind args)"]
+
+ duration: str = split[1]
+ message: str = " ".join(split[2:]) if len(split) > 2 else ""
+
+ datetime_or_error = validate_reminder_command(split, context_objs)
+
+ logger.info(f"datetime_or_error: {datetime_or_error}")
+
+ if isinstance(datetime_or_error, list):
+ return datetime_or_error
+
+ resp = boto3_handler.create_reminder(
+ comment_id=context_objs,
+ date_time=datetime_or_error.strftime("%Y-%m-%dT%H:%M:%S"),
+ pr_id=None,
+ issue_id=context_objs.issue.number,
+ message=message,
+ user=context_objs.sender.login,
+ )
+ logger.info(f"resp: {resp}")
+
+ context_objs.issue.create_comment(
+ dedent(
+ f"""
+ @{context_objs.sender.login}, ok! I will remind you {datetime_or_error.strftime("on %A, %B %-m, %y at %-H:%M %p")}!
+
+ {helpers.del_reply_comment()}
+ """
+ )
+ )
+ return ["added_issue_comment (reminder success)"]
+ case _:
+ logger.info("No issue command")
+ return []
+ #
+
+
+def handler(context_dicts: _types.Context, context_objs: _types.Objects) -> list[str]:
+ logger.info(f"action: {context_dicts.action}")
+ responses = []
+ match context_dicts.action:
+ case "opened":
+ logger.info("Using title handler due to opened issue")
+ responses.extend(title_handler(context_dicts, context_objs))
+ case "edited":
+ if context_dicts.changes.title and context_dicts.changes.title["from"]:
+ responses.extend(title_handler(context_dicts, context_objs))
+ case "created":
+ if context_dicts.comment:
+ responses.extend(delete_reply_handler(context_dicts, context_objs))
+ responses.extend(command_handler(context_dicts, context_objs))
+ return responses
diff --git a/.github/management_bot/pulumi/webhook_handler/issues/reminders.py b/.github/management_bot/pulumi/webhook_handler/issues/reminders.py
new file mode 100644
index 000000000..9a3a64ee7
--- /dev/null
+++ b/.github/management_bot/pulumi/webhook_handler/issues/reminders.py
@@ -0,0 +1,68 @@
+import os
+import re
+from textwrap import dedent
+from datetime import datetime, timedelta
+
+if os.environ.get("AWS_EXECUTION_ENV") is not None:
+ import _types
+ import helpers
+ import boto3_handler
+else:
+ from .. import _types
+ from .. import helpers
+ from .. import boto3_handler
+
+
+def parse_duration(duration) -> timedelta | None:
+ pattern = re.compile(r"(\d+)([wdhm])")
+ matches = pattern.match(duration)
+
+ if not matches:
+ return None
+
+ value = int(matches.group(1))
+ unit = matches.group(2)
+ delta: timedelta
+
+ if unit == "w":
+ delta = timedelta(weeks=value)
+ elif unit == "d":
+ delta = timedelta(days=value)
+ elif unit == "h":
+ delta = timedelta(hours=value)
+ elif unit == "m":
+ delta = timedelta(minutes=value)
+ else:
+ return None
+
+ return delta
+
+
+def invalid_usage(context_objs) -> list[str]:
+ context_objs.issue.create_comment(
+ dedent(
+ f"""
+ Invalid usage. Example usage:
+ - `/remind 2d`
+ - `/remind 1w revamp this`
+
+ {helpers.del_reply_comment()}
+ """
+ )
+ )
+ return ["added_issue_comment (invalid /remind args)"]
+
+
+def validate_reminder_command(split, context_objs) -> datetime | list[str]:
+ if len(split) < 2:
+ return invalid_usage(context_objs)
+
+ duration = parse_duration(split[1])
+ if not duration:
+ return invalid_usage(context_objs)
+
+ print(f"duration: {duration}")
+ print(f"now: {datetime.now()}")
+ print(f"result: {datetime.now() + duration}")
+
+ return datetime.now() + duration
diff --git a/.github/management_bot/pulumi/src/lambda_handler.py b/.github/management_bot/pulumi/webhook_handler/lambda_handler.py
similarity index 96%
rename from .github/management_bot/pulumi/src/lambda_handler.py
rename to .github/management_bot/pulumi/webhook_handler/lambda_handler.py
index 3d2f10326..5d6cdd59b 100644
--- a/.github/management_bot/pulumi/src/lambda_handler.py
+++ b/.github/management_bot/pulumi/webhook_handler/lambda_handler.py
@@ -65,12 +65,16 @@ def lambda_handler(event: dict, lambda_context):
req.add_header("X-Aws-Parameters-Secrets-Token", aws_session_token)
config = urllib.request.urlopen(req).read()
+ logger.info(f"Using stage: {stage}")
+
ssm_result: json = json.loads(config)
ssm_value: json = json.loads(ssm_result["Parameter"]["Value"])
PRIVATE_KEY = decode_private_key(ssm_value["private_key"])
APP_ID = ssm_value["app_id"]
+ logger.info(f"Using app id: {APP_ID}")
+
auth = Auth.AppAuth(APP_ID, PRIVATE_KEY)
gi = GithubIntegration(auth=auth)
g: Github = gi.get_installations()[0].get_github_for_installation()
@@ -79,6 +83,7 @@ def lambda_handler(event: dict, lambda_context):
context_dicts = _types.Context(
event=event,
+ lambda_context=lambda_context,
action=event.get("action", ""),
issue=_types.fill_dataclass_from_dict(_types.Issue, event.get("issue", {})),
pull_request=_types.fill_dataclass_from_dict(_types.PullRequest, event.get("pull_request", {})),
diff --git a/.github/management_bot/pulumi/webhook_handler/prs/__init__.py b/.github/management_bot/pulumi/webhook_handler/prs/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/.github/management_bot/pulumi/src/prs/actions.py b/.github/management_bot/pulumi/webhook_handler/prs/actions.py
similarity index 100%
rename from .github/management_bot/pulumi/src/prs/actions.py
rename to .github/management_bot/pulumi/webhook_handler/prs/actions.py
diff --git a/.github/management_bot/pulumi/src/prs/handler.py b/.github/management_bot/pulumi/webhook_handler/prs/handler.py
similarity index 100%
rename from .github/management_bot/pulumi/src/prs/handler.py
rename to .github/management_bot/pulumi/webhook_handler/prs/handler.py