Skip to content

Commit c5d3753

Browse files
author
Iurii
committed
init
0 parents  commit c5d3753

File tree

12 files changed

+681
-0
lines changed

12 files changed

+681
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.env

README.md

Whitespace-only changes.

glitch.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"install": "pip3 install --user -r requirements.txt",
3+
"start": "uvicorn main:app",
4+
"watch": {
5+
"ignore": [
6+
"\\.pyc$"
7+
],
8+
"install": {
9+
"include": [
10+
"^requirements\\.txt$"
11+
]
12+
},
13+
"restart": {
14+
"include": [
15+
"\\.py$",
16+
"^start\\.sh"
17+
]
18+
},
19+
"throttle": 1000
20+
}
21+
}

main.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from fastapi import FastAPI, Request
2+
from fastapi.responses import HTMLResponse
3+
from fastapi.staticfiles import StaticFiles
4+
from fastapi.templating import Jinja2Templates
5+
from pymongo import MongoClient
6+
import json
7+
8+
import os
9+
from dotenv import load_dotenv
10+
load_dotenv('.env')
11+
12+
from src.models import load_data
13+
from src.bot import Bot, process_message
14+
15+
# === GLOBAL OBJECTS ===
16+
client = MongoClient(os.environ.get('MONGODB_URI'))
17+
db = client.streak
18+
bot = Bot(token=os.environ['TELEGRAM_TOKEN'])
19+
templates = Jinja2Templates(directory="templates")
20+
app = FastAPI()
21+
app.mount("/static", StaticFiles(directory="static", html=True), name="static")
22+
23+
24+
@app.get("/", response_class=HTMLResponse)
25+
async def read_index(request: Request):
26+
return templates.TemplateResponse(
27+
"index.html", {
28+
"participants": load_data(db.submissions.find()),
29+
"request": request,
30+
}
31+
)
32+
33+
34+
@app.post("/hook")
35+
async def hook(request: Request):
36+
process_message(bot, db, json.loads(await request.body()))
37+
return "OK"

requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
uvicorn
2+
fastapi
3+
jinja2
4+
pymongo
5+
python-dotenv

src/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__init__.py

src/bot.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from datetime import datetime
2+
from pydantic import BaseModel
3+
import requests
4+
from src.common import submission_link
5+
6+
7+
LEETCODE_DAILY_GROUP_ID = '-1002270137956'
8+
9+
10+
class Bot(BaseModel):
11+
token: str
12+
13+
def api_url(self):
14+
return f"https://api.telegram.org/bot{self.token}/"
15+
16+
def send_message(self, chat_id, message, useV2=False):
17+
if chat_id is None or message is None:
18+
return False
19+
20+
params = {
21+
'chat_id': chat_id,
22+
'text': message,
23+
'disable_web_page_preview': True,
24+
}
25+
if useV2:
26+
params['parse_mode'] = 'MarkdownV2'
27+
res = requests.post(self.api_url() + "sendMessage", data=params).json()
28+
print(res)
29+
30+
if res is None or 'result' not in res or 'message_id' not in res['result']:
31+
return None
32+
return res['result']['message_id']
33+
34+
35+
def process_message(bot, db, data):
36+
try:
37+
message = data['message']
38+
chat_id = message['chat']['id']
39+
text = message['text']
40+
today = datetime.today().strftime('%Y-%m-%d')
41+
42+
username = message['from']['username'] if 'username' in message['from'] else '😊'
43+
first_name = message['from']['first_name'] if 'first_name' in message['from'] else '😊'
44+
name = f'{first_name} ({username})'
45+
46+
if text.startswith('/start') or text.startswith('/help'):
47+
bot.send_message(chat_id, "Please, send me a submission id for today's daily challenge")
48+
elif text.isnumeric():
49+
submission_id = text
50+
51+
if submission_id.isnumeric():
52+
print(f"Will update submissions for {username} on {today}: {text}")
53+
result = db.submissions.update_one(
54+
{'username': name, 'date': today, 'chat_id': chat_id},
55+
{'$set': {'text': text}},
56+
True,
57+
)
58+
59+
print(username, today, text, result)
60+
bot.send_message(chat_id, f"Updated submission for {today}: {submission_link(text)}")
61+
bot.send_message(LEETCODE_DAILY_GROUP_ID, f"New submission from {name}: {submission_link(text)}")
62+
else:
63+
bot.send_message(chat_id, "Incorrect input! please send me a submission id")
64+
raise ValueError(f"Incorrect link {data}")
65+
66+
except Exception as e:
67+
print("Oops!")
68+
print(e)

src/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def submission_link(submission_id):
2+
return f'https://leetcode.com/submissions/detail/{submission_id}'

src/models.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
from datetime import datetime, timedelta
2+
from pydantic import BaseModel
3+
from typing import List
4+
from collections import defaultdict
5+
from src.common import submission_link
6+
from random import randint
7+
8+
9+
10+
START_DATE = datetime(2024, 9, 16)
11+
12+
13+
class DisplaySubmission(BaseModel):
14+
level: int
15+
url: str
16+
text: str
17+
18+
19+
class Participant(BaseModel):
20+
name: str
21+
reputation: int
22+
submissions: List[DisplaySubmission]
23+
24+
25+
def load_dummy_data():
26+
return [
27+
Participant(
28+
name="Bob Marley",
29+
reputation=55,
30+
submissions=[
31+
# week 1
32+
DisplaySubmission(level=4, text="Submission", url=""),
33+
DisplaySubmission(level=4, text="Submission", url=""),
34+
DisplaySubmission(level=1, text="Submission", url=""),
35+
DisplaySubmission(level=1, text="Submission", url=""),
36+
DisplaySubmission(level=0, text="Submission", url=""),
37+
DisplaySubmission(level=0, text="Submission", url=""),
38+
DisplaySubmission(level=0, text="Submission", url=""),
39+
40+
# week 2
41+
DisplaySubmission(level=2, text="Submission", url=""),
42+
DisplaySubmission(level=2, text="Submission", url=""),
43+
DisplaySubmission(level=1, text="Submission", url=""),
44+
DisplaySubmission(level=5, text="Submission", url=""),
45+
DisplaySubmission(level=3, text="Submission", url=""),
46+
DisplaySubmission(level=4, text="Submission", url=""),
47+
DisplaySubmission(level=0, text="Submission", url=""),
48+
49+
],
50+
).dict(),
51+
52+
Participant(
53+
name="John McClane",
54+
reputation=45,
55+
submissions=[
56+
# week 1
57+
DisplaySubmission(level=4, text="Submission", url=""),
58+
DisplaySubmission(level=4, text="Submission", url=""),
59+
DisplaySubmission(level=1, text="Submission", url=""),
60+
DisplaySubmission(level=1, text="Submission", url=""),
61+
DisplaySubmission(level=0, text="Submission", url=""),
62+
DisplaySubmission(level=0, text="Submission", url=""),
63+
DisplaySubmission(level=0, text="Submission", url=""),
64+
65+
# week 2
66+
DisplaySubmission(level=2, text="Submission", url=""),
67+
DisplaySubmission(level=2, text="Submission", url=""),
68+
DisplaySubmission(level=1, text="Submission", url=""),
69+
DisplaySubmission(level=5, text="Submission", url=""),
70+
DisplaySubmission(level=3, text="Submission", url=""),
71+
DisplaySubmission(level=4, text="Submission", url=""),
72+
DisplaySubmission(level=0, text="Submission", url=""),
73+
74+
],
75+
).dict(),
76+
]
77+
78+
79+
def load_data_from_cursor(cursor):
80+
data = defaultdict(lambda: defaultdict(str))
81+
users = defaultdict(str)
82+
for record in cursor:
83+
k = record['chat_id'] if 'chat_id' in record else record['username']
84+
if 'username' in record:
85+
users[k] = record['username']
86+
data[k][record['date']] = record['text']
87+
88+
89+
start_date = START_DATE
90+
end_date = datetime.today()
91+
92+
result = []
93+
days_of_week = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
94+
for k, p in data.items():
95+
current_date = start_date
96+
submissions = []
97+
while current_date <= end_date:
98+
d = current_date.strftime("%Y-%m-%d")
99+
display_date = days_of_week[current_date.weekday()] + ", " + d
100+
if d in p:
101+
submissions.append(DisplaySubmission(level=4, text=display_date, url=submission_link(p[d])))
102+
else:
103+
submissions.append(DisplaySubmission(level=0, text=display_date, url=''))
104+
current_date += timedelta(days=1)
105+
106+
result.append(Participant(name=users[k].split()[0], reputation=len(p), submissions=submissions).dict())
107+
108+
result.sort(key=lambda p: (p['reputation'], randint(0, 100)), reverse=True)
109+
return result
110+
111+
def load_data(cursor=None, dummy=False):
112+
return load_dummy_data() if dummy else load_data_from_cursor(cursor)

0 commit comments

Comments
 (0)