Add code
This commit is contained in:
commit
0c3180d789
|
|
@ -0,0 +1 @@
|
||||||
|
.env
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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
|
||||||
Loading…
Reference in New Issue