Pull to refresh

Мигаем Scroll lock'ом при входящем email сообщении (perl + bash)

Reading time3 min
Views8.5K
По мотивам этого вопроса.

Задача: научиться моргать светодиодом клавиатуры в случае, если есть новые непрочитанные сообщения в нашем почтовом ящике.

Доп. условия: ОС — linux, язык — perl (проверка почты) и bash (собственно моргание).



Собственно как и советовали в изначальном топике разобьем задачу на составляющие:
  1. Научиться моргать диодом
  2. Научиться проверять почту на наличие новых сообщений
  3. Подружить оба пункта между собой


Учимся моргать диодом

Существует демон Ledcontrol, однако в моих репозитариях его не оказалось, поэтому я буду мигать курсором используя команду xset
#!/bin/bash
trap 'xset -led named "Scroll Lock"; exit ' SIGINT SIGTERM 

while(true); do
	xset led named "Scroll Lock" ; 
	sleep 0.05 ; 
	xset -led named "Scroll Lock"; 
	sleep 0.05; 
done

этот код был сохранен в файл beepScroll, файл положен в один из каталогов перечисленных в $PATH и даны права на выполнение. Обратите внимание что при получении SIGINT мы выключаем светодиод и выключаемся через trap.
Учимся проверять почту на наличие новых сообщений

sub mymail(){
    use Mail::IMAPClient;
    use IO::Socket::SSL;
    	while (42){
		my $user = 'login';
		my $server = 'server.ltd';
		my $pass = 'P@ssw0rd';
		
		my $socket = IO::Socket::SSL->new(
				PeerAddr => $server,
				PeerPort => 993,
			) or die "socket(): $@";
		my $imap = Mail::IMAPClient->new(
		Socket   => $socket,
		Server => $server,
		User => $user,
		Password => $pass,
		Debug => 0) or die "Cannot connect to $server as $user: $@";
		my $not_read;
		foreach my $f ($imap->folders) {
			my $i =  $imap->unseen_count($f)||0 ;
			$not_read += $i;        
		}
		$imap->logout();
		print $not_read; 
	}
}


Здесь мы подключаем нужные модули (Mail::IMAPClient не входит в стандартную поставку, его потребуется доставить отдельно) и запускаем бесконечный цикл который 1) подключается по imap к серверу, получает количество непрочитанных сообщений в директории INBOX и ее поддиректориях и возвращает их число, ждет минуту и начинает с начала.

Дружим все вместе

Тут бы хотелось остановиться подробнее. Мы будем запускать 2 потока. Первый будет проверять почту и посылать количество непрочитанных сообщений второму. Второй будет получать сообщения от первого запускать\останавливать скрипт моргания курсором.
Итак:
#!/usr/bin/perl
# Нужные модули
use strict;
use threads; #Модуль потоков
use Thread::Queue; #Модуль для организации общей очереди между потоками

my $mypipe = Thread::Queue->new; #Инициализируем очередь

# Стартуем оба потока
threads->create(\&mymail); # Проверка почты
threads->create(\&myleds)->join; #Разбор сообщений от первого потока

join во втором потоке значит что нам перед продолжением действия требуется дождаться окончания работы этого потока.
Сам код функции myleds:
sub myleds(){
	my $beep = 0; #1 если сейчас есть новые сообщения. 
	my $mypid; # pid процесса, который мограет
	
	while($_ = $mypipe->dequeue){ # Принимаем сообщения из очереди
		if ($_ > 1){ 
			if ($beep == 0){
				$mypid = `nohup beepScroll > /dev/null 2>&1 &echo \$!`;  #Такая хитрая конструкция для получения pid'а запущенного процесса
				$beep = 1;
			} 
		} else {
		`kill $mypid` if $mypid; # Если новых писем нет, то выключаем моргание и обнуляем переменные.
		$mypid = 0 if $mypid;
		$beep = 0;
		}
	}
}
, а в функции mymail() следует заменить
prinr $not_read
на
$mypipe->enqueue($not_read+1)
"$not_read+1" потому что если while получит 0, то посчитает что условия ложное.

Конечная версия скрипта
#!/usr/bin/perl

use strict;
use threads;
use Thread::Queue;

my $mypipe = Thread::Queue->new;
threads->create(\&mymail);
threads->create(\&myleds)->join;

sub mymail(){
use Mail::IMAPClient;
use IO::Socket::SSL;
	while (42){
		my $user = 'login';
		my $server = 'server.ltd';
		my $pass = 'P@ssw0rd';
		
		my $socket = IO::Socket::SSL->new(
				PeerAddr => $server,
				PeerPort => 993,
			) or die "socket(): $@";
		my $imap = Mail::IMAPClient->new(
		Socket   => $socket,
		Server => $server,
		User => $user,
		Password => $pass,
		Debug => 0) or die "Cannot connect to $server as $user: $@";
		my $not_read;
		foreach my $f ($imap->folders) {
			my $i =  $imap->unseen_count($f)||0 ;
			$not_read += $i;        
		}
		$imap->logout();
		$mypipe->enqueue($not_read+1); 
		sleep 60;
	}
	# Это никогда не выполниться ... но на всякий случай :)
	$mypipe->enqueue(undef);
	exit 1;
}
sub myleds(){
	my $beep = 0;
	my $mypid;
	
	while($_ = $mypipe->dequeue){
		if ($_ > 1){ 
			if ($beep == 0){
				$mypid = `nohup beepScroll > /dev/null 2>&1 &echo \$!`;
				$beep = 1;
			} 
		} else {
			$beep = 0;
			`kill $mypid` if $mypid;
			$mypid = 0 if $mypid;
		}
	}
}
Tags:
Hubs:
Total votes 18: ↑18 and ↓0+18
Comments2

Articles