Telegram CLI on Raspberry PI

Telegram is an (encrypted) messenger service which also offers an API. This makes it ideal for bots and automation. Here it is shown hot to set up telegram-cli on your Pi.

Get a shell on your RasPi, I prefer to do this over SSH. You can also plug your RasPi to a screen and ann a keyboard.

Build

sudo apt-get install -y git
sudo apt-get install -y libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev make
git clone --recursive https://github.com/vysheng/tg.git && cd tg
./configure
make
mkdir ~/bin
cp bin/telegram-cli ~/bin/

Then, add this like to your ~/.bashrc to make the binary available in your PATH:

export PATH=$HOME/bin:$PATH

Login

telegram-cli -k <your pubkey>

Get the public key from https://my.telegram.org (Manual), you have to register a new app. Save the contents to a file.

Then you have to enter the code you get per sms to your phone.

You can now use the telegram CLI.

Hack the spectrum - hide images in it!

You have probably heard of the song [Equation] by Aphex Twin. It is known for having a scary face hidden in its spectrum.

So I asked myself, how can I do this by myself? It is possible using a bit of matlab and basic knowledge in signal processing.

I choose a short mp3 track to modify: Cartoon (Interlude) by French House Artist SabastiAn.

This is the image we’ll try to hide:

Skull
Skull

Here is the matlab code and an explanation for it.

clear;

fname = '12 Cartoon (Interlude).mp3';
imgname = 'img1.png';
outname = 'skull.wav';

% duration/pos. of projected image (seconds)
duration = 15;
offset = 2;

window_fact = 0;
%apply_func = @(S, Img) S .* (-(Img > 0)*5 + 1);
apply_func = @(S, Img) S ./ (1 + Img * 2);

%Hz
Fmin = 7000;
Fmax = 12000;

% vertical resolution of projected image
% bigger value makes stripe-pattern, sound
% gets chirping
hres = 200;

% read audio
[y,Fs] = audioread(fname);
track_len = size(y,1);

% chunk size
chunk_s = floor(hres / (Fmax-Fmin) * Fs/2) * 2;

% channels
y1 = y(:,1);
y2 = y(:,2);

pad_len = chunk_s - mod(track_len, chunk_s);
track_len = track_len + pad_len;
y1 = [y1; zeros(pad_len, 1)];
y2 = [y2; zeros(pad_len, 1)];

y1 = reshape(y1, chunk_s, track_len/chunk_s);
y2 = reshape(y2, chunk_s, track_len/chunk_s);

% we have real data, so only take first half of fft
get_half = @(Y) flipud(Y(1:chunk_s/2+1,:));
get_whole = @(Y) [flipud(Y); conj(Y(2:end-1,:))];
Y1 = get_half(fft(y1));
Y2 = get_half(fft(y2));

%%%% find top frequency for later filtering
freq = fliplr(log(sum(abs(Y1) + abs(Y2),2))');
[m, index] = max(smooth(freq < min(freq) * 1.1, chunk_s/10) > 0.5);
filter_freq = floor(index / size(Y1,1) * Fs / 2);

%%%% load the image, put in right shape %%%%

I = imread(imgname);
if ndims(I) == 3
    I = rgb2gray(I);
end
I = imcomplement(I);
% I = imcomplement(I);
I = imresize(I, [hres duration*Fs/chunk_s]);
I = double(I);

% append zeros left and right

I = [zeros(hres, floor(offset*Fs/chunk_s)) I];
I = [I zeros(hres, size(Y1,2)-size(I,2))];

% append zeros top and bottom
I = [zeros(floor(chunk_s/2 - (chunk_s*Fmax/Fs)), size(Y1,2)); I];
I = [I; zeros((size(Y1,1) - size(I,1)), size(Y1,2))];

%%%% append window to filter %%%%
I = ifft(get_whole(I));
I = reshape(I, 1, track_len)';

window = hanning(size(I,1));
I = I .* (1 - window_fact + window_fact * window);

I = reshape(I', chunk_s, track_len/chunk_s);
I = get_half(fft(I));

%%%% append image %%%%
% plot before
colormap(hsv);
ax1 = subplot(2,1,1);
image(sqrt(abs(Y1)) * 15);
ax1.XTick = linspace(0, size(Y1,2), track_len/Fs);
ax1.XTickLabel = 0:track_len/Fs;
max_freq = floor(Fs / 2 / 1000) * 1000;
ax1.YTickLabel = linspace(max_freq, 0, max_freq/2000 + 1);
ax1.YTick = fliplr(linspace(max_freq, 0, max_freq/2000 + 1)/(Fs/2)*chunk_s/2);

Y1 = apply_func(Y1, I);
Y2 = apply_func(Y2, I);

% plot after
ax2 = subplot(2,1,2);
image(sqrt(abs(Y1)) * 15);
ax2.XTick = ax1.XTick;
ax2.XTickLabel = ax1.XTickLabel;
ax2.YTick = ax1.YTick;
ax2.YTickLabel = ax1.YTickLabel;

%%%% reversion %%%%
y1 = ifft(get_whole(Y1));
y2 = ifft(get_whole(Y2));

y1 = reshape(y1, 1, track_len)';
y2 = reshape(y2, 1, track_len)';

%%%% low-pass filter %%%%
df = designfilt('lowpassfir','FilterOrder',200,'CutoffFrequency',filter_freq/(Fs/2));
D = mean(grpdelay(df));

y1 = filter(df,[y1; zeros(D,1)]);
y2 = filter(df,[y2; zeros(D,1)]);
y1 = y1(D+1:end);
y2 = y2(D+1:end);

audiowrite(outname, [y1, y2], Fs);

First, there are many parameters to set:

  • names of the files to read in and write out
  • how long the part of the song with modified spectrum should be
  • where in the track to place the image
  • between which frequencies to place the image
  • spectrum resolution to work with

Basically, we then read in the song and apply a FFT on it. We than throw away the first half of the FFT because our input data was real, and thus it is just a reflection of the second half. Then we find the maximum frequency occurring in the track now, so we can use a lowpass filter later to get rid of artifacts.

The comes the interesting step: we load the image, and add it to the FFT after scaling. After that, we just reverse all the transformations we did before, and apply a lowpass filter.

This is the resulting image (I used Spek to view the spectrum):

Skull in spectrum
Skull in spectrum

If you listen closely in the modified track, you can hear the chirping and missing of some frequencies in the modified version. This is why I choose a french house track: There are many high frequencies so it won’t be that bad when some go missing.

Painless way to install NumPy & Co. on OSX

When working with scientific data, NumPy, SciPy etc. are often used. They can be quite a pain in the ass to setup: many dependencies, some modules have to be compiled (which sometimes works…), so often PIP does not work out of the box. Also, the Apple Developer Command Line Tools have to be installed for this to use the compilers.

The very simple solution to this is Anaconda, a Python distribution containing almost anything would ever want: NumPy, Skikit-Learn, and also file format libraries like H5Py.

Just grab the installer here. I choose to install it into my home folder, so the anaconda python interpreter is to be found as $HOME/anaconda/bin/python.

If you want to have an additional package in the distribution which is available via PIP, just use the distributions pip:

$HOME/anaconda/bin/pip install somepackage

Anaconda also has its own package management system, conda. To install OpenCV, for example:

$HOME/anaconda/bin/conda install -c https://conda.binstar.org/jjhelmus opencv

I have written a little wrapper script for using the anaconda interpreter:

#!/bin/bash
    
run="$1"
[ -z "$1" ] && run='process.py'
    
if [ "$run" = 'repl' ]; then
    run=''
fi
    
if [ -x "$HOME/anaconda/bin/python" ]; then
    "$HOME/anaconda/bin/python" $run
else
    python $run
fi

Per default, it tries to run a script called process.py. ./run.sh repl starts the Python REPL.

Das ultimative Shell-Setup

Als Softwareentwickler unter OSX und Linux ist die Kommandozeilen-Shell ein ständiger Begleiter. Sie macht vieles möglich, was per GUI unmöglich oder einfach vergleichsweise aufwändig ist. Dies gilt vor allem unter OSX, wo man mit dem Finder nur eine ziemlich beschränkte und unflexible GUI-Shell zur Verfügung hat. Zwangsweise darauf ausweichen muss man, um per SSH Linux-Server zu administrieren. Wie in der Welt der grafischen Oberflächen hat sich auch in der Welt der CLIs (Command Line Interfaces) viel getan. Viele Lösungen existieren eigentlich schon seit Jahren (z.B. zsh), werden aber viel weniger schnell adaptiert wie Verbesserungen des GUI. Da sind vor allem die OS-Hersteller und Linux-Distributoren schuld, die immer noch auf veraltete Standardeinstellungen setzen (Bash anstatt ZSH, keine Farb-Einstellungen etc.).

Installation

Um alles hier aufgeführte bequem zu installieren: siehe hier, im Git-Repo.

iTerm2

Viel besser als für OSX mitgelieferte Terminal App ist iTerm2. Es lässt z.B. ein “Hotkey Window” einrichten, dass auf eine Taste hin erscheint und wieder verschwindet. So hat man die Shell überall und immer verfügbar.

ZSH und Oh-my-ZSH

Ich benutze seit geraumer Zeit die ZSH (Z Shell) anstelle von Bash. Sie bietet viele benutzerfreundliche Verbesserungen, z.B. bei der Autovervollständigung. Die Killer-Applikation ist jedoch “Oh my Zsh” von Roby Russel. Das ist ein Paket von Scripts, Plugins und Themes für die Z-Shell. Installieren lässt es sich sehr einfach per Git:

git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh
# .zshrc ist das Äquivalent zu .bashrc
cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc
# default shell: bash durch zsh ersetzen
chsh -s /bin/zsh

Im Home-Verzeichnis gibt es nun den Ordner .oh-my-zsh, sowie die Datei .zshrc. Schaut euch die Datei mal in eurem Lieblings-Texteditor an. Die wichtigsten beiden Zeilen sind:

# Look in ~/.oh-my-zsh/themes/
ZSH_THEME="agnoster-joni"

plugins=(svn gnu-utils python sublime osx macports adb debian rvm)

Hier können also die Plugins und Shell Prompt Themes festgelegt werden. Unter ~/.oh-my-zsh/themes/ und ~/.oh-my-zsh/plugins/ finden sich die verfügbaren Prompt Themes und Plugins.

Promt Themes

Ich verwende hier agnoster-joni. Dies ist eine Zusammenführung agnoster-Themes (https://gist.github.com/3712874) und meines alten joni-Themes, das sich im angehängten Repo ebenfalls noch findet.

Wie es aussieht, probiert man am besten selbst aus. Nur einer von vielen Vorteilen von zsh ist, dass man auch ein Prompt für die Rechte Seite setzen kann. Ich zeige z.B. den Return Code des letzten Kommandos an, falls er nicht 0 (d.H. kein Fehler) war:

local return_code="%(?.. %{$bg[red]%}%{$fg[white]%}%? ↵ %{$reset_color%})"
RPROMPT="${return_code}"

Aliase

Shell Aliase dienen dazu, häufig benötigte Kommandos abzukürzen. Unter Oh-My-Zsh werden sie unter ~/.oh-my-zsh/custom abgelegt. Ich habe 4 Dateien, wobei anzumerken ist, dass ich diese Konfiguration unter Linux-Servern sowie auf meinem Macbook benutze:

  • aliases_global.zsh: Gelten für überall
  • aliases_linux.zsh: Nur unter Linux
  • aliases_osx.zsh: Nur unter OSX
  • aliases_local.zsh: Nur auf der lokalen Maschine. Im Repo gibt es nur aliases_local.zsh.example dabei, es wird vom Setup-Script nach aliases_local.zsh kopiert und sollte unbedingt angepasst werden.
Meteor - Notes to myself

Folder structure

  • server: svr-only js
  • client: client-only js
  • public: webroot
  • private: private server data assets

js everywhere else: executed by server and client

Atmosphere/Meteorite

Get additional packages from Atmosphere by using Meteorite.

npm install -g meteorite
mrt add router

Tutorials/Resources

Meteor Apps (as examples)