<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Saltaformajo</title>
    <description>The latest articles on DEV Community by Saltaformajo (@saltaformajo).</description>
    <link>https://dev.to/saltaformajo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F461399%2F8db26909-7f71-45e1-9af1-73d697265062.png</url>
      <title>DEV Community: Saltaformajo</title>
      <link>https://dev.to/saltaformajo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/saltaformajo"/>
    <language>en</language>
    <item>
      <title>Neovim plugin for synchronization with remote servers</title>
      <dc:creator>Saltaformajo</dc:creator>
      <pubDate>Wed, 01 Nov 2023 15:16:04 +0000</pubDate>
      <link>https://dev.to/saltaformajo/neovim-plugin-for-synchronization-with-remote-servers-4lal</link>
      <guid>https://dev.to/saltaformajo/neovim-plugin-for-synchronization-with-remote-servers-4lal</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/coffebar/transfer.nvim"&gt;Transfer.nvim&lt;/a&gt; is a Neovim plugin that enables you to synchronize files between your local machine and a remote server using rsync and OpenSSH. It is a lightweight and user-friendly plugin suitable for syncing various types of files, including code, configuration files, and documents.&lt;/p&gt;

&lt;p&gt;It's integrated with nvim-notify and will show you the status of the transfer in the animated colorful notification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;File Synchronization using rsync and OpenSSH.&lt;/li&gt;
&lt;li&gt;Lua Configuration: Configuration for synchronization is written in Lua, making it accessible for those familiar with Neovim configuration.&lt;/li&gt;
&lt;li&gt;Excluded Paths: Excluded paths can be defined to avoid synchronizing specific directories.&lt;/li&gt;
&lt;li&gt;Commands includes creating a configuration file, opening a diff view, repeating the last transfer command, uploading, downloading, and directory diff.&lt;/li&gt;
&lt;li&gt;Customization: Change deployment config template, rsync params, keybindings.&lt;/li&gt;
&lt;li&gt;Notifications: Integrated with nvim-notify.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who Benefits from Transfer.nvim
&lt;/h2&gt;

&lt;p&gt;Transfer.nvim is a valuable tool for individuals who regularly need to synchronize files with remote servers, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers: Developers often require file synchronization between their local machines and remote development servers. Transfer.nvim simplifies and streamlines this process.&lt;/li&gt;
&lt;li&gt;System Administrators: System administrators can edit configuration files locally and upload them to the server with ease using Transfer.nvim.&lt;/li&gt;
&lt;li&gt;Users with Multiple Devices: Whether you need to sync files between your work computer and home computer, &lt;a href="https://github.com/coffebar/transfer.nvim"&gt;Transfer.nvim&lt;/a&gt; makes the process more straightforward.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's important to note that the synchronization configuration is written in Lua, making it accessible for those familiar with Neovim configuration. To simplify configuration, you can create the configuration file using the &lt;code&gt;:TransferInit&lt;/code&gt; command.&lt;/p&gt;

&lt;h4&gt;
  
  
  Deployment config example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- .nvim/deployment.lua&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"example_name"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"myhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- optional&lt;/span&gt;
    &lt;span class="n"&gt;mappings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"local"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"live"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- path relative to project root&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/var/www/example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- absolute path or relative to user home&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"local"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/var/www/test.example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;excludedPaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;-- optional&lt;/span&gt;
      &lt;span class="s2"&gt;"live/src/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- local path relative to project root&lt;/span&gt;
      &lt;span class="s2"&gt;"test/src/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You only need to edit this configuration to specify which local directory corresponds to which remote directory on a server. You can also define excluded paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;With Lazy.nvim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"coffebar/transfer.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;lazy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="s2"&gt;"TransferInit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"DiffRemote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"TransferUpload"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"TransferDownload"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"TransferDirDiff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="s2"&gt;"TransferRepeat"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Commands
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TransferInit&lt;/code&gt; - Creates a configuration file and opens it for editing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DiffRemote&lt;/code&gt; - Opens a diff view with the remote file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TransferRepeat&lt;/code&gt; - Repeats the last transfer command (except TransferInit, DiffRemote).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TransferUpload [path]&lt;/code&gt; - Upload.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TransferDownload [path]&lt;/code&gt; - Download.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TransferDirDiff [path]&lt;/code&gt; - Compares the local directory with its remote counterpart and displays the changed files in the quickfix.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keybindings
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Transfer.nvim&lt;/code&gt; does not provide any keybindings by default. The only exception is the &lt;code&gt;&amp;lt;leader&amp;gt;b&lt;/code&gt;, which is mapped to remote buffer in diff view to close it with &lt;code&gt;:diffoff&lt;/code&gt; and &lt;code&gt;bdelete&lt;/code&gt;. You can change or disable this mapping in the opts.&lt;/p&gt;

&lt;p&gt;Also, You can find pre-made solutions on the plugin page. Copy them to add all commands to your which-key keybindings and to the Neo-tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Auth only by ssh key (passwordless).&lt;/li&gt;
&lt;li&gt;You can have multiple servers, but you need to map one local directory to one remote directory, not one to many.&lt;/li&gt;
&lt;li&gt;You can't change deployment config path. It's not an issue, file doesn't contain any sensitive data.&lt;/li&gt;
&lt;li&gt;Windows is not supported.&lt;/li&gt;
&lt;li&gt;If you need auto-upload on &lt;code&gt;BufWritePost&lt;/code&gt; event - it is on you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Probably, some of this may change after the publication of the article. Pay attention to the date and the current README of the plugin.&lt;/p&gt;

&lt;p&gt;It might be a good idea to add support for Amazon S3, Google Cloud Storage, and other cloud storage services in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/coffebar/transfer.nvim"&gt;Transfer.nvim&lt;/a&gt; offers a user-friendly and efficient solution for individuals seeking hassle-free file synchronization in their Neovim environment. Streamline your workflow and stay in control of your data with this feature-rich plugin.&lt;/p&gt;

</description>
      <category>neovim</category>
      <category>ssh</category>
      <category>nvim</category>
      <category>vim</category>
    </item>
    <item>
      <title>Boost Your Productivity with This Neovim Plugin for Project Management!</title>
      <dc:creator>Saltaformajo</dc:creator>
      <pubDate>Thu, 12 Oct 2023 14:19:15 +0000</pubDate>
      <link>https://dev.to/saltaformajo/boost-your-productivity-with-this-neovim-plugin-for-project-management-2bd8</link>
      <guid>https://dev.to/saltaformajo/boost-your-productivity-with-this-neovim-plugin-for-project-management-2bd8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Neovim, with its extensible architecture and plugin ecosystem, holds immense potential to transform into an exceptional IDE (PDE). If you've ever experienced the struggles of navigating between projects in traditional IDEs, you know how sluggish and cumbersome it can be. The frustration of switching contexts and dealing with delays often leads us to haphazardly bundle projects into a single chaotic workspace.&lt;/p&gt;

&lt;p&gt;Neovim, however, offers a refreshing change. It introduces a world of possibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The built-in &lt;code&gt;:mksession&lt;/code&gt; command allows you to save and restore the current state of the editor.&lt;/li&gt;
&lt;li&gt;There are various plugins tailored for project management.&lt;/li&gt;
&lt;li&gt;tmux (You will use a separate instance of Neovim for each project)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first option, using &lt;code&gt;:mksession&lt;/code&gt;, is functional but requires some manual effort to streamline your workflow. Fortunately, many plugins have put in significant work to make your life easier. As for tmux, it can be customized, but configuring it to be unobtrusive and seamless is no small task. Moreover, Neovim already incorporates many features offered by tmux, eliminating the need for redundancy.&lt;/p&gt;

&lt;p&gt;The most efficient and convenient choice is utilizing Neovim plugins. I've experimented with a few, and I'm excited to share my experiences with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Neovim Session Manager
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Shatur/neovim-session-manager" rel="noopener noreferrer"&gt;Neovim Session Manager&lt;/a&gt; is a popular plugin, that uses built-in &lt;code&gt;:mksession&lt;/code&gt; command. It simplifies the process of saving and restoring sessions by implementing auto-commands and user-commands. This plugin can automatically save your session when you close Neovim and restore it the next time you open it.&lt;/p&gt;

&lt;p&gt;It is stable and well-maintained, but it is not an all-in-one solution for project management.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI
&lt;/h3&gt;

&lt;p&gt;Uses &lt;code&gt;vim.ui.select()&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project.nvim
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ahmedkhalf/project.nvim" rel="noopener noreferrer"&gt;Project.nvim&lt;/a&gt; is a plugin that aims to provide a simple and intuitive project management experience. Unfortunately, it can't save sessions like Neovim Session Manager, and can't work in tandem with it. The main function of the &lt;code&gt;Project.nvim&lt;/code&gt; is to detect project root by patterns and LSP, but this task is quite challenging and sometimes it misses. This project has a lot of opened PR. It seems that developer is not interested in these changes or is no longer interested in supporting this plugin.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI
&lt;/h3&gt;

&lt;p&gt;It uses a Telescope:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F36672196%2F129409509-62340f10-4dd0-4c1a-9252-8bfedf2a9945.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F36672196%2F129409509-62340f10-4dd0-4c1a-9252-8bfedf2a9945.png" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Neovim Project
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/coffebar/neovim-project" rel="noopener noreferrer"&gt;Neovim Project&lt;/a&gt; bridges the gap and addresses the challenges of comprehensive project management. It not only works seamlessly with Neovim Session Manager but also incorporates it as a module. Therefore, when you install Neovim Project, you also acquire Session Manager, eliminating the need to configure two separate plugins.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI
&lt;/h3&gt;

&lt;p&gt;It uses a Telescope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F3100053%2F268505274-b75e9373-d694-48e4-abbf-3abfe98ae46f.png" alt="img"&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configuration and project structure
&lt;/h3&gt;

&lt;p&gt;Neovim Project encourages a structured and organized approach to project management. It adopts a declarative project structure, requiring you to decide in which directory your projects should be placed. This effectively resolves the issue of determining project root directories and enables the plugin to automatically discover all your projects as soon as you create a single project directory. You have the flexibility to categorize projects by context, programming language, or any other criteria, depending on the masks you add to the plugin's configuration. It even supports nested projects.&lt;/p&gt;

&lt;p&gt;Here's an example of my configuration:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;projects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"~/proj/work/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"~/proj/github/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"~/proj/aur/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"~/.config/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here is another interesting thing. Project's &lt;a href="https://github.com/coffebar/neovim-project" rel="noopener noreferrer"&gt;README&lt;/a&gt; suggests adding &lt;code&gt;.config/*&lt;/code&gt; mask. This is an excellent idea, as it allows you to see your Neovim configuration as a project. This is particularly useful for editing other dotfiles. For instance, you can open a project like &lt;code&gt;bspwm&lt;/code&gt; to quickly locate and edit your &lt;code&gt;bspwmrc&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keybindings
&lt;/h3&gt;

&lt;p&gt;I have set up three keybindings for Neovim Project. These keybindings trigger the following commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;:Telescope neovim-project history&lt;/code&gt; - This command allows you to select a project from your recent history.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;:Telescope neovim-project discover&lt;/code&gt; - Use this command to find a project based on patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;:NeovimProjectLoadRecent&lt;/code&gt; - This command is used to open the previous session.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most frequently used command among these is the first one (history). I use it to switch between projects with Telescope fuzzy finder. It displays projects sorted by access time. However, it won't show a project until it has been accessed at least once. To address this, I use the &lt;code&gt;discover&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NeovimProjectLoadRecent&lt;/strong&gt; command is invaluable when you need to swiftly return to the previously accessed project, whether for reference or to copy something.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Efficiency and organization are at the core of every developer's success. The right tools and plugins can significantly enhance your productivity and project management. With Neovim ecosystem, you have the power to create a tailored development environment that meets your specific needs. Whether you opt for Neovim Session Manager, Project.nvim, or Neovim Project, each plugin brings its unique strengths to the table. Choose the one that aligns with your workflow and enjoy a more streamlined, efficient, and organized coding experience. Happy coding!&lt;/p&gt;

</description>
      <category>neovim</category>
      <category>linux</category>
      <category>vim</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Telegram bot for Google Sheets in cloud</title>
      <dc:creator>Saltaformajo</dc:creator>
      <pubDate>Sun, 06 Sep 2020 07:05:26 +0000</pubDate>
      <link>https://dev.to/saltaformajo/telegram-bot-for-google-sheets-in-cloud-3akc</link>
      <guid>https://dev.to/saltaformajo/telegram-bot-for-google-sheets-in-cloud-3akc</guid>
      <description>&lt;p&gt;Google Sheets is a powerful cross-platform app with a rich set of formulas and the ability to integrate with other apps. &lt;br&gt;
However, such functionality is often redundant for simple tasks, which makes the work not very convenient. This is especially true for mobile devices. So it would be cool to make it possible to work with tables through other interfaces.&lt;/p&gt;

&lt;p&gt;One option is to create a Telegram bot. This bot will receive some data from user and write it as new row into Google Sheets table.&lt;br&gt;
It can be hosted for free! &lt;br&gt;
&lt;strong&gt;With zero cost, we get a convenient combination of 2 powerful tools.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Tables API
&lt;/h2&gt;

&lt;p&gt;The first step is to look at the tables documentation: &lt;a href="https://developers.google.com/sheets/api/quickstart/nodejs"&gt;https://developers.google.com/sheets/api/quickstart/nodejs&lt;/a&gt;&lt;br&gt;
You can found a very useful button on this page:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k3IigJN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/n7e08a0m1ntufbgjbkm8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k3IigJN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/n7e08a0m1ntufbgjbkm8.png" alt="Alt Text" width="800" height="249"&gt;&lt;/a&gt;&lt;br&gt;
Click on that button to create a new project and download credentials file (credentials.json).&lt;/p&gt;

&lt;p&gt;Create a new file package.json&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "spreadsheets-bot",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" &amp;amp;&amp;amp; exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "googleapis": "^59.0.0",
    "telegraf": "^3.38.0"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This package file contains 2 dependencies: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;googleapis - library for google api&lt;/li&gt;
&lt;li&gt;telegraf - Telegram Bot framework&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can run &lt;code&gt;npm install&lt;/code&gt; to install this dependencies.&lt;/p&gt;

&lt;p&gt;In the meantime, let's look at an &lt;a href="https://github.com/gsuitedevs/node-samples/blob/master/sheets/quickstart/index.js"&gt;example provided by Google&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');

// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = 'token.json';

// Load client secrets from a local file.
fs.readFile('credentials.json', (err, content) =&amp;gt; {
  if (err) return console.log('Error loading client secret file:', err);
  // Authorize a client with credentials, then call the Google Sheets API.
  authorize(JSON.parse(content), listMajors);
});
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quickstart code solves the authorization problem to gain access to your personal tables, which is what we need. &lt;br&gt;
However, it is advisable to immediately make a change: remove ".readonly" from the line&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly'];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will change readonly access to read &amp;amp; write.&lt;/p&gt;

&lt;p&gt;You need to put your credentials.json into project folder, run this file (index.json) by &lt;code&gt;node index.js&lt;/code&gt;, open given link in browser, obtain a key, put this key into console. &lt;/p&gt;

&lt;p&gt;If everything is done correctly, a file token.json will be created. Next authorizationы will work using the token from this file. Your bot will have access to all of your spreadsheets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Telegram Bot
&lt;/h2&gt;

&lt;p&gt;Open &lt;a class="mentioned-user" href="https://dev.to/botfather"&gt;@botfather&lt;/a&gt; in Telegram and send command &lt;code&gt;/newbot&lt;/code&gt;. Provide a name for bot and you will get access token.&lt;br&gt;
For our example we can hardcode this token in the script index.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');

const { Telegraf } = require('telegraf'); // import Telegraf
// Telegram bot access token:
const BOT_TOKEN = '1234567890:a1b2Cg2cLR4LGO1EpvDHawYPVjunkW3Bfk9';
// spreadsheets scope: read &amp;amp; write
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets'];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://telegraf.js.org/"&gt;telegraf.js docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can receive messages and send replies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const bot = new Telegraf(BOT_TOKEN);

// Load client secrets from a local file.
fs.readFile('credentials.json', (err, content) =&amp;gt; {
    if (err) return console.log('Error loading client secret file:', err);
    // Authorize a client with credentials, then call the Google Sheets API.
    authorize(JSON.parse(content), startBot);
});

function startBot(auth) {
    bot.on('message', (ctx) =&amp;gt; {
        if (ctx.from.id !== '123456789') { // your user id
            return ctx.reply('Access Denied');
        }
        console.log(ctx.message.text);
        ctx.reply("Hi, " + ctx.from.id);
    });
    bot.launch();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the task, you can compose commands or a dialogue with the bot, in which you can use the ability to read and write from tables.&lt;/p&gt;

&lt;p&gt;Here is an example of writing to a table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
const rowNumber = "2";
let values = [
    [
        "Formula:", // string value
        "=128/2+ROUND(8/3;2)", // any formula
        "User:",
        "Ben", 
        '', // empty cell
     ]
];
const resource = {values};
let valueInputOption = [
   'RAW', // as string
   'USER_ENTERED',// will be parsed as user input
   'USER_ENTERED',
   'USER_ENTERED',
   'USER_ENTERED',
];
const range = sheet + '!A' + rowNumber + ':E' + rowNumber;
const spreadsheetId = 'mKj7bEUzG7miu4m5nsBt4KWTM6IIgstwn9g1a7IvVwz0';

sheets.spreadsheets.values.update({
   spreadsheetId,
   range,
   valueInputOption,
   resource,
}, (err, result) =&amp;gt; {
   if (err) {
      // Handle error
      console.log(err);
      ctx.reply(err.toString());
   } else {
      ctx.reply("Data saved."); //reply from bot to user
      console.log('%d cells updated.', result.data.updatedCells);
   }

});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Upload your bot to cloud for free
&lt;/h2&gt;

&lt;p&gt;For convenience, the bot must be running 24/7. Then you can use it from your phone even when all your computers are turned off.&lt;/p&gt;

&lt;p&gt;One of the options is Free Trial on Google Cloud Platform:&lt;br&gt;
&lt;a href="https://console.cloud.google.com/"&gt;https://console.cloud.google.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q5NQEJ_s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/dnsxcog64jyu3bqu9ll7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q5NQEJ_s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/dnsxcog64jyu3bqu9ll7.png" alt="Alt Text" width="508" height="885"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For me this is not charging anything. Because the load on the server is too low.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y5Q8npfy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/74nijndvsmwfz3xthrj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y5Q8npfy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/74nijndvsmwfz3xthrj2.png" alt="Alt Text" width="401" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need to setup a new minimal cloud VM with Ubuntu or what you like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XryomRtn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/fwfsq96ffn372c5dls1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XryomRtn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/fwfsq96ffn372c5dls1u.png" alt="Alt Text" width="491" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you connect to your new server by ssh, you can install node, npm and run your bot.&lt;/p&gt;

&lt;p&gt;I recommend using PM2 to run script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# install node &amp;amp; npm
curl -sL https://nsolid-deb.nodesource.com/nsolid_setup_3.x | sudo bash
sudo apt install -y nodejs npm
# install pm2
sudo npm install pm2@latest -g
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple upload your script with rsync from local host to google cloud host: &lt;br&gt;
&lt;code&gt;rsync -ruv LOCAL_PROJECT_PATH SERVER_HOST:REMOTE_PATH&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and start on the remote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd REMOTE_PATH
pm2 start index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that you should replace all my "uppercase" with your data (path to the project, server's ip, etc.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HNVcQ6e3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/dc74701k2c7qrhvdsds5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HNVcQ6e3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/dc74701k2c7qrhvdsds5.png" alt="Alt Text" width="727" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bot is started and you can use it 24/7&lt;/p&gt;

</description>
      <category>node</category>
      <category>telegram</category>
      <category>sheets</category>
      <category>googlecloud</category>
    </item>
  </channel>
</rss>
