Tuesday, May 17, 2016

“(#100) No matching user found” - Facebook Messenger Bot Bug

On May 13th, I found that my Facebook Messenger bot failed to response some users, and as I read the error log of my webhook process, then got something like:
"error": {
    "message": "(#100) No matching user found",
    "type": "OAuthException",
    "code": 100,
    "fbtrace_id": “XXXXXXXXXXX”
}

Some Backgrounds

At this point, Facebook Messenger Bot is still new, which is reasonable to have some bugs. I’m using `Node.js for my webhook on Heroku, and I followed the tutorial provided by Facebook for setting up the bot.

Why?

Soon, I found this bug is discussed on Facebook Bug Page here. The problem is that Facebook decided to switch their encoding to use strings instead of ints for user & page IDs, which made the example code (template code) on Facebook official tutorial page fail to response users with string IDs.

Then?

Facebook send out notifications to the app developers saying:
On Tue May 17 format of user and page ids delivered via webhooks will change from an int to a string to better support default json encoder in js (that trims long ints). Please make sure your app works with string ids returned from webhooks as well as with ints.

Solution

I believe that Facebook will make the original code in the tutorial work pretty soon; however, there are people providing the solution online already. Here’s the template code that should work:
var express = require('express');
var bodyParser = require('body-parser');
var request = require("request");

var app = express();

const JSONbig = require('json-bigint')

app.set('port', (process.env.PORT || 5000));

app.use(express.static(__dirname + '/public'));
app.use(bodyParser.text({ type: 'application/json' }))

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});

var token = "<YOUR_TOEKN_HERE>";

function sendTextMessage(sender, text) {
  messageData = {
    text:text
  }
  request({
      url: 'https://graph.facebook.com/v2.6/me/messages',
      qs: {access_token:token},
      method: 'POST',
      json: {
        recipient: {id:sender},
        message: messageData,
      }
  }, function(error, response, body) {
    if (error) {
      console.log('Error sending message: ', error);
    } else if (response.body.error) {
      console.log('Error: ', response.body.error);
    }
  });
}

app.post('/webhook/', function (req, res) {

  var data = JSONbig.parse(req.body);
  messaging_events = data.entry[0].messaging;

  for (i = 0; i < messaging_events.length; i++) {

    event = data.entry[0].messaging[i];
    sender = event.sender.id.toString();

    if (event.message && event.message.text) {
      text = event.message.text;
      sendTextMessage(sender, text);
    }

  }

  res.sendStatus(200);

});
Make sure you’ve added body-parser, express, json-bigint, and request to your NPM.

Finally

My Bot, Ducky, is now working well and be public, please feel free to message him here: http://m.me/ducky.bot!


Thursday, May 5, 2016

Qt Mac Application Failed to Create Self-contained App Bundle (Qt Creator Build)

Recently, I encountered a problem in creating an app bundle using Qt Creator with Qt 5.6, so I posted my question with detail on StackOverflow here.

In this post, I am going to point out the places I got wrong, and some studies.

Scott

Scott is a friend of mine for years, and he is best programmer I’ve ever met in Taiwan. He helped me on this question, and I would like to quote his words here:

Do try to figure out what you did wrong before. Look at the RPATH, install names etc in your executable and update your StackOverflow question with those findings. Finding out what you did wrong is an important step in understanding a system. This makes your exercise of publishing apps on multiple platforms more meaningful.

@executable_path, @loader_path, @rpath

The first reason I couldn’t build the app build is that I didn’t fully understand the path names used on Mac, and here is my study of @executable_path, @loader_path, and @rpath.

  • @executable_path: the folder path of application’s executable
    • ex. /Applications/Foo.app/Contents/MacOS
    • useful for frameworks embedded inside the applications
  • @loader_path: the folder path of the related plug-in’s code
    • ex. /Library/Application Support/Foo/Plug-Ins/Bar.bundle/Contents/MacOS
    • useful for frameworks embedded inside plug-ins
    • availabe from Mac OS X 10.4
  • @rpath: instructs the dynamic linker to search a list of paths in order to locate the framework
    • no need to specify the “install path” using either @executable_path or @loader_path, but pass additional flags when building the host application (ex. -rpath @excutable/…/Frameworks or /Library/Frameworks)
    • availabe from Mac OS X 10.5

otool

The second reason I was stuck is that otool didn’t resolve @rpath names, so I was confused when it always returned me the same thing.

However, Scott wrote another version of otool that resolves the rpaths here. Here are the steps that demostrate the difference:

> otool -L bibi.app/Contents/MacOS/bibi
bibi.app/Contents/MacOS/bibi:
        @rpath/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.6.0, current version 5.6.0)
        @rpath/QtGui.framework/Versions/5/QtGui (compatibility version 5.6.0, current version 5.6.0)
        @rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.6.0, current version 5.6.0)
        /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
        /System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
        /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)

> otool-rpath bibi.app/Contents/MacOS/bibi
/Users/heron/Qt/5.6/clang_64/lib

> macdeployqt ./*.app -verbose=3 -always-overwrite -appstore-compliant

> otool -L bibi.app/Contents/MacOS/bibi
bibi.app/Contents/MacOS/bibi:
        @rpath/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.6.0, current version 5.6.0)
        @rpath/QtGui.framework/Versions/5/QtGui (compatibility version 5.6.0, current version 5.6.0)
        @rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.6.0, current version 5.6.0)
        /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
        /System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
        /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)

> otool-rpath bibi.app/Contents/MacOS/bibi
@executable_path/../Frameworks

macdeployqt

The last reason I failed to understand what’s going on is the output of macdeployqt, which confused me.

> macdeployqt bibi.app
File exists, skip copy: "bibi.app/Contents/PlugIns/platforms/libqcocoa.dylib"
File exists, skip copy: "bibi.app/Contents/PlugIns/printsupport/libcocoaprintersupport.dylib"
File exists, skip copy: "bibi.app/Contents/PlugIns/imageformats/libqdds.dylib"
File exists, skip copy: "bibi.app/Contents/PlugIns/imageformats/libqgif.dylib"
File exists, skip copy: "bibi.app/Contents/PlugIns/imageformats/libqicns.dylib"
File exists, skip copy: "bibi.app/Contents/PlugIns/imageformats/libqico.dylib"
File exists, skip copy: "bibi.app/Contents/PlugIns/imageformats/libqjpeg.dylib"
File exists, skip copy: "bibi.app/Contents/PlugIns/imageformats/libqtga.dylib"
File exists, skip copy: "bibi.app/Contents/PlugIns/imageformats/libqtiff.dylib"
File exists, skip copy: "bibi.app/Contents/PlugIns/imageformats/libqwbmp.dylib"
File exists, skip copy: "bibi.app/Contents/PlugIns/imageformats/libqwebp.dylib"
WARNING:
WARNING: "bibi.app/Contents/Resources/qt.conf" already exists, will not overwrite.
WARNING: To make sure the plugins are loaded from the correct location,
WARNING: please make sure qt.conf contains the following lines:
WARNING: [Paths]
WARNING:   Plugins = PlugIns

However, in Scott’s solution, he gave following additional arguments:

  • -verbose=3: see how the rpaths are updated in details (Scott’s log)
  • always-overwrite: copy files even if the target file exists, so the first (Scott: I used “always-overwrite” to get predictable results after repeated testing, since the Qt frameworks would be copied into the app bundle.)
  • appstore-compliant: skip deployment of components that use private API (Scott: appstore-compliant was just for your convenience)

Test

Testing is one additional thing the made the original question harder to be solved: there’s no easy way to see if my app bundle works on the other machine without Qt installed.

Instead of asking friends to run the app, Scott mentioned that we can use `lsof at run-time.

> ps aux|grep bibi
heron           21610   0.0  0.5  2632680  40272   ??  S    Tue09PM   5:32.80 /Users/heron/Project/bibi/bibi/build-bibi-Desktop_Qt_5_6_0_clang_64bit-Release/bibi.app/Contents/MacOS/bibi
heron           39245   0.0  0.0  2434840    664 s003  R+    9:31AM   0:00.00 grep --color=auto bibi

> lsof -p 39183 | grep QtCore
bibi    21610 heron  txt      REG                1,4   6441676 168354669 /Users/heron/Qt-free/5.6/clang_64/lib/QtCore.framework/Versions/5/QtCore

After macdeployqt, the app bundle no longer needs to link to frameworks outside the bundle:

> ps aux|grep bibi
heron           39352   0.0  0.0  2435864    788 s003  S+    9:32AM   0:00.00 grep --color=auto bibi
heron           39315   0.0  0.8  2611176  63000   ??  S     9:32AM   0:00.68 /Users/heron/Project/bibi/bibi/bibi/bibi.app/Contents/MacOS/bibi

> lsof -p 39315 | grep QtCore
bibi    39315 heron  txt      REG    1,4   6017532 171823963 /Users/heron/Project/bibi/bibi/bibi/bibi.app/Contents/Frameworks/QtCore.framework/Versions/5/QtCore

Summary

I would say the biggest problem is that I didn’t know how to read @rpath, so Scott’s otool-rpath or lsof helps eventually.

Reference