Posts in Uncategorized (15)

Building a WeChat (Weixin) robot

Matt presented a talk on Building a WeChat robot at Barcamp Shanghai Spring 2014! Here's a recap of the talk, as well as a link to download the source code of the robot.

If you'd like to skip the technical jargon and just play with the real bot, scan this QR code in WeChat and send our bot a message, or just search for "ReignDesign"!

Weixin Bot.006

Now, robots are cool, but building a real robot requires lots of hardware knowledge.

Weixin Bot.004

And one common topic you'll notice in today's talk is that I am quite lazy.

Weixin Bot.003

So when I say "robot" I'm talking about a robot you can build with software: a chatterbot. Send a message to a WeChat account, and get a message back.

Why WeChat? WeChat is ubiqituous in China, and so it's a great way of getting your service in front of as many people as possible, without them having to download another app.

Weixin Bot.005

Now you or I have personal WeChat accounts, but if we want to build a bot we need an official account. There are two types of these, Subscription Accounts and Service Accounts.

Weixin Bot.007

Subcription Accounts are best for marketing purposes. You have the ability to send up to 5 "broadcasts" each month to all your followers - perhaps if you are a restaurant, you want to announce some new specials. The downside of subscription accounts is they are hidden away in the WeChat interface inside the Subscription Accounts section.

Service Accounts are listed in the main WeChat friends list. You're only allowed to send one broadcast a month. But, any time someone sends you a message, you can reply to them immediately, and also send them messages for up to 48 hours. So, if you're trying to do customer service via WeChat, or build a bot, a Service Account is a great choice.

Here's the signup form for a WeChat official account (currently Chinese only).

Weixin Bot.008

You'll need to provide some basic information like name, email address, and which type of account you want. The two pieces of information that may require some effort are the 营业执照 (Company License) and 组织机构代码证 (Organization number). Right now you'll need to have a Chinese company license to apply for a WeChat official account.

After submitting all your data, be prepared to wait. It will take about 3 business days for your app to get approved.

Weixin Bot.009

One you're finally approved, you have access to the WeChat API. This comes in two flavours: basic API and advanced API. The basic API provides all that we need to build a basic chatterbot!

Weixin Bot.010

In the Developers section, you now need to provide a URL and Token. At the URL provided you now need to provide a simple web server. This will listen for requests from the WeChat server. So, for example every time someone sends you a message, you receive an HTTP request with the details of their message.

Weixin Bot.012

For our backend server we used a simple stack running on Heroku, with Node.js and Express, but you could use any language and framework, like Ruby on Rails, or PHP.

Weixin Bot.013

When you first register an application with WeChat they will send you an authorization request to your URL. This includes the token you provided earlier, plus various other OAuth credentials, and an parameter called "echo_str", which you should return if everything checks out OK.

As mentioned previously, I'm quite lazy, so to avoid having to figure out the authentication issues, I simply return the echo_str for ALL requests. This should not be recommended in a production application.

Weixin Bot.016

Now, each time you receive a message to your account, you'll get an HTTP request. You may be hoping for a nice JSON payload.

Weixin Bot.017

Unfortunately Tencent decided to use XML, and not very well structured XML at that.

Weixin Bot.018

Here's what a typical incoming message looks like.

Weixin Bot.019

You get a FromUserName and ToUserName. These are actually encrypted so you don't have access to the user's real WeChat ID. There's also a timestamp, the type of message (in this case text, but it could also be image, video or voice) and the text content.

To parse this in Node.js, we install an npm package called express-xml-bodyparser and configure Express to use it.

Weixin Bot.020

Next we implement a method to handle the request. We parse the values out of the XML...

Weixin Bot.021

Then we construct some XML to send back as a response. Notice that we've switched the to username and from username, to ensure the message gets sent back to the recipient. The text of our reply is "Thanks for sending me a message saying (original message)".

Weixin Bot.022

How does this work? Like this!

Weixin Bot.023

Now this is great, but of course version 1 of our robot is pretty stupid. How can we imbue our bot with some more intelligence?

To solve this I turned to the work of Joseph Weizenbaum.

Weixin Bot.024

He was a brilliant German-American computer scientist based at MIT. In 1966 he wrote a program called ELIZA. ELIZA was one of the first programs which tried to interpret and respond to natural language inputs from users.

It did this via some basic pattern matching. ELIZA could be fed with different scripts of patterns, and the most famous of these, called "DOCTOR", imitated a psychiatrist.

Weixin Bot.026

Here's ELIZA in action. You can see that ELIZA picks out certain words in a statement, and is able to formulate a reply using the words.

Weixin Bot.025

Now you're just talking nonsense!
> What makes you believe now I am just talking nonsense?

Weizenburg was surprised to discover that despite the primitiveness of the logic, users became quickly emotionally involved with interacting with ELIZA.

ELIZA is a fascinating episode in computing history, and I was delighted to see that the ELIZA logic is available as a npm module. That means integrating it into my bot was very simple.

First, install the module

Weixin Bot.027

Then, replace the reply logic with a new function.

Weixin Bot.028

Here's the code for replying. We keep a dictionary of ElizaBot objects. That way we can give each unique user who messages us their own instance. That means the message "memory" can be kept distinct.

If this is a new user, we set up a new ElizaBot for them, and request an initial phrase like "Please tell me your problem".

Weixin Bot.029

If this is an existing user, we simply call eliza.transform to get a suitable response, based on the last line of input (and previous inputs).

Weixin Bot.030

Let's see how this works:

Weixin Bot.031

There seems to be great potential for using ELIZA for people who are interested in app development services in Shanghai ;)

Weixin Bot.032

Of course, while this is a fun example, there's lots of cool stuff you could make with WeChat robots. A robot could return weather or air quality data on demand. If a user sends you a picture, you could do image processing on that picture and send them back something really creative. The only limit is your imagination!

Weixin Bot.034

To try out the ReignDesign bot, scan this QR code!

qrcode_for_gh_5108e39bfd75_1280

To download the source code, check out the project on GitHub

Interested in finding out more about what's possible with WeChat, mobile apps, or other new technologies like iBeacon? Contact us!

Happy Year of the Horse! 新年快乐!

To kickoff the CNY celebrations, ReignDesign sponsored a lion dance for our friends in Park Space to enjoy. IMG_0284

To reserve the red and yellow clad lions, Fluxa and Matt used their Uber taxi app. Instead of booking a taxi, they requested a lion dance. Brilliant idea! IMG_0075

 

Like all CNY celebrations, this party was loud! And these guys were excellent!IMG_0093

 

Sadly, not everyone can be a skilled musician. IMG_0288

ReignDesign would like to wish you and your loved ones a happy, healthy, and prosperous year of the horse!IMG_0061

 

Tips & tricks for how to not have your new site look awful on phones

If you build a website for a company that promotes mobile development especially - make sure that it looks semi-decent (doesn't look completely distorted) on a portable device.

Supported devices with media queries:

Know how to target each device and it's orientation. If these are all the devices that you might want to support, create the following media query tags:

  1.  
  2.  
  3. /* Smartphones (landscape) ----------- */
  4. @media only screen
  5. and (min-width : 321px) {
  6. /* Styles */
  7. }
  8.  
  9. /* Smartphones (portrait) ----------- */
  10. @media only screen
  11. and (max-width : 320px) {
  12. /* Styles */
  13. }
  14.  
  15. /* iPads (landscape) ----------- */
  16. @media only screen
  17. and (min-device-width : 768px)
  18. and (max-device-width : 1024px)
  19. and (orientation : landscape) {
  20. /* Styles */
  21. }
  22.  
  23. /* iPads (portrait) ----------- */
  24. @media only screen
  25. and (min-device-width : 768px)
  26. and (max-device-width : 1024px)
  27. and (orientation : portrait) {
  28. /* Styles */
  29. }
  30.  
  31. /* iPhone 4 ----------- */
  32. @media
  33. only screen and (-webkit-min-device-pixel-ratio : 1.5),
  34. only screen and (min-device-pixel-ratio : 1.5) {
  35. /* Styles */
  36. }
  37. /* kindle fire ----------- */
  38. only screen and (-webkit-min-device-pixel-ratio : 1.5),
  39. only screen and (min-device-pixel-ratio : 1.5),
  40. and (min-device-width : 600px)
  41. {
  42. /* Styles */
  43. }
  44.  

More info on media query syntax here , and some good examples to get you excited about implementing them here.

Adjusting the layout viewport

Now don't forget to add these properties to the viewport-meta tag anywhere in the header of your html document, so that the site is being prevented to scale down when viewed on the iphone :

  1.  
  2. <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; minimum-scale=1.0; user-scalable=no;" />
  3.  

For more information on the viewport meta-tag read here.

What to do about 7-inch tablets?

As Jacob Nielsen recommended for 10-inch tablets (iPad) - we simply served the desktop layout (it looks perfectly fine for our site).
While 7-inch tablets like the Kindle Fire or various other android tablets out there should be served the mobile version of a website. According to him - a site that is optimized for 3.5-inch mobile screens feels luxurious (in terms of space) on a larger 7 inch screen.

For having your iPad use the scaled desktop version of your site, after you deactivated scaling in your viewport, simply change the viewport properties in the header to depend on the screen-size:

  1.  
  2. <script type="text/javascript">
  3. if ((screen.width<=600) ) {
  4.    document.write('<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; minimum-scale=1.0; user-scalable=no;" />');    
  5. }
  6. </script>
  7.  

More about detecting device-orientation

iOS 4, as iPad with iOS 3.2, supports the media-query "orientation" attribute, so you can apply different styles for portrait and landscape orientations.
Yet older iOS versions for iPhone do not, so we still included those work-arounds:

You can a combination of min/max-width: For older iPhone, landscape, you want to make sure the min-width is at least 321px, that way these styles won’t be applied in portrait mode. The max-width property is used if you implement iPad-size specific media-queries so that you don’t affect the desktop counterparts that support the orientation media query, it's set to 1024px which is the iPad's max landscape width.

For more complex orientation event handling, take a look at safari's event handling documentation here.

Use separate style-sheets to keep things clean

We didn't actually do this, - opposed to the applied best practices in our real development projects - our site's stylesheet is a meter-long mess.
But I would, next time around!

  1.  
  2. <head>
  3. <link rel="stylesheet" href="smartphone-landscape.css"
  4. media="only screen and (min-width : 321px)"/>
  5. <link rel="stylesheet" href="smartphone-portrait.css"
  6. media="only screen and (max-width : 320px)"/>
  7. <!-- and so on -->
  8. </head>
  9.  

The next two tips are not really needed, but you can add them if you believe they make the experience of viewing your site on a mobile device special.

Have a iOS specific bookmark-icon

If a viewer wants to bookmark your site on home-screen - normally the iOS device will just create a screenshot of your site.
Now you can create your own "app icon" specific for your site - that is looking just as any other iOS app icon.

  • add this code to the section of your site.
    1.  
    2. <rel="apple-touch-icon" href="images/appicon.png"/>
    3. <link rel="apple-touch-icon" sizes="72x72" href="images/apple-touch-icon-72x72.png">
    4. <link rel="apple-touch-icon" sizes="114x114" href="images/apple-touch-icon-114x114.png">
    5.  
  • Create an icon in 57x57 pixel size, (one for retina display: 114x114 pixels, one for ipad: 72x72 pixels in .png format - do not add the rounded corners, apple does that for you.
  • Name the image it as you want, and link the path in the code snippet above.

  • Use special phone tags, if you link to phone-numbers

    1.  
    2. <a href="tel:008612345678900">Call me</a>
    3. <a href="sms:008612345678900">Send me a text</a>
    4.  

    (You ARE designing for a phone, duh.)

    Update Flurry, Appirater now to avoid rejection from iTunes App Store

    It's always frustrating when your app is rejected in the iTunes App Store approval process. Today we noted a new twist on the reason for rejection which we hadn't encountered before.

    We found that your app uses one or more non-public APIs, which 
    is not in compliance with the App Store Review Guidelines. 
    The use of non-public APIs is not permissible because it can 
    lead to a poor user experience should these APIs change. 
    
    We found the following non-public API/s in your app:
    
    appName
    setAppName:
    multitaskingSupported
    
    If you have defined methods in your source code with 
    the same names as the above-mentioned APIs, we suggest
     altering your method names so that they no longer 
    collide with Apple's private APIs to avoid your 
    application being flagged in future submissions.
    
    Additionally, one or more of the above-mentioned APIs 
    may reside in a static library included with your 
    application. If you do not have access to the 
    library's source, you may be able to search the 
    compiled binary using "strings" or "otool" command
     line tools. The "strings" tool can output a list
     of the methods that the library calls and 
    "otool -ov" will output the Objective-C class 
    structures and their defined methods. These 
    techniques can help you narrow down where the 
    problematic code resides.
    

    This seems to be a new change in the implementation of Apple's policy, as we've submitted apps including this code several times in the past. Other people are also hitting this issue in the last couple of days We've investigated the issue, and the problem seems to lie in two third-party libraries which we use: Appirater (for encouraging users to rate your app) and Flurry (Analytics).

    Updating to the latest version of Appirater solves the problem with the use of the multitaskingSupported API. To check if you need to upgrade, check for the presence of the string "multitaskingSupported" in your Appirater.m file. If it's there, you need to update.

    Updating to the v3 of the Flurry SDK also solves the problem with the use of the appName and setAppName API. To check if you need to upgrade, check if your Flurry header file is called FlurryAPI.h (v2) or FlurryAnalytics.h (v3).

    Verification:

    strings libFlurry.a | grep setAppName

    returns reults, showing that there's a method named setAppName used in the v2 SDK


    strings libFlurryAnalytics.a | grep setAppName

    returns empty, showing that this method is removed in the v3 SDK

    The new libraries should be a simple drop-in replacement, but be sure to retest the functionality of your app before re-submitting! Of course it's good practice to regularly check for new updates/patches to any third-party libraries you may be using in your app.

    We've also heard of an app using ASIHTTPRequest being rejected, possibly for similar reasons. If you have any other information on this issue, do let us know in the comments.