-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit aa51e9e
Showing
12 changed files
with
868 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"deno.enablePaths": [ | ||
"./" | ||
], | ||
"deno.enable": true, | ||
"editor.inlayHints.enabled": "off" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Telegram Upload API | ||
|
||
**Effortlessly upload and fetch files to Telegram 🚀** | ||
|
||
--- | ||
|
||
1. **Clone The Repository** | ||
|
||
```sh | ||
git clone https://github.com/TeaByte/telegram-upload-api | ||
cd telegram-upload-api | ||
``` | ||
|
||
2. **Setup Localhost DenvKV** | ||
|
||
Use the following Docker command to mounts a local folder into the container to store the database, and it hosts a denoKV Connect endpoint at `http://localhost:4512` with an access of `234xs266623t`. | ||
|
||
```sh | ||
docker run -it --init -p 4512:4512 -v ./data:/data ghcr.io/denoland/denokv --sqlite-path /data/denokv.sqlite serve --access-token 234xs266623t | ||
``` | ||
|
||
3. **Edit The config.json File** | ||
|
||
In the config.json file, update the following fields with your Telegram credentials and bot information: | ||
|
||
```json | ||
{ | ||
"apiId": "your_api_id", | ||
"apiHash": "your_api_hash", | ||
"chatId": -1002036530000, | ||
"botToken": "your_bot_token", | ||
"serverPort": 8080, | ||
"denoKV": "http://localhost:4512/", | ||
"kvToken": "234xs266623t" | ||
} | ||
``` | ||
|
||
- Replace `your_api_id` and `your_api_hash` with your Telegram credentials from https://my.telegram.org/auth. | ||
- Obtain a bot token from [@BotFather](https://t.me/BotFather) and replace `your_bot_token`. | ||
- `chatId` is the ID of a Telegram group where files will be saved. | ||
|
||
4. **Start The Server** | ||
|
||
```sh | ||
deno task start | ||
``` | ||
|
||
5. **Test The Server Endpoints** | ||
|
||
```sh | ||
deno task test | ||
``` | ||
|
||
--- | ||
|
||
Feel free to contribute by opening issues or submitting pull requests. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"apiId": 1, | ||
"apiHash": 1, | ||
"chatId": -1002036530000, | ||
"botToken": "your_bot_token", | ||
"serverPort": 8080, | ||
"denoKV": "http://localhost:4512/", | ||
"kvToken": "234xs266623t" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
const config = JSON.parse(Deno.readTextFileSync("./config.json")); | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/// <reference lib="deno.unstable" /> | ||
import { cuid } from "https://deno.land/x/cuid@v1.0.0/index.js"; | ||
import config from "./config.ts"; | ||
|
||
type recordId = string; | ||
interface dbRecord { | ||
fileName: string; | ||
fileId: string; | ||
fileSize: number; | ||
} | ||
|
||
Deno.env.set("DENO_KV_ACCESS_TOKEN", config["kvToken"]); | ||
const kv = await Deno.openKv(config["denoKV"]); | ||
|
||
export async function set( | ||
fileName: string, | ||
fileId: string, | ||
fileSize: number | ||
): Promise<recordId> { | ||
const id = cuid(); | ||
await kv.set(["uploads", id], { | ||
fileName, | ||
fileId, | ||
fileSize, | ||
}); | ||
return id; | ||
} | ||
|
||
export async function get(id: recordId): Promise<dbRecord> { | ||
return (await kv.get(["uploads", id])).value as dbRecord; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"tasks": { | ||
"start": "deno run -A --unstable server.ts", | ||
"dev": "deno run -A --watch --unstable server.ts", | ||
"test": "deno test tests.ts -A" | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { Hono, Context } from "https://deno.land/x/hono@v4.0.8/mod.ts"; | ||
|
||
import * as db from "./database.ts"; | ||
import { getFromTelegram, uploadToTelegram } from "./telegram.ts"; | ||
import { saveToSystem, deleteFromSystem } from "./system.ts"; | ||
import config from "./config.ts"; | ||
|
||
const app = new Hono(); | ||
|
||
app.post("/upload", async (c: Context) => { | ||
const body = await c.req.parseBody(); | ||
const file = body["file"]; | ||
if (file instanceof File) { | ||
try { | ||
if (file.size === 0 || file.size > 2000000000) { | ||
return c.json( | ||
{ | ||
message: "File size is too large or empty", | ||
}, | ||
400 | ||
); | ||
} | ||
const savedPath = await saveToSystem( | ||
new Uint8Array(await file.arrayBuffer()), | ||
file.name | ||
); | ||
const fileId = await uploadToTelegram(savedPath); | ||
await deleteFromSystem(savedPath); | ||
const recordId = await db.set(file.name, fileId, file.size); | ||
return c.json( | ||
{ | ||
message: "File uploaded successfully", | ||
recordId, | ||
}, | ||
200 | ||
); | ||
} catch (error) { | ||
console.error(error); | ||
return c.json( | ||
{ | ||
message: "Failed to upload the file", | ||
}, | ||
500 | ||
); | ||
} | ||
} else { | ||
return c.json( | ||
{ | ||
message: "Missing or invalid file", | ||
}, | ||
400 | ||
); | ||
} | ||
}); | ||
|
||
app.post("/get", async (c: Context) => { | ||
const body = await c.req.json(); | ||
const recordId = body["recordId"]; | ||
if (recordId) { | ||
try { | ||
const dbData = await db.get(recordId); | ||
if (!dbData) { | ||
return c.json( | ||
{ | ||
message: "File not found", | ||
}, | ||
404 | ||
); | ||
} | ||
const path = await getFromTelegram(dbData["fileId"], dbData["fileName"]); | ||
const file = await Deno.readFile(path); | ||
await deleteFromSystem(path); | ||
return c.body(file.buffer, 200, { | ||
"Content-Type": "application/octet-stream", | ||
"Content-Disposition": `attachment; filename="${dbData["fileName"]}"`, | ||
}); | ||
} catch (error) { | ||
console.error(error); | ||
return c.json( | ||
{ | ||
message: "Failed to download the file", | ||
}, | ||
500 | ||
); | ||
} | ||
} else { | ||
return c.json( | ||
{ | ||
message: "File ID is required", | ||
}, | ||
400 | ||
); | ||
} | ||
}); | ||
|
||
Deno.serve({ port: config["serverPort"] }, app.fetch); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
export async function saveToSystem( | ||
buffer: Uint8Array, | ||
fileName: string | ||
): Promise<string> { | ||
const path = `./temp/${fileName}`; | ||
const file = await Deno.open(path, { | ||
write: true, | ||
create: true, | ||
truncate: true, | ||
}); | ||
await file.write(buffer); | ||
await file.close(); | ||
return path; | ||
} | ||
|
||
export async function deleteFromSystem(path: string) { | ||
await Deno.remove(path); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { | ||
Client, | ||
StorageLocalStorage, | ||
} from "https://deno.land/x/mtkruto@0.1.155/mod.ts"; | ||
|
||
import config from "./config.ts"; | ||
|
||
export const client = new Client( | ||
new StorageLocalStorage("client"), | ||
config["apiId"], | ||
config["apiHash"] | ||
); | ||
|
||
await client | ||
.start(config["botToken"]) | ||
.catch((error) => console.error("Failed to start client", error)); | ||
|
||
try { | ||
const me = await client.getMe(); | ||
console.log(me); | ||
} catch { | ||
console.error( | ||
"Failed to get me make sure the bot token is valid and apiId and apiHash are correct" | ||
); | ||
} | ||
|
||
export async function uploadToTelegram(path: string) { | ||
const file = await client.sendDocument(config["chatId"], path); | ||
return file.document.fileId; | ||
} | ||
|
||
export async function getFromTelegram(fileId: string, fileName: string) { | ||
const path = `./temp/${fileName}`; | ||
const outFile = await Deno.open(path, { | ||
write: true, | ||
create: true, | ||
truncate: true, | ||
}); | ||
try { | ||
for await (const chunk of client.download(fileId, { | ||
chunkSize: 256 * 1024, | ||
})) { | ||
await Deno.write(outFile.rid, chunk); | ||
} | ||
} finally { | ||
await Deno.close(outFile.rid); | ||
} | ||
return path; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import requests | ||
|
||
|
||
def upload(): | ||
r = requests.post("http://localhost:8000/upload", | ||
files={"file": open("deno.json", "rb")}) | ||
|
||
print(r.text) | ||
|
||
|
||
def get(): | ||
r = requests.post("http://localhost:8000/get", json={ | ||
"recordId": "clta660bb0000s0ug07zqm5nq"}) | ||
|
||
print(r.text) | ||
print(r.status_code) | ||
print(r.headers) | ||
|
||
|
||
if __name__ == "__main__": | ||
get() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { assertEquals } from "https://deno.land/std@0.218.0/assert/mod.ts"; | ||
import axiod from "https://deno.land/x/axiod@0.26.0/mod.ts"; | ||
import config from "./config.ts"; | ||
|
||
const port = config["serverPort"]; | ||
|
||
Deno.test("Upload Test", async () => { | ||
const formData = new FormData(); | ||
formData.append("file", new File(["Hello, World!"], "test.txt")); | ||
const uploadResponse = await axiod.post( | ||
`http://localhost:${port}/upload`, | ||
formData | ||
); | ||
|
||
const result = await uploadResponse.data; | ||
|
||
assertEquals(result["message"], "File uploaded successfully"); | ||
assertEquals(uploadResponse.status, 200); | ||
console.log("Upload Response: ", result); | ||
|
||
const getResponse = await axiod.post(`http://localhost:${port}/get`, { | ||
recordId: result["recordId"], | ||
}); | ||
|
||
assertEquals(getResponse.data, "Hello, World!"); | ||
assertEquals(getResponse.status, 200); | ||
console.log(getResponse.data); | ||
}); | ||
|
||
Deno.test("Get Non-Existent File Test", async () => { | ||
await axiod | ||
.post(`http://localhost:${port}/get`, { | ||
recordId: "i-don't-exist", | ||
}) | ||
.catch((e) => { | ||
assertEquals(e.response.data["message"], "File not found"); | ||
assertEquals(e.response.status, 404); | ||
console.log(e.response.data); | ||
}); | ||
}); | ||
|
||
Deno.test("Upload Empty File Test", async () => { | ||
const formData = new FormData(); | ||
formData.append("file", new File([""], "test.txt")); | ||
await axiod.post(`http://localhost:${port}/upload`, formData).catch((e) => { | ||
assertEquals(e.response.data["message"], "File size is too large or empty"); | ||
assertEquals(e.response.status, 400); | ||
console.log(e.response.data); | ||
}); | ||
}); |