update
parent
65ac000169
commit
78b2b4b753
File diff suppressed because it is too large
Load Diff
|
@ -3,6 +3,10 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"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/react": "^12.1.2",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
|
@ -40,6 +44,5 @@
|
|||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,5 +39,13 @@
|
|||
To begin the development, run `npm start` or `yarn start`.
|
||||
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>
|
||||
</html>
|
||||
|
|
38
src/App.css
38
src/App.css
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
});
|
199
src/App.tsx
199
src/App.tsx
|
@ -1,70 +1,167 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
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() {
|
||||
let [countriesList, setcountriesList] = useState([] as any[]);
|
||||
let phone = React.createRef<HTMLInputElement>();
|
||||
|
||||
useEffect(() => {
|
||||
let client = new Client({
|
||||
api_id: 18987971,
|
||||
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 [phone, setPhone] = useState("");
|
||||
let [code, setCode] = useState("");
|
||||
let [phoneCodeHash, setPhoneCodeHash] = useState("");
|
||||
let [country, setCountry] = useState({} as Country | null);
|
||||
let [user, setUser] = useState({} as User);
|
||||
const [fileList, setFileList] = useState([]);
|
||||
|
||||
let sendCode = () => {
|
||||
console.log("phone:", phone.current?.value);
|
||||
let client = new Client({
|
||||
api_id: 18987971,
|
||||
api_hash: "fcfd9e6ed3f9e48a360bb57cc0d59d98",
|
||||
});
|
||||
client.setDefaultDc(5);
|
||||
client
|
||||
.call("auth.sendCode", {
|
||||
phone_number: phone.current?.value,
|
||||
settings: {
|
||||
_: "codeSettings",
|
||||
},
|
||||
})
|
||||
console.log("phone:", phone);
|
||||
Telegram.call("auth.sendCode", {
|
||||
phone_number: (country ? country.country_code : "") + phone,
|
||||
settings: {
|
||||
_: "codeSettings",
|
||||
},
|
||||
})
|
||||
.then((result: any) => {
|
||||
console.log("auth.sendCode:", result);
|
||||
setPhoneCodeHash(result.phone_code_hash);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
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 (
|
||||
<div className="App">
|
||||
<input ref={phone} />
|
||||
<button onClick={sendCode}>登录</button>
|
||||
<input />
|
||||
<button onClick={login}>登录</button>
|
||||
{countriesList.map((country: any, index) => {
|
||||
return (
|
||||
<div key={index}>
|
||||
{country}
|
||||
<br />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Container maxWidth="sm">
|
||||
<Paper
|
||||
elevation={3}
|
||||
sx={{
|
||||
padding: "150px 50px",
|
||||
position: "absolute",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
height: "40%",
|
||||
}}
|
||||
>
|
||||
<CountriesInput
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
|
@ -1,14 +1,13 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import App from "./App";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
document.getElementById("root")
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
|
|
|
@ -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 };
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -0,0 +1,5 @@
|
|||
function between(min: number, max: number): number {
|
||||
return Math.floor(Math.random() * (max - min) + min);
|
||||
}
|
||||
|
||||
export default between;
|
Loading…
Reference in New Issue