master
icechen 2022-02-18 20:03:12 +08:00
parent 65ac000169
commit 78b2b4b753
13 changed files with 17466 additions and 157 deletions

17128
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,10 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@mui/icons-material": "^5.4.2",
"@mui/material": "^5.4.2",
"@testing-library/jest-dom": "^5.16.2", "@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.2", "@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
@ -40,6 +44,5 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
}, }
"devDependencies": {}
} }

View File

@ -39,5 +39,13 @@
To begin the development, run `npm start` or `yarn start`. To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`. To create a production bundle, use `npm run build` or `yarn build`.
--> -->
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>
</body> </body>
</html> </html>

View File

@ -1,38 +0,0 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@ -1,9 +0,0 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -1,70 +1,167 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import "./App.css"; import "./App.css";
import Client from "mtproton/envs/browser"; import {
Button,
Container,
Input,
InputAdornment,
Paper,
TextField,
} from "@mui/material";
import CountriesInput, { Country } from "./component/countriesInput";
import Telegram, { uploadBigFile } from "./telegram";
import SendIcon from "@mui/icons-material/Send";
import { User } from "./user";
import { Upload } from "@mui/icons-material";
import ab2str from "./utils/arraybuffer2str";
function App() { function App() {
let [countriesList, setcountriesList] = useState([] as any[]); let [phone, setPhone] = useState("");
let phone = React.createRef<HTMLInputElement>(); let [code, setCode] = useState("");
let [phoneCodeHash, setPhoneCodeHash] = useState("");
useEffect(() => { let [country, setCountry] = useState({} as Country | null);
let client = new Client({ let [user, setUser] = useState({} as User);
api_id: 18987971, const [fileList, setFileList] = useState([]);
api_hash: "fcfd9e6ed3f9e48a360bb57cc0d59d98",
});
client.call("help.getCountriesList").then((result: any) => {
console.log("country:", result);
let resp: any[] = [];
result.countries.map((country: any) => {
// @ts-ignore
resp.push(
country.default_name + ": " + country.country_codes[0].country_code
);
return true;
});
setcountriesList(resp);
});
}, []);
let sendCode = () => { let sendCode = () => {
console.log("phone:", phone.current?.value); console.log("phone:", phone);
let client = new Client({ Telegram.call("auth.sendCode", {
api_id: 18987971, phone_number: (country ? country.country_code : "") + phone,
api_hash: "fcfd9e6ed3f9e48a360bb57cc0d59d98", settings: {
}); _: "codeSettings",
client.setDefaultDc(5); },
client })
.call("auth.sendCode", {
phone_number: phone.current?.value,
settings: {
_: "codeSettings",
},
})
.then((result: any) => { .then((result: any) => {
console.log("auth.sendCode:", result); console.log("auth.sendCode:", result);
setPhoneCodeHash(result.phone_code_hash);
}) })
.catch((error: any) => { .catch((error: any) => {
console.log("auth.sendCode:", error); console.log("auth.sendCode:", error);
}); });
}; };
let login = () => {}; let login = () => {
console.log("auth code:", code);
console.log("auth hash code:", phoneCodeHash);
Telegram.call("auth.signIn", {
phone_number: (country ? country.country_code : "") + phone,
phone_code_hash: phoneCodeHash,
phone_code: code,
})
.then((result: any) => {
console.log("auth.signIn:", result);
setUser(result.user);
SendMessage();
})
.catch((error: any) => {
console.log("auth.signIn:", error);
});
};
let SendMessage = () => {
Telegram.call("messages.sendMessage", {
peer: {
_: "inputPeerSelf",
user_id: user.id,
},
message: "ice",
random_id: (10000 + Math.random() * (100000 - 10000))
.toFixed()
.toString(),
})
.then((result: any) => {
console.log("messages.sendMessage:", result);
})
.catch((error: any) => {
console.log("messages.sendMessage:", error);
});
};
let uploadFile = () => {
console.log("uploadFile:", fileList);
// @ts-ignore
uploadBigFile(fileList[0]?.bytes)
.then((file_id) => console.log("uploadFile ret:", file_id))
.catch((error) => {
console.log("uploadFile error:", error);
alert(error);
});
};
let changeFile = (e: any) => {
console.log(e.target.files);
const reader = new FileReader();
reader.onload = function (event) {
if (event.target && event.target.result) {
let bytes = ab2str(event.target.result as ArrayBuffer);
console.log(bytes);
e.target.files[0].bytes = bytes;
setFileList(e.target.files);
}
};
reader.readAsArrayBuffer(e.target.files[0]);
};
return ( return (
<div className="App"> <Container maxWidth="sm">
<input ref={phone} /> <Paper
<button onClick={sendCode}></button> elevation={3}
<input /> sx={{
<button onClick={login}></button> padding: "150px 50px",
{countriesList.map((country: any, index) => { position: "absolute",
return ( display: "flex",
<div key={index}> flexDirection: "column",
{country} justifyContent: "space-around",
<br /> height: "40%",
</div> }}
); >
})} <CountriesInput
</div> onChange={(country) => {
console.log(country);
setCountry(country);
}}
/>
<TextField
label={"Phone Number"}
InputProps={{
startAdornment: (
<InputAdornment position="start">
{country && country.country_code
? "+" + country.country_code
: ""}
</InputAdornment>
),
}}
value={phone}
onChange={(e) => {
console.log(e.target.value);
setPhone(e.target.value);
}}
/>
<TextField
label={"Auth Code"}
value={code}
onChange={(e) => {
console.log(e.target.value);
setCode(e.target.value);
}}
/>
{phoneCodeHash === "" ? (
<Button variant={"contained"} onClick={sendCode}>
</Button>
) : (
<Button variant={"contained"} onClick={login} endIcon={<SendIcon />}>
</Button>
)}
<Input type="file" onChange={changeFile} />
<Button variant={"contained"} onClick={uploadFile} endIcon={<Upload />}>
</Button>
</Paper>
</Container>
); );
} }

View File

@ -0,0 +1,81 @@
import { Autocomplete, Box, TextField } from "@mui/material";
import React, { useEffect, useState } from "react";
import Telegram from "../telegram";
export interface Country {
default_name: string;
country_code: string;
// patterns?: string;
iso2: string;
}
function CountriesInput(props: {
onChange: (country: Country | null) => void;
}) {
let [countriesList, setCountriesList] = useState([] as Country[]);
useEffect(() => {
Telegram.call("help.getCountriesList").then((result: any) => {
console.log("country:", result);
let resp: Country[] = [];
result.countries.map((country: any) => {
resp.push({
default_name: country.default_name,
country_code: country.country_codes[0].country_code,
// patterns:
// country.country_codes[0]?.patterns.length > 0
// ? country.country_codes[0]?.patterns[0]
// : "",
iso2: country.iso2,
});
return true;
});
setCountriesList(resp);
});
}, []);
return (
<Autocomplete
disablePortal
autoHighlight
id="country-select"
options={countriesList.sort(function (a, b) {
const nameA = a.default_name.toUpperCase(); // ignore upper and lowercase
const nameB = b.default_name.toUpperCase(); // ignore upper and lowercase
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
// names must be equal
return 0;
})}
sx={{ width: 300 }}
onChange={(_event, value) => {
props.onChange(value);
}}
getOptionLabel={(option) => option.default_name}
renderOption={(props, option) => (
<Box
component="li"
sx={{ "& > img": { mr: 2, flexShrink: 0 } }}
{...props}
>
<img
loading="lazy"
width="20"
src={`https://flagcdn.com/w20/${option.iso2.toLowerCase()}.png`}
srcSet={`https://flagcdn.com/w40/${option.iso2.toLowerCase()}.png 2x`}
alt=""
/>
{option.default_name} ({option.iso2}) +{option.country_code}
</Box>
)}
renderInput={(params) => <TextField {...params} label="Country" />}
/>
);
}
export default CountriesInput;

View File

@ -1,13 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@ -1,14 +1,13 @@
import React from 'react'; import React from "react";
import ReactDOM from 'react-dom'; import ReactDOM from "react-dom";
import './index.css'; import App from "./App";
import App from './App'; import reportWebVitals from "./reportWebVitals";
import reportWebVitals from './reportWebVitals';
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById("root")
); );
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function

87
src/telegram.ts 100644
View File

@ -0,0 +1,87 @@
import Client from "mtproton/envs/browser";
import between from "./utils/rand";
import exp from "constants";
class TelegramHelper {
private client: any;
constructor(appID: number, appHash: string) {
this.client = new Client({
api_id: appID,
api_hash: appHash,
test: true,
});
}
// @ts-ignore
async call(method: string, params?: object, options?: object) {
try {
return await this.client.call(method, params, options);
} catch (error) {
console.log(`${method} error:`, error);
// @ts-ignore
const { error_code, error_message } = error;
// if (error_code === 420) {
// const seconds = Number(error_message.split('FLOOD_WAIT_')[1]);
// const ms = seconds * 1000;
//
// await sleep(ms);
//
// return this.call(method, params, options);
// }
if (error_code === 303) {
const [type, dcIdAsString] = error_message.split("_MIGRATE_");
const dcId = Number(dcIdAsString);
if (type === "PHONE") {
await this.client.setDefaultDc(dcId);
} else {
Object.assign(options, { dcId });
}
return this.call(method, params, options);
}
return Promise.reject(error);
}
}
}
async function uploadBigFile(bytes: string): Promise<number> {
let file_id = between(10000, 99999);
console.log("file_id", file_id);
const file_total_parts = Math.ceil(bytes.length / 524288);
console.log("file_total_parts", file_total_parts);
for (let i = 0; i < file_total_parts; i++) {
console.log("push part: ", i);
try {
let finished = await Telegram.call("upload.saveBigFilePart", {
file_id: file_id,
file_part: i,
file_total_parts: file_total_parts,
bytes: bytes.slice(i * 524288, (i + 1) * 524288),
});
if (finished) {
console.log("finished");
} else {
console.log("not finished");
}
} catch (error) {
return Promise.reject(error);
}
}
console.log("uploaded");
return file_id;
}
const appID = 18987971;
const appHash = "fcfd9e6ed3f9e48a360bb57cc0d59d98";
let Telegram = new TelegramHelper(appID, appHash);
export default Telegram;
export { uploadBigFile };

29
src/user.ts 100644
View File

@ -0,0 +1,29 @@
export interface Status {
_: string;
was_online: number;
}
export interface User {
_: string;
flags: number;
self: boolean;
contact: boolean;
mutual_contact: boolean;
deleted: boolean;
bot: boolean;
bot_chat_history: boolean;
bot_nochats: boolean;
verified: boolean;
restricted: boolean;
min: boolean;
bot_inline_geo: boolean;
support: boolean;
scam: boolean;
apply_min_photo: boolean;
fake: boolean;
id: string;
access_hash: string;
first_name: string;
phone: string;
status: Status;
}

View File

@ -0,0 +1,8 @@
function ArraybufferToStr(buffer: ArrayBuffer) {
let uint8 = new Uint8Array(buffer);
let decoder = new TextDecoder("utf8");
return decoder.decode(uint8);
}
export default ArraybufferToStr;

View File

@ -0,0 +1,5 @@
function between(min: number, max: number): number {
return Math.floor(Math.random() * (max - min) + min);
}
export default between;