Skip to content

Commit 52dab1e

Browse files
committed
feat: sending daily leetcode question to telegram channel via aws lambda
0 parents  commit 52dab1e

14 files changed

+938
-0
lines changed

.editorconfig

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
end_of_line = lf
7+
charset = utf-8
8+
trim_trailing_whitespace = true
9+
insert_final_newline = true
10+
11+
[*.py]
12+
indent_size = 4
13+
14+
[*.md]
15+
max_line_length = 0
16+
indent_size = 4
17+
trim_trailing_whitespace = false
18+
19+
[Makefile]
20+
indent_style = tab
21+

.env

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
TELEGRAM_TOKEN=XXXXXXXXXX:XXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXX
2+
TELEGRAM_CHAT_ID=XXXXXXXXXXXXXX

.github/workflows/release.yml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: ci
2+
on:
3+
push:
4+
tags:
5+
- 'v*'
6+
7+
jobs:
8+
release:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Checkout repo
12+
uses: actions/checkout@main
13+
- name: Setup Node.js version
14+
uses: actions/setup-node@main
15+
with:
16+
node-version: 14.x
17+
- name: Generate release body
18+
run: |
19+
npx rexreplace "(#+ \[\d+\.\d+\.\d+].*?)#+ \[?\d+\.\d+\.\d+]?" "_" -s -M -G -m -o "CHANGELOG.md" > RELEASE_BODY.md
20+
result=$(cat RELEASE_BODY.md)
21+
22+
if [[ $? != 0 ]]; then
23+
echo "Command failed."
24+
exit 1;
25+
elif [[ $result ]]; then
26+
echo "Release body generated."
27+
else
28+
echo "This is the first release, using different command to generate release body."
29+
npx rexreplace "(#+ \[?\d+\.\d+\.\d+]?.*)" "_" -s -M -G -m -o "CHANGELOG.md" > RELEASE_BODY.md
30+
fi
31+
- name: Create release
32+
id: create-release
33+
uses: actions/create-release@v1
34+
env:
35+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36+
with:
37+
tag_name: ${{ github.ref }}
38+
release_name: ${{ github.ref }}
39+
draft: false
40+
prerelease: false
41+
body_path: RELEASE_BODY.md

.gitignore

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# Runtime data
4+
*.seed
5+
*.log
6+
*.out
7+
*.pid
8+
*.swp
9+
*.tsbuildinfo
10+
11+
pids
12+
logs
13+
results
14+
tmp
15+
16+
__pycache__
17+
18+
# API keys and secrets
19+
.env.local
20+
21+
# Dependency directory
22+
node_modules
23+
24+
# Editors
25+
.idea
26+
*.iml
27+
.vscode
28+
29+
# OS metadata
30+
.DS_Store
31+
Thumbs.db
32+
33+
# Distribution / packaging
34+
.Python
35+
env/
36+
venv/
37+
build/
38+
develop-eggs/
39+
dist/
40+
downloads/
41+
eggs/
42+
.eggs/
43+
lib/
44+
lib64/
45+
parts/
46+
sdist/
47+
var/
48+
*.egg-info/
49+
.installed.cfg
50+
*.egg
51+
52+
# Serverless directories
53+
.serverless

.nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
14.17

.versionrc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"compareUrlFormat": "https://github.com/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}",
3+
"commitUrlFormat": "https://github.com/{{owner}}/{{repository}}/commit/{{hash}}",
4+
"issueUrlFormat": "https://github.com/{{owner}}/{{repository}}/issues/{{id}}"
5+
}

Makefile

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
.PHONY: create-venv clean-venv test-with-venv-local test-with-venv first-release release lint
2+
3+
INIT_ENVIRONMENT_VARIABLE_SCRIPT=./scripts/load-environment-variables.sh
4+
VENV_ACTIVATE_PATH=venv/bin/activate
5+
APP_FOLDER=app/
6+
7+
# Init new virtual environment
8+
create-venv:
9+
virtualenv venv --python=python3
10+
11+
# Remove existing virtual environment
12+
clean-venv:
13+
rm -rf venv
14+
15+
# Test with environment variables saved in .env.local or .env (if .env.local does not exist)
16+
test-with-venv:
17+
( \
18+
. ${VENV_ACTIVATE_PATH}; \
19+
pip install -r requirements.txt; \
20+
. ${INIT_ENVIRONMENT_VARIABLE_SCRIPT}; \
21+
serverless invoke local -f cron; \
22+
)
23+
24+
# Run pylint checking
25+
lint:
26+
( \
27+
. ${VENV_ACTIVATE_PATH}; \
28+
pip install -r requirements.txt; \
29+
pylint ${APP_FOLDER} \
30+
)
31+
32+
# Deploy to AWS lambda
33+
deploy:
34+
( \
35+
. ${INIT_ENVIRONMENT_VARIABLE_SCRIPT}; \
36+
serverless deploy --aws-profile serverless; \
37+
)
38+
39+
# Create the first release
40+
first-release:
41+
npx standard-version --first-release
42+
43+
# Create a new release
44+
release:
45+
npx standard-version

README.md

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Leetcode Daily Question Telegram Bot
2+
3+
> Fetch question of today from Leetcode's GraphQL API and send it to Telegram channel.
4+
5+
## Development Notes
6+
7+
### Tech Stack
8+
* Python 3
9+
* [Serverless@2.63.0](https://www.npmjs.com/package/serverless)
10+
* AWS Lambda
11+
* AWS CloudFormation - Used by Serverless when doing deployment
12+
* AWS CloudWatch - Automatically Setup by Serverless when doing deployment
13+
* AWS S3 - Used by Serverless when doing deployment
14+
15+
> **Note:**
16+
> Both Lambda, CloudWatch, and CloudFormation has free tier provided. However for AWS S3, it has only has free tier for limited time.
17+
>
18+
> To check the tier limit, head to [https://aws.amazon.com/free/](https://aws.amazon.com/free/). It is recommended to setup a billing alert by following the documentation on [https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/tracking-free-tier-usage.html](https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/tracking-free-tier-usage.html).
19+
20+
### Development Setup
21+
22+
#### Prerequisites
23+
* Node.js 14 installed
24+
* Python 3 installed
25+
* `virtualenv` installed
26+
27+
#### Prepare `serverless` environment
28+
29+
##### NPM
30+
31+
```bash
32+
npm install serverless@2.63.0 --global
33+
npm install
34+
```
35+
36+
##### Yarn
37+
38+
```bash
39+
yarn global add serverless@2.63.0
40+
yarn
41+
```
42+
43+
#### Setup Python Virtual Environments
44+
45+
```bash
46+
# Create a new virtual environment
47+
virtualenv venv --python=python3
48+
49+
# Activate virtual environment created
50+
. venv/bin/activate
51+
52+
# Install dependencies listed in requirements.txt
53+
pip install -r requirements.txt
54+
```
55+
56+
#### Setup Bot to send message
57+
58+
Save a copy of `.env` and name it as `.env.local`, place your Telegram bot token and Telegram Chat ID in the configuration file.
59+
60+
> **Note:**
61+
> Environment variables are used instead of `ConfigParser` provided by Python since [by default AWS Lambda will encrypt environment variables using KMS](https://docs.aws.amazon.com/whitepapers/latest/kms-best-practices/encrypting-lambda-environment-variables.html), and we don't want to store tokens using plain text.
62+
63+
##### 1. How to get a Telegram bot token
64+
65+
1. In Telegram, search for the user `@BotFather`.
66+
2. Use the command `\newbot` and choose a name and username for your bot.
67+
3. `@BotFather` will return you the token of the bot created. Remember to keep it safe!
68+
69+
##### 2. How to get a Telegram chat ID
70+
71+
1. Send a `/start` command to the telegram bot created in the previous step
72+
2. Visit `https://api.telegram.org/bot<BOT_TOKEN>/getUpdates`
73+
3. Look at the API response, `result[0]['message']['chat']['id']` should contains ID of the chat. Remember to copy the `-` prefix if exists.
74+
75+
##### 3. Test if Bot token and Chat ID is correct or not
76+
77+
Open Terminal, run the following command. You will need to replace `<BOT_TOKEN>` and `<CHAT_ID>` with the one you get in previous steps.
78+
79+
```bash
80+
curl -X POST "https://api.telegram.org/bot<BOT_TOKEN>/sendMessage" -d "chat_id=<CHAT_ID>&text=Hello World"
81+
```
82+
83+
#### Deployment
84+
85+
##### Create named AWS profile (for first time deployment)
86+
87+
1. Create new user on AWS, with "Programmatic access"
88+
2. Assign permission to user by "Attaching existing policies directly"
89+
* In the official Serverless blog, they have introduced [serverless-policy generator](https://github.com/dancrumb/generator-serverless-policy) to help you generate IAM policies.
90+
3. Configure new AWS credientials to use newly created user
91+
```bash
92+
aws configure --profile <PROFILE_NAME>
93+
```
94+
95+
##### Deploy to Lambda
96+
97+
```bash
98+
# Load environment variables
99+
. load-environment-variables.sh
100+
101+
# Deploy to AWS Lambda using serverless cli
102+
serverless deploy --aws-profile <PROFILE_NAME>
103+
```
104+
105+
#### Settings
106+
107+
##### Add / Edit bot messages
108+
All messages are stored in `app/messages.txt`, and will be randomly picked when handler is triggered. To update the message sets, simply edit and save the file. Re-deployment is needed for changes to take effect.
109+
110+
##### Add / Edit stickers
111+
All sticker IDs are stored in `app/stickers.txt`, and will be randomly picked when handler is triggered. To update the sticker sets, you will need to get the sticker ID by sending stickers to the created bot. Visit `https://api.telegram.org/bot<BOT_TOKEN>/getUpdates`, stickers ID can be found in the API reponse node `result[n]['message']['sticker']['file_id']`. Re-deployment is needed for changes to take effect.
112+
113+
##### Edit bot schedule
114+
115+
Open `serverless.yml`, edit `functions['cron']['schedule']`. Syntax reference: [https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents-expressions.html](https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents-expressions.html)
116+
117+
#### Test
118+
119+
##### Testing the handler
120+
121+
Run the following command inside virtual environment:
122+
```bash
123+
# Load environment variables
124+
. load-environment-variables.sh
125+
126+
# Execute handler
127+
serverless invoke local -f cron
128+
```
129+
130+
#### Others
131+
132+
##### Delete deployed Lambda function
133+
134+
```bash
135+
serverless remove --aws-profile <PROFILE_NAME>
136+
```

0 commit comments

Comments
 (0)