Skip to content

Commit

Permalink
Merge pull request #22 from jaARke/main
Browse files Browse the repository at this point in the history
Reduced cold start latency
  • Loading branch information
jaARke authored Sep 28, 2023
2 parents f6cec95 + faff44e commit 282b468
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 98 deletions.
10 changes: 4 additions & 6 deletions .env_sample
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# The following are lambda environment variables:
# The following variables should be added as GitHub secrets
APP_ID=DISCORD_APP_ID
PUBLIC_KEY=DISCORD_PUBLIC_KEY
BOT_TOKEN=DISCORD_BOT_TOKEN
NEWS_API_KEY=NEWS_API_KEY
FINNHUBB_API_KEY=FINNHUBB_API_KEY

# The following are GitHub secrets:
AWS_ACCESS_KEY_ID=AWS_ACCESS_KEY_ID # S3 bucket access key
AWS_SECRET_ACCESS_KEY=AWS_SECRET_ACCESS_KEY # S3 bucket secret key
AWS_SECRET_ACCESS_KEY=AWS_SECRET_ACCESS_KEY # S3 bucket secret key
NEWS_API_KEY=NEWS_API_KEY
FINNHUB_API_KEY=FINNHUB_API_KEY
29 changes: 19 additions & 10 deletions .github/workflows/awsLambda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
jobs:
upload_deps:
if: github.repository != 'jaARke/discord-lambda-py'
name: Upload Layer to AWS Lambda
name: Upload Dependencies
runs-on: ubuntu-latest

steps:
Expand All @@ -26,17 +26,25 @@ jobs:
with:
python-version: 3.9

- name: Zip it all up and upload to S3
- name: Upload Dependencies
env:
release_bucket: acestockbot
release_bucket_uri: s3://acestockbot
release_id: deps.zip
release_layer: ACEBot_Dependencies
function_name: ACEBot_handler
APP_ID: ${{ secrets.APP_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
run: |
# Package up the dependencies
mkdir -p python/lib/python3.9/site-packages
docker run -v "$PWD":/var/task "public.ecr.aws/sam/build-python3.9" /bin/sh -c "pip install -r requirements.txt -t python/lib/python3.9/site-packages/; exit"
zip --quiet -r $release_id python
PYTHONPATH=python/lib/python3.9/site-packages python -m discord_lambda.CommandRegistryPickler
zip --quiet -r $release_id python CommandRegistry.pickle
echo "Uploading $release_id to $release_bucket_uri"
aws s3 cp $release_id $release_bucket_uri
aws lambda publish-layer-version --layer-name $release_layer --content S3Bucket=$release_bucket,S3Key=$release_id --compatible-runtimes python3.9
Expand All @@ -51,11 +59,11 @@ jobs:
layer_arn=$(aws lambda list-layer-versions --layer-name $release_layer --query 'LayerVersions[0].LayerVersionArn' --output text)
# Update the lambda function to use the new layer
aws lambda update-function-configuration --function-name ACEBot_handler --layers $layer_arn
aws lambda update-function-configuration --function-name $function_name --layers $layer_arn
upload_source:
if: github.repository != 'jaARke/discord-lambda-py'
name: Upload Source to AWS Lambda
name: Upload Source
runs-on: ubuntu-latest

steps:
Expand All @@ -75,7 +83,8 @@ jobs:
aws_region: us-east-2
function_name: ACEBot_handler
zip_file: bundle.zip




environment:
APP_ID=${{ secrets.APP_ID }},
PUBLIC_KEY=${{ secrets.PUBLIC_KEY }},
NEWS_API_KEY=${{ secrets.NEWS_API_KEY }},
FINNHUB_API_KEY=${{ secrets.FINNHUB_API_KEY }}
28 changes: 3 additions & 25 deletions .github/workflows/syncToTemplate.yml
Original file line number Diff line number Diff line change
@@ -1,51 +1,34 @@
---
# Credit : https://github.com/solvaholic/template

# - Run this workflow to pull changes from the template repository. Does the following:
# - Clone this repository. Check out a branch
# - Copy files from the template onto this clone
# - Push the branch to this repository
# - Create a pull request in this repository

name: Sync changes from template
name: Sync to Template
on:
# Run at 0517 UTC each Friday
schedule:
- cron: "17 5 * * 5"

# Run when this file changes
push:
paths:
- .github/workflows/syncToTemplate.yml

# Run when manually triggered by...
workflow_dispatch:

env:
BASE_BRANCH: main
HEAD_BRANCH: chore/sync-from-template
HEAD_BRANCH: chore/sync-to-template
GIT_AUTHOR_NAME: ${{ github.repository_owner }}
GIT_AUTHOR_EMAIL: ${{ github.repository_owner }}@users.noreply.github.com
REPO_TEMPLATE: jaARke/discord-lambda-py
THIS_REPO: ${{ github.repository }}

jobs:
sync-from-template:
# Do not run on the template repository itself
if: github.repository != 'jaARke/discord-lambda-py'
name: Sync changes from jaARke/discord-lambda-py
runs-on: ubuntu-latest
continue-on-error: true

steps:
# Clone the template repository
- name: Check out template repository
uses: actions/checkout@v2
with:
repository: ${{ env.REPO_TEMPLATE }}
token: ${{ github.token }}
path: ${{ env.REPO_TEMPLATE }}

# Clone the target repository. Check out a branch
- name: Check out ${{ github.repository }}
uses: actions/checkout@v2
with:
Expand All @@ -60,7 +43,6 @@ jobs:
"remotes/origin/${HEAD_BRANCH}" || \
git -C "${THIS_REPO}" checkout -b "${HEAD_BRANCH}"
# Copy files from the template onto the target clone
- name: Copy template contents
run: |
_files="$(find ${REPO_TEMPLATE} \
Expand All @@ -84,9 +66,7 @@ jobs:
done
git -C "${THIS_REPO}" diff
# Commit changes, if there are any
- name: Commit changes, if any
if: steps.verify_diff.outputs.changed == 'true'
run: |
git -C ${THIS_REPO} config user.name "${GIT_AUTHOR_NAME}"
git -C ${THIS_REPO} config \
Expand All @@ -95,11 +75,9 @@ jobs:
git -C ${THIS_REPO} commit \
-m "Sync from template@${{ github.sha }}"
# Push the branch to the target repository
- name: Push topic branch
run: git -C ${THIS_REPO} push -u origin "${HEAD_BRANCH}"

# Create a pull request in the target repository
- name: Create pull request
env:
GITHUB_TOKEN: ${{ github.token }}
Expand Down
4 changes: 2 additions & 2 deletions commands/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ def help(inter: Interaction, command: str = "help") -> None:
embed = Embedding("Sentiment Analysis", "Collects data, performs sentiment analysis on a company based on news and social media posts.", color=0x00FF00)
embed.add_field("Usage", "`/sentiment <type> <company> <interval>`", False)
embed.add_field("Parameters",
"`type`: can be \"data\" or \"analyze\" to choose between (only) collecting data or running an analysis\n" \
"`type`: can be \"collect\" or \"analyze\" to choose between (only) collecting data or running an analysis\n" \
"`company`: the company to analyze; can be a company name or ticker symbol\n" \
"`interval`: the timespan (days into the past) to collect data from; must be in [1, 30]; default is 7 days\n",
False)
embed.add_field("Examples",
"`/sentiment data Apple`\n" \
"`/sentiment collect Apple`\n" \
"`/sentiment analyze AAPL 30`\n",
False)

Expand Down
4 changes: 2 additions & 2 deletions commands/sentiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ def analyze_helper(embed: Embedding, headlines: list[dict]) -> None:

# Add headline samples and results
headlines_samples = ""
for i, headline in enumerate(headlines[:5]):
for i, headline in enumerate(headlines[:3]):
headline['title'] = headline['title'].replace("\n", " ")
headlines_samples += f"{i+1}. [{headline['title']}]({headline['link']})\n"

headlines_sentiment = ""
for i, headline in enumerate(headlines[:5]):
for i, headline in enumerate(headlines[:3]):
headlines_sentiment += f"{i+1}. {round(headline['score'], 2)} - {headline['sentiment']}\n"
headlines_sentiment += f"**Average:** {round(headlines_avg, 2)}"

Expand Down
8 changes: 8 additions & 0 deletions discord_lambda/CommandRegistryPickler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import os
import pickle
from .CommandRegistry import CommandRegistry

registry = CommandRegistry(command_dir="commands", app_id=os.environ.get('APP_ID'), bot_token=os.environ.get('BOT_TOKEN'))

with open("CommandRegistry.pickle", "wb") as f:
pickle.dump(registry, f, protocol=pickle.HIGHEST_PROTOCOL)
14 changes: 14 additions & 0 deletions discord_lambda/Interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@ def __create_channel_message(self, content: str = None, embeds: list[Embedding]
return response


def ping_response(self) -> None:
try:
requests.post(self.callback_url, json={"type": 1}).raise_for_status()
except Exception as e:
raise Exception(f"Unable to send ping response: {e}")


def defer(self, ephemeral: bool = True) -> None:
try:
requests.post(self.callback_url, json={"type": 5, "data": {"flags": 1 << 6 if ephemeral else None}}).raise_for_status()
except Exception as e:
raise Exception(f"Unable to defer response: {e}")


def send_response(self, content: str = None, embeds: list[Embedding] = None, ephemeral: bool = True) -> None:
try:
requests.patch(self.webhook_url, json=self.__create_channel_message(content, embeds, ephemeral)).raise_for_status()
Expand Down
44 changes: 0 additions & 44 deletions discord_lambda/InteractionHandler.py

This file was deleted.

1 change: 0 additions & 1 deletion discord_lambda/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from .CommandArg import CommandArg
from .InteractionHandler import InteractionHandler
from .Interaction import Interaction, Embedding
from .CommandRegistry import CommandRegistry
35 changes: 32 additions & 3 deletions lambda_function.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
import os
from discord_lambda import InteractionHandler
from nacl.signing import VerifyKey
from discord_lambda import Interaction, Embedding
import pickle


def verify_signature(event: dict) -> None:
raw_body = event.get("rawBody")
auth_sig = event['params']['header'].get('x-signature-ed25519')
auth_ts = event['params']['header'].get('x-signature-timestamp')

message = auth_ts.encode() + raw_body.encode()
verify_key = VerifyKey(bytes.fromhex(os.environ.get('PUBLIC_KEY')))
verify_key.verify(message, bytes.fromhex(auth_sig))

handler = InteractionHandler(command_dir="commands", app_id=os.environ.get("APP_ID"), public_key=os.environ.get("PUBLIC_KEY"), bot_token=os.environ.get("BOT_TOKEN"))

def lambda_handler(event, context):
handler.handle(event)
try:
verify_signature(event)
except Exception as e:
# Return a 401 Unauthorized response
raise Exception(f"[UNAUTHORIZED] Invalid request signature: {e}")

interaction = Interaction(event.get("body-json"), os.environ.get('APP_ID'))

if interaction.type == 1:
interaction.ping_response()

elif interaction.type == 2:
interaction.defer(ephemeral=True)

registry = pickle.load(open("/opt/CommandRegistry.pickle", "rb"))
try:
func, args = registry.find_func(interaction.data)
func(interaction, **args)
except Exception as e:
interaction.send_response(embeds=[Embedding(":x: Error", f"The request could not be completed:\n`{e}`", color=0xFF0000)])
17 changes: 12 additions & 5 deletions utils/sentiment/analysis.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import nltk
from nltk.sentiment.vader import SentimentIntensityAnalyzer

nltk.data.path.append("/tmp")
nltk.download("vader_lexicon", download_dir="/tmp")
analyzer = SentimentIntensityAnalyzer()

def setup_vader() -> SentimentIntensityAnalyzer:
nltk.data.path.append("/tmp")
try:
nltk.data.find("vader_lexicon")
except LookupError:
nltk.download("vader_lexicon", download_dir="/tmp")
analyzer = SentimentIntensityAnalyzer()
return analyzer

def analyze_sentence(text: str) -> float:

def analyze_sentence(text: str, analyzer: SentimentIntensityAnalyzer) -> float:
return analyzer.polarity_scores(text)["compound"]


def analyze_data(data: list[dict]) -> None:
analyzer = setup_vader()
avg = 0
for x in data:
score = analyze_sentence(x["title"])
score = analyze_sentence(x["title"], analyzer)
x["score"] = score
x["sentiment"] = "positive" if score > 0 else "negative" if score < 0 else "neutral"
avg += score
Expand Down

0 comments on commit 282b468

Please sign in to comment.