← xiaopingfeng.com/notes/ 随笔

30 Hackdays · Season 1 · 工具日志 (2014)

NB · — · 2014-10-09 · 30hackdays

30 hackdays

看了著名的Learning 30 Technologies in 30 Days: A Developer Challenge以后,我决定也要挑战一下自己。原作者是学习的基本都是一些好玩的技术,我会挑选工作中会用到的需求选择新技术来实现(新技术:以前没有使用该技术完成过项目)。然后我想叫它30 hackdays。我的目的,一来是好玩,二来是提高自己在正式项目中采用新技术的速度。

10月10日起,每天一篇比较正式的blog(文章质量参考原作者,第二天早上3点前发出算数)。为了好玩一点,我想用这个挑战玩一次对赌:参加的人只需要留下你认为我能否成功的判断,成功(30天)或者失败之后,我挨个请猜对的人吃串儿~猜错的人自觉请我吃~(所以其实我可以盈利的么...)

文章发在wordpress托管的博客www.fengxiaoping.com,墙内的同学不好意思了,可能加载速度比较慢,而且有些图片看不到。

(30 hackdays day 1) Firebase - Rethink database

今天是第一天!按照之前的盘口数据,成功比失败差不多是1:1,这,的确给了我很大的动力...这篇文章我会讲述一个有意思的Real-time API服务,FirebaseLeanstack.io将这类服务称为realtime backend api,Firebase对自己的定位也是“A powerful API to store and sync data in realtime.”。但我觉得Firebase带来的更是一种新型的“数据操作的思维方式”。

Firebase是什么?

简单的说,Firebase是一个简化数据同步工作的API服务。现代的应用开发者常会用BaaS(Backend as a Service)来简化开发工作,原因很简单:大部分后端工程师做的大部分事情都是重复的,但却很容易漏洞百出的,而且扩展性需要完全靠这些可能没有几年经验的工程师来完成,基本会是个很脆弱的状态。而在前端工程师得意于例如ParseJSAVOScloud这样的服务,可以更少的关心后端的时候,有一些蛋疼的应用又需要让所有终端保持数据一致。那这时候Firebase就登场了!

Firebase可以让所有终端简单的接入SDK后,就可以访问和操作一个云中的统一数据库。任何对其中数据的修改都会在极短的时间内通知到所有其他在线的终端。

Firebase能做什么?

最简单的应用场景就是聊天室啦。以前,想要写一个聊天的App有多难?后端工程师需要设计至少两个接口:Post message和Query message。而利用Firebase就可以把后端这工程师踢掉了。通过简单的创建一个聊天室Object(是的,Firebase中的数据只有Object,每个应用都是一个巨大的Object)。把自己想发送的内容添加到这个Object的messages里,类似chatroom.messages.push('啦啦啦')。所有监听注册了这个chatroom的终端就都会得到一个数据变动的Event,从而拿到这个新的chatroom.messages用来更新聊天室内容。

哦对忘了说了,前端时间因为香港占中一炮而红的Firechat,其中Global聊天部分就是基于Firebase的。

如何入手

和所有的BaaS服务一样,Firebase的Get Started极度易懂,推荐采用Javascript的教程走完前三步就够了:Install,Save,Update。通过这三个步骤,一个Firebase的基本功能就展现出来了。

Object.set({ ... }) 是直接生效的,不用再调用什么Object.save()

替代Object.get( ... )的是Object.on('event', function(data){ ... })。开发者要思考的,不再是我需要多久更新一下这个数据,而是可以专注在每次拿到手头这个Object的更新数据的时候,我需要实现什么逻辑。

我实现的Demo

IMG_0020

很早以前就想实现一个idea:我在卧室听有声小说的时候,突然要去上厕所。这时候我只要走出卧室,小说声音就会暂停,而当我走进厕所,厕所里的一个手机会继续卧室里的播放进度继续播放。也就是一个让媒体播放可以根据人的位置自动转移的新型体验。当然这也可以扩展到视频播放等。

检测人位置的功能我用iBeacon来实现。在两个距离较远的位置放置两台pad,上面运行着一个我的应用。应用所做的事情很简单:1. 检测用户距离多远。2. 根据服务器的指令播放媒体内容。

iBeacon SDK能做到的就是时刻告诉我周围的iBeacon设备距离我设备多远。而服务器用这些数据来实时判读到底应该在哪台设备上播放媒体内容。

这里Firebase的用法是

App将当前周围iBeacon的信息实时保存(默认情况下,每秒钟都会保存一次最新的数据)在某一个表示当前pad的Object下,用beacons的一个子子Object来表示。其中包括每一个iBeacon距离当前pad的距离。

一个Nodejs的Server。时刻检查着当前所有连上服务器的pad有哪些。一旦这里面有任何数据变化(某一个pad距离旁边的某一个iBeacon距离变化都会造成到root Object的数据变化事件),就会运行一段JS来判断当前哪个pad离用户最近,判断一下之前哪个pad在播放什么内容。如果最近的pad有变化,则将之前播放中的pad暂停,指挥新的pad播放该内容。如果没有在任何一个pad的范围内,则所有App都不会播放。

App会做的另一件事情就是根据当前所对应的Object下的一个域值:Settings里的一些配置来决定是否要提高或者降低亮度。记得前面说的吗,这个实现的方式并不是Object.get(),而是Object.on('value', callback),是不是很像JS里的observe。有趣的地方就是,这里的控制信号是通过一个类似数据库里的某个数值来完成的,而不是什么远程调用之类的。

总结

这样一个idea,通过引入Firebase,将一个终端App退化成了一个Sensor+根据数据库某个数值来改变view的简单实现。而同步那堆乱七八糟的什么的,完全看不到了。是不是很酷~

Source code https://github.com/fxp/flowapp

(30 hackdays day 2) Diffbot - 问题来啦!(1)

挖掘机技术哪家强?严肃点,我们来认真讨论这个问题。 假设我们讨论的是哪家培训挖掘机技术最强。首先,我们得知道有哪些地方能够学到挖掘机技术。然后,我们要想个办法定义“强”。最后我们得能算出来结果。

挖掘机技术学校

要知道都有哪些学校教挖掘机,我能想到的就两个来源:技校的黄页,搜索结果。前者可能有专业的技校汇聚网站可以爬取到,后者可以用第三方的搜索服务获取。于是我Google了下“挖掘机技术培训学校列表”。发现前几条结果都是www.peixun360.com他家的,所以我决定先把这个网站的挖掘机学校列表爬下来。

Diffbot

Diffbot是一个帮助人们将网页数据转换为结构化信息(其实就是爬虫干的事儿)的在线服务。通过简单的点选网页上的信息,指定到对应的结构化信息。它就能帮你把一个网站的信息转换成一个结构化的API。换句话说就是一个普通用户也能爬京东,把某类产品的网页变成一个“excel”。

Diffbot的API基本都分为Automatic和Custom两种,前者不用做任何事儿,算法自动帮你提取信息,后者可以有更大的自由度。

Product API是Diffbot重要API之一,用处就是帮助你自动分析一个“产品”页面的信息。比如“潞城挖掘机精品班”(是的,我看到28913也惊了,但放心,后面不是连续的...)。扔给Diffbot以后就会分析出下面的信息。

img1

是不是挺整齐的了?这还是我完全没有控制的情况自动提取的结构信息。下面我们来用下Custom API,也就是指哪打哪那个。 img2

先创建一个Custom API的Rule。可以看到Diffbot提供的Product的基本信息已经有很多了,什么OFFER PRICE,REG. PRICE,SAVE AMT.,BRAND之类的。那我们来把品牌加上吧。

img3

img4

img5

可以看到这里挑选一个域数据的方式很直观,鼠标选择一个Div,Diffbot就会帮你把它赋值过去。这里的小问题是它前端代码对中文的支持还有bug。但Save以后数据是正常的中文。当我们定制了一个新Field以后,这个自定义的Product的Rule就创建好了。这个Custom API也就能正常提取同类网页数据啦。 然后我就想试试Bulk API和Crawlbot。前者可以让你输入一系列的URL,比如几家挖掘机学校的详情页URL列表,后者可以爬取一个网站,从而对某些符合规则的网页调用Custom API。但...但...丫是收费API,而且...而且…我交不起的300刀一个月...所以…所以…不是我偷懒~

好啦,这就是一个帮助SB也能爬网页的产品啦~(我得想别的办法拿到挖掘机学校列表了...)明天见...

(30 hackdays day 3) Koding – 把chromebook变成开发利器

今天我要试试一款用来一起搅基Coding的产品Koding。现在尝试远程协作的团队越来越多,光靠Github来协同编码对于一些技术能力没那么强的团队是有些困难的。一来,Git的协作使用门槛还是比较高的,二来,这样的团队经常是3,1,1,1...这样的能力配置,也就是一个中等水平的工程师和多个初级水平的工程师。

这种配置就会经常出现一个问题:初级工程师经常要向中等水平工程师问一些初级问题。这种问题有几种询问方法(远程协作情况下):粘贴代码发QQ,QQ桌面共享,先提交代码再clone到本地检查,电话。这些方法都非常低效a且无法异步工作。

另外呢另外呢...Chromebook是个很不错的笔记本,但要想在上面做开发,就得找个在线的IDE...所以...

img

Koding是一款在线开发平台,它提供全套的开发工具栈:VM/IDE/Termianl。跟Cloud9类似,Koding也提供终端用于执行各种命令,也就是在浏览器里就能执行Shell里那些命令,包括sudo。另外,Koding也支持大部分Web技术:Go, NodeJS, Ruby, Python, PHP, Java, C, C++, Javascript, Coffeescript。所以,一般开发一个Web产品,Koding就足够了。

Koding比较突出的特点就是它为每一个workspace创建了一个Docker虚拟机来运行,可以配置公共IP地址。并且这个虚拟机是跑在AWS的1G RAM,3G Storage,单核上的Ubuntu 14.04,性能对于一般的开发环境来说已经挺不错的了。

开发工具集:AVOS + Koding + Chromebook(假装我在用!)

AVOS提供了很好用的shell工具,帮助快速开发后端nodejs应用。既然Koding自称支持node,那肯定也支持AVOS啦。那我这次就要假装在Chromebook上,用Koding来开发一个基于AVOS的项目。

首先创建一个AVOS工程,云代码-下载项目框架-Web主机版。把这个button对应的url拷贝下来。

创建一个Koding工程,在Terminal里执行

wget "[之前的那个url]" -O code.zip

可能需要把https改成http。创建一个新folder,比如mkdir avosproj,把code.zip拷贝进去,然后再unzip。

在这个avosproj里安装avoscloud-code,npm install avoscloud-code

等安装成功以后就能在avosproj里执行avoscloud啦。正常的话执行完了就会跑起来一个server,点击左边VM里右边的三个点儿就能看到这个VM的public IP xx.xx.xx.xx。再开一个Tab,访问xx.xx.xx.xx:3000就能看到那个avos项目运行起来的样子啦!

img2

(30 hackdays day 4) PubNub - Connect everything in REALTIME way

上周末,我和刘宇波去上海参加了一个"Hackathon",中日黑客马拉松,在做完Demo以后就直接走了。第四天啦,这次跟工作相关啦。

目的

我想做个简单好玩的爬虫,可以爬Web,以后可以扩展到Sensor,App,QS设备之类的各种数据源。

限制

  • 支持pull,push两种数据抓取方式
  • 爬虫运行环境higher than VPS

过程

第一个蹦到脑子里的词儿就是Cloud crawler。Google了一下发现又多出好多类似的服务,以前知道的有80legsscrapinghub,这次找到了ParsehubGrepsrDiffbot。还有这样明确的外包做这个的人工服务crawley-cloud

首先,我想尽量少写正则(写吐了),其次,我不想买个VPS干这事儿,然后,我想以后可以有更多数据源接入的时候,可以尽量减少对现有结构的干扰,最后,要实现够快!

第一步,设计一个大概系统结构。

arch

爬虫部分设计成了一个被动调动的API列表,每个API对应一个数据源的获取接口。API由一个统一的Controller来调用。每次API调用都被赋予一个Task,用来记录爬取任务的结果。

所有的爬取结果都会被保存到Log里。由Filter对每一条数据进行净化处理。通过了Filter的数据被保存在Item里。

Worker用来对Item里的数据进行统计计算,结果保存在Data里。Worker也是由Controller控制触法的。最后用两个WebApp来展示Data和一个用来展示中间数据的Dashboard。

这次的点PubNub

PubNub

可以看出前面这种架构的结果就是,分布在不同项目里,有好多Server在互相弱耦合的运行着。那他们之间怎么通信呢?PubNub就出场啦~通常我们会想到RabbitMQ这样的Message Queue类服务,但PubNub的胃口比这个大的多。从他的Slogan就能看出来:"Build and Scale Realtime Apps for Connected Devices.",他们想做物联网的普世消息服务。仔细看看他们支持的平台,有点吓人...从Codenameone到PIC32...也就是说理论上,基于PubNub可以很容易写出一个用js写的native应用来通知一颗芯片点亮某个腿对应的灯泡。

还是说回这个项目。PubNub可以轻易实现pub-sub形的通信。Controller和Crawler安装上pubnub,使用key初始化好,就可以向某个channel发消息啦。下面是它的栗子。

var message = { "some" : "data" };

pubnub.publish({ channel : 'my_channel', message : message, callback : function(e) { console.log( "SUCCESS!", e ); }, error : function(e) { console.log( "FAILED! RETRY PUBLISH!", e ); } });

超直观吧!Subscription也很简单。

pubnub.subscribe({
  channel : "my_channel",
  callback : function(message) {
  console.log( " > ", message );
  }
});

所以当我看到PubSub的时候就感觉到传说中霸气的"Javascript on Everything"的时代,终于要到来了!

比起Firebase,PubSub更偏向一个Message Queue的角色,很直观的在传输消息,而前者更“数据库”一些,在设计的过程中需要仔细考虑如何将数据的变化作为通信的触发条件。

PubNub另一个特色就是它提供了大量的案例,而且不断在增加。而且这些案例里大量的都是物联网相关的,至少这些Case对我来说大部分都还是挺新挺好玩的。最后,爬虫的代码以后再公开~最后的最后,看到下面这张图脑子中又出现了三个字“买买买”...

SmartDevice

(30 hackdays day 5) Page2Images – 特别肤浅的收费服务

上一篇讲到爬虫,爬取页面里的文字内容是最基础的,除此之外我还想要整个网页的截图怎么办呢。Page2Images就可以上场啦。在Leanstack上,这类服务被称为Screenshot as a Service(又一个SaaS...)。这么看来Service as a Service不远了啊(是说就是consultant么,那有没有consultant的consultant service呢)。

img

作为一个肤浅的服务,最重要的就是...脸。相比它的同行URL2PNG,Page2Images的小章鱼和Github有一拼,所以,好了就它了。突然想到,要是有个服务让我一输入名字就显示出Ta的脸,该是有多...肤浅。于是我搜了下“冯小平”,找到了第一张单脸照。

img

好,等我出名以后再找这种服务...

为啥Images是复数

从名字Page2Images可以看出,他们的数据库试图设计成一对多的关系。因为,“男人不止一面”。一个页面也可以有很多Screenshot,尤其是在这个Responsive肆虐的世界。好,你想到了Phone,Pad,Desktop不同尺寸是不是?嗯,直接来看最简单的使用方法:在你的页面插入一个标签,里面显示某个网页的某个尺寸的截图。章鱼给了个简单易用的工具来帮助你生成想要尺寸的截图。出来的HTML差不多长这样。

<img id="”p2i_demo”" src="//apple.com&amp;p2i_device=4&amp;p2i_screen=768×1024&amp;p2i_key=b00cc2e6ac5e8f**″" />

img

好了我知道你想说:我这里现实的没有框啊!嗯,自己找参数去。

好了我知道你又想说什么,为啥目标URL不encode啊!呃,其实吧,不encode又怎么着...嗯,除非你蛋疼的爬到一个URL

http://service.exmail.qq.com/cgi-bin/help?u=0&amp;p2i_device=6&amp;id=28

懂了吗?没懂去锻炼锻炼。

Direct Linking API KEY & Rest API KEY

这类服务为了收钱,一定得把收费的部分做到比较严谨。所以Page2Images里把API key分成两类,Direct Link和Rest API。前者用于在前端页面使用,比如img,javascript,后者用于服务端调用。所以前者除了要验证Token以外,还要被绑定在某个Domain或上,而后者只要带着对的Token就能访问。(所以如果有一个建站工具里,a.xxx.com是一个站,如果创建Direct Link Key的时候没有注意,Domain写了*.xxx.com,则b.xxx.com也能用前者的Token了)。

img

为毛比URL2PNG贵

URL2PNG不提供免费账户,但Page2Images提供,为毛呢?Price Table里有一个很重要的参数:Hits(Cached),然后每天Unique URLs是100个。懂了吧,最大负担就是每个账号每天100个请求了。

最后感慨一下,人家做这么个都能收费。哎...

(30 hackdays day 6) UserApp – 管管那帮用户

框架可以提供给你很多方便的特性,但也会剥夺你的自由。活的久的框架都不做太多“实事儿”。

用户管理这事儿很麻烦。原来只有User/Password,后来发现要加密,再后来出来了OAuth。如果现在你要做一个产品,还在纠结怎么做用户管理比较方便,有扩展性。UserApp会是你喜欢的。

作为一个管理用户的服务,最重要的,当然是开心。最好是能帮我把用户各种登录,连界面一起写好~嗯嗯,最好还能按我的需求来选择前端和后端的技术;还有还有,最好要加上付费,听说那个很麻烦;当然,标准价,套餐价什么的要是能加上就太好了。

好吧,我是在说一只咸鱼的理想。

一只咸鱼的理想

为什么只有一边有脸 是的,又是Get Started,你们丫以前是不是专门做登录的...这里你可以选择前端后端的技术,比如我选择Front-End为Angularjs,后端为Nodejs,然后它就会给你生成一个具有登录,还有脸!的Angularjs程序框架,连partials都准备好了。然后后端就会生成一个带Passport的Nodejs工程(作为Passport的一个Strategy),npm install一下就能跑了。 最后,在Congratulations的页面还告诉你,你的账号系统可以一键跟MailChimp同步!打开More发现还有mixpanel这个静静的美男子,以及stripe这货。

So sweet

为啥要咸鱼

“\w+类资料还是放在我们自己的服务器上比较好”,一定match了很多你lead的话吧。用户登录凭证保存在自己服务器里的唯一好处就是,在天朝里访问速度可能快那么一点点。而坏处呢,配HTTPS,容易被爆库,自己做SSO,自己关联所有其他服务的UserID...it's ok, fine.

fine

ACL matters

问题又来啦,一个用户管理系统对严肃的系统最大的影响是什么?权限控制。当然ACL可以适用于基本所有普通系统。UserApp提供了许多服务(对于UserApp这些也是它的库),以至于调用起来时这样的(我爱JS):

UserApp.[service].[method](arguments, callback);

哦,跑题了。ACL,最重要的呢,也是开心。Dev看ACL就两条:这SB用户有啥权限,这SB用户有没有这SB权限。

user.get(); user.hasPermission() 

OAuth painless

UserApp内置支持Google Github Facebook Linkedin,这些OAuth所需的配置都在UserApp的Dashboard里就能完成,点点就好。但是,但是...我没找到在哪里添加自定义的OAuth的说明。看来如果想在中国用只能自己写了.. 好吧好吧,我知道你失望了,我发个mail问问他们... OAuth

最后,这又是一个付费服务。不过,是按用户量来算的。所以如果你的应用一直维持在几十几百上千的量级,基本就不用交钱哈。 总结呢,还是用AVOS吧...

(30 hackdays day 7) Iron.io + beantalk 来颗铁豆

前面说到我打算用PubNub实现爬虫模块之间通信,但PubNub有一个比较麻烦的问题就是不能像MessageQueue那样实现异步消息传递。虽然PubNub也提供history()这样的函数可以封装为MQ的样子,但始终是有点文不对题的意思。 所以我决定再找一个MessageQueue的服务来做这事儿。以前我用过RabbitMQ,但那个毕竟要自己搭建服务。所以决定尝试一下另一个MessageQueue的Service Provider

Iron.io

iron.io Iron.io提供一系列用于Scale的服务,IronMQ (message queue),IronWorker (其实这才是重头戏), IronCache (呃 就是cache)。 这次我们只用message queue。Iron.io使用的是自家的IronMQ,2011年得到过$1.1M Citrix的投资。Iron.io做了一个compare table来跟其他MQ做比较。主要的优势体现在scalability上,就是各种能轻易扩大规模,换句话说,就是等你做大了只要多交钱就好啦~

Naked Message Queue

用过RabbitMQ的人都应该对它SDK封装的程度印象深刻(简单说就是超好用)。而IronMQ的特点就是...赤裸裸...至少Nodejs的SDK是这样。 node SDK提供的接口里最让人不爽的就是Retrieve message的部分。一次只能get一条消息,然后一旦没有消息就得到一个empty message的callback。虽然get可以设置一个wait,让它不立刻返回。但wait最多也就等30秒。所以如果要做一个简单的Daemon来接受消息就还要做一些循环封装(弄点儿Timeout啥的)。 作为一个这么懒的人,这咋行呢!按以往的习惯,这时候应该已经开始寻找替代品了。但...我交了5刀...所以...

Beanstalk Interface

为了5刀,一条条翻看Document,突然发现了个好玩的东西:Beanstalk Interface。稍微一搜发现这玩意儿正是我要的!(有没有想起JavaBean...)

Beanstalk is a simple, fast work queue.

Beanstalk是个抽象设计,所以有各种语言的实现。一一翻看Nodejs部分的Client。 NPM里的repo可以看到每天的下载量,这就给开发者一个很好的选择标准。我的标准是这样 NPM downloads > Github watch > Github fork > Github last update time。fivebeansnodestalker,最后决定用后者,因为更新时间比较近。

Watchman

测试了一下nodestalker的watch以后,发现似乎它的实现是同步的,会导致整个node项目无法处理新的request。而nodejs默认来说每个Process就一个thread,所以如果要在一个项目里同时监控message以及随时可以发送message的话,就得单起一个Process。worker.js是我的一个用来放watch process的文件。

var childProcess = require("child_process");
this._retrieveChild = childProcess.fork("worker.js");

Iron.io的服务需要认证,而认证的方式似乎比较奇怪:连上Queue以后先发一个Message,里面包含认证信息。

var authJob = client.put('oauth ' + MQ_TOEN + ' ' + MQ_PROJECT_ID);

在这个Job完成以后就可以做事儿啦~

抱怨

因为nodestalker用的是大名鼎鼎的mikeal的request。而因为受到AVOS的熏陶,写点儿啥都想用Promise。但...丫就是不支持!还看到一个牛逼哄哄的回复

niubihonghong

你丫就得搭金字塔,你丫就得...哎...所以我就搜Promise Request mikeal。发现一坨坨的...Promisify大有“互联网思维”的范儿。

Sample Source code

(30 hackdays day 8) Physical web - 给鞋子一个网址

本来今天是想写Google Alerts的API的。结果发现之前的API的repo不能用了。悻悻然,就再找一个吧~

因为一直在关注iBeacon,所以前段时间看到Google的Chrome团队出了个Physical-Web项目,一下就亮了。

让你的鞋子有个URL

example.png 听起来很酷,让每个东西都有自己的URL,很典型的Google式思维。具体做法也跟iBeacon很像,只不过把广播帧里传输UUID/Major/Minor变成了直接传URL。我暂时把这种Beacon技术叫做webBeacon。做过iBeacon开发的人都看得出这里一个很大的好处,就是省去了一步翻译过程,可以更容易的做cache了。下面就是它的广播帧。

uint8_t advdata[] =
{
  0x03,  // length
  0x03,  // Param: Service List
  0xD8, 0xFE,  // URI Beacon ID
  0x0A,  // length
  0x16,  // Service Data
  0xD8, 0xFE, // URI Beacon ID
  0x00,  // flags
  0x20,  // power
  0x00,  // http://www.
  0x41,  // 'A'
  0x42,  // 'B'
  0x43,  // 'C'
  0x07,  // .".com"
};

实话说前面的部分没太理解,多了解下BLE再看。后面的部分自然就是发射功率和URL了。发射功率是用来做RSSI定位的。URL部分看来是不打算支持unicode的哈,还给.com专门搞了个数字7(你让Bell情何以堪...再Terminal里按control+G就能碰到它)。

话说突然想到入珠这个话题...然后就突然想到支持Physical-Web的入珠。然后就脑补出一个颠颠的走在路上的家伙,丁丁不断Broadcast一个广播,是一个网址。然后旁边的人掏出手,机(总觉的这句话在这个情景下好奇怪),看到一个丁丁的网站...

Coder的BLE - RFduino

借由文章的介绍,我了解到了还有可以让小破Coder也能轻松开发BLE底层的工具RFduino。因为那帮Googler觉得市场上现有的beacon不方便改广播帧,所以推荐了RFduino,还在Repo里专门放了个用于RFduino的firmware和一个用于发现这种广播的Android程序。进去以后发现不光是RFduino可以做成一个webBeacon,Android,Arduino,甚至nodejs都可以...模拟成一个webBeacon了!因为Android的要求系统最低时Android L,所以我只能试试nodejs版本的了。好吧,最后它告诉我Mac不支持,只支持linux。那..看来最可能的就是我把Nexus5升级到Android L。

Android端接收程序

所以呢...这么短的博客不好吧...

OpenBeacon

我只能硬着头皮往后看。发现了个OpenBeacon这样一个发射端实现。进去网页发现这是个2006年开始的开源项目。当初是做开源的2.4G RFID,后来加入了BLE。

里面的一个视频是他们的愿景:线下人们之间的接触行为+线上结构化语义数据+线上SNS的数据=实时的整合社交图谱。视频里它给出了一个实验室案例。真的很酷!但...它勾起了我当年做网络仿真时不好的回忆...也罢也罢...

看了那实时的效果是不是瞬间觉得Person of Interests里的世界似乎已经发生了呢~

图片描述

好啦好啦,终于感觉不那么愧疚了!睡觉!

(30 hackdays day 9) Rekognition - 借我借我一双慧眼吧

我们知道前段时间有报道说Facebook说他们的人脸识别技术达到,甚至超过了人类识别人脸的能力。很酷是不是。今天介绍个可以直接用的更酷的东西:物体识别。

Concept Recognition

如果你想让程序知道这张图大概是讲什么的,怎么办?Rekognition(是的,破名字...)就可以做到。他们家提供了最普通的人脸识别以外,还提供了场景识别明星识别,以及今天要讲的概念识别。 先直观感受一下Demo。通过这张图,Rek可以识别出70%概率这张图讲的是beach。 图片描述

This image is about :
beach:70.65%
cloud:66.57%
sky:33.56%
island:10.88%
shore:7.56%

“丫绝对是找了个special case!”,是的我也这么想的。好,我决定用我的narrative拍的照来测试下。这是我18号在路上的一张照片。其中很明显的有两种东西:汽车和路牌。

图片描述

分析结果如下

basketball court:60.2%
car:31.89%
street sign:29.07%
motor scooter:20.09%
pop bottle:12.09%

看到第一个失望了吧。篮球个毛啊,哪只眼睛看到篮球了...但往后看就觉得我靠,还挺准。汽车,路牌,小摩托。

我整天都在跟什么玩意儿打交道

自动我买了Narrative以后,就一直在思考怎么把这些图片数据用起来。那就让我用Rekognition的Concept API来下手吧。API很简单,如果不传图片信息而用URL来表示,就用GET就ok了。 遇到一个问题:如果按Docs填写jobs参数就永远没有结果,只能照抄Demo页面里的参数,Concept的jobs写scene_understanding_3。 我用15张照片做了一个批处理,算出这15张照片对应的Concept的数据,都添加到一个统一的results里。

var request = require('request'),
    qs = require('querystring');

var pictures = [ 'xxx.jpg', 'xxxx.jpg', ];

var results = {};

for (var i = 0; i &lt; pictures.length; i++) { processImg(pictures[i]); }

function processImg(pic) { calculate('http://xxx.qiniudn.com/' + pic, function (info) { for (var i = 0; i &lt; info.scene_understanding.matches.length; i++) { var match = info.scene_understanding.matches[i]; if (results[match.tag]) { results[match.tag] += match.score; } else { results[match.tag] = match.score; } } }) }

function calculate(url, cb) { var params = { api_key: &quot;4321&quot;, api_secret: &quot;8765&quot;, name_space: &quot;apiDemo&quot;, user_id: &quot;apiDemo&quot;, jobs: &quot;scene_understanding_3&quot;, urls: url }; request.get({ url: 'http://rekognition.com/func/api/?' + qs.stringify(params) }, function (error, response, body) { if (!error &amp;&amp; response.statusCode == 200) { var info = JSON.parse(body); if (cb) cb(info); } } ) }

照片存在七牛了,每次传一个URL就ok。最后得到的结果如下。

{
  &quot;awning&quot;:0.0556,
  &quot;architecture&quot;:0.0099,
  &quot;dorm room&quot;:0.11510000000000001,
  &quot;people&quot;:0.0508,
  &quot;face&quot;:0.1062,
  &quot;finger&quot;:0.1521,
  &quot;jeans&quot;:0.1502,
  &quot;cat&quot;:0.0189,
  &quot;pet&quot;:0.0087,
  &quot;seat belt&quot;:0.21689999999999998,
  &quot;document&quot;:0.0095,
  &quot;human&quot;:0.0722,
  &quot;flare&quot;:0.0093,
  &quot;laptop&quot;:0.3152,
  &quot;batting helmet&quot;:0.0095,
  &quot;desk&quot;:0.0174,
  &quot;molding&quot;:0.1219,
  &quot;pc&quot;:0.1488,
  &quot;television&quot;:0.0394,
  &quot;flight&quot;:0.0098,
  &quot;person&quot;:0.0095,
  &quot;loudspeaker&quot;:0.0295,
  &quot;apartment room&quot;:0.0094,
  &quot;rat&quot;:0.1126,
  &quot;lecture&quot;:0.0112,
  &quot;aquarium&quot;:0.0104,
  &quot;underpants&quot;:0.0089
}

其中最高的是0.3152,是...laptop...=.=|||。你丫是猴子派来黑我的吗! 第二名是比较奇怪的seat belt。第三名是手指(不不不,我只放算了几张白天的照片!)。后面还有牛仔裤,PC等等。基本还是可以判定通过这少量数据就能一定程度判定一个人整天在跟什么玩意儿混。 让我有点奇怪的是里面居然没有手机,于是去浏览了一遍那几张图,真的没有。

哎,再让我伤心一会儿...

(30 hackdays day 10) Material design - 可用的Material

Google的Material Design出来一阵子了,但是不是感觉还是个看不见摸不着的概念?按照Google的说法

Material Design is a specification for a unified system of visual, motion, and interaction design that adapts across different devices.

Material应该是一个跨平台的,统一视觉,动画和交互设计的基本模型。好吧,工程师思维作祟,为什么听起来像bootstrap呢。还有,什么Material is the metaphor,Swift out,到底咋实现呀...

于是,几个Ionic FrameworkAngularJS的核心开发者站了出来,开始了一个酷酷的项目Material。这个项目目前还处于初期高度活跃时期,所以最好不要在自家成熟系统上玩耍。

写过Angular跟很多现有控件打交道的人都会体会到它们之间的“代沟”。除了各种$scope.$apply以外,Angular崇尚的组件复用,Directive等经常会反而成为整合的绊脚石。所以很早就有个项目Angular-UI试图把一些常用的,跟Angular整合很好的组件统计在一起,让大家各取所需。但后来不知为何,这网站并没有成为Angular Directive的集散地。

后来PolymerDart出来了,感觉Google下决心要彻底改造前端技术了。但囿于之前GWT的失败,我总觉得Google也就适合做做新语言,Compiler之类的,直接做前端界面总是会被骂碎。

但这次看了Material这个项目的Demo(好多东西)以后,我真心感到,Google工程师审美提高了...

Material组成

按照作者所说

Our goal is to deliver a lean, lightweight set of AngularJS-native UI elements that implement the material design system for use in Angular SPAs.

Material项目的目的是提供一系列实现了Material设计系统的Angular原生的UI控件(想象一下再也不用$scope.$apply来让这世界刷新了)。Component里面有目前提供的控件列表。其中,CardmdToastTextField都是做的比较好玩的(想象一下一个页面也能用Android的Toast)。

写了个小登陆框,用了md-card, md-text-float, md-button。用了几个Directive就搞定了。虽然感觉还是比Bootstrap写的麻烦一些,但出来这些交互还是挺爽的。

图片描述

其实Firstname应该是粉红色,很漂亮的粉红色。然后点击TextField以后Firstname会上去,空出位置来,动画也是挺赞的。

另外,赞一下他们的文档。他们的文档是用dgeni写的。需要装一下然后build一下就能看到。Demo里获取例子的源码也方便了很多,点击右上角的Source就可以。直接看Angular出来的HTML就是作死。

最后,这个Google的家伙是这个项目里的一员:https://plus.google.com/+NaomiBlack/posts/bCS9DSPdVug

嗯,我知道今天的文章很水...但...也算写了好吗!

(30 hackdays day 11) Woopra - 你的用户在胡搞瞎搞

对于追踪用户行为来说,有很多工具都可以实现,比如国内的友盟,老牌的Google Analytics,近些年很火的Mixpanel。但如果你很想实时看到你的用户在你的产品里如何玩耍的话,你应该试试Woopra

logo

WooPra

Woopra不算是新产品了,2008年起就在提供服务了。产品也很低调,2011年起就没有出现在Startup新闻里。似乎也没找到近些年的投资纪录。按照CrunchBase的描述,Woopra是一个为销售和市场提供客户实时分析服务的产品。

Woopra is a real-time customer analytics service providing solutions for sales, service, marketing and product teams.

不过按Alexa的数据来看,Woopra最近的日子并不好过,流量一直在下降。

图片描述

然而Bounce Rate,Daily Pageviews per Visitor都在上升。看来用户忠诚度还是蛮高的。

用户纬度行为跟踪

从首页的Banner就可以看出,Woopra可以帮你把用户在应用里的行为转换为用户本身的行为时间线。 图片描述

免费用户也是能注册的,不过仅限30000次行为每月的数量,确实比起Mixpanel就差太多了。不过,实时也许是个好玩且值得的体验。

Setup

很简单,贴一段JS到head里。跟Mixpanel一样,Woopra可以允许你把自己系统里的用户对应到Woopra的用户。只需要执行一句

woopra.identify({
  email: '{{ account.email }}',
  name: '{{ account.name }}'
});

然后就可以在各种地方执行事件记录代码啦

woopra.track('wanshua', {
  with: '王铁锤',
  activity: '羞羞'
});

只要一执行trace函数,在后台就能看到这个事件。包括你所有的事件细节。还有用户地点(翻墙到日本)以及浏览器,系统。

图片描述

当然,跟Mixpanel一样,Woopra也提供report,funnel,retention这类的report。不过我觉得最酷的也只有他家的Dashboard了,实时的呦。

图片描述

AppConnect

他们最近的新动作就是AppConnect,允许Woopra介入第三方应用。嗯,它的接入的含义就是,Woopra可以将统计数据输出到各种第三方服务中。比如把你的report输出到Dropbox保存为文件。把用户做了啥当作消息输出到Slack。想象一下你的slack会跟你说,“用户王大锤和蛋蛋搞基了,本周的第10次”。

不过我觉得更明智的做法是用Zapier,让你的信息可以跟几乎所有暴露API的服务互通。

(30 hackdays day 12) Cine/Twilio - 第三方FaceTime/FaceTime Audio

视频直播,是不是挺起来就有技术难度?各种压缩,各种广播多播协议啥的?嗯,今天介绍一个超简单的视频直播服务,Cine

图片描述

7行代码的广播端

var streamId = '543de35085130b0800ecf111'
  , password = 'cleff32'
  , domId = 'publisher-example';

var publisher = CineIO.publish( streamId, password, domId );

publisher.start();

img

其中domId就是...随便一个dom的...id...不必是个video啥的。因为...因为...丫是基于flash的...哎,瞬间就感觉这文章没得写了。好吧,这次买一送一!

Twilio

图片描述 这是一个让你的应用可以打电话的服务。嗯是的,在你的App里打电话。可以打给另一个装了这个SDK的App,也可以打给一个手机或者一个座机。 一句Curl就可以打电话,是他们的一大特点,来看看咋做的。

curl -X POST https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Calls \ 
--data-urlencode "To=+14155551212" \ 
--data-urlencode "From=+14158675309" \ 
--data-urlencode "Url=http://www.drawtheowl.com/ahoy.php" \
-u {AccountSid}:{AuthToken}

具体的body呢,如果想用程序控制发一段话就这样

Yo holly shit!

想播放一段音频给对方就发这个命令。收发短信,收发电话,都ok。

 audio to the caller just by passing the URL of the audio file.

也可以加WebHook哦~(好吧,我还不知道能干啥用...)

嗯,心情不好,就写这么多吧。抱歉敷衍了。晚安。

(30 hackdays day 13) Bancbox - 只是金钱的搬运工

今天介绍一个没法测试的API,Bancbox Invest。按照官网的说法

图片描述

Bancbox builds apps that move money.

Bancbox不创造钱,Bancbox只做金钱的搬运工

Bancbox是一个2008年创建的公司,做事儿很专注:"把钱挪来挪去"。一共有三个服务:投资托管,减税代理(我也不知道是啥...),自动交易。与其他服务最大的区别呢,就是他们提供API!(btw 网页做的很漂亮,字体也不错,Egyptienne URW Light

为啥"搬运工"要提供API呢?按照Techcrunch去年的文章里提到的创始人的话

the first independent escrow service aimed at crowdfunding portals that operate on equity, debt or revenue share models

Bancbox的第一个独立产品瞄准的是股权,债券和收入分成(??)类的众筹平台。

当时已经跟三个如上类产品进行合作了99Funding, SoMoLend, Localstake。作为一个纯小白,根据文章描述看起来似乎是Bancbox可以帮助原来那些受法律规定,必须提供第三方担保账户的众筹项目,减少大量的paper work,从而更加关注在本身的业务上(就是“骗钱”)。

$$$

让我们来看看一个例子(如果理解错误请见谅,如果觉得不烦的话请指教哈)。

图片描述

假设有个众筹平台,比如蝌蚪众筹~,想为一堆项目(Issuer是那些找投资的公司)做股权众筹。这时候肯定会需要每个项目有一个银行账户来保存这些众筹来的钱。似乎由于一些政策原因,这些钱不能流过众筹平台本身。那这个账号就需要由一个第三方来保管(除去Crowd funding platform和Issuer以外)。 另外,每个众筹项目都要经由Issuer和众筹平台的一些文书签名确认就可以创建一个第三方托管账号。这个过程结束之后,托管账号会被激活。众筹平台就可以开始为项目募款了。

下面的图讲的是一个天使投资人如何投资一个项目。每个方块代表了一个或多个API。创建天使投资人,转移投资款到托管账号,创建投资项目,投资,收回投资。 图片描述

APIs

看看人家文档,直接给程序员curl...

curl -k -v https://sandbox-api.bancboxcrowd.com/crowd/v0/cfp/withdrawFunds \
-d api_key=someApiKey \
-d secret=someSecret \
-d method=ACH \
-d investor_id=someInvestorId \
-d investor_reference_id=someInvestorReferenceId \
-d amount=50 \
-d text="I authorize Bancbox to make this transaction" \
-d client_ip_address=127.0.0.1 \
-d submit_timestamp="2013-11-10 01:31:00" \
-d bank_account_number=someBankAccountNumebr \
-d bank_account_type=SAVING \
-d bank_account_holder=John \
-d bank_account_routing=someBankRoutingNumber

创建投资人,创建投资项目,开户啥的都有API。我们要看什么呢...嗯...看看API设计吧...

POST https://sandbox-api.bancboxcrowd.com/crowd/v0/cfp/createInvestor

在二级域名区分的sandbox-api下(这样比AVOS那样在API的body里更方便一些,切BaseURL就好了),crowd是产品名,然后是API版本,然后是cfp, crowd founding platform,(这么看来还有给其他终端的API,比如后台管理,统计之类的吧),行为。不过,createInvestor这显然不RESTful哈,做成POST .../investors会不会更好些呢。

再看看Response

{
    "request_id": 85748180313,
    "status": 1,
    "api_status": 1,
    "id": 99909063016,
    "event_id": 641159
}

status指的是业务层面的状态,api_status是指的本次API调用的情况(我喜欢这个)。如果遇到Warning或者Error(我很少见过HTTP API里面设计Warning的,你知道这帮家伙不鸟那玩意儿的...),会在结果根下添加。errorCode这类东西变成了一个W-014这样的string。

"warning": {
    "type": "W-014",
    "message": "BBX admin will need to manually verify this Investor. Please check your email."
}

当然Bancbox也支持Webhook来获得各种事件(也就是类似ping++的回调)。

好啦,Bancbox就介绍这么多,因为很多流程都是美国的,所以基本在中国用不了。但鉴于中国各种众筹如此蓬勃,明年又传说是“投资冬天”,还有目前众筹方面的各种风险。中国版的Bancbox也许是个不错的方向。

(30 hackdays day 14) Muse - 了解自己

Muse是一款Electroencephalography(EEG)。跟MindwaveEmotiv一样,佩戴上以后人们就能得到各种脑波数据。现在的主要用处是用来获取用户的专注度。

图片描述

怂恿之下,在Mindwave,Mindwave mobile,Emotiv之后,又入手了一副Muse。本不抱太大希望的(民用级的EEG也就Emotiv这样了),但经过简单的试用以后发现Muse真是一款挺不错的新产品~连接稳定(好,写到文章最后我决定去掉这个特性...),有刘海的情况下还算隐蔽,5个主要传感区域,戴上不像科学怪人,对佩戴要求并不高。

需要翻墙的App

要想使用他们的Sample App,居然需要翻墙(包括注册账户)...原因是他们的Server是在Google App Engine,存储静态文件也是在Google App Engine Storage里。就连他们的document也是放在Google site下的。(真幸福...)

痛苦的SDK

Muse的SDK是几个EEG里装起来最麻烦的(貌似把它整个toolchain都当了一遍...),差不多耗时1个半小时,但还好这过程没出任何错误。通过执行muse-io可以判断SDK是否安装成功。

图片描述

奇怪的是,似乎本地的测试程序每次只能拿到一个数据,然后就死在那了...

MuseIOReceiver

有一种数据叫OSC,是用来在电脑,各种多媒体设备间进行通信的开放协议。

Open Sound Control (OSC) is an open, transport-independent, message-based protocol developed for communication among computers, sound synthesizers, and other multimedia devices.

Muse基于OSC来进行数据传输(事实上,Muse还采用了Protobuf来存储数据)。所以甚至还像模像样的提供了oscdump工具。

MuseIOReceiver是用来接收Muse家的OSC数据的。给出的Sample代码可以给出delta, theta, alpha, beta and gamma值(好吧,原来还有这么多值...)然后他们提供了Android和iOS两个版本的源码。

原本的计划是我想拿到这实时数据,但这程序似乎不那么好用...再加上今天超困超困...以后再补上~

(30 hackdays day 15) Bonsai + LeanCloud - 要有Search!

LeanCloud(原AVOScloud,还是喜欢这名字哈)是国内数一数二的BaaS产品。但由于还在快速发展期,所有还是缺少不少基本功能,比如搜索。好,问题来啦,搜索技术哪家强?Google?Bing?他们肯帮你的产品做搜索吗?Google Site Search?嗯,他们可以帮你定制一个好看的搜索结果页,可以输出XML。但,除非你用他家的服务器,否则它不能用你的数据直接做搜索。假如你想要一个只搜数据库里某张表里的数据,又不想让自己的服务器,或者BaaS平台变成阻碍。那就应该试试这个产品。

Bonsai

我们接触了各种XaaS,Search as a Service也是理所当然的啦。只是这种搜索即服务也有各种形式:

  • 三温暖全套服务。Site Search那种我帮你爬网页,帮你分词索引,帮你做Ranking,最后你告诉我个关键词我就给你返回页面URL。
  • 某些特定平台下的搜索。比如Wordpress各种"better search, search everything"。
  • 针对数据库的搜索引擎。数据库固然有内置的搜索功能,Mongodb等等的document数据库也正需要靠这个挣钱。但始终是不能满足startup简单,快速,暴力的开发标准。尤其是搜索并不是产品的核心功能,但又是必须的情况下。

也许还有别的哈。第三种Search as a Service更适合API Mashup起来的新产品,它不会因为你的页面改变而需要重爬网站,也可以快速的让搜索引擎感知到内容的变化。 总之,搜索这事儿也是花钱就可以快速搞定的~让我们看看怎么把Bonsai和Leancloud结合起来。

logo.png

为什么要选择Bonsai?这得从Elasticsearch说起。Elasticsearch是一个基于Lucene的搜索引擎,它最大的特点就是提供且只提供了一套标准的Schema-less的RESTful API。这套API是这个搜索引擎的全部接口。所以,我们可以把Elasticsearch看成一个黑盒,一个搭好就能用HTTP API往里扔数据,往外Query结果的傻瓜搜索引擎。用BugSense的例子来看。

tumblr_mddwolK0jO1qhxx40.png

BugSense把从手机上收集到的Crashlog扔到Google的BigTable里(Google storage类服务),然后再扔到ElasticSearch里,最后输出给一个前端应用一个用复杂搜索结果组成的Report。

LeanCloud + Bonsai

我要做的也是类似的,我有个小爬虫,跑在LeanCloud的云代码里,每次被调用都会爬一次网页,把结果保存在数据库里,假设这个保存的东西的Class叫Page。

AV.Cloud.define("doCrawl", function(request, response) {
  // Something like request("http://www.google.com")
  page.save().then(function(page){
    response.success({"title":"Google blahblahblah"});
  });
});

好,这时候我就想能搜到title里有Google的Page,那就得看看Bonsai咋玩啦。 登陆首页bonsai.io注册一个账号,创建一个新的Cluster。

图片描述

这时候你肯定在寻找美妙的Get Started!但...但这里没有“十分钟入门”...因为...因为做Bonsai的人在首页就说了。

图片描述

// I have to...
console.log("We're developers, too")

好吧,现在去学学怎么玩ElasticSearch吧。我就不推荐啥快速入门文章啦,请自行Google "tutorial elasticsearch"之类的(tutorial是个特别好的关键词,我都想搞个自动帮你填上这个词儿的搜索引擎了...)。 总之,我们可以很轻易的通过RESTful API来向Bonsai创建出来的ElasticSearch服务器灌数据。ElasticSearch中,创建数据通常也意味着索引数据。

curl -X PUT http://[SERVER]/[appname]/[mydatatype]/[object_id] -d '{  }'

Bonsai简化了这个过程,在刚才的界面里,选择PUT,右边写上path,下面写上数据的具体的内容(当然是JSON格式的)。点击Run,数据就进区啦。

图片描述

我们想搜一下有啥数据里有ohuihui的话,只要写一个Query DSL,POST到想要搜索的数据类型下,就能得到JSON的结果啦~

POST /testapp/testdata/_search
{
    "query":{
        "query_string": {
            "query": "ohuihui"
        }
    }
}

图片描述

够简单吧?同样,只要用上面的Cluster URL作为baseurl,用任何一种语言都很容易通过HTTP API可以向Bonsai里添加数据~ 好,现在的问题就是,怎么让我们在AVOS的数据进到Bonsai里呢?还好,AVOS的云代码提供了Hook功能。在每一个数据创建的时候,都可以由AVOS自动调用一段你的代码。比如

AV.Cloud.afterSave("Page", function(request) {
  // Post the page(request.object) to Bonsai
});

这样,每次有新的Page保存到AVOS里的时候,Bonsai里就会被自动添加上这个数据。然后就可以再写一个云代码来访问Bonsai获得用户某个query的结果。

AV.Cloud.define("search", function(request, response) {
  // Something like
  var queryBody = {
      "query":{
        "query_string": {
          "query": request.params['query']
    };
  request.post({
    url: "http://xxx.bonsai.io/testapp/testdata/_search",
    json: true,
    body: JSON.stringify(queryBody)
  })
});

That's it. 整个结构很简单,如下图。以后你想自己做搜索了,自己搭一个ElasticSearch,重新倒一遍数据,改一下search那个云函数里的BaseURL就好啦~

图片描述

Good night, hope you like it!

(30 hackdays day 16) Filepicker - 上传文件如此简单

基本上,各种产品都会用到一个很普通,但又很难实现的很漂亮的功能——文件上传。比较早期的一些Web框架还特地为File Upload做了专用的组件来方便开发者上传以及在后端处理上传的文件。这次我就介绍一个帮助大家超轻松实现文件上传功能的服务,Filepicker

logo

Filepicker.io

Filepicker做的事情很简单:帮助你用最少的代码把文件传到你想要的地方。我们直接看一个最简单的例子,使用Filepicker提供的Drag-Drop Pane widget来上传图片。

filepicker.makeDropPane($('#exampleDropPane'), {
  multiple: true,
  onSuccess: function(Blobs) {
    $("#exampleDropPane").text("Done, see result below");
    console.log(JSON.stringify(Blobs));
  },
  onError: function(type, message) {
    $("#localDropResult").text('('+type+') '+ message);
  },
  onProgress: function(percentage) {
    $("#exampleDropPane").text("Uploading ("+percentage+"%)");
  }
});

只要在页面上加一个id是exampleDropPane的div,它就会变成一个可以往进拖拽文件并上传的控件。上传成功以后,onSuccess里就可以得到上传以后的文件信息,比如如下。

[{"url":"https://www.filepicker.io/api/file/n4Ba7BdNSJyaLZfu2Qm5","filename":"logo-primary.png","mimetype":"image/png","size":8452,"isWriteable":false}]

返回信息里包括文件URL,mime(filepicker帮你分析出来的,我测试了下apk也可以分析的出来,挺不错的)和一起其他信息。

也许你会很奇怪,这文件没有传到我的服务器上呀?是的,Filepicker不光帮你写前端代码,也帮你搞定了文件存储(默认情况)。这意味着一个前端工程师,使用BaaS类服务,加上Filepicker就可以超容易的做出一个允许用户上传文件的WebApp——在onSuccess里在BaaS里创建一个文件对象,把url指向Blobs.url就可以啦。

如果你很在意用其他数据存储服务,比如S3,Azure,Dropbox,Rackspace。可以简单的在makeDropPane里添加选项location:'dropbox'就可以啦。在Developer Portal里你可以配置如上几种服务的各种Key。

Filepicker.io   Developer Portal   fxp007 gmail.com.png

还有一种情况,你的App需要把数据存在用户自己的某类存储账号中,比如用户自己的Box,用widget就可以很容易enable这个功能。

API

Filepicker支持各种终端,并提供SDK(当然有Javascript的)。

Filepicker SDK

btw. Meteor的发展速度真是猛啊!

CDN

如果你的网站是个IO集中型的,换言之很在乎CDN的,可以使用Amazon的CloudFront和Filepicker.io一起使用。

Price

Filepicker帮你保存文件,提供一行代码添加uploader,可以存在各种地方,你觉得得多少钱?只需要29或者99!刀...价目表很清楚,我感觉对于小公司也是超值的。另外就是,我很好奇最牛逼方案里20GB大文件是咋搞... Filepicker.io   Easy  Powerful File Uploads.png

ok,抱歉又水了一篇,太困了,晚安~

想了解这个30 hackdays可以查看这篇文章。也欢迎访问我的墙外站

(30 hackdays day 17) AngelList - IT桔子啥时候开放API

最近在做的事情需要收集创业公司的信息,所以这次我介绍一个获取Startup信息的API。AngelList,是一个为创业公司提供投资,招聘等服务的平台。

AngelList现在号称"Over $10,000,000 invested online this month”,我数了下数字,差不多一个月1000万刀的投资。但...logo为毛做的这么二...

图片描述

废话不多说直接看API https://angel.co/api/

第一眼望去看到的是OAuth,第二眼望去就看到一个不认识的词儿,Accreditation。看起来好像是得到认证过的投资信息。不管了,不用到暂时。

先看一个综合的搜索Search API。按名字来搜索公司,用户,地理Tag等。这个不需要认证就可以访问。比如我们找个叫leap motion的公司

GET https://api.angel.co/1/search?query=leap%20motion&amp;type=Startup

就可以得到一堆跟leap motion有关的公司。可是,前两个名字都叫Leap motion...不过根据AngelList的排行,第一名应该还是比较靠谱的,而且第一个还有pic这个域。

{
    "id":6343,
    "pic":"https://d1qb2nb5cznatu.cloudfront.net/startups/i/6343-3704fb09e9b74996b5826f5cc1ab5a00-thumb_jpg.jpg?buster=1369156838",
    "url":"https://angel.co/leap-motion",
    "name":"Leap Motion",
    "type":"Startup"
}

这里面的信息很少,想要详细信息就得访问另外的Startups API

Startups API也是个很综合性的接口。除了获得某个Startup的信息以外,还能得到它的评论,以及Startup的排行榜和具有某个tag的所有Startup。 我们用刚才拿到的ID来访问Leap motion的公司信息。

GET https://api.angel.co/1/startups/6343

就能拿到一个挺全面的公司基本信息啦。包括公司首页,公司logo,产品描述等等,公司涉及领域等。

另外一个可以用来窥探公司方向的API事Jobs API。看看Leap motion在什么。

GET https://api.angel.co/1/startups/6343/jobs

可以看到他们在招Software Engineer,期权,薪水,技能都有描述。他们用了一个Array的Tag来描述技能,工作地点之类的,这个用法还是挺好玩的。

图片描述

看,Leap motion在招会用C++写算法的软件工程师哈。再见... 最后,某些牛逼投资人的following也是很有价值的,那就是Follows API

(30 hackdays day 18) Dexlabs - 有语言的逆向分析服务

以前我做过一段时间的Android逆向,当时是为了检查广告检查病毒,后来用来找到公开的intent跳转到应用内做搜索,再后来为了找到这些应用都跟哪些API打交道,从而统计SDK和产品之间的关联度。之后就再也没接触过这些了。不过前两天发现我还在订阅apk analyze的Google alert邮件,所以就决定再回来看看现在这领域都在玩哪些新玩意儿啦~(其实是找不到新API了吧...)

android-apktool

绝对的老牌选手了,从10年开始就在维护。貌似现在是Connor Tumbleson这家伙在维护。这家伙是Techcom Worldwide的程序员,看起来一直很专注在Android OS这块,以至于他的Vibe里只有两个Tag:Technology, Android OS。对比一下我的...唉,惭愧。(为毛会有粤语...) 图片描述 图片描述

丫做了一堆网站,看了下觉得都挺好玩的。另外最近他把Wordpress换成了Ghost,太酷了!有没有Ghost的博客托管平台啊!顺便再膜拜一下Brut.alll!

  • http://leafapp.co
  • http://ibotmodz.net
  • http://ibotpeaches.com
  • http://connortumbleson.com
  • https://keybase.io/ibotpeaches

图片描述

先看看2.0.0RC2的样子。好久没用过apktool了,第一眼看到的区别就是-advance这个参数,高级信息...是个啥...其他的,似乎跟上次见到的时候无甚差别。不过根据文章介绍,ApkTool终于支持Java7了。

具体的逆向过程我就不讲啦,因为以前做过这个。

dexlabs

主角儿来啦。当我说Reverse engineer的时候,你应该就想到了Reverse engineer as a Service吧~是的,Dexlab就是这样一个产品。看名字就很明白啦,这是个以分析APK为生的公司。

图片描述

里面又碰到一个第一眼不认识的词儿Heuristics(这是要改成30天速成英语四级么...)。好吧,一会儿再看这到底是啥高级功能。

这产品使用很简单,注册个账号,上传个apk,等它告诉你分析完了,就可以开始看结果啦。所以这是个编辑都会干的活儿(没有贬低的意思=.=)。因为是静态分析,所以也不用指望有什么很突破的功能。通过首页的示例基本就可以看出其中一个很重要的功能应该就是类似IDA pro的关联分析静态分析功能。

GMS

普通的小应用不值得分析,让我们直接上大招,Google Mobile Service。这是一套Google在Android上的核心服务,在国产手机上基本不能有。

上传完以后就开始分析,着进度条晃啊晃啊的烦死了,既然你丫不知道进度就谦逊一点嘛!大概4分钟以后分析完了(27MB确实比较大,以前的经验来说这种程序如果是java的就会跑崩了...)。好,下面是大图!可以很快的划过去。

bigfile

这就是这个庞大的GMS的基本信息,就是APKTool能分析出来的那些。重点是看右边的那个Package Deps。好吧,完全看不出来啥了...基本也就能看出几种基本服务类型,Ad啊location啊啥的。

图片描述

最有意思的,是他们还为程序静态分析设计了一套类似SQL的语言,DXQL - Dexter Query Language。不过,通过他们在twitter上的这只推来看(只有一个retweet),应该不会有其他地方使用这种语言...

哦对,其实,这个平台主推的应该还是协作分析的功能。邀请他人来一起分析某个APK,听起来就跟搅基是同义词~

最后,我一直在寻找的API,在FAQ里找到了...

Which extraction/export interfaces does Dexter offer?
None yet. However we want to implement a SDK which can run on the user’s client computer for further analysis. If you contact us about that, it might help to schedule our priorities ;)

事实上,他们对很多功能的回复都是这个: If you contact us about...好吧,这是第一个没有API的XaaS。下次我尽量不那么水...

(30 hackdays day 19) CARA - EQ不足CARA补

受朋友之邀调研有没有那种提供视频中的人脸识别并自动添加上某些面具类的服务或者API,如果能根据表情调整面具内容就更好。

首先,人脸识别的API有超多,国内很有名的Face++之类的都可以提供很好的结果。但这里有三个不同:视频,表情,面具。

视频

理论上,视频也就是一堆静态图片而已,但按照目前HTTP API的效率来说,一秒钟24次API真的还是挺难的。但如果效果好,也可以跟对方合作,用长连接类的API来调用他们API,也许能撑得住。

表情

于是我就去搜emotion detection API,发现了这么个服务Imrsv,先体验一下他们的栗子服务(挺好玩的!)。我的检测结果。

图片描述

他会让你看一段视频,然后观察你的表情,分析出笑,惊讶,不喜欢和注意情况。你肯定想到他们做这玩意儿是干啥的了...

Cara-cloud

这就是提供刚才那种分析服务的云服务,CARACloud。demo页面有两个功能:根据图片分析和服务器端分析。 我们先看看图片分析的。先扔进去一张乔布斯的~

图片描述

[
    {
        "date_created": 1414518641,
        "age": 2,
        "gender": 1,
        "width": 307,
        "height": 307,
        "x": 51,
        "y": 236
    },
    {
        "date_created": 1414518641,
        "age": 3,
        "gender": 1,
        "width": 283,
        "height": 283,
        "x": 464,
        "y": 211
    }
]

其中的age只是一个类型,2是young adult(30岁以下),3是adult(30-50我记得是)。让我们再扔一个进去~

1373255755982.jpg

结果是...我很认真的挑了好几张霍金的玉照...结果都是...

{
    "error": "CloudNoFaceDetectedException",
    "error_description": "No face detected."
}

翻来翻去发现他们从13年就开始叫嚣会出API doc,结果现在还是没消息。一个隐蔽的小论坛里,发消息的频度也是以月计算的。 好吧,那只能用他Demo页的接口来用啦。

DevTools查一下,写如下的curl:

curl --data "type=image_url&amp;cara_cloud_demo_image_url%5Bimage_url%5D=http%3A%2F%2Fcdn.macrumors.com%2Farticle-new%2F2013%2F06%2Fsteve_jobs_bio_covers-800x604.jpg&amp;cara_cloud_demo_image_url%5B_token%5D=165932fef9c0c28ea1a048b860b5b5384fb3a722&amp;submit-url=Submit"  https://imrsv.com/cara-cloud-demo

修改其中图片URL的部分拿到结果页面的HTML,查看里面确实有结果。注意%5B和%5D是中括号[和]。不过每次都要改一下_token的值。

下面那个Server Side Video Processing只能看视频了,结果看起来很棒。如果能深度合作肯定能做出来那种根据表情实时变化的效果。

看了一圈,发现没有地方注册网页...login里的Create an account居然是连接到contact...好吧,你们丫以前肯定是能注册的,程序员肯定不愿意把那功能删掉!

所以呢,我们就搜一搜cara 或者imrsv register呗。然后就发现了这个注册页面。然后,注册,确认,登陆,就能看到它的后台啦。

Document里选择成为Developer,填满以后自动就会把AppID和AppSecret给你。再点Document就能看到各种API的文档啦。懒得注册还需要全部文档截图的可以找我要哈。(那该是什么样的人呢...)

图片描述

其中有一段Upload media的描述让我很困惑。这,到底是License先有还是先Upload呢...

Upload either image/video for processing. This API requires a license. License would not be available until processing is done.

不过通过测试发现确实是会给你的License,在等一阵子以后收到的mail里。从media_id里看出,这公司至今也就处理了14万张图片...好惨...

图片描述

到点儿啦,Downloads里还有好几个好玩的东西,PHP脚本啥的,有兴趣的去看看哈。

试用了下CARACV的Mac版,真的很好玩,指定一个证书以后,录一段视频就能得到一个分析报告。有什么注意了多久呀,看了几瞥呀啥的。有钱的商铺绝对可以用来做广告效果统计哈。

图片描述 图片描述 图片描述

这个故事告诉我们:笑起来更年轻~ 各种文件下载可以看这里。文档截图。好啦,所有文件都在这里

(30 hackdays day 20) Clarify - 处于pending状态的QS服务

如果你还不知道量化自身,那么建议你看看Quantified Self。量化自身对于我来说,就是希望在我比较短的生命预期内,能尽量留下我的数据,从而在身体死掉前,用算法创建出一个IO近似于我的“个体”。

“我TM说啥了?”

这是两三年前就在做的一件事情,时断时续的我用一些12小时的录音笔记录了一天我周围的声音(当然也包括我自己的)。最简单的目的就是希望能知道我之前说了啥(我记性比较差...)。但当时找了一些voice recognition的SDK来分析记录下来的语音信息。结果都特别不如意,基本没有能用的。 直到最近买了一个Narrative Clip,我又开始重燃QS的热情了。Rekognition - 借我借我一双慧眼吧文章中,我第一次把narrative里的部分数据拿来分析,得到的结果意外的好。这坚定了我之前的想法:

如果有一个一开始设计好的idea的api flow的blueprint,让机器能帮你在发展的过程中不断紧盯这些API能不能满足需求,直到某一天,这个pipeline通了。丫就告诉你,爷,你丫的idea跑通了。

Clarify

好啦,BB这么多的目的是介绍一个不那么牛逼的服务,Clarify。Clarify号称自己做三件事儿:Capture,Analyze,Discover。简单的说就是把语音视频数据扔给它,它帮你做信号处理(估计就是降噪),语音识别和自然语言处理。当然这也是收费的,音频每小时3刀,每5000个搜索一刀。

听着很简单,但当我看到这个服务的时候,我知道这就是我想要的~不用装SDK,不用运行在我的server上。我只需要把媒体数据扔到一个像七牛的地方,拿到URL,扔给Clarify。稍等就可以问它“我前天这时候说下周去哪聚会来着?”。好吧,至少能做到,搜索,上周这时候,有“聚会”的这段音频。

API

丫居然用PHP来做tutorial...好吧,不发表言论...创建一个App,拿到AppKey。找到nodejs SDK,翻翻tutorial,猜一猜就写个测试程序出来。

var clarifyio = require('clarifyio');
var client = new clarifyio.Client("api.clarify.io", "[key]")

var bundleId; var data = { name: "qwerty bundle " + Math.random(), media_url: "https://s3-us-west-2.amazonaws.com/op3nvoice/harvard-sentences-1.wav" }; client.createBundle(data, function (err, res) { if (err) return done(err); bundleId = res.id; client.search({query: "fire", query_fields: "*"}, function (err, res) { if (err) return done(err); console.log('search'); console.dir(res); client.removeBundle(bundleId, function (err) { if (err) return done(err); client.removeBundle(bundleId); }); }); });

就是这句 client.search({query: "fire", query_fields: "*"},...)。执行结果也很简单。

{ total: 1,
  limit: 10,
  search_terms: [ { term: 'fire' } ],
  item_results: [ { score: 1, term_results: [Object] } ],
  _class: 'SearchCollection',
  _links:
   { self: { href: '/v1/search?query=fire&amp;query_fields=*' },
     items: [ [Object] ],
     first: { href: '/v1/search?query=fire&amp;query_fields=*&amp;iterator=S10a47fbc8ef' } } }

它就是告诉你某个bundle里(bundle就是一个media的集合)有没有这个字出现。所以具体在哪里出现,似乎也还是得很多人工介入的。

无论如何,Clarify让我能用十几行代码就能分析一个我之前的录音(这个就不放出来了~)。虽然结果不理想,但我相信不久的将来我一定能等到那个"test全部通过"的API的~

另外,介绍一个全球第一个虚拟Hackathon。是之前提到的Koding举办的。https://koding.com/Hackathon 在一个WebIDE里,和一群全世界的hacker们玩一次hackathon,一定很酷!

哎,最近精力有限,水点儿请见谅...

(30 hackdays day 21) Wakatime - 现在几点了,你在做什么呢

前面提到了一个用来QS自己的工具Clarify。这次介绍一个专门给程序员用的工具。 大家都知道一万小时定律,但我究竟写java写了多久了呢?WakaTime知道。估计很多人都知道RescueTime,我也是用RescueTime来记录我的工作时间。但它无法记录更具体的行为(也许实际也做了),比如我写了多久javascript,多久objective-C。Waka就可以啦,因为它提供了几个主要的IDE的插件,只需要安装对应的插件,Waka就能自动记录你在IDE里的行为。

WakaTime

Waka的基本设计和rescuetime类似。每个人注册完将获取一个key,装一个客户端,把key输进去(登陆是同一个道理),然后它就把本地的所有行为带个key扔给服务器来统计,一段时间之后给你个报表。不过Waka做的真的很精准,精确到每一个文件用了多少秒,每一种语言用了多少时间。

图片描述 图片描述

据说Waka还能知道你在每个branch用了多久,这还真是一个量化程序员的好办法。如果有项目管理会用这种工具,相信也能更精准的把握开发进度,控制每一次开发尝试的代价。

作为一个付费用户狂人,登陆以后做的第一件事当然是搜Pricing~对比一下,付费用户的功能就是无限期数据查看和原始数据导出。想了想...算了这次...还有团队版本哦~

工作原理

Waka让我觉得好玩的是他的plugin都是开源的,而且在他的开源项目列表里看到了一个叫wakatime的核心项目,python写的,说是大家基本不需要直接使用的。基本可以确定是每个plugin都会去调用的核心库。

从queue.py里看出所有的log都会被放到~/.wakatime.db下,那我们就去看看它存了啥。打开以后发现是空的...想必是Waka把log都传走了以后就删掉了本地的。于是,断网,假装写代码,再看看。

图片描述

可以看到它记录了我在哪个文件,什么时间,什么项目下,写了什么语言,甚至在哪一行下都记录下来了。如果这个数据结合版本管理,动态的展现出来,将会是多酷啊~

Anyway,由于我很想用koding这样的WebIDE来工作,这样就又没发用Waka了,所以我需要知道发送记录的具体API是什么。

这也叫API文档

Wakatime是觉得代码是最好的文档么,居然在API文档里没有写出这些log是如何被打进数据库的...文档里只有认证,获得统计信息,和当前用户信息的接口...

好吧,那我们直接去刚才那个python的repo里看吧。首先,这里面肯定有个代码文件是包括http和wakatime.com的。那就搜吧,找到了init.py里一个函数叫send_action。很明显这是用来发log的。

那先让我们用curl模拟一下这个请求吧(看起来也不用太多域)。

$ curl -d '{"time":"1414688349","file":"/root","lines":"123","language":"javascript","is_write":"0","project":"manhattan"}' -H "User-Agent: space" -H "Content-Type: application/json" -H "Authorization: Basic ODMwOTliYjMtZGUwZS00NWFkLThhODItZmY4OWUwYzxxxxxxx" https://wakatime.com/api/v1/actions
{
  "data": {
    "id": "83682870-8306-4d12-b4a9-e0ba5f6cc295"
  }
}

把Key base64一下,用date +%s拿一下当前的timestamp,就可以往Waka里扔数据啦。扔完了以后看看Dashboard~好啦,我们的曼哈顿项目出现啦~

图片描述

如果你愿意可以把它改成各种SDK(当然browser里会出现跨域的问题,所以你可以找个中间服务器做这事儿)。

在未来做点儿事儿

刚才看到了有个timestamp来表示时间,如果改成以后呢。估计waka不会蛋疼到做这个检查吧...往2015年1月1日扔几个action,等一小会儿,看manhattan项目的URL,改成https://wakatime.com/project/manhattan?start=2014-12-26&end=2015-1-3。就能看到我在未来写javascript了~

扩展的waka

所以其实wakatime是一个特别基础的action记录和统计平台。如果它愿意开源(其实这真的还不如一个logger分析平台...),完全可以搭建成一个QS小服务。比如晚上9点连续发出20分钟加速度变化数据,然后就停止了10分钟...

(30 hackdays day 22) Import.io - 最简单好用的网页爬虫服务

import.io,一个2012年成立的公司。至今已经有3m刀的...种子轮...为毛...

注意,这是一篇由脑残和图片组成的文章。

图片描述

anyway,import.io是我用过最简单的爬虫,没有之一。简单到...只要输入一个网址(当然其实它可以更简单到不用输入http://),就可以获得一个该页面对应的API。更牛逼的是,这是一个我想寻找付费服务却寻觅不得的产品!

两步得到网站API

  1. 打开https://magic.import.io/ 输入要爬取的网址,比如http://producthunt.com
  2. 调整你需要的数据列,比如把url_link那列改为title
  3. 点击下面的GET API...

然后import.io就会给出一个GET API,一个POST API,甚至还有直接从Google sheets取数据的地址!

图片描述

这个API足够足够简单吧!也不用[o]auth就直接拿数据!唯一可惜的一点是,似乎它有一些延迟,没法很实时获得数据(也正是这时候我开始满页面找pricing...)。

除了GET以外,你还可以往里拽(POST)其他URL,很适合那种同构的多页爬取。

当然,虽然你觉得Http GET已经足够简单了,但import.io不这么认为,它觉得你还是需要SDK的...好吧,其实我是为了展示他的API doc页面...里红色的那部分!想起了一个以前看到过别人分享的注释

//Attempt Handshake: Hello? This is London calling. Are we reaching you?
//Handshake Failed: I don't understand...he just hung up.

图片描述

import.io不光是一个爬网页的平台,它还提供存储,搜索(是的...)等服务。赞一下里面的文档样式。

图片描述

App

一个爬虫用的桌面App?想到了啥?像浏览器一样,圈圈点点? 从这里下载import.io的桌面应用,安装完了去桌面打开(谁知道为啥它不扔launcher里...),splash都cute到死...

图片描述

打开以后呢,发现呢,就是个firefox内核的浏览器...区别就在与在这个app里可以使用chrome里无法使用的API from URL 2.0,API from Authenticated URL这些功能。估计就是为了把各种登陆cookie都很容易拿到,所以就做了个浏览器的壳吧。

用起来才发现这简直是...简直了...找到要爬的页面,鼠标选择要爬取的内容,搞定以后publish就可以产生一个某类页面的API了,以后只需要把新的文章URL扔给他就ok了。

图片描述

可以看出import.io其实没有diffbot那么智能,但对于那些懒得写xpath的程序员,import.io真的很方便!说到xpath,似乎我真的好久没写过了...可以看出import.io也是用xpath来实现的。

图片描述

好啦,这篇真的是一行代码都没有(这完全是为了配合import.io的好用好吧)...好吧,我有姿势我自豪~

(30 hackdays day 23) Favico.js - 网页杀死处女

是不是觉得网页的favicon(就是一个tab上的小图片)太乏味?用微信头像谋杀处女座还不够爽?怕用户看不到“您有新的消息”?好吧,用Favico.js来屠戮用户吧!

图片描述

看到了吧...凄苦吧...想象一下以后你的浏览器变成这样,还在跳来跳去...是不是想死...

图片描述

这个库使用起来超简单:创建一个Favico,.badge设置数字,done

var badge = 5;
var favicon = new Favico({
    animation : 'slide'
});
favicon.badge(badge);

原理也很简单,js里创建一个canvas的dom,把图片变成DataURL,然后直接改head里icon的href。所以,它还有高级用法:把html5视频放到favicon里,甚至把摄像头内容变成favicon...

var favicon=new Favico();
favicon.webcam();
//stop
favicon.webcam('stop');

好吧我承认我幻想过在tab里看porn,who care有码无码...

哎,写到现在都不知道写啥了...没心情写码态。先这样吧,以后再增加一些源码分析。

(30 hackdays day 24) Atatus - what happened before crash

Atatus是一个用来记录“犯罪现场”的工具。正如其他类似的统计异常的SDK一样,Atatus除了收集你的web app里发生的异常,在后台看到各种Bug的统计,最重要的功能的可以记录发生异常的时候用户的行为。

图片描述

如何重现

工程师在解决bug的时候遇到的最讨厌的事情莫过于不知道如何重现问题。现代Web App的复杂度越来越高,一个错误可能是长时间的javascript执行的积累造成的,很可能用户遇到一个问题,但只能报告一个有#的url。而之前的操作才是引发这个bug的原因。 Atatus就是来解决这个问题的。用户只要配置好SDK,一旦遇到Exception,Atatus就会把它,以及之前所做的行为报告给服务器。

比如我做了个登陆页面,我在一个input里输入了用户名,在另一个密码登陆框里输入了密码,然后点击了登陆按钮,bang,出bug了。整个栈被如实打出。同时,一旦你觉得某个bug被解决了,可以点resolve隐藏掉这个bug。对于小型项目来说还是很方便的。

图片描述

Angular

Angular是个FrontEnd又爱又恨的的框架,其中一恨就是有些错误超难track。Atatus专门为Angular写了一小段错误处理代码(当然这个不是Atatus专用的)。

.factory('$exceptionHandler', ["$window", function ($window) {
  return function (exception, cause) {
    if (exception.stack) {
      exception.stack = exception.stack.replace('new ', '');
    }
    if ($window.atatus) {
      $window.atatus.send(exception);
    }
  };
}]);

增加这么一个处理exception的factory,你的Angular程序就能在发生错误的时候统计下整个的用户行为和错误了。

离线存储

一个H5应用经常会面临用户不在线的情况,只要打开offline模式,等到用户连上网以后就会自动发送。它用的是localstorage,所以不用怕关掉tab。

atatus.enableOffline(true)

Pricing

唯一有点遗憾的就是它不提供永久免费的方案。当然你可以试用30天之后再注册一个账号... 不过,12刀15万个exception听起来还是不错的(除非你的App写了个抛异常的死循环)~

(30 hackdays day 25) Tuling123 - 光棍节找个机器人聊天吧

Tuling123

今天介绍又一个AI机器人Tuling123,当然是开放API的。而且,Tuling123号称可以创建个性化的机器人哦~(别跟我说这logo像个啥...)

图片描述

注册获取API Key,看平台接入文档。发现一个悲惨的事实:发送请求的API居然是GET的...而且URL还设计成这样 http://www.tuling123.com/openapi/api...让我猜一下,以后你们会把新的API设计成/openapi/api2吧。这惨状仅次于我见到过的一个把发送评论API设计成GET的。

而且,这可怜的GET还要承载userid,location,lon(lon....哎),lat以及询问信息info所有内容...

想要测试这个API很容易,curl一下就好啦。

curl "http://www.tuling123.com/openapi/api?key=xxxxxx&amp;info=我喜欢你"
{"code":100000,"text":"宝贝,我也很爱你,来,亲一下"}

这个回答看起来不错。但当我问“哪里当片儿”时,丫居然说

{"code":100000,"text":"最好别有这样的想法。为了你好才说的。 希望对你能有好处。 yin是生活事业学业福气寿命婚姻命运的克星。一个人消耗完了自己相应的福报,等待他的是苦果自受。 看、想、听、说、做yin秽的事,都会大大折损人的福报。"}

只能说...真没幽默感...

另外,这API居然把unauthorized的statusCode写在200的reponse里,而且还跟正常业务的statusCode写在一个地方,受不鸟...还40001,40002...C时代穿越过来的么...请好好看看How To Design A Good API And Why It Matters Google

再吐槽一下:Luling123把referrer link居然翻译成“升级连接”,还真是...目的导向...是不是应该考虑把消息发送按钮改名叫“约”呢...哎,再吐呆毛就爆了...

好啦,官方只提供了两端调用代码,PHP和Java的=.=。让我们来改造成nodejs的吧~

unirest

受够了mikeal/request的non-promise,随便一搜就找到了新欢:unirest。选择的一个重要原因是,Mashape做的~

var API_ASK = 'http://www.tuling123.com/openapi/api',
function ask(info, cb, err) {
    var query = {
        'key': 'xxx',
        'info': info
    }
    unirest.get(API_ASK)
        .query(query)
        .headers({'Accept': 'application/json'})
        .end(function (response) {
            if (response.ok) {
                if (cb) cb(response.body);
            } else {
                if (err) cerr(response.error);
            }
        });
}

虽然不提供美妙的promise返回,但其他的一些超便利的功能,以及好看的字体,足以让人从mikeal的傲慢中舒缓不少~

grunt-init

好,让我们把它封装成一个npm吧。我猜你肯定不想从npm init开始。所以我们找找那些肯定存在的Project Scaffolding吧,很显然Grunt就有Project Scaffolding。我们选nodejs的grunt-init-node来用。

安装好以后grunt一下确定没问题。把之前的代码改吧改吧扔进去。

var unirest = require('unirest'),
    Q = require('q');

var API_ASK = 'http://www.tuling123.com/openapi/api', API_KEY, RESPONSE_TYPE = { 100000: 'text', 200000: 'url', 301000: 'novel', 302000: 'news', 304000: 'app', 305000: 'train', 306000: 'plane', 307000: 'groupon', 308000: 'coupon', 309000: 'hotel', 310000: 'lottery', 311000: 'price', 312000: 'restaurant' }

exports.init = function (key) { API_KEY = key; };

exports.ask = function (info) { if (!API_KEY) { throw new Error('Please init with api key first'); } var deferred = Q.defer(); var query = { 'key': API_KEY, 'info': info } unirest.get(API_ASK) .query(query) .headers({'Accept': 'application/json'}) .end(function (response) { if (response.ok) { var result = JSON.parse(response.body); result.type = RESPONSE_TYPE[result.code]; if (result.type) { deferred.resolve(response.body); } else { deferred.reject('unknown code,' + response.body.code); } } else { deferred.reject(response.error); } });

return deferred.promise; };

哦sorry忘了提了,我用了可爱的Q来Promisify一下这个接口。想看这完整代码的请看tuling123 nodejs-sdk。如果有人有兴趣一起维护欢迎加入哈。哎,看惯了国外API产品的文档看国内的真是不习惯。

最后抱怨一下,Tuling123的登陆timeout设置的太短了...而且,似乎跟cookie里写的不一样...

然后呢...欢迎注册Tuling123 via Link~

(30 hackdays day 26) Mailgun - 发Mail是个技术活儿

发“垃圾”Email对于很多Web产品来说都是一种稳定的营销手段。让我们看一个众多Startup都在用的Mail服务,Mailgun。这是一个众多牛逼Startup都在使用的服务,里面不乏37signal,Github,Parse,Stripe这样的大佬。

图片描述

为啥要用第三方Mail服务?各种原因,一个是自己host mail server不安全,二是在程序端调用Mail发送邮件还是比较麻烦,万一换个邮箱所有代码都要变,三是自己的smtp server发的mail很可能被扔到垃圾邮箱里,还有就是Mailgun还可以帮助你追踪邮件的发送状态,比如多少被列入spam了(咋做到的呢?),以及各种行为,比如打开邮件,点击链接,以及有多少订阅用户再也无法投递到了。(还有6789)

图片描述

针对为啥用HTTP而不用SMTP,Mailgun的说法是

The HTTP API has some advantages:

  • It’s faster.
  • Better for large scale sending.(这个很关键)
  • You don’t have to deal with MIME because we will assemble it on our side.
  • Just use a request library available for your language of choice.

使用

按照signup之后的guide很容易的就能发送一个email,就是这么简单。

curl -s --user 'api:key-xxx' \
    https://api.mailgun.net/v2/sandboxa26b3f9c5f31491a906e26bb87864007.mailgun.org/messages \
    -F from='Mailgun Sandbox ' \
    -F to='Xiaoping Feng '\
    -F subject='Hello Xiaoping Feng' \
    -F text='Congratulations Xiaoping Feng, you just sent an email with Mailgun!  You are truly awesome! 

想把发送者的域名改为自己的,需要添加自己的域名,配置DNS的TEXT,CNAME和MX,之后等一等就好啦。

Nodejs

怒,官方居然对Nodejs部分只写了个参见NPM module...发现指向的是一个不知道什么人写的小module...时间会让他们后悔的!

Track

前面说到,Mailgun的Track是很重要的功能。用户打开事件的监测应该是按照图片的view行为触发的The email recipient opened the email and enabled image viewing。click链接事件是根据对mail内link地址的replace来实现的。其他的都好说啦~

可以通过API来下载以上事件的数据(包括query),也是超简单实用的~

curl -s --user 'api:key-xxx' -G \
      https://api.mailgun.net/v2/samples.mailgun.org/events \
      --data-urlencode begin='Fri, 3 May 2013 09:00:00 -0000' \
      --data-urlencode ascending=yes \
      --data-urlencode limit=25 \
      --data-urlencode pretty=yes \
      --data-urlencode [email protected]

Sender vs From

Docment里的一个FAQ让我涨了姿势,邮件里的Sender和From还可以是不一样的。

The sender domain is what the receiving email server sees when initiating the session.
The from address is what your recipients will see.
For better deliverability it is recommended to use the same from domain as the sender.

哈,为了早起,今天再偷懒一下~晚安各位,祝打枪愉快~

(30 hackdays day 27) CloudRail - 云存储的POSIX

这是一个Mashup的时代,所有你知道的API都将成为你设计开发新产品的武器。但跟App一样,太多的选择反而给开发者带来了困扰:到底哪家的服务更好,SDK用起来更舒服,以后维护的成本更低?存储是所有应用都会用到的,Cloudrail的出现正是帮助开发者摆脱SDK选择困难的工具。

图片描述

Mashape类似,Cloudrail认为存储API应该拥有共通的结构和描述方式。但是,跟Cloud Element的Document hub类应用不一样,为了让用户安心,Cloudrail并不用将数据都都经过它的服务器,而是让用户直接和存储服务提供者通信。通过下面的图能很清楚看出Cloudrail是怎么做到的。

图片描述

这就是Cloudrail最核心的技术:Self Adapting API。它的作用是保证客户端随时获得最新的API提供者的API定义,换句话说,就是保证开发者不用换SDK就可以持续访问最新的API(所以叫'No API Updates')。Javascript的实现方法大家都能想到,就是每次都下载新的connector库。Android呢想一想估计是用ClassLoader或者DexClassLoader之类的吧,iOS就不知道了...不过也许通过WebView的js层做一个中转也是一个低效率的方案。看了下Download页面,发现果然Android版本还没有上线,剩下的是Others。只有JS版本的可以用。不过目前只是Beta嘛,可以理解~

API

CloudRail为数据存储服务定义了一系列标准接口,通过一个CloudRailInterface把各家的存储服务封装起来。比如Sample代码中,CloudRail把Dropbox和GoogleDrive的API封装为一个CloudRailInterface,通过一个CloudRailClient来统一访问。其中用户在不同平台的的标识client_id可以按照帮助文档获得到。

var CRC = CloudRailClient,
    CRI = CRC.CloudRailInterface,
    DBServiceTag = 'Dropbox',
    GDServiceTag = 'GoogleDrive',
    userIdentities = [],
    userIdentitieServiceTags = [],
    serviceDB, serviceClientDB,
    serviceGD, serviceClientGD;

/* Initialize services */

serviceDB = CRI.initService(DBServiceTag); serviceClientDB = new CRC.ClientIdentity(DBServiceTag, {'client_id': 'abcd'}); // insert here your own client id

serviceGD = CRI.initService(GDServiceTag); serviceClientGD = new CRC.ClientIdentity(GDServiceTag, {'client_id': '1234'}); // insert here your own client id

/* Read User Identities */

var dbUser = new CRC.NodeIdentifier(CRI.ObjectType.USER_IDENTITY, DBServiceTag), gdUser = new CRC.NodeIdentifier(CRI.ObjectType.USER_IDENTITY, GDServiceTag);

CRI.read(dbUser, serviceClientDB, (function(servicetag) { return function(res) { saveUserIdentity(res, servicetag); }; })(DBServiceTag)); CRI.read(gdUser, serviceClientGD, (function(servicetag) { return function(res) { saveUserIdentity(res, servicetag); }; })(GDServiceTag));

目前CloudRail提供了基本的CRUD API,看起来还是挺通用的。突然觉得,CloudRail是不是想实现一个云存储版的POSIX接口呢。

文档最后提到了一个额外代码CloudRailClient.Crypto.Hash.SHA256。看来是为了对文件作Hash用的。

Price

官网的说法是'Integrations to Cloud Storage Providers will be free forever.',So...他们怎么活下来...

StrongLoop

最后,提一下之前用过的Nodejs框架。其中有一个很好用的功能,Auto generate SDK。在后端创建一个数据Model以后,Grunt一下就会自动把这个Model的访问代码。比如Angular的版本生成一个访问该Model的Service。

图片描述

提到这个原因是,Nodejs+browser这样的脚本语言组合太适合WebApp的开发了,这样组合所带来的灵活性也带来了很多新的可能。像CloudRail,StrongLoop这样动态接口比原来那些“死板”的接口通信拥有更强大的整合能力。也许以后就会出现一批服务,专门负责接口的维护,而文中这类技术就是这种服务的基础。不过CloudRail还会遇到很多问题,诸如版本管理,平台支持能力等。

另外,CloudRail也是个国际团队,总部在德国的曼海姆,在旧金山也有办公室,公司内居然有幼儿园...

(30 hackdays day 28) Repose - 给API穿上衣服

图片描述

最近需要把一些功能拆分为HTTP API,于是找到了它。Repose是一款开源的API中间件,它可以帮你轻松实现那些API设计里最基本的功能,诸如认证,限速,验证,Logging等。Repose是Rackspace旗下的产品,所以靠谱程度很高。

当你写了个一个页面Parser,一个“1秒钟算1BIL次蛋疼指数”的程序,一个通过一个mail判断一个人是不是靠谱的“占卜服务”之后。若果你想让更多的人能享受到你的智慧,最简单的想法就是把代码开源出去。但很多人是不愿意这样做的,于是就会想到把它作成网络服务,收费提供出去。也就变成了前面二十多篇文章介绍的各种API服务。

API hosting as a Service

经过这多年的积累,Web API服务已经逐渐形成了一套相对标准的做法,从各种开放平台的做法就能看出来。

  • 注册平台账号
  • 创建App,获得AppKey(也许需要付费)
  • 将SDK嵌入自己应用
  • 用AppKey初始化SDK
  • 开始调用各种API(也许需要付费,也许会限速)

工程师用多了这类产品以后,遇到新的服务往往就会轻车熟路的不用看任何Tutorial。直到有一天,工程师决定自己也要搞一套这样的服务,问题就出现了。这几个环节具体都要如何实现呢?

于是就在API as a Service(其实就是只提供API的Software as a Service)之外,出现了个API hosting as a Service的概念(是不是觉得XaaS特别Ass...)。也就是我帮你托管你的API,帮你做掉前面那些步骤的事儿,您安心开发您的核心算法,提高效率,降低错误率就好啦。其实,对于Amazon这样的老早就看到解耦合,把业务都拆分成细小服务的公司来说,这个概念已经太古老了。

同样提供这类服务的也有不少,比如界面做的太漂亮的Apiary.io,搭建API全生态链的Mulesoft。但因为我是想搭建在自己的服务器上的,所以暂时不考虑那些托管的平台。

Repose

感慨于Docker神速的发展,Respose最简单的部署方法就是部署一个Docker container。隐隐就已经看到了一个Repose as a Service的产品了...

Repose的工作原理很简单,就是在你的裸API和用户之间做一个代理。

图片描述

文档中描述的Repose的基本功能如下。

  • Translating。API格式转换。
  • Rate。限速。
  • Authenticating。
  • Validating。保证API符合Web Application Description标准。
  • Logging。
  • Blacklisting。credentials and roles
  • Caching。
  • Role-based access control (RBAC)。基于角色访问控制。
  • Mapping the state of your API。好吧,没懂,估计是类似NAT那样的玩意儿吧。

有好几种部署方法,嵌入源码,WAR,独立Proxy server,独立Proxy server cluster。我们肯定不能假设服务器是java的,所以肯定选择独立Proxy server这种模式。

配置里最重要的就是Destination endpoint,一个典型的如下,很清晰的描述一个即将被封装的API endpoint。(又看到了xml配置文件,让一个former javaer还是感慨良多)

由于还没有图形界面配置的功能呢,所以强烈建议从Repose+101入手。装个Virtualbox,里面装docker。

// TODO 过两天把装了Repose的VM传上去

Repose里除了刚才提到的Endpoint以外还有两个重要概念,filter和service。filter就是一个request一路经过的劫难。service就是诸如分布式数据库之类的东西。

Mock Server & Config

nodejs写一个简单的mock server来假装一个牛逼服务。node app.js跑起来。

var express = require('express');
var app = module.exports = express();

app.get('/api/nbservice', function(req, res){ res.send('cool to die'); });

app.listen(8080);

写一个简单的Repose配置

刚才说到建议装docker,其实吧,最简单的还是直接跑jar~从maven上当他们最新的jar。运行java -jar valve-2.3.5.jar start -p 8888 -c /etc/repose/执行起来。

然后呢,curl 127.0.0.1:8888/api/nbservice就能看到它跑通啦~

Freelancer

在搜索这个主题内容的时候,遇到了这个老牌Freelancer网站的一个服务,居然叫RESTful hire。其实就是在技能里要有RESTful这一项而已。难道,RESTful已经变成了一种“世界观”了么...

(30 hackdays day 29) Google TTL - 让Google讲小说

最近一直在喜马拉雅听有声小说《轮转长生》。每天会更新两集。但由于我记忆力差,所以如果等着每天的小说更新,就会由于记不住之前的情节而感觉很迷茫。所以我去搜了下,发现了这个小说的文本。于是就想如果能用Text To Speech把文字变成语音的话,不就能一直听下去,而不用等播音了嘛。

于是我就去搜各种TTS API Chinese。发现了iSpeech, tts-apivoicerss之类的服务。确实,TTS技术发展了这么多年,肯定有巨多的服务提供商。但翻了一圈发现中文的效果都很差。郁闷的时候想起Google Translate的TTS,就是那个整天被人当B-Box玩的东西。

图片描述

Google-TTS

这是一个非官方的Google TTS库,但是文档里说到Playbackwill only work when running the script locally as Google's server only returns audio if you can prevent the browser from sending the Referrer HTTP Header to their server,看来是Google对referrer做了检查。不过这点后面看起来似乎没那么严格。

我们先用个简单的页面测试一下播音(注意科学上网),播放一小段中文听起来还不错。

    var tts = new GoogleTTS('zh-CN');
    tts.play('你好呀呀呀呀', 'zh-CN', function (err) {
        console.log('complete');
    });

拿小说

这就用到了之前介绍的技术Import.io。安装桌面应用,Create API from URL 2.0,找一个详情页,比如第一百五十三章 照片上的三口之家,选择文章内容的div改column名为text。就可以create啦。 看一下文章的url是连续的,好,那就用node写个循环,然后用AVOS把文章内容保存下来。

// ...省略初始化
var Chapter = AV.Object.extend('Chapter');

_.each(_.map(_.range(27888, 27996), function (num) { return 'http://book.guidaye.com/daomu/85/' + num + '.html'; }), function (url) { var q = new AV.Query(Chapter); q.equalTo('url', url); q.first() .then(function (chapter) { if (chapter) { console.log('exists,%s',url); } else { unirest.get('https://api.import.io/store/data/c2f29086-18a8-4c7b-9d5d-6ea1d54a9bc5/_query') .query({ 'input/webpage/url': url, '_user': '[UER]', '_apikey': '[API KEY]' }) .end(function (response) { new Chapter({ url: response.body.pageUrl, text: response.body.results[0].text }).save().then(function () { console.log('done,%s', response.body.pageUrl); }, function (err) { console.log(err); }) }) } })

})

跑一遍基本就ok啦,如果有发生错误的就再执行一遍验证下。

读小说

好,我们要一个前端页面来读小说。

<div id='playing'></div>

AV.initialize("[APPID]", "[APPKEY]");

function speakChapter(chapterNum) { var url = 'http://book.guidaye.com/daomu/85/' + chapterNum + '.html'; var Chapter = AV.Object.extend('Chapter'); var query = new AV.Query(Chapter); query.equalTo('url', url); query.first() .then(function (chapter) { document.getElementById('playing').text = "playing:" + url; console.log('playing,%s', chapter.get('url')); var tts = new GoogleTTS('zh-CN'); tts.play(chapter.get('text'), "zh-CN", function (err) { console.log('complete,%s', chapter.url); }); }, function (err) { console.log(err); }) } //speakChapter(27889);

我们在console里实验一下speakChapter(27889);,终于开始读啦!听起来感觉还好,但还是TTS的最大普遍问题:语气不对。每个人说的,旁白说的都是一个味道...另一个问题是,读文章的时候一段以后会需要几秒钟的加载时间去加载下一段。而这个gap很有可能是一句话的中间。这就让人听起来很诡异了...“雷英...(上个厕所)...雄笑了笑”...

最后说一下浏览器问题。我发现safari打开这个页面就能正常播放,所以,也还算可忍~

好啦,按这个思路我们可以把自己喜欢的小说播放出来~如果你想把声音保存成mp3的话,可以用node-google-text-to-speech来把数据pipe到文件里,再用pushbullet类的产品把数据同步到移动端去。

29天啦,坐等吃串!

(30 hackdays day 30) StrongLoop - Javascript全栈框架

SL_Logo_Stacked_RGB.png

StrongLoop是一个基于Nodejs的强大框架,几乎包含了移动开发全栈所需要的所有功能。2013年成立,很少的员工,一个技术驱动,执行力强大的团队。也是在13年我开始接触StrongLoop,当时是为了做nodejs方面的技术选型,看了许多框架,LoopBack是我觉得最酷的一个。我还记得当时是觉得LoopBack的文档太差(主要是跟在线的版本不一样),不知道能活多久所以才放弃了它。时隔一年回来看到这个绿油油的框架,这一年可真是突飞猛进呢。

LoopBackarch7162014-3.png.pagespeed.ce.tUGpVTVgYP.png

全栈框架StrongLoop

StrongLoop基本提供了制作一个移动产品所有的框架和工具,从标准的Backend server,Devops,应用监控,。要想介绍完全StrongLoop的所有产品得写一个长篇连载了,这里只简单的浏览一遍。

  • LoopBack 一个功能很强大的WebServer框架,隐约看到Spring的影子...
    • SDK 这是当时我觉得StrongLoop最好用的一点:根据后端Model自动生成对应的前端SDK。
    • RESTful API MEAN stack推动了所有新框架默认支持RESTful API,比如Nodejs里的Node-restifySailjs之类的天生就是为了serve API的。StrongLoop还提供了一个用来设计API的工具(beta状态),当然跟RAML比还是差不少的。
    • Data model 很容易创建数据模型,自动产生对应的RESTful API。
    • Connectors 当然可以对接各种数据库。
  • StrongLoop Controller Debug, Package Management, Build, Deploy, Cluster, Log等等一堆Devops工具。
  • Application Monitor 一堆监控服务器的模块。
  • mBaaS 使用StrongLoop,你也可以创建自己的LeanCloud啦,包括Push,地理位置计算(Geopoint),Social Login,User management,各种Replication,Offline sync(离线同步是个好东西),对接各种Stroage云(AWS,Rackspace之类内置了的)。当然,其实这些都是LoopBack里提供的功能,意思是用StrongLoop得永生。

开始

安装StrongLoop sudo npm install -g strongloop 创建一个应用 slc loopback(找个空白的目录哦) 它只会问你项目叫啥。 创建一个Model slc loopback:model (是不是想起了Yo generator~),然后它就会问一堆乱七八糟的问题了。

图片描述

plural指的是RESTful API的route名,一个Model对应的route默认情况下会被plural(复数化),比如Post的路径是Posts。

猜一下,像LeanCloud里类似,这时候应该去访问一个类似127.0.0.1:3000/xxx的地方看到他的后台吧~嗯 xxx=explorer http://127.0.0.1:3000/explorer 就能看到一个用Swagger做的API dashboard。

图片描述

看一下项目的结构,像Meteor一样,LoopBack将JS代码分成服务端(server),前端(client),以及共用部分(common,个人认为这种纯schema的方法比Meteor分割的更清晰)。

略过后面的API Explorer,如何连Database,直接看如何在前端使用刚刚创建出来的API。LoopBack的做法是帮助你创建你当前API的各端SDK,目前只支持Android/iOS/Angularjs。如果你用别的Web框架可能就只能自力更生了。

创建自己的SDK lb-ng server/server.js client/lb-services.js 看看client/lb-services.js,不错吧~

嗯,你的前端工程师需要个文档?执行 lb-ng-doc client/lb-services.js 有时候需要在前面添加sudo(不知道为啥),然后访问 http://localhost:3030/ 就能看到文档啦~这个功能是基于Docular做的。另外,如果你点LoopBack Services进去没东西,别担心,刷新一下。

看看是不是很像早期Angular的文档~

图片描述

API Designer

相信很多程序员喜欢Parsejs或者LeanCloud的原因都是那个图形化界面的Model设计(或者叫API设计)后台。如果前面用的那个"Yo generator"的API creator看起来还不够贴心,你可以试试StrongLoop新推出的StrongLoop Studio beta。

  • 在工程根目录下sudo npm install -g http://get-studio.strongloop.com/strong-studio.tgz 安装Studio。
  • 启动Studio strong-studio,自动打开浏览器
  • 去StrongLoop官网右上角注册个账号(文档里居然写了On the bottom right is a link to go to the registration page on strongloop.com,你们不知道页面很容易改版么...)
  • 用注册的用户名登录进去看到API Composer和Profiler。
  • Bingo!各种玩耍吧!

图片描述

p.s.如果这时候你遇到了类似SyntaxError: Cannot parse package.json: Unexpected token e这样的问题,检查一下你工程根下的package.json里是不是被jslint搞乱了(I dont't know why...) 好吧好吧,我告诉你这个beta还真是特别beta...尽量不要在之前写过代码的项目里用...

懒人们

如果你缺个可以玩耍的Mongodb:Mongolab 如果你想要个可以协作开发的IDE:Koding 如果你想要个便宜好用的VPS:Digital Ocean 如果你觉得他们家不够便宜:Serverbear 如果你连VPS都不想要,只想部署个Node项目:Nodejitsu 如果你想设计个API尽快给客户端同学,又不想搭Node,找地方部署:Apiary 如果你想做(抄)个APP,又不想花钱雇一堆人:Appdupe

30天结束

这是最后一篇30hackdays啦,终于熬出来了(吁...)。Anyway,这三十天我学到了很多,如何寻找自己想要的服务,如何快速实现原型,如何比较开发者产品的竞品...

回头看,文章的内容肯定没有Shekhar的那篇Learning 30 Technologies in 30 Days: A Developer Challenge写的详尽(确实也没人家投入那么多时间精力),但我选择的技术更广泛(或者叫更乱七八糟),对我也是一种扩展视野的过程。

希望我的这个系列能给大家一些启发。技术并不都是那么深奥难懂,拥抱它也许不能立刻涨工资,但至少在寒冷的日子里,没有wifi,孤独寂寞冷的时候,还可以打开浏览器的console,输入个while,来暖暖手。

下面

嗯,不是你想的那个,也不是用来吃的。之后我会继续时不时写一些发现的好玩的开发者服务;另外,我会写一个StrongLoop的系列文章来介绍这个框架更多的特性,希望大家能够尝试这个迅猛发展的产品。

再做个小广告:最近在做一个小班(免费),专门教高中生技术,直到达成可以自行参加Hackathon的程度。如果身边有高中生对技术感兴趣,欢迎联系我~ [email protected]

ok,那些输了的情自行来约~

ec0a6159252dd42af84c70ce003b5bb5c9eab82a.jpg