This commit is contained in:
Timo Schmidt 2024-06-11 23:31:42 +02:00
commit 0c3180d789
4 changed files with 187 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.env

137
main.py Normal file
View File

@ -0,0 +1,137 @@
import os
import time
import json
import httpx
import requests
import pandas as pd
from fastapi import FastAPI, Request
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.environ.get('BOT_TOKEN')
BASE_URL = f"https://api.telegram.org/bot{TOKEN}"
BASE_FILE_URL = f"https://api.telegram.org/file/bot{TOKEN}"
client = httpx.AsyncClient()
app = FastAPI()
async def send(text: str, chat_id: int) -> None:
await client.get(f"{BASE_URL}/sendMessage?chat_id={chat_id}&text={text}")
async def extract_cmd(text: str, chat_id: int) -> tuple[str, str]:
orig_text = text
cmd, *text = text.strip().split(maxsplit=1)
if cmd[0] != '/':
await send(f'Invalid command: {orig_text}', chat_id)
return 'error', ''
cmd = cmd[1:]
if cmd == 'bulk':
if not text:
await send('bulk command requires argument', chat_id)
return 'error', ''
return cmd, text
elif cmd == 'test':
if not text:
await send('test command requires argument', chat_id)
return 'error', ''
return cmd, text
else:
await send(f'Unknown command: {cmd}', chat_id)
return 'error', ''
async def handle_bulk(arg: str, chat_id: int) -> None:
await send('BULK HANDLED', chat_id)
async def handle_test(arg: str, chat_id: int) -> None:
await send('TEST HANDLED', chat_id)
async def handle_cmd(text: str, chat_id: int) -> None:
cmd, arg = await extract_cmd(text, chat_id)
if cmd == 'error':
pass
elif cmd == 'bulk':
await handle_bulk(arg, chat_id)
elif cmd == 'test':
await handle_test(arg, chat_id)
else:
await send(f"Command {cmd} not implemented.", chat_id)
def is_valid_phonenumber(phone: str) -> bool:
phone = phone.strip()
if phone[0] == '+':
phone = phone[1:]
if not phone.startswith('998'):
return False
if len(phone) != 12:
return False
if not phone.isascii() or not phone.isdecimal():
return False
return True
async def phone_already_in_db(users: dict, phone: str, name: str, chat_id: int) -> bool:
for user in users:
if user['phone'] == phone:
if user['name'] != name:
await send(f'Phone {phone} already has user {user["name"]} associated with it. Cannot overwrite with new user {name}.', chat_id)
return True
return False
async def import_document(document: dict, chat_id: int) -> None:
file_name = document['file_name']
file_id = document['file_id']
resp = requests.get(f'{BASE_URL}/getFile?file_id={file_id}')
if resp.status_code != 200:
await send(f"Couldn't save file: {file_name}", chat_id)
return
file_meta = resp.json()
file_path = file_meta['result']['file_path']
resp = requests.get(f'{BASE_FILE_URL}/{file_path}')
local_file_path = f'tmpfile_{int(time.time())}.xlsx'
with open(local_file_path, 'wb') as xlsx:
xlsx.write(resp.content)
await send('File saved successfully', chat_id)
try:
workbook = pd.read_excel(local_file_path)
except Exception as e:
await send(f"Couldn't open file: {file_name}", chat_id)
return
try:
with open('users.json', encoding='utf-8') as users_file:
users = json.load(users_file)
except FileNotFoundError:
users = []
for index, row in workbook.iterrows():
name = str(row[0]).strip()
phone = str(row[1]).strip()
if not is_valid_phonenumber(phone):
await send(f'User {name} has invalid phone number: {phone}.', chat_id)
continue
if await phone_already_in_db(users, phone, name, chat_id):
continue
users.append({'name': name, 'phone': phone})
os.rename('users.json', 'users.bak.json')
with open('users.json', 'w', encoding='utf-8') as users_file:
json.dump(users, users_file, indent=2, ensure_ascii=False)
@app.post("/webhook/")
async def webhook(req: Request):
data = await req.json()
try:
chat_id = data['message']['chat']['id']
if data['message'].get('document') is not None:
await import_document(data['message']['document'], chat_id)
elif data['message'].get('text') is not None:
text = data['message']['text']
await handle_cmd(text, chat_id)
else:
await send('Unknown message type.', chat_id)
except KeyError as e:
print(e)
print(data)

47
requirements.txt Normal file
View File

@ -0,0 +1,47 @@
annotated-types==0.7.0
anyio==4.4.0
certifi==2024.6.2
charset-normalizer==3.3.2
click==8.1.7
dnspython==2.6.1
email_validator==2.1.1
et-xmlfile==1.1.0
exceptiongroup==1.2.1
fastapi==0.111.0
fastapi-cli==0.0.4
h11==0.14.0
httpcore==1.0.5
httptools==0.6.1
httpx==0.27.0
idna==3.7
Jinja2==3.1.4
markdown-it-py==3.0.0
MarkupSafe==2.1.5
mdurl==0.1.2
numpy==1.26.4
openpyxl==3.1.3
orjson==3.10.4
pandas==2.2.2
pydantic==2.7.3
pydantic_core==2.18.4
Pygments==2.18.0
python-dateutil==2.9.0.post0
python-dotenv==1.0.1
python-multipart==0.0.9
pytz==2024.1
PyYAML==6.0.1
requests==2.32.3
rich==13.7.1
shellingham==1.5.4
six==1.16.0
sniffio==1.3.1
starlette==0.37.2
typer==0.12.3
typing_extensions==4.12.2
tzdata==2024.1
ujson==5.10.0
urllib3==2.2.1
uvicorn==0.30.1
uvloop==0.19.0
watchfiles==0.22.0
websockets==12.0

2
run.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
fastapi dev --port 8080 main.py