Remote-controlling macOS with a Python Telegram bot

Ray Chen
Chatbots Life
Published in
5 min readSep 20, 2017

I needed a quick way to remotely perform system operations: adjusting & muting the system volume, screen brightness, and putting the display to sleep. After exploring several options, I found none of them to be viable for me, and thus, I set out to develop my own solution.

My requirements were simple, but if I wanted this to be truly remote and accessible anywhere, my only options are the Web or a bot I can send commands to. I decided on the latter because it’s easier to secure than a web app, and I decided on Telegram because it’s widely available and probably the most straightforward.

The full code is available on Github. If you run into any troubles with the steps below, look to the repository as a reference.

First things first,

We’ll need to create a Telegram bot by speaking to @BotFather. Follow the on-screen instructions: name your bot and choose a username for it, and at the end of the steps, you will be given an API access token. We’ll use this token to interact with Telegram’s API.

Tip: If you don’t want your bot to be discovered, choose a random username or prepend some random characters to it.

Next, create a new Python project and install python-telegram-bot:

$ mkdir tg-mac-remote && cd tg-mac-remote
$ virtualenv .venv
$ source .venv/bin/activate
$ pip install python-telegram-bot
$ touch bot.py

Open bot.py in your editor of choice, and we’re ready to get started. Let’s check and see if our bot is working by having it respond to the “/hello” command with our Telegram first name and user ID:

bot.py, “/hello” command

Note: If you’re unsure how python-telegram-bot works, check out the “Getting started” section of their Github repo.

Run python bot.py and hit your bot up on Telegram with “/hello”. It should greet you with your Telegram first name and UID. Great! Now our bot is alive and responding. Make a note of your Telegram UID, we’ll be using this to ensure the bot only responds to our commands.

Before we proceed further…

Let’s take a step back and think about how we can programatically adjust the system volume, display brightness, and turn the display off. How do we do all of that programatically, and with Python?

PyObjC will be the first answer people usually jump to, but there’s an easier way: command line utilities. Instead of trying to manipulate the system natively through PyObjC, we will be calling several command line utilities with Python’s subprocess.

osascript, brightness, and pmset

We will use osascript to execute AppleScript. Turns out, there’s a ton of stuff we can accomplish with AppleScript. This article (links to osxdaily.com) gives a nice round-up of what we can do. To set the system volume, we can run:

$ osascript -e "set Volume n"

in a Terminal window to set the system volume to n. Handy, isn’t it? I’d think everything we want to accomplish can be done through AppleScript one way or another, and that is true to a certain extent, but most of that involves opening other applications and interacting with the UI through AppleScript. This is rather slow, so we’ll use other CLI utilities more suitable to the operation we wish to perform.

To turn off the display immediately, we can use $ pmset displaysleepnow. To adjust screen brightness, we’re going to install brightness and verify that it works:

$ brew install brightness
$ brightness 0.3 # set display brightness to 30%

Now that we have everything thought through, let’s write wrappers for the above utilities.

Create a new file named commands.py in the same directory as bot.py. We’ll make use of subprocess.run() to call the CLI utilities. In each of the functions, we will return a string indicating the action performed for the bot to reply with. All command logic goes here; the bot should only be concerned with calling the appropriate function, passing in any arguments, and replying to the user.

commands.py

Note: subprocess.run() is only available in > Python 3.5, so if you’re using an older version, you might have to make the calls another way. Refer to the Python docs.

And back to the bot…

Now we can import the functions above for use in bot.py, but before that, we’re going to ensure the bot only processes commands from authorized users. You certainly don’t want a bot capable of executing such commands to just listen to anyone, do you? Especially when Telegram bots are public. There’s a neat @restricted decorator from python-telegram-bot’s docs that does exactly what we want:

bot.py, @restricted decorator

Next, let’s write the command handlers. We have two commands: /v for volume, and /d for display. Both of them takes exactly one argument:

/v mute      - Mute or unmute system volume.
/v n(0-100) - Set system volume to n.
/d sleep - Put display to sleep.
/d n(0-100) - Set display brightness to n.

We will call the appropriate function according to the argument received. For all other cases, we assume the command is invalid and reply with the command’s syntax. And let’s not forget to restrict these commands to authorized users only with @restricted. Translating that into code:

bot.py, “/v” and “/d” command handlers

Finally…

Add the command handlers to the dispatcher:

bot.py, add commands to dispatcher

And we’re all set! Fire up the bot and take it for a spin.

What now?

It’s up to you — hack away! The full code is available on Github.

You could add more commands yourself; anything you wish to do remotely with AppleScript and such, you just have to follow through the process outlined above.

Or you could look into other methods for manipulating the system. What about emulating keypresses? Maybe do all of that through AppleScript? For instance, emulating F1 & F2 keys for brightness control would eliminate the need for brightness.

Or if you’d like to mess around further with Telegram bots, you can look into making a custom keyboard to make it fancier. Admittedly, the status quo is not very user-friendly. Why type commands when you can just press a button?

Or if you want to use this on other platforms (Messenger, Slack, etc.), you can adapt the commands to them easily because they are decoupled from the bot; there’s nothing Telegram-specific in commands.py, you can import it for use in whatever. And don’t forget, be careful not to let unauthorized users command your bot.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Published in Chatbots Life

Best place to learn about Conversational AI. We share the latest News, Info, AI & NLP, Tools, Tutorials & More.

No responses yet

What are your thoughts?