简单的服务器端 2G 移动电话应用程序

  目前科技新闻中充斥着有关最新 iPhone、Droid 和 Palm Pre™ 应用消息但是新闻媒介更加关注简单移动电话如何为世界各地人们提供新通信和创业机会特别是在电力缺乏地区这次主要宣传在许多有网络地方人们使用移动电话不仅仅是为了通话SMS 文本消息让他们可以相互交换信息帮相他们处理细小事务

  常用缩写词

  SMS:短消息服务

  XML:可扩展标记语言

  言归正传您有多少朋友和家人没有款具有彩色高分辨率触摸屏能够浏览网站WebSite和安装各种专业软件Software移动电话?他们使用是 2G(第 2代)移动电话当这类移动电话在 20 世纪 90 年代推出时它们和第代前辈产品是区别它们是数字化可以发送文本消息对于比较节俭人来说2G 移动电话和按月付费方式还是容易承担对于全球大多数人来说3G 移动电话并不在考虑范围内去年苹果公司销售了 2500 万台 iPhone这似乎很多但 International Telecommunication Union 最近项评估表明到 2010 年底全球 68 亿人中有 50 亿人将使用移动电话这介绍说明在今后几年内世界范围内 3G 移动电话使用率相对较低

  2G 移动电话可以向电子邮件地址发送文本消息编写脚本来根据电子邮件内容自动回复也不是什么难事尤其是在您知道您脚本将会响应不超过 160 个消息时将这些结合在您将发现您可以编写对大多数 2G 电话所有者而言类似于可处理其请求专业信息来源应用作为开发人员如果您将这些移动电话看作将参数传递给所编写小型终端您将会发现向简单、廉价移动电话所有者提供信息服务非常容易

  作为例子下这样个服务它接收包含个 3 位数美国区号文本消息并返回有关该区号信息要使用它假设我移动电话上 “Missed Calls” 列表显示个区号为 “407” 人试图呼叫我如果我想知道该区号表示哪个地方我使用 2G 移动电话发送条 SMS 文本消息 “407” 到我 Area Code Information 服务然后会返回以下信息:Florida (Orlando, Florida, St. Cloud and central eastern Florida)(在本文中服务电子邮件地址是 [email protected]但在实际应用中(您可以亲自尝试)电子邮件地址为 “aci” 而不是 “acinfo”)

  该应用基本步骤(都使用了简单脚本)如下:

  检查所有收到电子邮件如果来自 [email protected]将其发送给 Python 脚本 aci.py它将执行余下步骤

  在个区号信息列表中搜索收到电子邮件正文中文本

  如果在列表中将返回消息设置为所存储相关信息(在上述例子中为 Florida (Orlando, Florida, St. Cloud and central eastern Florida))

  如果不在列表中在返回消息中介绍说明没有发现和收到消息相关信息

  将返回消息发送回发送原始邮件地址并记录下来

  我应用搜索个简单文本文件来进行信息查询但在您应用只要您能够想象得到且您脚本能够访问数据源您能够实现很多操作

  检查收到电子邮件并发送给正确处理:procmail

  自动回复所收到消息关键在于个称为 procmial 著名 UNIX® 实用许多扫描垃圾邮件和根据邮件头信息在特定文件夹中排序电子邮件最早期系统都是在 procmail 基础上建立并且现在仍可使用它如果您带有主机提供帐户使用基于 Linux® 系统且提供了 shell 访问那么您可以为您帐户创建个 procmail 配置文件扫描所收到邮件模式并根据发现结果执行操作

  对于通过此 .procmailrc 配置文件路由邮件而言还需要另外个或两个步骤在过去您可以创建 .forward 文件来路由电子邮件但是现在主机提供通常会提供个 Web 表单供您填写以告诉它们系统在邮件到达时检查 .procmailrc 文件

  在主机提供中配置帐户来执行此任务时我通过以下 3行向 .procmailrc 文件增加了条规则:

:0 
* ^To: <[email protected]>? 
| /usr/home/bobd/aci/aci.py 


  第行指出这是个 procmail 规则开始第 2行以个星号开始表示您指定了个条件行余下部分是个正则表达式指定要在邮件中从开始处开始搜索内容:“To: [email protected]邮件地址两边尖括号是可选(这些尖括号可有可无这是您在处理可能来自各种电子邮件客户和电话电子邮件时必须考虑致性个例子)我所创建这个电子邮件地址仅用于区号信息请求因此这个规则适用于向这个地址发送所有邮件

  .procmailrc 规则第 3行可以命名应该转发此邮件邮箱但这条规则所做事更加有趣竖杠符号指定邮件内容应作为输入发送到某个指定:个名为 aci.py Python 脚本

  分析输入并选择种脚本语言

  查看 aci.py 的前先看下它必须处理输入条 SMS 文本消息显示为封带有发送者地址电子邮件这个地址包含电话号码和电话公司使用域名清单 1 展示了当我通过 Verizon 网络从 LG env2™ 电话以文本消息形式发送区号 407 时显示举例 SMS 电子邮件在清单中将发出电话号码更改为了 (434) 000-0000

清单 1. 条 SMS 文本消息电子邮件版本

From [email protected] Wed Mar 10 00:50:01 2010 
Return-Path: <[email protected]> 
Delivered-To: bobd-snee:[email protected] 
X-Envelope-To: [email protected] 
Received: (qmail 21729 invoked from network); 10 Mar 2010 00:50:00 -0000 
Received: from mailwash38.pair.com (66.39.2.38) 
     by oomur.pair.com with SMTP; 10 Mar 2010 00:50:00 -0000 
Received: from localhost (localhost [127.0.0.1]) 
     by mailwash38.pair.com (Postfix) with SMTP id 021054142C 
     for <[email protected]>; Tue, 9 Mar 2010 19:50:00 -0500 (EST) 
X-Spam-Check-By: mailwash38.pair.com 
X-Spam-Status: No, hits=2.9 required=4.0 tests=BAYES_00, FROM_STARTS_WITH_NUMS, 
     MISSING_SUBJECT, TVD_SPACE_RATIO autolearn=no version=3.002005 
X-Spam-Flag: NO 
X-Spam-Level: ** 
X-Spam-Filtered: a7b240700a36d5e6c2608f9ce43a92c9 
Received: from lrx5634xmtasa.alltel.net (lrx5634xmtasa.alltel.net 
     [205.142.19.193]) 
     by mailwash38.pair.com (Postfix) with ESMTP id 3FF774142F 
     for <[email protected]>; Tue, 9 Mar 2010 19:49:59 -0500 (EST) 
X-Policy: RELAYLIST-$RELAYED 
Received: from unknown (HELO s2006qwigfe) ([10.135.9.57]) 
     by lrx5634xmtasa.alltel.net with ESMTP; 09 Mar 2010 18:49:58 -0600 
Message-ID: <26597005.1268182198841.JavaMail.root@s2006qwigfe> 
From: [email protected] 
To: [email protected] 
Subject: 
Mime-Version: 1.0 
Content-Type: text/plain; char=utf-8 
Content-Transfer-Encoding: 7bit 
Date: Tue, 9 Mar 2010 19:49:59 -0500 (EST) 
 
407 


  这里有很多代码但脚本仅需要两部分信息:发送消息设备电子邮箱地址([email protected])和它发送消息(407在最后行上)Perl 常常是首选简单文本处理脚本编写语言而且编写 Perl 脚本来从 清单 1 中电子邮件中提取电子邮件地址和信息在区号列表中查找该消息以及将请求信息发送会回表示发送电话电子邮件地址这些都比较容易

  在我移动电话上这段 Perl 脚本运行良好但是当我在更多移动电话上进行测试时发现通过移动电话发送电子邮件并不像我期望那样我前面已经提到.procmailrc 文件必须考虑到电子邮件地址在和不在尖括号中两种情况用 Perl 很容易处理这种情况事实证明其余电子邮件结构也有些可能差异需要考虑

  清单 2 显示了封更复杂电子邮件它将 “305” 作为个包括多个部分 MINE 消息从 iPhone 发出(当然iPhone 不是部 2G 电话, 但我想用它来进行测试是个不错主意)不要去找 “305”它已被编码寻找正确消息部分进行解码这使我 Perl 脚本越来越长而且它已经能够用于其他多部电话

清单 2. 条 SMS 文本消息更复杂电子邮件表示

From [email protected] Sun Feb 28 21:00:03 2010 
Return-Path: <[email protected]> 
Delivered-To: bobd-snee:[email protected] 
X-Envelope-To: [email protected] 
Received: (qmail 18219 invoked from network); 28 Feb 2010 21:00:03 -0000 
Received: from mailwash38.pair.com (66.39.2.38) 
     by oomur.pair.com with SMTP; 28 Feb 2010 21:00:03 -0000 
Received: from localhost (localhost [127.0.0.1]) 
     by mailwash38.pair.com (Postfix) with SMTP id B1D8A41430 
     for <[email protected]>; Sun, 28 Feb 2010 16:00:02 -0500 (EST) 
X-Spam-Check-By: mailwash38.pair.com 
X-Spam-Status: No, hits=3.0 required=4.0 tests=BAYES_20, FROM_STARTS_WITH_NUMS, 
     TVD_SPACE_RATIO autolearn=no version=3.002005 
X-Spam-Flag: NO 
X-Spam-Level: *** 
X-Spam-Filtered: a7b240700a36d5e6c2608f9ce43a92c9 
Received: from schemailmta08.cingularme.com (schemailmta08.cingularme.com 
     [209.183.37.70]) 
     by mailwash38.pair.com (Postfix) with ESMTP id F35394142C 
     for <[email protected]>; Sun, 28 Feb 2010 16:00:00 -0500 (EST) 
X-Mms-MMS-Version: 18 
Date: Sun, 28 Feb 2010 15:13:10 -0600 
X-Nokia-Ag-Internal: ; smiltype=false; ernaldate=1267391590642 
Content-Type: multipart/mixed; 
     boundary="----=_Part_9705244_14454315.1267391590647" 
Received: from schagw01 ([172.16.130.170]) by schemailmta08.cingularme.com 
     (InterMail vM.6.01.04.00 201-2131-118-20041027) with ESMTP id 
     <20100228210001.QHEZ5910.schemailmta08.cingularme.com@schagw01> 
     for <[email protected]>; Sun, 28 Feb 2010 15:00:01 -0600 
X-Mms-Transaction-ID: 1267390700-6 
From: <[email protected]> 
To: [email protected] 
Mime-Version: 1.0 
Message-ID: <33144584.1267391590647.JavaMail.wluser@schagw01> 
X-Mms-Message-Type: 0 
Subject: Multimedia message 
X-Nokia-Ag-Version: 2.0 
 
------=_Part_9705244_14454315.1267391590647 
Content-Type: text/plain; char=utf-8 
Content-Transfer-Encoding: base64 
Content-Disposition: inline 
 
MzA1 
------=_Part_9705244_14454315.1267391590647-- 


  现在我想起了选择编程语言条重要准则那就是可以使用什么样库来处理应用中较为单调任务而解析电子邮件头、寻找正确邮件部分和根据需要解码邮件无疑属于单调任务 在 CPAN 上我找到了个 Perl 模块来解析各种电子邮件头这样无论各种邮件头格式的间有多大差异我都可以通过获取所需信息然而这个库依赖于其他 Perl 库并且我主机提供有其中个库过期版本因此我在这个方面分析了 Python 提供功能我找到了个 Python email 包编写了些简单测试然后决定使用 Python 重新编写我在网上快速搜索就可以找到些可用于 Ruby、Java™、PHP 和其他编程语言类似因此如果您想编写个电子邮件自动回复脚本不再局限于只使用 Perl 或 Python 语言

  自动回复脚本

  清单 3 给出了 aci.py 脚本请注意开始处 import 语句如何拉入 email.Parser 库以及其他几个流行 Python 库底部 ____ 部分保存基本逻辑:解析收到消息从中拉取发件人地址和消息正文(存储在变量 areaCode 中)使用在脚本中定义 areaCodeInfo 搜索有关特定区号信息将此信息作为回复发送然后记录该消息

清单 3. aci.py 脚本

#!/usr/local/bin/python 
 
# aci.pl: (area code information) read e-mail message to find area 
# code, then send information about that area code. 
# Bob DuCharme 2010-01 no warranty expressed or implied 
 
import os 
import email.Parser 
import sys 
import datetime 
import re 
 
def multipartBody(msg): 
# following code from 
# http://docs.python.org/library/email-examples.html 
 
 partCounter=1 
 
 for part in msg.walk: 
   part.get_content_type"multipart": 
   continue 
  name=part.get_param("name") 
   nameNone: 
   name="part-%i" % partCounter 
  partCounter1 
  msgText = part.get_payload(decode=1) 
 
 msgSender = msgSenderText 
  msgText.strip   # strip whitespace 
 
 
def areaCodeInfo(areaCode): 
 
 # Look for data about that area code in areacodes.txt. 
 # First initialize values that should get overridden. 
 
 response = "No information available for area code " + areaCode + "." 
 foundAreaCode = False 
 line = "dummy" 
 acFile = open(aciPath + "areacodes.txt") 
 while ((not foundAreaCode) and line): 
  line = acFile.readline 
   (line[0:5]  areaCode + ": "):  # e.g. "212: " 
   response = line 
   foundAreaCode = True 
 
  response 
 
 
def sendReply(msgSender,response): 
 
 f = os.popen("%s -t" % SENDMAIL, "w") 
 f.write("To: " + msgSender + "\n") 
 f.write("From: area code information <[email protected]>\n") 
 f.write("Return-Path: area code information <[email protected]>\n") 
 f.write("Content-type: text/plain\n\n") 
 f.write(response) 
 sts = f.close 
 
def logIt(msgSenderText,areaCode): 
 
 timestamp = datetime.datetime.today.isoformat[0:19] 
 log = open(aciPath + "log.txt",'a') 
 log.write(timestamp + " " + msgSenderText + " " + areaCode + "\n") 
 log.close 
 
 
 __name__  "____": 
 
 SENDMAIL = "/usr/sbin/sendmail" # sendmail location 
 aciPath = "/usr/home/bobd/aci/" 
 keepFullLog = False # For debugging. More detailed than log.txt. 
 response = "" 
 
 # Parse the standard input to find the message and sender value 
 mailFile=sys.stdin 
 p=email.Parser.Parser 
 msg=p.parse(mailFile) 
 mailFile.close 
 
 msgSenderText = msg['From'] # text showing msg sender's name 
 msgSender = msgSenderText  # save msgSenderText for log 
 
 # If msgSender has the form "Some Guy <[email protected]>" 
 # then we just want [email protected] 
 emailAddrRegEx = re.compile(r"\<(?P<emailAddr>.+)\>") 
 result = emailAddrRegEx.search(msgSender) 
  result != None: 
   msgSender = result.group('emailAddr') 
 
  keepFullLog: 
  output = open(aciPath + "fulllog.txt",'a') 
  output.write(str(msg) + "\n-- end of mail msg --\n\n") 
  output.close 
 
  msg.has_key("X-Mailer") and \ 
    msg["X-Mailer"][0:24]  "Microsoft Office Outlook": 
  response = "Microsoft Outlook format is not supported." 
  areaCode = "" 
 : 
  areaCode = multipartBody(msg) 
  response = areaCodeInfo(areaCode) 
 
 sendReply(msgSender,response) 
 logIt(msgSenderText,areaCode) 


  获得您应用通用简码地址

  您可能已经注意到些基于 SMS 应用支持其用户将消息发送到个特殊 5 位数电话号码例如发送到 46645 (GOOGL) 可以向 Google 发送信息发送到 40404 可以向 Twitter 发送信息如果您用户可以将他们文本消息发送到个通用简码(CSC)而不是个电子邮件地址那该多好啊!这是可行但注册费用很高尤其是想挑选自己 5 位数号码时这些由 Common Short Code Administration(CSCA)管理这是 International Association for the Wireless Telecommunications Industry 个代理 旦您签订了按月付费使用特定 CSC 合同您还必须和每个运营商达成协议来激活该简码而且美国简码不能在美国以外地区使用CSCA 网站WebSite提供了更多信息

  几点注意事项:

  logIt 向文本文件 log.txt 中存储这行包含记录时间、请求信息移动设备电子邮件地址、以及这个移动设备希望知道区号个举例行如下:2010-03-19T19:50:02 [email protected] 407如果布尔变量 keepFullLog 设置为 True将收到整条消息保存在另个日志文件中整条消息十分冗长但对于调试非常有用

  Microsoft® Outlook® 发送电子邮件格式是 email.Parser 库所无法理解但是没有 2G 电话会发送这样电子邮件当脚本检测到个 Outlook 邮件时将会发送适当回复

  脚本使用另个著名 UNIX 实用 sendmail 来发送该电子邮件这并不是某个包含需要特殊库操作和 UNIX 组装应用组件原理更加那就是将来自个组件信息传输到另个组件Python 脚本就像打开文本文件样打开 sendmail向其中写入适当信息然后 “关闭” 它



  areaCodeInfo 在文件 areacodes.txt 中搜索信息清单 4 给出了这个文件部分我已在个 Wikipedia 页面上给出了该文件

清单 4. areacodes.txt 部分

210: Texas (San Antonio area) 
211: Community Services Hotline (e.g., crisis line, United Way, etc.) 
212: New York (Manhattan except for Marble Hill) 
213: Calornia (central Los Angeles) 
214: Texas (Dallas area) 


  在文本文件中搜索串并返回包含该这非常简单当您开发自己应用来向 2G 移动电话回复消息时您可以获得更多创意:您可以对本地和远程存储任意组合执行数据库查询交叉引用它发现信息执行各种逻辑来向发送查询电话返回有用信息只要它不超过 SMS 消息 160 限制

  可以将移动电话看作运行您应用命令行界面客户端记住应用可以是段简单脚本可以让其他库来执行困难和复杂工作您将看到适用于全球数十亿部电话服务器端应用非常容易



Tags: 

延伸阅读

最新评论

发表评论