diff --git a/README.md b/README.md index 1bd97bd..c30f733 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,31 @@ cd website && yarn dev Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +If icons from components-sdk are not loading for you Comment this alias in website/vite.config.ts + +```bash +{find: /^components-sdk.*$/, replacement: resolve(__dirname, '../components-sdk/src')}, +``` + +## 📦 Building + + +First, you need to have the steps above done + +Build the website library: + +```bash +cd website && yarn build +``` + +Run the build server of the website: + +```bash +serve dist +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + ## ⚠️ Commercial use Although the `website/` project is licensed under the permissive MIT License, it depends on the `components-sdk/` package, which is **licensed under the PolyForm Noncommercial License 1.0.0**. diff --git a/website/src/App.tsx b/website/src/App.tsx index 896ec31..31e2f8b 100644 --- a/website/src/App.tsx +++ b/website/src/App.tsx @@ -32,7 +32,10 @@ function App() { const stateManager = useMemo(() => new DisplaySliceManager(dispatch), [dispatch]); const state = useSelector((state: RootState) => state.display.data) const webhookUrl = useSelector((state: RootState) => state.display.webhookUrl); + const messageLink = useSelector((state: RootState) => state.display.messageUrl); const response = useSelector((state: RootState) => state.display.webhookResponse); + const username = useSelector((state: RootState) => state.display.setusername); + const avatar = useSelector((state: RootState) => state.display.setavatar); const [page, setPage] = useRouter(); const [postTitle, setPostTitle] = useState(""); useHashRouter(); @@ -53,6 +56,26 @@ function App() { return () => clearTimeout(getData) }, [webhookUrl]); + useEffect(() => { + const getData = setTimeout(() => localStorage.setItem("discord.builders__messageLink", messageLink), 1000) + return () => clearTimeout(getData) + }, [messageLink]); + + useEffect(() => { + document.querySelectorAll('._emoji_c7tgn_78').forEach(e => (e.childElementCount == 0) ? e.style.display = "none" : e.style.display = ""); + }); + + let parsed_msg_url: URL | null = null; + try { + parsed_msg_url = new URL(messageLink); + + if (parsed_msg_url.pathname.startsWith('/channels/') && parsed_msg_url.hostname === 'discord.com') { + parsed_msg_url.protocol = 'https:'; + } + + const parsed_query = new URLSearchParams(parsed_msg_url.search); + parsed_msg_url.search = parsed_query.toString(); + } catch (e) {} let parsed_url: URL | null = null; try { @@ -61,6 +84,9 @@ function App() { if (parsed_url.pathname.startsWith('/api/webhooks/') && parsed_url.hostname === 'discord.com') { parsed_url.protocol = 'https:'; parsed_url.pathname = '/api/v10/webhooks/' + parsed_url.pathname.slice('/api/webhooks/'.length); + if (parsed_msg_url != null) { + parsed_url.pathname += '/messages/'+parsed_msg_url.pathname.split('/').pop(); + } } const parsed_query = new URLSearchParams(parsed_url.search); @@ -75,10 +101,65 @@ function App() { const threadId = useMemo(() => getThreadId(webhookUrl), [webhookUrl]); const sendMessage = async () => { - const req = await fetch(String(parsed_url), webhookImplementation.prepareRequest(state)) + var method_req; + let username_in = undefined; + let avatarurl_in = undefined; + if (parsed_msg_url != null) {method_req = "PATCH"} + else { + method_req = "POST"; + if (username != "") username_in = username; + if (avatar != "") avatarurl_in = avatar; + } + const req = await fetch(String(parsed_url), webhookImplementation.prepareRequest(state, method_req, ...Array(1), username_in, avatarurl_in)) + + if (username == "" || avatar == ""){ + const req_2 = await fetch(String(webhookUrl), webhookImplementation.prepareRequest(state, "GET")) + let data_2 = await req_2.json() + if (username == "") dispatch(actions.setUsernameData(data_2["name"])) + if (avatar == "") dispatch(actions.setAvatarData("https://cdn.discordapp.com/avatars/"+data_2["id"]+"/"+data_2["avatar"]+".png")) + } const status_code = req.status; if (status_code === 204) return dispatch(actions.setWebhookResponse({"status": "204 Success"})); + else if (status_code === 200) return dispatch(actions.setWebhookResponse({"status": "200 Success"})); + + const error_data = await req.json(); + + if (error_data?.code === 220001 && dialog.current !== null) { + dialog.current.showModal(); + dispatch(actions.setWebhookResponse(null)); + return; + } + + dispatch(actions.setWebhookResponse(error_data)) + } + + const getMessage = async () => { + const req = await fetch(String(parsed_url), webhookImplementation.prepareRequest(state, "GET")) + + const status_code = req.status; + if (status_code === 204) return dispatch(actions.setWebhookResponse({"status": "204 Success"})) + else if (status_code === 200) { + let loaded_data = await req.json(); + let reg_check = /"id":(([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9])|"[^"]*"),/gm; //Discord includes ids in the component but this doesnt like the ids (it causes duplicates), this is to remove them + try { + var fixed_data = JSON.parse(JSON.stringify(loaded_data).replaceAll(reg_check,"")); + } catch(err) { + console.error(err); + } + dispatch(actions.setComponentsData([])) + for (const comp of fixed_data["components"]){ + let data_to_add = comp + if ("id" in data_to_add){ + delete data_to_add.id; + } + dispatch(actions.appendKey({"key":["data"],"value":data_to_add})) + } + dispatch(actions.setUsernameData(loaded_data["author"]["username"])) + dispatch(actions.setAvatarData("https://cdn.discordapp.com/avatars/"+loaded_data["author"]["id"]+"/"+loaded_data["author"]["avatar"]+".png")) + + return dispatch(actions.setWebhookResponse({"status": "200 Success"})) + } const error_data = await req.json(); @@ -95,7 +176,7 @@ function App() { if (!postTitle) return; dialog.current?.close(); - const req = await fetch(String(parsed_url), webhookImplementation.prepareRequest(state, postTitle)) + const req = await fetch(String(parsed_url), webhookImplementation.prepareRequest(state, "POST", postTitle)) const status_code = req.status; if (status_code === 204) return dispatch(actions.setWebhookResponse({"status": "204 Success"})); @@ -114,7 +195,7 @@ function App() { stateManager={stateManager} stateKey={stateKey} passProps={passProps} - className={Styles.preview} + className={Styles.input} errors={errors} /> @@ -135,7 +216,7 @@ function App() { onChange={ev => dispatch(actions.setWebhookUrl(ev.target.value))}/> @@ -146,6 +227,32 @@ function App() { dispatch(actions.setThreadId(ev.target.value))} placeholder={"Optional. If you want to send the message to a thread, put the thread ID here."}/> +
+

Message Link

+
+
+ dispatch(actions.setMessageLink(ev.target.value))}/> +
+ +
+

Warning: The message must to be sent by the webhook that edits it and uploading a image or a file doesnt work with editing.

+
+ +
+
+
+

Username

+ dispatch(actions.setUsernameData(ev.target.value))} placeholder={((parsed_msg_url == null) ? 'Optional. If you want to change the username of the message.' : 'Cannot Change username in edit mode.')}/> +

Avatar Url

+ dispatch(actions.setAvatarData(ev.target.value))} placeholder={((parsed_msg_url == null) ? 'Optional. If you want to change the avatar of the message.' : 'Cannot Change avatar in edit mode.')}/> +
+
+

Warning: cant change name or profile when editing.

+
+
diff --git a/website/src/EmojiShow.tsx b/website/src/EmojiShow.tsx index 8796051..bf4e555 100644 --- a/website/src/EmojiShow.tsx +++ b/website/src/EmojiShow.tsx @@ -8,15 +8,20 @@ export const EmojiShow: EmojiShowType = ({emoji}) => { return getEmojiDataFromNative(emoji, 'twitter', data) }, []) - if (emoji.id !== null) return {`Discord + if (typeof(emoji) == "undefined"){ + return null + } + else { + if (emoji.id !== null && typeof(emoji.id) !== "undefined") return {`Discord - return + return + } } diff --git a/website/src/state.ts b/website/src/state.ts index 4a52626..b2169b4 100644 --- a/website/src/state.ts +++ b/website/src/state.ts @@ -52,7 +52,10 @@ export const displaySlice = createSlice({ initialState: () => ({ data: [] as Component[], webhookUrl: localStorage.getItem("discord.builders__webhookToken") || "", // Toolkit run this function so type is string - webhookResponse: null as object | null + webhookResponse: null as object | null, + messageUrl: localStorage.getItem("discord.builders__messageLink") || "", // Toolkit run this function so type is string + setusername: "", + setavatar: "", }), reducers: { wrapKey(state, action: PayloadAction>) { @@ -150,6 +153,22 @@ export const displaySlice = createSlice({ state.webhookUrl = action.payload }, + setMessageLink(state, action: PayloadAction) { + state.messageUrl = action.payload + }, + + setUsernameData(state, action: PayloadAction) { + state.setusername = action.payload + }, + + setAvatarData(state, action: PayloadAction) { + state.setavatar = action.payload + }, + + setComponentsData(state, action: PayloadAction) { + state.data = action.payload + }, + setThreadId(state, action: PayloadAction) { try { const parsed_url = new URL(state.webhookUrl); diff --git a/website/src/webhook.impl.ts b/website/src/webhook.impl.ts index 0b88dec..3358d1b 100644 --- a/website/src/webhook.impl.ts +++ b/website/src/webhook.impl.ts @@ -58,16 +58,19 @@ export const webhookImplementation = { } }, - prepareRequest(state: Component[], thread_name?: string): RequestInit { + prepareRequest(state: Component[], method_req?: string, thread_name?: string, username?: string, avatar_url?: string): RequestInit { const files = this.scrapFiles(state); const data = JSON.stringify({ components: state, flags: 32768, thread_name, + username, + avatar_url, + }); - if (!files.length) return {method: "POST", body: data, headers: {"Content-Type": "application/json"}} + if (!files.length) return {method: method_req, body: ((method_req == "GET") ? null : data), headers: {"Content-Type": "application/json"}} const form = new FormData(); form.append('payload_json', data); @@ -75,7 +78,7 @@ export const webhookImplementation = { const blob = window.uploadedFiles[filename]; form.append(`files[${idx}]`, blob, filename); }) - return {method: "POST", body: form, headers: {}} + return {method: method_req, body: form, headers: {}} }, getErrors(response: unknown) { @@ -91,4 +94,4 @@ export const webhookImplementation = { return components as Record; } -} \ No newline at end of file +}