目前,科技新闻中充斥着关于{zx1}的 iPhone、Droid 和 Palm Pre™ 应用程序的消息,但是新闻媒介更加关注的是,简单的移动电话如何为世界各地的人们提供新的通信和创业机会,特别是在电力缺乏的地区。这次主要宣传的是,在许多有网络的地方,人们使用移动电话不仅仅是为了通话,SMS 文本消息让他们可以相互交换信息,帮相他们处理细小的事务。
言归正传,您有多少朋友和家人没有一款具有彩色高分辨率触摸屏,能够浏览网站和安装各种专业软件的移动电话?他们使用的是 2G(第二代)移动电话。 当这类移动电话在 20 世纪 90 年代推出时,它们与{dy}代前辈产品是不同的,因为它们是数字化的,可以发送文本消息。对于比较节俭的人来说,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) 。(在本文中,服务的电子邮件地址是 acinfo@snee.com,但在实际应用中(您可以亲自尝试),电子邮件地址为 “aci” 而不是 “acinfo”。)
该应用程序的基本步骤(都使用了简单的脚本)如下:
-
检查所有收到的电子邮件,如果来自 acinfo@snee.com,将其发送给 Python 脚本 aci.py,它将执行余下的步骤。
-
在一个区号信息列表中搜索收到的电子邮件正文中的文本。
-
如果在列表中,将返回消息设置为所存储的相关信息(在上述例子中为
Florida (Orlando, Florida, St. Cloud and central
eastern Florida) )。
-
如果不在列表中,在返回消息中说明没有发现与收到消息相关的信息。
-
将返回消息发送回发送原始邮件的地址,并记录下来。
我的应用程序搜索一个简单的文本文件来进行信息查询,但在您的应用程序中,只要您能够想象得到且您的脚本能够访问数据源,您能够实现很多操作。
自动回复所收到消息的关键在于一个称为 procmial 的xx UNIX® 实用程序。许多扫描垃圾邮件和根据邮件头信息在特定文件夹中排序电子邮件的最早期系统都是在 procmail 基础上建立的,并且现在仍可使用它。如果您带有主机提供程序的帐户使用基于 Linux® 的系统,且提供了 shell 访问, 那么您可以为您的帐户创建一个 procmail 配置文件,扫描所收到邮件的模式并根据发现的结果执行操作。
对于通过此 .procmailrc 配置文件路由的邮件而言,还需要另外一个或两个步骤。在过去,您可以创建 .forward 文件来路由电子邮件,但是现在,您的主机提供程序通常会提供一个 Web
表单供您填写,以告诉它们的系统在邮件到达时检查 .procmailrc 文件。
在主机提供程序中配置帐户来执行此任务时,我通过以下三行向 .procmailrc 文件增加了一条规则:
{dy}行指出这是一个 procmail 规则的开始。第二行以一个星号开始,表示您指定了一个条件,这一行余下的部分是一个正则表达式,指定要在邮件中从一行的开始处开始搜索的内容:“To: acinfo@snee.com”,邮件地址两边的尖括号是可选的。(这些尖括号可有可无,这是您在处理可能来自各种电子邮件客户和电话的电子邮件时必须考虑的不一致性的{dy}个例子。)我所创建的这个电子邮件地址仅用于区号信息请求,因此这个规则适用于向这个地址发送的所有邮件。
.procmailrc 规则的第三行可以命名应该转发此邮件的邮箱,但这条规则所做的事更加有趣。竖杠符号指定邮件内容应作为输入发送到某个指定的程序:一个名为 aci.py 的 Python 脚本。
查看 aci.py 程序之前,先看一下它必须处理的输入。一条 SMS 文本消息显示为一封带有发送者地址的电子邮件,这个地址包含电话号码和电话公司使用的域名, 展示了当我通过 Verizon 网络从 LG env2™ 电话以文本消息的形式发送区号 407 时,显示的示例 SMS 电子邮件,在清单中将发出电话号码更改为了 (434) 000-0000。
From 4340000000@vtext.com Wed Mar 10 00:50:01 2010
Return-Path: <4340000000@vtext.com>
Delivered-To: bobd-snee:com-acinfo@snee.com
X-Envelope-To: acinfo@snee.com
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 <acinfo@snee.com>; 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 <acinfo@snee.com>; Tue, 9 Mar 2010 19:49:59 -0500 (EST)
X-Policy: RELAYLIST-$RELAYED
Received: from unknown (HELO ifs2006qwigfe) ([10.135.9.57])
by lrx5634xmtasa.alltel.net with ESMTP; 09 Mar 2010 18:49:58 -0600
Message-ID: <26597005.1268182198841.JavaMail.root@ifs2006qwigfe>
From: 4340000000@vtext.com
To: acinfo@snee.com
Subject:
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit
Date: Tue, 9 Mar 2010 19:49:59 -0500 (EST)
407
|
这里有很多代码,但脚本仅需要两部分信息:发送消息的设备的电子邮箱地址(4340000000@vtext.com)和它发送的消息(407,在{zh1}一行上)。Perl 常常是{sx}的简单文本处理脚本编写语言,而且编写 Perl 脚本来从 中的电子邮件中提取电子邮件地址和信息,在区号列表中查找该消息,以及将请求的信息发送会回表示发送电话的电子邮件地址,这些都比较容易。
在我的移动电话上,这段 Perl 脚本运行良好,但是,当我在更多的移动电话上进行测试时,发现通过移动电话发送的电子邮件并不像我期望那样一致。我前面已经提到,.procmailrc 文件必须考虑到电子邮件地址在和不在尖括号中两种情况,用 Perl 很容易处理这种情况。事实证明,其余电子邮件结构也有一些可能的差异需要考虑。
显示了一封更复杂的电子邮件,它将 “305” 作为一个包括多个部分的 MINE 消息从 iPhone 发出。(当然,iPhone 不是一部 2G 电话, 但我想用它来进行测试是个不错的主意。)不要去找 “305”,它已被编码。寻找正确的消息部分进行解码,这使我的 Perl 脚本越来越长,而且它已经能够用于其他多部电话。
From 6170000000@mms.att.net Sun Feb 28 21:00:03 2010
Return-Path: <6170000000@mms.att.net>
Delivered-To: bobd-snee:com-acinfo@snee.com
X-Envelope-To: acinfo@snee.com
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 <acinfo@snee.com>; 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 <acinfo@snee.com>; 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; internaldate=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 <acinfo@snee.com>; Sun, 28 Feb 2010 15:00:01 -0600
X-Mms-Transaction-ID: 1267390700-6
From: <6170000000@mms.att.net>
To: acinfo@snee.com
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; charset=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 语言。
给出了 aci.py 脚本。请注意开始处的 import 语句如何拉入 email.Parser 库以及其他几个流行的 Python 库。底部的 __main__ 部分保存程序的基本逻辑:解析收到的消息,从中拉取发件人地址和消息正文(存储在变量 areaCode 中),使用在脚本中定义的 areaCodeInfo 函数搜索有关特定区号的信息,将此信息作为回复发送,然后记录该消息。
#!/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():
if part.get_content_maintype()=="multipart":
continue
name=part.get_param("name")
if name==None:
name="part-%i" % partCounter
partCounter+=1
msgText = part.get_payload(decode=1)
msgSender = msgSenderText
return 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()
if (line[0:5] == areaCode + ": "): # e.g. "212: "
response = line
foundAreaCode = True
return response
def sendReply(msgSender,response):
f = os.popen("%s -t" % SENDMAIL, "w")
f.write("To: " + msgSender + "\n")
f.write("From: area code information <acinfo@snee.com>\n")
f.write("Return-Path: area code information <acinfo@snee.com>\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()
if __name__ == "__main__":
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 <someguy@example.com>"
# then we just want someguy@example.com
emailAddrRegEx = re.compile(r"\<(?P<emailAddr>.+)\>")
result = emailAddrRegEx.search(msgSender)
if result != None:
msgSender = result.group('emailAddr')
if keepFullLog:
output = open(aciPath + "fulllog.txt",'a')
output.write(str(msg) + "\n-- end of mail msg --\n\n")
output.close()
if msg.has_key("X-Mailer") and msg["X-Mailer"][0:24] == "Microsoft Office Outlook":
response = "Microsoft Outlook format is not supported."
areaCode = ""
else:
areaCode = multipartBody(msg)
response = areaCodeInfo(areaCode)
sendReply(msgSender,response)
logIt(msgSenderText,areaCode)
|
几点注意事项:
在文本文件中搜索一个字符串并返回包含该字符串的行,这非常简单。当您开发自己的应用程序来向 2G 移动电话回复消息时,您可以获得更多的创意:您的程序可以对本地和远程存储的任意组合执行数据库查询,交叉引用它发现的信息,执行各种逻辑来向发送查询的电话返回有用的信息,只要它不超过 SMS 消息的 160 字符限制。
可以将移动电话看作运行您应用程序的命令行界面的客户端,记住,您的应用程序可以是一段简单的脚本,可以让其他库来执行困难和复杂的工作。您将看到适用于全球数十亿部电话的服务器端应用程序非常容易。
学习 - (Nicholas Chase,developerWorks,2004 年 6 月):通过一个将 Web 服务集成到应用程序中的真实示例演示如何传输 SMS 消息。
- (Cameron Laird,developerWorks,2001 年 8 月);查看早期关于 SMS 信息在 Web 服务构架中的潜在角色的有趣观点。
- Wikipedia 上的 :在列表中查找美国、加拿大、加勒比海以及美国太平洋区域的区号。
- 网站:通过一部白皮书、常见问题解答等了解通用简码的更多信息。
- :查找描述 promail 的简单和复杂用途的许多出色资源的链接。
- :定制您的 developerWorks 体验。
- :了解如何才能成为一名 IBM 认证的 XML 和相关技术开发人员。
- :访问 developerWorks XML 专区,获得广泛的技术文章和技巧、教程、标准和 IBM 红皮书。
- developerWorks 和 :通过这些会议关注{zx1}的技术进展。
- :收听面向软件开发人员的有趣访谈。
获得产品和技术 - :获取 CPAN 库来解析电子邮件消息和邮件头。
- 库:下载这个包来解析电子邮件信息和邮件头。
-
:下载或 ,开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
Bob DuCharme 是 TopQuadrant 的一位解决方案构架师,TopQuadrant 是一家语义 Web 应用程序建模、 开发和部署软件提供商。它撰写了 4 部关于信息及技术的图书,还在网上发表和印刷出版了近百篇相关文章,而在任何这些作品中从未使用过 “功能” 的概念。他的博客地址为:http://www.snee.com/bobdc.blog。
原文链接:
|