w7—网络遗书服务思路与尝试

最近大家项目提得蛮多的,我也有了新的想法,打算尝试下。

关于网络遗书

其实有这个想法已经蛮久了,大学时候就有同班同学不幸殒命,自己也老坐飞机高铁,也担心一旦出了什么意外没有任何交代,造成徒劳的损失。尤其是近几年,毕了业后代替父母承担了为家庭理财的任务,很多家庭资产通过网络投资的方式分散投资在了量化、债券、信托、p2p、货币基金等各个平台上,也没有纸质的合同放在什么牢靠的地方,自己理清都有难度,父母就更不清楚了。独子、单身、独自外地工作,万一出了任何意外,自己这个核心节点一断开,恐怕各平台的资产就无人知晓了,说不好落入了他人之手。

留遗书其实是最稳妥和理智的方式,但一方面,自己没有这方面经验又懒得学和做;另一方面,也没完全想清楚这个东西怎么写、怎么保存、怎么更改、怎么流转。

我查了下,这并不是什么新兴的东西,国外就已经有成型的了,比如Perpetu,国内也有遗书网(虽然惨不忍睹)。

关于这个应用,我翻来覆去的想到底是不是个好的idea,担心像之前那个那年18一样,做着做着自己先觉得没意思了。后来想明白了,这个服务对于自己还是很必要的,即便没有什么别的人用,哪怕只有自己一个人用,写个程序自动化的执行也是很有好的。

我决定做这个。

首先我先参考了下国外的Perpetu是如何的一个运转方式。

屏幕快照 2015-12-01 20.58.43.png

很清晰,它的流程分3步:

  1. 留一个遗书
  2. 告知你信任的人一个唯一的code,如果有人把这个code发给服务商,服务商就去确认你是否已经过逝
  3. 当你过逝后,你设置的这些应用会改变成你设定状态。

我的注册被要求跳转到facebook或twitter上,我用facebook登录后,在它的引导下我注册了用户名和密码,进入后它要求我指定一些在线账号在我过逝后的显示状态

屏幕快照 2015-12-01 21.16.27.png

其中gmail的很有意思:

Gmail

What if people keep sending you emails, after you’re gone? Ever thought of forwarding or auto-replying to them? Do you know your existing emails may be shown to others, without your consent?

Manage your Gmail accounts with Perpetu Pro!

还有github的:

Open source your projects!

With Perpetu Pro, you can select repositories that you would like to make public after you're gone

不过这些很多都是pro版本才有的功能,而且仔细的看了下,这看上去这和我想象中的遗书服务并不太像,更多的是死亡后社交网络虚拟资源等等的状态设置变更。

我是个俗人,更倾向于遗嘱形式的,相比于社交网络上的状态,我觉得要是自己都没了的话也就没什么所谓了,倒是觉得我平时的在线金融平台投资和父母不清楚价值的私人遗产才是重点。

为此,我搜集了一些关于网络遗嘱的资料,并且听说了一个叫做『人生黑匣子』的网站,可是怎么也搜不到,但也搜到了一些其他的不错的资料。

曾经我立了一个遗嘱,后来没续费,于是就过期了——由网络遗嘱想到的杂七杂八

去世后怎么处理在线资产?Perpetu想做帮人们管理网络遗产的公司

“网络遗嘱”在中国悄然兴起

网络遗嘱”引发的是非争议

但是目前看来,相关的立法还是不完善的,所以即便有了这个也未必具备法律效力:

法律人士:合理但不受法律保护,需要完善

那么,“网络遗嘱”是否具有法律效力呢?一些法律界人士也给出了答复,大家认为这种网络遗嘱“合理但不受法律保护,需要完善”。

从事多年民事诉讼的王律师告诉记者,根据《继承法》规定,自己书写的遗嘱需遗嘱人亲笔书写、签名、注明日期后才有效。网上立遗嘱是一种新事物,一定要慎重。如果遗嘱人写的遗嘱经过公证,在法律上就是被认可的,如果没有经过公证,就很容易产生纠纷,因为这种网络遗嘱属于新兴事物,还不受法律保护。

有法律工作者认为,“网络遗嘱”符合现在社会发展的趋势,但要想将这种方式继续下去,必须要在现在的模式上逐步进行完善,避免法律漏洞,比如说要记载见证人和遗嘱执行人的信息,对见证人和遗嘱执行人信息真实性的认可,一个评价,这样也是可以的

先不管这么多,既然决定了,就先做起来再说。

服务的形式

首先,既然是遗书,先想想遗书应该怎么写罢,我就百度了个范本。

遗嘱

立遗嘱人:

我因__,特立此遗嘱,对__如下处理:

一、__

二、__

三、_各执一份,存__公证处一份。

本遗嘱由__监督执行。

立遗嘱人_

证明人_

代书人_

。。。。正式倒是真的很正式,可能法律上行的通,但是感觉实在是冷冰冰的狠,毕竟我的目的只是想向亲人正常简单地交代自己的遗产等问题,避免无谓的损失而已,所以这种八股文遗嘱对我没什么意义。

我还是知乎上找答案罢,很快就找到了很靠谱的答案在中国大陆应该怎么订立遗嘱?程序如何

著作权归作者所有。 商业转载请联系作者获得授权,非商业转载请注明出处。 作者:郭磊 链接:http://www.zhihu.com/question/19763327/answer/12885321 来源:知乎

遗嘱事关重大,但是具体到格式和内容,其实没有那么多讲究,只要是真实的意思表示,处置的是个人合法财产,符合相关法律条件,内容完整、清晰、易于理解就好了。我想要说的是,在现代社会订立遗嘱,是一件很明智,对自己和家人都很负责的作法。但是有了遗嘱,并不意味着一了百了,现实中有太多因为争抢财产而斩断了亲情、爱情的事例。因此,我觉得遗嘱在充分体现自己想法的同时,也要讲情分,讲公平合理,尽量避免引发更多的家庭矛盾。

遗嘱有很多种,最常见的就是自书遗嘱,就是遗嘱人亲笔制作的书面遗嘱,正文最好由本人亲自书写,写清具体的日期时间和订立地点,这些内容可能会影响到遗嘱的效力,比如不同内容的书面遗嘱,以订立时间在最后的为准。另外,如果在遗嘱上进行修改,务必要在一旁标明修改的字数,并签名。

相对自书遗嘱,还有代书遗嘱,当遗嘱人不识字或者因病无法自书时,由两位以上的见证人当场见证,其中一人代书,也要注明具体日期时间和地点,然后代书人、遗嘱人和其他见证人在上面签字。不过在法律实践中,某些特殊情况导致形式要件稍有瑕疵,一般也不会推翻遗嘱效力,比如由基层法律服务所、法律援助中心的法律工作者代写,虽然人数不满两人,但一般认为这样的遗嘱是真实有效的。

还有录音遗嘱,也要求有两个以上的见证人当场见证,最好采用见证人和遗嘱人对话的方式。有时候在遗嘱人情势危急的情况下,会采用口头遗嘱,同样需要两个以上的见证人,见证人同遗嘱人不能存在利害关系,而且一旦情势好转,最好将口头遗嘱改为自书、代书遗嘱等。最后是公证遗嘱,要求遗嘱人亲自到公证机关办理,特殊情况,公证员可以登门办理。如果同时存在多份非公证遗嘱,都不得撤销或变更公证遗嘱,可见公证遗嘱拥有最高的法律效力。

办理公证遗嘱,要携带遗嘱人的身份证、户口本以及遗嘱涉及的财产凭证,比如股权证、房屋产权证等,还要带齐婚姻关系和亲属关系的相关证明,在公证处填写公证申请书,审查后由公证处予以公证。另外,一些腰缠万贵的遗嘱人会采取遗嘱信托的方式,指定特别的遗嘱执行人,比如具备一定理财能力的律师、会计师或者相关机构,建立财产信托。这种情况比较复杂,且和普通市民关系不大,这里暂且不表。

然后至于具体的内容,我找到了一个匿名有经验的用户回答

著作权归作者所有。 商业转载请联系作者获得授权,非商业转载请注明出处。 作者:匿名用户 链接:http://www.zhihu.com/question/20528175/answer/22119189 来源:知乎

我写过两封遗书了,马上要写第三封。 先声明,身体健康,暂无自杀倾向,家庭幸福,正在读大二 性格原因吧,写的比较随意,大体四个部分:

遗体处理

财产(或者说零花钱-_-||学生党)

生前所有物品

致生者言

算是怪人了,至于为什么要写这个,原因很复杂,题主如果感兴趣,过些日子补充。

推荐一个今晚刚刚发现的网站:遗书网,看看上面的留言吧,应该会给人很多启发http://www.yishuwang.net/mb.php

然后我又找到了一篇美丽的指导性文章

著作权归作者所有。 商业转载请联系作者获得授权,非商业转载请注明出处。 作者:中秋君 链接:http://www.zhihu.com/question/36598086/answer/68222023 来源:知乎

遗书的写法词曲:中村中演唱:中孝介翻译:游鱼传说写遗书时,请先从“感谢”开始之后请务必加上那些重要的人的名字那一个个名字见证了你人生的足迹

写遗书时,请以“我爱你们”结尾并且请让其中的某人阅读一下他一定会指出你自己无法察觉的错误或许他会为你哭泣 或许他会抽你耳光疼痛过后残留在脸上的炙热就是你活着的证据写完遗书后,乘上飞机随风而去吧拥抱着那些重要的人们飞向明天吧为了这些人,你要幸福起来啊每天落下的夕阳和升起的朝日虽然从来都不曾变化,但谁都知道它的存在那就是你活着的证据不得不忍受孤独,既然我们不能变的坚强起来不得不相互伤害,若是我们保护不了自己与其争斗,倒不如就这么软弱下去比较好吧不论明天会怎样,请你一定要幸福

所以我大致定了我的遗书格式:

  1. 当你看到这封信的时候,我应该离开这个世界了。
  2. 首先感谢---;还要感谢---
  3. 关于我的遗体,我期望---
  4. 至于我留下的财产,大概分为以下几部分---,我期望交给---
  5. 最后,我希望---

大致就是这样罢。

遗书格式定了,那么怎么提交,保存与按期望呈现遗书呢?

首先我应该能够有个可以编辑和提交遗书的功能;然后我还需要设定,当我不在人世之后应该把这个遗书发送给谁;最后我应该需要一个方法来让机器判定我是否还在世。

第一个功能是比较容易实现的,但难点在于,如何保证敏感信息的安全性,因为我的遗书内期望填写自己的财产相关的账号密码,要是在平台上由于安全性的原因泄露就得不偿失了。

第二个也不难,我应该维持一个可编辑的联系列表,可能是手机号、邮箱、微信、qq等方式,而且我的遗书不应该是直接明文写在邮件里,而是应该发送一个可认证的链接,保证一定的安全性。

第三个比较有争议,Perpetu是靠自己的朋友发送统一code来向平台报送,再由平台向主人公确认,最终发送的;而国内的人生黑匣子和遗书网都是通过登录间隔过长来确认的,以我个人而言,我倾向于后者,逻辑清晰简单,但这个间隔应该是服务主动向我提醒来维持的,避免我自己忘记,所以我应该设计一个相对可靠的循环提醒服务,暂定周一1个月。

技术分析

先做个完整的逻辑规划:

注册——登录——写遗书——设定联系人——提交——判断是否活着——如果长期没有响应——投递遗书链接——查看遗书——结束

好像并不是很难的样子,规划也好做:

  1. 建立一个web站点,具备注册、登录、表单编辑与提交与呈现的功能。
  2. 设置一个定时的向用户发消息确认心跳的可确认的链接。
  3. 设置一个邮件与信息发送器用于给最终联系人发送信息。

如果这个做的都ok能正常使用后,应该把全部精力集中在加密与解密上,需要事先打下基础《信息论》。

在暂时不考虑加解密的前提下,首先分析初步需要的组件:

  1. web,作为一个常规web项目,首先规范出前后端的选型。沿袭以前的习惯,还是使用Bootstrap+Flask的组合,需要一个简单文本编辑器,和大量表单配合,但因为这次是做大众应用,所以不可以是markdown编辑器。
  2. 数据库,这次最主要存储的是遗书留言,显然使用kv库更合适,选择mongodb,初步用一个表,之后会尝试分成三个表,一个用户名密码认证,一个专门存遗书,另一个专门存参数。
  3. 心跳,这应该是个独立组件,用于扫描库中用户设定的参数来和用户通过各种联系方式,保持心跳信息。
  4. 遗书通知,在与用户失去心跳过长时间后,通知用户设置的亲属等联系人查看用户最终遗留遗书的地址。

如果算上加密的话,还需要拥有一个专门对用户的遗书进行强加密的手段,并且用户最终遗留遗书由于本身可能包含敏感信息,也应以密文的方式发送给查看者,我猜这应该这个项目最大的技术与逻辑难点。

最小原型

首先我用尽量少得组件构建一个单页的快速原型试试水,包含东西如下:

  1. web框架
  2. html表单
  3. 遗书内容文件
  4. 邮件发送
  5. 心跳接收

被大妈批每次过程太简略,这次我会尽量写详细一些。

首先我按照规范把文件头标注出来,并把字符格式保证一下

# -*- coding: UTF-8 -*-
'''
* 用于whenmgone的demo原型测试
* [email protected] 
* 2015.12.5
'''
import sys
sys.path.append("./")
reload(sys)
sys.setdefaultencoding( "utf-8" )

先开一个最简单模板

# 引入flask模块
from flask import Flask

# 创建一个Flask对象
app = Flask(__name__)

# 指定路由
@app.route('/')
# 定义响应函数
def index():
    # 返回成功
    return '<h1>demo whenmgone success!</h1>'

# 运行主函数
if __name__ == '__main__':
    # 对外开放8080端口
    app.run(host='0.0.0.0',port=8080,debug=True)

测试下:

屏幕快照 2015-12-05 12.10.42.png

完成后,我把它改成直接渲染html文件,首先先建立templates文件夹,并添加一个index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <h1>render template success!</h1>
</body>
</html>

然后我调整flask来渲染它

from flask import render_template

# 指定路由
@app.route('/')
# 定义响应函数
def index():
    # 渲染index.html
    return render_template('index.html')

返回后测试下:

屏幕快照 2015-12-05 12.23.39.png

页面太单薄,打算把bootstrap集成进来,有两种途径,一种是直接引用cdn的地址或下载它的css与js,另一种是直接用flask的集成bootstrap,这次试一下后者。

首先需要先安这个库:

sudo pip install flask-bootstrap

按照完这个库后据说只要简单地使用Jinja2的一些引用就能获取基本的css与js了,这样我就把index.html内容换成了这样。

{% extends "bootstrap/base.html" %}
{% block title %}whenmgone{% endblock %}
{% block navbar %}
{% endblock %}
{% block content %}
{% endblock %}

然后我到bootstrap官网,找一些范例nav与content贴进去,看下效果。

{% extends "bootstrap/base.html" %}
{% block title %}whenmgone{% endblock %}
{% block navbar %}
      <!-- Static navbar -->
      <nav class="navbar navbar-default">
        <div class="container-fluid">
          <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
              <span class="sr-only">Toggle navigation</span>
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">Project name</a>
          </div>
          <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
              <li class="active"><a href="#">Home</a></li>
              <li><a href="#">About</a></li>
              <li><a href="#">Contact</a></li>
              <li class="dropdown">
                <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
                <ul class="dropdown-menu">
                  <li><a href="#">Action</a></li>
                  <li><a href="#">Another action</a></li>
                  <li><a href="#">Something else here</a></li>
                  <li role="separator" class="divider"></li>
                  <li class="dropdown-header">Nav header</li>
                  <li><a href="#">Separated link</a></li>
                  <li><a href="#">One more separated link</a></li>
                </ul>
              </li>
            </ul>
            <ul class="nav navbar-nav navbar-right">
              <li class="active"><a href="./">Default <span class="sr-only">(current)</span></a></li>
              <li><a href="../navbar-static-top/">Static top</a></li>
              <li><a href="../navbar-fixed-top/">Fixed top</a></li>
            </ul>
          </div><!--/.nav-collapse -->
        </div><!--/.container-fluid -->
      </nav>
{% endblock %}
{% block content %}
    <!-- Begin page content -->
    <div class="container">
      <div class="page-header">
        <h1>Sticky footer</h1>
      </div>
      <p class="lead">Pin a fixed-height footer to the bottom of the viewport in desktop browsers with this custom HTML and CSS.</p>
      <p>Use <a href="../sticky-footer-navbar">the sticky footer with a fixed navbar</a> if need be, too.</p>
    </div>
{% endblock %}

由于不是自己写的head,有点担心中文的问题,测试一下居然没有问题,很智能啊。

屏幕快照 2015-12-05 12.53.23.png

我用bootstrap在线表单编辑器快速生成个表单看下。

屏幕快照 2015-12-05 13.15.13.png

貌似也不太是bootstrap风格,倒也无所谓,不妨在这个基础上先试下。

首先为了安全设置一个跨站请求伪造保护

# 设置跨站请求伪造保护
app.config['SECRET_KEY'] = 'hard to guess string'

我挖到了flask-WTF打算试一下,没有想象的那么顺利,不过大致搞清楚了基本逻辑。

  1. 把flask-WTF导入进来,连带着导入需要用到的各种表单与验证模块
  2. 建立一个表单类,表单的所有条目都在这里声明
  3. 在页面渲染的时候把实例化后的表单对象,渲染进html里面
  4. 设置针对表单提交等操作的反应

开始动手设置,这里面表单字段大概分成几种

  1. 单行文本:用户名,邮箱名,联系人等
  2. 密码字段:密码
  3. 多行文本:遗书留言
  4. 下拉框:心跳频率,心跳延迟

表单验证需要长度验证密码、邮箱验证和输入验证不为空即可所以按需导入的模块如下:

# 引入flask-WTF模块
from flask_wtf import Form
# 引入文本字段、密码字段、多行文本字段、下拉列表字段
from wtforms import TextField,PasswordField,SelectField
# 引入验证字段中的require、email
from wtforms.validators import DataRequired,Email,Length

导入后,开始设计表单项,就按照之前快速生成表单的格式即可,其中唯一个比较复杂的是下拉框的项目提供不知道写法,最后在stackoverflow上找到了答案。

# 创建表单类
class DemoForm(Form):
    # 用户名输入框
    name = TextField('姓名', validators = [DataRequired()])
    # 密码输入框
    password = PasswordField('密码',validators = [Length(min = 8, max = 40)])
    # 遗书输入框
    note = TextAreaField('留言', validators = [DataRequired()])
    # 接收人输入框
    reciver = TextField('接收人', validators = [DataRequired()])
    # 心跳邮箱
    heatbeatmail = TextField('心跳邮箱', validators = [Email()])
    # 心跳频率
    rate_choices = [('1', '每周'), ('2', '每月'), ('3', '每3个月')]
    heatbeatrate = SelectField('心跳频率',choices = rate_choices, default = '2')
    # 心跳延迟
    delay_choices = [('1', '1周后'), ('2', '1个月后'), ('3', '3个月后')]
    heatbeatdelay = SelectField('心跳延迟',choices = delay_choices, default = '2')

表单做完后,将它实例化,渲染进前端页面

# 指定路由
@app.route('/', methods=('GET', 'POST'))
# 定义响应函数
def index():
    # 实例化表单类
    form = DemoForm()
    # 渲染index.html
    return render_template('index.html', form=form)

并且在前端页面中把表单呈现出来,替换到快速生成的,并且附上bootstrap样式

    <form action="/" method="POST">
        {{ form.hidden_tag() }}
      <div class="form-group">
        {{ form.name.label }} {{ form.name(class="form-control") }}
      </div>
      <div class="form-group">
        {{ form.password.label }} {{ form.password(class="form-control") }}
      </div>
      <div class="form-group">
        {{ form.note.label }} {{ form.note(class="form-control") }}
      </div>
      <div class="form-group">
        {{ form.reciver.label }} {{ form.reciver(class="form-control") }}
      </div>
      <div class="form-group">
        {{ form.heatbeatmail.label }} {{ form.heatbeatmail(class="form-control") }}
      </div>
      <div class="form-group">
        {{ form.heatbeatrate.label }} {{ form.heatbeatrate(class="form-control") }}
      </div> 
      <div class="form-group">
        {{ form.heatbeatdelay.label }} {{ form.heatbeatdelay(class="form-control") }}
      </div>        
      <button type="submit" class="btn btn-default">Submit</button>
    </form>

前端看一下效果,就先这样了。

屏幕快照 2015-12-05 17.33.46.png

现在需要把输入的表单数据,保存到某个地方去,我准备在设置一条路由,告知提交成功,测试返回输入的数据。

首先引入额外的必要模块儿redirect和request

# 引入flask模块
from flask import Flask, render_template, redirect, request

然后调整函数让输入进去的东西print出来

# 指定路由
@app.route('/', methods=('GET', 'POST'))
# 定义响应函数
def index():
    # 实例化表单类
    form = DemoForm()
    # 如果接收到post提交
    if request.method == 'POST':
        name = request.form['name']
        password = request.form['password']
        note = request.form['note']
        reciver = request.form['reciver']
        heatbeatmail = request.form['heatbeatmail']
        heatbeatrate = request.form['heatbeatrate']
        heatbeatdelay = request.form['heatbeatdelay']
        # 打印出表单填写的内容
        print name,password,note,reciver,heatbeatmail,heatbeatrate,heatbeatdelay
        # 重定向回来
        return redirect('/')
    # 渲染index.html
    return render_template('index.html', form=form)

最后测试下

屏幕快照 2015-12-05 17.55.11.png

127.0.0.1 - - [05/Dec/2015 17:53:26] "GET / HTTP/1.1" 200 -
alan P@ssw0rd 支付宝账号是***,密码是***,由**继承 [email protected] [email protected] 2 2
127.0.0.1 - - [05/Dec/2015 17:54:30] "POST / HTTP/1.1" 302 -
127.0.0.1 - - [05/Dec/2015 17:54:30] "GET / HTTP/1.1" 200 -

下一步要做的是把表单获取的信息保存到数据库中,这里直接上mongodb好了,首先设置下数据库相关的连接:

# 导入pymongo模块
import pymongo

# 设置数据库地址
client = pymongo.MongoClient('172.16.191.163', 27017)
# 设置数据库名
db = client['demodb']
# 设置表名
collection = db['demotable']

然后把print函数换成插入数据库

    # 设置插入数据库的内容
    demo_data = {'name':name,'password':password,'note':note,'reciver':reciver,'heatbeatmail':heatbeatmail,'heatbeatrate':heatbeatrate,'heatbeatdelay':heatbeatdelay}
    # 插入数据
    collection.insert_one(demo_data)

然后启动运行docker的monogodb

$ sudo docker start mongodb01
mongodb01
$ sudo docker exec -ti mongodb01 mongod

再次尝试填写表单,用robomongo客户端查看是否成功插入数据库,发现插入成功!

屏幕快照 2015-12-05 21.12.11.png

然后,我再做一个页面用于给亲属显示遗书内容,即再加一条路由

# 指定路由
@app.route('/<name>')
# 定义响应函数
def note_content(name):
    # 实例化表单类
    form = DemoForm()
    # 找到名字对应的内容
    content = collection.find_one({'name':name}).get('note')
    # 渲染index.html
    return render_template('content.html', note = content)

然后为它搭配一个显示的页面

{% extends "bootstrap/base.html" %}
{% block title %}whenmgone{% endblock %}
{% block navbar %}
{% endblock %}
{% block content %}
    <!-- Begin page content -->
          <div class="inner cover">
            <h1 class="cover-heading">遗书内容</h1>
            <p class="lead">
              {{ note }}
            </p>
            <p class="lead">
              <a href="#" class="btn btn-lg btn-default">Learn more</a>
            </p>
          </div>
{% endblock %}

显示成功说明,基本web层面功能ok

屏幕快照 2015-12-05 23.20.32.png

下面要做的是发送邮件,廖雪峰教程里找到了一个SMTP发送邮件,我贴上用自己两个邮箱试了一下

# coding: utf-8  
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr

import smtplib

def _format_addr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))

from_addr = '[email protected]'
password = '******'
to_addr = '[email protected]'
smtp_server = 'smtp.163.com'

msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')
msg['From'] = _format_addr('Python爱好者 <%s>' % from_addr)
msg['To'] = _format_addr('管理员 <%s>' % to_addr)
msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode()

server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

运行后,cli闪了一长串处理

$ python3 mailtest.py
send: 'ehlo mac-alandemacbook-pro.local\r\n'
reply: b'250-mail\r\n'
reply: b'250-PIPELINING\r\n'
reply: b'250-AUTH LOGIN PLAIN\r\n'
reply: b'250-AUTH=LOGIN PLAIN\r\n'
reply: b'250-coremail 1Uxr2xKj7kG0xkI17xGrU7I0s8FY2U3Uj8Cz28x1UUUUU7Ic2I0Y2UF-adEGUCa0xDrUUUUj\r\n'
reply: b'250-STARTTLS\r\n'
reply: b'250 8BITMIME\r\n'
reply: retcode (250); Msg: b'mail\nPIPELINING\nAUTH LOGIN PLAIN\nAUTH=LOGIN PLAIN\ncoremail 1Uxr2xKj7kG0xkI17xGrU7I0s8FY2U3Uj8Cz28x1UUUUU7Ic2I0Y2UF-adEGUCa0xDrUUUUj\nSTARTTLS\n8BITMIME'
send: 'AUTH PLAIN AGxpYW5nY2hhb2JAMTYzLmNvbQAyMzY2NDU2MXc=\r\n'
reply: b'235 Authentication successful\r\n'
reply: retcode (235); Msg: b'Authentication successful'
send: 'mail FROM:<[email protected]>\r\n'
reply: b'250 Mail OK\r\n'
reply: retcode (250); Msg: b'Mail OK'
send: 'rcpt TO:<[email protected]>\r\n'
reply: b'250 Mail OK\r\n'
reply: retcode (250); Msg: b'Mail OK'
send: 'data\r\n'
reply: b'354 End data with <CR><LF>.<CR><LF>\r\n'
reply: retcode (354); Msg: b'End data with <CR><LF>.<CR><LF>'
data: (354, b'End data with <CR><LF>.<CR><LF>')
send: b'Content-Type: text/plain; charset="utf-8"\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: base64\r\nFrom: =?utf-8?b?UHl0aG9u54ix5aW96ICF?= <[email protected]>\r\nTo: =?utf-8?b?566h55CG5ZGY?= <[email protected]>\r\nSubject: =?utf-8?b?5p2l6IeqU01UUOeahOmXruWAmeKApuKApg==?=\r\n\r\naGVsbG8sIHNlbmQgYnkgUHl0aG9uLi4u\r\n.\r\n'
reply: b'250 Mail OK queued as smtp5,D9GowAA3fS4StGNWNm1XAQ--.9934S2 1449374739\r\n'
reply: retcode (250); Msg: b'Mail OK queued as smtp5,D9GowAA3fS4StGNWNm1XAQ--.9934S2 1449374739'
data: (250, b'Mail OK queued as smtp5,D9GowAA3fS4StGNWNm1XAQ--.9934S2 1449374739')
send: 'quit\r\n'
reply: b'221 Bye\r\n'
reply: retcode (221); Msg: b'Bye'

然后我到我的邮箱查看了下,成功收到!

屏幕快照 2015-12-06 12.09.57.png

再试下邮件格式html也是ok的:

屏幕快照 2015-12-06 12.17.55.png

我需要实现的邮箱功能有两个:

  1. 根据用户设置的心跳频率,定期发送邮件,并在里面插入一个按钮,返回心跳正常信息
  2. 当心跳失联超过心跳失联阀值后发给用户的联系人,查看用户遗书的地址。

这个模块的实现我在两种实现方式之间犹豫:

  1. 心跳一个函数,发送遗书地址一个函数,两个里面都是每天遍历数据库,然后找到应该发邮件的就发,并在数据库中维持一个状态参数
  2. 做一个筛选器和一个独立的邮件发送时间队列数据库,筛选器负责判断哪些用户需要在什么时候发送什么邮件,然后告知数据库,然后数据库把这些邮件参数按时间排序,最终通过统一的邮件发送函数发出去

当前来看我比较倾向于第二种,这里的具体的实现思路利弊打算先和教练沟通下。

当前来看,c2d2准备向教练和大家请教几个问题:

  1. 文本加密怎么做比较好,如何做强加密,并通过什么方式能让使用者感觉安心
  2. 在登录时候我发现用户名密码的post貌似是明文的,而且后台数据库里可以直接看到,怎样换成密文
  3. 心跳函数怎么遍历数据库会比较有效率,心跳模块的实现有没有更好地思路
  4. 心跳的REST接收方式,比较易用和简单地有哪些方法
  5. 遗书用markdown编辑器还是常规的文本框比较好,是否有推荐
  6. csrf逻辑
  7. 域名备案
  8. https的ca认证
  9. 大家对于使用这个服务有什么好的建议

前端效果

在web层面,首先需要有个首页,用于用户登录,并告知该服务基本模式;然后登录后需要分别填写遗书内容、遗嘱联系人,心跳设置等,并且需要一个成功页面,让用户可以最终预览并能够测试服务。

哦对,还需要一个页面面向联系人展示用户的遗书内容

另外,本着互联网产品『基本服务免费,高级功能收费』的传统,设置个『增强版』或者说『Pro』版,可以在数量与功能上提供更好和相对完善与特殊的服务。

还是mockup一个大致的效果出来先

demo.png

前端基本就这样

命力命力!

之前咨询了大妈关于『命名』的学问,得到回答还是很有收获的,这次,我尝试使用开脑洞的方式命名。

遗书本来就是个很敏感的词汇,和死亡相关,按照中国传统很明显的想到了阴间,我打算以『黑白无常』『阎王』『判官』等类似的词命名组件。

心跳服务,用于确认用户当前状态,是个常用的服务,如果叫它『白无常』会感觉很阴森,我把它萌化成『无常小白』;遗书通知,用于向用户亲属告知用户的最后留言,我叫它『无常小黑』。

将他们切成E文即『ghost_white』『ghost_black』