Browsing articles tagged with " not very good" I’m in your IRC, SMSing your SMSes

This script is a proof-of-concept that I wanted to try more than anything else. Lightly modelled on bitlbee, it pretends to be an IRC server, and accepts/sends SMSes via a gateway (or a gateway app on your Android phone). It’s very much not a finished product and is known to have memory leaks. It also doesn’t clean up after it’s threads.

This script requires threads, threads::shared and IO::Socket.

Config-wise, it’s fairly straightforward – it’s all done in lines 6-21 of the script source. Set $IRCport and $IRCbind to the IP and TCP port you want the server to listen on. Set $IRCpassword to be the password that the IRC client must submit to connect.

The only part that gets tricky is interacting with the gateway. $SMScommand should be set to a script to pass the SMS off to the gateway for delivery. You can use the variables %TO% and %TEXT% in the script path as parameters – these will be replaced with the recipient and the text of the message respectively before the script is called. This could be a PHP script (“/usr/bin/php send-sms.php %TO% ‘%TEXT%’”), a URL using curl or wget (“/usr/bin/wget http://localhost/cgi-bin/sms.cgi?to=%TO%&text=%TEXT%”), an email address using sendmail or similar (“echo %TEXT% | /usr/sbin/sendmail”) or anything else you might want to use.

Likewise, receiving SMSes can be a bit tricky. $SMSreceiveKey needs to be set to a key that will be used to ensure that the SMSes are received from the real gateway. SMSes are accepted from the gateway by connecting to the IRC server socket and sending nothing more than:

DELIVER sms-receive-key 1234567890 :SMS text goes here.\r\n

The connection does not have be registered as an IRC client or authenticated to the server. It’s important to note that the IP and port you have set earlier in the configuration must be accessible by whatever is delivering the SMSes as well, or it will not be able to connect. In a lot of cases, SMS gateway services or gateway apps running on your phone will only support email forwarding or HTTP delivery of incoming texts. In either of these cases, I suggest creating a script to deliver the SMS for you. In the former case, a Perl script that gets called by a pipe from the mail server which connects to the server’s socket and deliver’s the SMS would be pretty straightforward. Likewise, a PHP script on a webserver could be configured to accept an SMS by GET/POST parameters and connect to the socket to deliver it.

Use-wise, it’s fairly straightforward. You can type ‘help’ in #sms after connecting for a short summary of this information. When you receive an SMS, the phone number it is received from will join #sms and address the text message to your attention:

* 1234567890 has joined #sms.
<1234567890> your-nickname: Hello there.

You can reply in kind:

<your-nickname> 1234567890: Hiya.

You can also send a text message to a number not presently in the channel the same way – just address the message at the number and SMSbot will ensure it’s delivered for you.

You can also message people by opening a private chat with them – either use ‘/query 123456790′ or double-click their name on the userlist of most IRC clients to open a private chat. Send your message, and replies will automatically be sent back to you in private message. To switch back to receiving their messages in #sms, just message them from the channel again and their replies will go there instead.

Finally, you can use the channel’s userlist as a sort of contact list by adding and removing users from it. When you receive an SMS from someone not already in the channel, they will join. You can also add them to the channel without sending them a message:

<your-nickname> add 1234567890
* 1234567890 has joined #sms.

Likewise, you can remove someone from the channel whether they joined because you added them or because they sent you a message:

<your-nickname> remove 1234567890
* 1234567890 has left #sms.

If you remove someone from the channel, they will join again the next time they send you a message.

Script Source

use IO::Socket;
use threads;
use threads::shared;

##### Config #####

# IRCd settings. At least change the password.
my $IRCport = "21337";
my $IRCbind = "";
my $IRCpassword = "irc4sms";

# Shell command to send SMSes via gateway.
# Consider using wget or sendmail!
# Variables! %TO% and %TEXT%
my $SMScommand = "/usr/bin/wget http://localhost/cgi-bin/send-sms.cgi?to=%TO%&text=%TEXT%";

# Key to receive inbound SMSes. Change this.
my $SMSreceiveKey = "1rc45m5!";

##### End Config ######

$version = "0.2";
$crlf = "\r\n";

my @msgQueue;
my %joined;


my $listen_socket = IO::Socket::INET->new(
	LocalPort => $IRCport,
	LocalAddr => $IRCbind,
	Listen => 10,
	Proto => 'tcp',
	Reuse => 1
die "Can't bind local socket: $!\n" unless $listen_socket;

while(my $connection = $listen_socket->accept){
	my $child = threads->create("read_data", $connection);

sub deliver_data {
	my($socket, $nickname) = @_;
		foreach my $message(@msgQueue) {
				my ($sender, $text) = $message =~ /^(.+?)\:(.+)$/;
				if($sender && $text){
						if($joined{$sender} < 1){
								$joined{$sender} = 1;
								print $socket ':'.$sender.' JOIN #sms'.$crlf;
						if($joined{$sender} == 1){ print $socket ':'.$sender.' PRIVMSG #sms :'.$nickname.': '.$text.$crlf; }
						if($joined{$sender} > 1){ print $socket ':'.$sender.' PRIVMSG '.$nickname.' :'.$text.$crlf; }
		@msgQueue = ();

sub read_data {
	my($socket) = @_;
	my $registered = 0;

	print $socket ':irc4sms.local NOTICE AUTH :irc4sms ready. Login or deliver an SMS.'.$crlf;
		$data = $_;
		print $data;
		$data =~ s/[\r\n]//ig;

		if($data =~ /^PING (.+)$/){
			print $socket ':irc4sms.local PONG irc4sms.local :'.$1.$crlf;

		if($registered < 3){
			if($data =~ /^DELIVER (.+?) (.+?) \:(.+)$/ && $1 eq $SMSreceiveKey){
				$sender = $2;
				$text = $3;
				$sender =~ s/^\+//;
				$msgQueue[++$#msgQueue] = $sender.":".$text;
				print $socket 'MESSAGE OK ('.$sender.'): '.$text.' - Queued for delivery.'.$crlf;
			}elsif($data =~ /^USER (.+?) .+ (?:\:|)(.+)$/){
				$username = $1;
				$realname = $3;
			}elsif($data =~ /^NICK (.+)$/){
				$nickname = $1;
				print $socket ':irc4sms.local 464 '.$nickname.' :P assword required.'.$crlf;
			}elsif($data =~ /^PASS (.+)$/){
				if($1 eq $IRCpassword){
					print $socket ':irc4sms.local 464 '.$nickname.' :Invalid password.'.$crlf;

			if($registered == 3){
				print $socket ':irc4sms.local 001 '.$nickname.' :Welcome'.$crlf;
				print $socket ':irc4sms.local 002 '.$nickname.' :Host irc4sms.local is running irc4sms '.$version.$crlf;
				print $socket ':irc4sms.local 003 '.$nickname.' :irc4sms ('.$crlf;
				print $socket ':irc4sms.local 004 '.$nickname.' irc4sms.local abiswRo ntC'.$crlf;
				print $socket ':irc4sms.local 005 '.$nickname.' PREFIX=(ohv)@%+ CHANTYPES=# CHANMODES=,,,ntC NICKLEN=25 CHANNELLEN=25 NETWORK=irc4sms SAFELIST CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server'.$crlf;
				print $socket ':irc4sms.local 375 '.$nickname.' :- irc4sms.local MOTD:'.$crlf;
				print $socket ':irc4sms.local 372 '.$nickname.' :- Welcome to irc4sms '.$version.$crlf;
				print $socket ':irc4sms.local 376 '.$nickname.' :End of MOTD'.$crlf;
				print $socket ':'.$nickname.'!'.$username.'@irc4sms.local MODE cm :+sR'.$crlf;
				print $socket ':'.$nickname.'!'.$username.'@irc4sms.local JOIN :#sms'.$crlf;
				print $socket ':irc4sms.local MODE #sms +Ct'.$crlf;
				print $socket ':irc4sms.local 353 '.$nickname.' = #sms :@'.$nickname.' @SMSbot'.$crlf;
				print $socket ':irc4sms.local 366 '.$nickname.' #sms :End of /NAMES list'.$crlf;
				print $socket ':irc4sms.local 332 '.$nickname.' #sms :irc4sms '.$version.$crlf;
				print $socket ':irc4sms.local 333 '.$nickname.' #sms SMSbot!SMSbot@irc4sms.local '.(time - 60).$crlf;
				$joined{$nickname} = 1;
				$joined{'SMSbot'} = 1;
				my $delivery = threads->create("deliver_data", $socket, $nickname);
			if($data =~ /^MODE/){
				print $socket ':irc4sms.local 324 '.$nickname.' #sms +Ct'.$crlf;
			}elsif($data =~ /^WHO/){
				print $socket ':irc4sms.local 352 '.$nickname.' #sms '.$username.' irc4sms.local irc4sms.local '.$nickname.' H@ :0 '.$realname.$crlf;
				print $socket ':irc4sms.local 352 '.$nickname.' #sms irc4sms irc4sms.local irc4sms.local SMSbot H@ :0 irc4sms '.$version.$crlf;
			}elsif($data =~ /^PRIVMSG \#sms \:(.+?)(?:\:|\,) (.+)$/){
				if($joined{$1} > 0){
					$joined{$1} = 1;
				$to = $1;
				$text = $2;
				$cmd = $SMScommand;
				$cmd =~ s/\%TO\%/$to/g;
				$cmd =~ s/\%TEXT\%/$text/g;
			}elsif($data =~ /^PRIVMSG ([a-zA-Z0-9\+]+?) \:(.+?)$/){
				if($joined{$1} > 0){
					$joined{$1} = 2;
				$to = $1;
				$text = $2;
				$cmd = $SMScommand;
				$cmd =~ s/\%TO\%/$to/g;
				$cmd =~ s/\%TEXT\%/$text/g;
			}elsif($data =~ /^PRIVMSG \#sms :( .+?)$/){
				$command = $1;
				if($command =~ /^add (.+)/ && !$joined{$1} ){
					$joined{$1} = 1;
					print $socket ':'.$1.' JOIN #sms'.$crlf;
				}elsif($command =~ /^remove (.+?)$/ && ($joined{$1})){
					$joined{$1} = '';
					print $socket ':'.$1.' QUIT :Leaving...'.$crlf;
				}elsif($command =~ /^help/){
					print $socket ':SMSbot PRIVMSG #sms :Commands: add 442392123456, remove 442392123456'.$crlf;
					print $socket ':SMSbot PRIVMSG #sms :If you /msg a contact, responses will automatically come as a /msg'.$crlf;
					print $socket ':SMSbot PRIVMSG #sms :Otherwise, address the contact in this channel, and their replies will come here'.$crlf;
					print $socket ':SMSbot PRIVMSG #sms :I don\'t recognise that as a valid command. Sorry. Try "help".'.$crlf;