├── example.env ├── package.json ├── README.md ├── .gitignore └── index.js /example.env: -------------------------------------------------------------------------------- 1 | TELEGRAM_BOT_TOKEN= 2 | TELEGRAM_ADMIN_ID= 3 | ROAM_EMAIL= 4 | ROAM_PASSWORD= 5 | ROAM_GRAPH= 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "roamgram", 3 | "version": "0.1.0", 4 | "description": "Add Notes To Roam Research using Telegram", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": "Arnav Gosain ", 8 | "scripts": { 9 | "start": "node index.js", 10 | "dev": "nodemon -w index.js index.js" 11 | }, 12 | "dependencies": { 13 | "dotenv": "^8.2.0", 14 | "node-telegram-bot-api": "^0.51.0", 15 | "pm2": "^4.5.5", 16 | "roam-research-private-api": "^0.9.3" 17 | }, 18 | "devDependencies": { 19 | "nodemon": "^2.0.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Roamgram 2 | 3 | Send notes to Roam Research using a Telegram Bot. 4 | 5 | ### Telegram Setup 6 | 7 | - Open Telegram 8 | - Start a conversation with [@userinfobot](https://t.me/userinfobot) 9 | - Press start 10 | - Copy the `id` property from the message it sends, this is your `TELEGRAM_ADMIN_ID` 11 | 12 | - Now create a bot using the instructions [here](https://core.telegram.org/bots#6-botfather) 13 | - At the end of the process you should get a token to access the HTTP API, this is your `TELEGRAM_BOT_TOKEN` 14 | 15 | ### Node.js Setup 16 | 17 | - `git clone git://github.com/arn4v/roamgram -b main` 18 | - Install Node.js v14.15.5 (this is what I tested with but any version v12+ should work) 19 | - `cd roamgram` 20 | - Copy `example.env` to `.env` using `cp example.env .env` 21 | - Edit .env to add the following details: 22 | ``` 23 | TELEGRAM_BOT_TOKEN= 24 | TELEGRAM_ADMIN_ID= 25 | ROAM_EMAIL= 26 | ROAM_PASSWORD= 27 | ROAM_GRAPH= 28 | ``` 29 | - Run `npm start` 30 | - Now send `/add ` to your bot on Telegram and see it show up on your Roam Daily Notes Page 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | 118 | .env -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const TelegramBotApi = require("node-telegram-bot-api"); 2 | const RoamResearchPrivateApi = require("roam-research-private-api"); 3 | 4 | class RoamApi extends RoamResearchPrivateApi { 5 | async appendBlock(text, order = 0, uid) { 6 | const result = await this.page.evaluate( 7 | (text, order, uid) => { 8 | if (!window.roamAlphaAPI) { 9 | return Promise.reject("No Roam API detected"); 10 | } 11 | const result = window.roamAlphaAPI.createBlock({ 12 | location: { "parent-uid": uid, order }, 13 | block: { string: text }, 14 | }); 15 | return Promise.resolve(result); 16 | }, 17 | text, 18 | order, 19 | uid 20 | ); 21 | // Let's give time to sync. 22 | await this.page.waitForTimeout(1000); 23 | return result; 24 | } 25 | } 26 | 27 | const main = async ({ token, adminId, roam: { graph, email, password } }) => { 28 | if (typeof adminId === "string") adminId = parseInt(adminId); 29 | 30 | const bot = new TelegramBotApi(token, { polling: true }); 31 | const validator = (message) => { 32 | if (message.from.id == adminId) return true; 33 | return false; 34 | }; 35 | const roam = new RoamApi(graph, email, password, { 36 | headless: true, 37 | }); 38 | 39 | await roam.logIn(); 40 | console.log("Logged into Roam"); 41 | 42 | bot.onText(/\/id*(.+)/, (message) => { 43 | bot.sendMessage( 44 | message.chat.id, 45 | `User id: ${message.from.id}\nChat id: ${message.chat.id}` 46 | ); 47 | }); 48 | 49 | bot.onText(/\/add (.+)/, (message) => { 50 | const chatId = message.chat.id; 51 | if (validator(message)) { 52 | const dailyNoteId = roam.dailyNoteUid(); 53 | const dailyNoteTitle = roam.dailyNoteTitle(); 54 | 55 | roam 56 | .runQuery( 57 | `[ :find (pull ?e [*]) :where [?e :node/title "${dailyNoteTitle}"]]` 58 | ) 59 | .then((result) => { 60 | try { 61 | return result[0][0].children.length; 62 | } catch { 63 | return result[0].length; 64 | } 65 | }) 66 | .then((order) => { 67 | roam 68 | .appendBlock( 69 | message.text.replace(/\/add /, ""), 70 | order ?? 0, 71 | dailyNoteId 72 | ) 73 | .then((result) => { 74 | if (result) { 75 | bot.sendMessage(chatId, `Added text to Roam Daily Notes`); 76 | } else { 77 | bot.sendMessage( 78 | chatId, 79 | `Failed to add message to Roam Daily Notes` 80 | ); 81 | } 82 | }) 83 | .catch((err) => { 84 | bot.sendMessage( 85 | chatId, 86 | `Failed to add message to Roam Daily Notes.\n${err.toString()}` 87 | ); 88 | }); 89 | }) 90 | .catch((err) => { 91 | bot.sendMessage( 92 | chatId, 93 | `Failed to add message to Roam Daily Notes.\n${err.toString()}` 94 | ); 95 | }); 96 | } else { 97 | console.log(message.from.id); 98 | bot.sendMessage(chatId, "Invalid user."); 99 | } 100 | }); 101 | }; 102 | 103 | module.exports = main; 104 | 105 | require("dotenv").config(); 106 | 107 | (async () => { 108 | [ 109 | "ROAM_GRAPH", 110 | "ROAM_EMAIL", 111 | "ROAM_PASSWORD", 112 | "TELEGRAM_BOT_TOKEN", 113 | "TELEGRAM_ADMIN_ID", 114 | ].forEach((key) => { 115 | if (!process.env[key]) { 116 | console.log(`${key} not found in env file.`); 117 | process.exit(1); 118 | } 119 | 120 | if (key === "TELEGRAM_ADMIN_ID") { 121 | if (typeof process.env[key] === "string") 122 | process.env[key] = parseInt(process.env[key]); 123 | } 124 | }); 125 | 126 | await main({ 127 | token: process.env.TELEGRAM_BOT_TOKEN, 128 | adminId: process.env.TELEGRAM_ADMIN_ID, 129 | roam: { 130 | graph: process.env.ROAM_GRAPH, 131 | email: process.env.ROAM_EMAIL, 132 | password: process.env.ROAM_PASSWORD, 133 | }, 134 | }); 135 | })().catch((err) => console.log(err)); 136 | --------------------------------------------------------------------------------