diff --git a/.gitignore b/.gitignore index fcf4aa2..2073943 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ node_modules # The build folder stores the compiled files and will not be pushed to the repository build # The storage folder stores log-in credentials and should not be pushed to the repository -storage \ No newline at end of file +storage +_td_database +_td_files \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 288bc17..3de13a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@nodegui/nodegui": "^0.70.0", "dotenv": "^16.4.6", "jsonc": "^2.0.0", + "prebuilt-tdlib": "^0.1008041.0", "tdl": "^8.0.1", "telegram": "^2.26.8", "typescript": "^5.7.2" @@ -82,6 +83,71 @@ "tar": "^6.1.6" } }, + "node_modules/@prebuilt-tdlib/darwin-arm64": { + "version": "0.1008041.0", + "resolved": "https://registry.npmjs.org/@prebuilt-tdlib/darwin-arm64/-/darwin-arm64-0.1008041.0.tgz", + "integrity": "sha512-5VCPIVebmj5eRGDveFoQgJT9XK/JFVVpOPSSvEZtRqQcgEvAddipH8MT/+E6QQl/+2/GuufOnyF8saVqIyLOGQ==", + "cpu": [ + "arm64" + ], + "license": "0BSD", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@prebuilt-tdlib/darwin-x64": { + "version": "0.1008041.0", + "resolved": "https://registry.npmjs.org/@prebuilt-tdlib/darwin-x64/-/darwin-x64-0.1008041.0.tgz", + "integrity": "sha512-RN6lZkDyHDfnPz/gxltL+rB4JxRzNjYBK7+xgsqZNvnnOmE1u3G/sUwaFwmgHzjXnh9fZ2/E/ygfN50b8H3lLA==", + "cpu": [ + "x64" + ], + "license": "0BSD", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@prebuilt-tdlib/linux-arm64-glibc": { + "version": "0.1008041.0", + "resolved": "https://registry.npmjs.org/@prebuilt-tdlib/linux-arm64-glibc/-/linux-arm64-glibc-0.1008041.0.tgz", + "integrity": "sha512-pLF7snR5Ise+38oVkItlsle/89PFlLAgIzA/Nc0pNhK/Bzubq6yNpbvGzS+IDVDoNYKVxBNLJwV7zAXEe738pA==", + "cpu": [ + "arm64" + ], + "license": "0BSD", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@prebuilt-tdlib/linux-x64-glibc": { + "version": "0.1008041.0", + "resolved": "https://registry.npmjs.org/@prebuilt-tdlib/linux-x64-glibc/-/linux-x64-glibc-0.1008041.0.tgz", + "integrity": "sha512-t9/h/KnaeV6y6WmTxTuYuueS4rakWYYjQcbkhsInJlojZaUdwfvH3hX81WPb/dig2IDpU/3tWHfToefnOEI4Uw==", + "cpu": [ + "x64" + ], + "license": "0BSD", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@prebuilt-tdlib/win32-x64": { + "version": "0.1008041.0", + "resolved": "https://registry.npmjs.org/@prebuilt-tdlib/win32-x64/-/win32-x64-0.1008041.0.tgz", + "integrity": "sha512-80yHuWadJ7ENXTWbwjCEqbaFWDWj1pAKDCFQsSw4WR3gjIhvKb9LGszj5MTBGLFOeXIPwuGuQxa3N0POknIQYg==", + "cpu": [ + "x64" + ], + "license": "0BSD", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@types/node": { "version": "22.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", @@ -1500,6 +1566,19 @@ "postcss": "^7.0.17" } }, + "node_modules/prebuilt-tdlib": { + "version": "0.1008041.0", + "resolved": "https://registry.npmjs.org/prebuilt-tdlib/-/prebuilt-tdlib-0.1008041.0.tgz", + "integrity": "sha512-9uk2uQ5pRGykaAR+tOaWFAhtmJvsjmHE6LjBcj8u6w/rgtIxyVqI/RRmXDY/06r4nTDnKtLHsvvFribCKaWBSA==", + "license": "MIT", + "optionalDependencies": { + "@prebuilt-tdlib/darwin-arm64": "0.1008041.0", + "@prebuilt-tdlib/darwin-x64": "0.1008041.0", + "@prebuilt-tdlib/linux-arm64-glibc": "0.1008041.0", + "@prebuilt-tdlib/linux-x64-glibc": "0.1008041.0", + "@prebuilt-tdlib/win32-x64": "0.1008041.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", diff --git a/package.json b/package.json index 718036c..57ef4f3 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@nodegui/nodegui": "^0.70.0", "dotenv": "^16.4.6", "jsonc": "^2.0.0", + "prebuilt-tdlib": "^0.1008041.0", "tdl": "^8.0.1", "telegram": "^2.26.8", "typescript": "^5.7.2" diff --git a/src/telegram/config.jsonc b/src/telegram/config.jsonc new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/telegram/config.jsonc @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/telegram/telegram.ts b/src/telegram/telegram.ts index 1426225..53c4f3f 100644 --- a/src/telegram/telegram.ts +++ b/src/telegram/telegram.ts @@ -13,10 +13,11 @@ tdl.configure({ tdjson: getTdjson() }); function _createClient(appid? : number, apphash? : string) { // Check if the app(id/hash) are either in the dotenv, the jsonc file or the arguments if (!appid || !apphash) { + // TODO: Encrypt this! dotenv.config(); - const jsoncFile = jsonc.parse(fs.readFileSync('config.jsonc', 'utf-8')); - appid = appid || process.env.APP_ID || jsoncFile.APP_ID; - apphash = apphash || process.env.APP_HASH || jsoncFile.APP_HASH; + //const jsoncFile = jsonc.parse(fs.readFileSync('config.jsonc', 'utf-8')); + appid = Number(appid || process.env.TG_APP_ID) //|| jsoncFile.TG_APP_ID; + apphash = apphash || process.env.TG_APP_HASH //|| jsoncFile.TG_APP_HASH; } // If the app(id/hash) are not found, throw an error if (!appid || !apphash) { @@ -41,14 +42,14 @@ function _createClient(appid? : number, apphash? : string) { * Logs in to the Telegram API. * @returns A tuple containing a boolean and the client instance if successful, or a boolean and an error message if unsuccessful. */ -module.exports._login = async function () { +export async function login() { const [success, client] = _createClient(); if (!success || typeof client !== 'object') { return [false, client]; } await client.login(); // Temp: Prompt the terminal for the phone number and the code const me = await client.invoke({_: 'getMe' }); - console.log('Logged in as ', me); + console.log('Logged in as ', me.first_name, me.last_name, ' (', me.id, ')'); return [true, client]; }; @@ -57,7 +58,7 @@ module.exports._login = async function () { * @param client The client instance to log out of. * @returns A boolean indicating whether the logout was successful, or an error message if unsuccessful. */ -module.exports._destroy = async function (client : tdl.Client) { +export async function destroy(client : tdl.Client) { try { await client.close(); return true; @@ -72,7 +73,7 @@ module.exports._destroy = async function (client : tdl.Client) { * @param client The client instance to get the information from. * @returns An object containing the user's information, or an error message if unsuccessful. */ -module.exports.getMe = async function (client : tdl.Client) { +export async function getMe(client : tdl.Client) : Promise { try { const me = await client.invoke({_: 'getMe' }); return me; @@ -86,7 +87,7 @@ module.exports.getMe = async function (client : tdl.Client) { * @param userId The id of the user to get the information of. * @returns An object containing the user's information, or an error message if unsuccessful. */ -module.exports.getUser = async function (client : tdl.Client, userId : number) { +export async function getUser(client : tdl.Client, userId : number) { try { const user = await client.invoke({_: 'getUser', user_id:userId }); return user; @@ -100,11 +101,40 @@ module.exports.getUser = async function (client : tdl.Client, userId : number) { * @param chatId The id of the chat to get the information of. * @returns An object containing the chat's information, or an error message if unsuccessful. */ -module.exports.getChat = async function (client : tdl.Client, chatId : number) { +export async function getChat(client : tdl.Client, chatId : number) : Promise { try { const chat = await client.invoke({_: 'getChat', chat_id:chatId }); return chat; } catch (err) { return err; } +}; + + +/** Sends a message in a chat. + * @param client The client instance to send the message with. + * @param chatId The id of the chat to send the message in. + * @param message The message to send. + * @returns The message response if successful, or an error message if unsuccessful. +*/ +export async function sendMessage(client : tdl.Client, chatId : number, message : string) { + try { + const messageSent = await client.invoke({_: 'sendMessage', chat_id:chatId, input_message_content:{_: 'inputMessageText', text:{_: 'formattedText', text:message} } }); + return messageSent; + } catch (err) { + return err; + } +}; + +/** Gets a list of chats + * @param client The client instance to get the chats from. + * @returns An array containing the chats, or an error message if unsuccessful. +*/ +export async function getChats(client : tdl.Client, limit : number = 100) : Promise { + try { + const chats = await client.invoke({_: 'getChats', limit: limit }); + return chats; + } catch (err) { + return err; + } }; \ No newline at end of file diff --git a/src/tests/telegram.ts b/src/tests/telegram.ts new file mode 100644 index 0000000..55a8ff0 --- /dev/null +++ b/src/tests/telegram.ts @@ -0,0 +1,55 @@ +// Simple test that sends a message in saved messages +import * as telegram from '../telegram/telegram'; + +(async () => { + // Attempt to log in + const [success, client] = await telegram.login(); + if (!success) { + console.error('Failed to log in:', client); + return; + } + if (typeof client !== 'object') { + console.error('Client is not an object:', client); + return; + } + // Get the logged in user's information + const me = await telegram.getMe(client); + if (typeof me !== 'object') { + console.error('Failed to get user information:', me); + return; + } + console.log('Logged in as:', me.first_name, me.last_name, '(', me.usernames.editable_username || 'No username', ')'); + // Send a message to the saved messages chat + // This is done by sending a message to the chat with the same id as the user id + const chatList = await telegram.getChats(client); + if (!chatList) { + console.error('Failed to get chat list:', chatList); + return; + } + // For each chat, get the chat information + for (const chat of chatList.chat_ids) { + const chatInfo = await telegram.getChat(client, chat); + if (!chatInfo) { + console.error('Failed to get chat information:', chatInfo); + return; + } + console.log('Chat:', chatInfo.title, '(', chatInfo.id, ')'); + } + // Send a message to the saved messages chat + const message = await telegram.sendMessage(client, me.id, `Hello world! The time is ${new Date().toLocaleTimeString()}`); + if (!message || typeof message !== 'object') { + console.error('Failed to send message:', message); + return; + } + console.log('Message response:', message); + // Wait for 5 seconds + await new Promise(resolve => setTimeout(resolve, 5000)); + // Close the client + const logout = await telegram.destroy(client); + if (!logout) { + console.error('Failed to log out:', logout); + return; + } + console.log('Logged out successfully'); + return; +})(); \ No newline at end of file diff --git a/supported/Telegram.md b/supported/Telegram.md index 711445d..59519b9 100644 --- a/supported/Telegram.md +++ b/supported/Telegram.md @@ -3,7 +3,7 @@ Active Maintainer: [StellaraUmbrix](https://github.com/StellaraUmbrix) Telegram group: TBD (Join the blog's discussion channel for now: https://t.me/OpossumSociety) # Current roadmap -- [ ] Log(in/out) +- [x] Log(in/out) - [ ] Send/Recieve messages - [ ] Send/Recieve attachments - [ ] Send/Recieve voice and video messages