闲聊么

/ 技术笔记 / 0 条评论 / 565浏览

闲聊几句

大概是一年前,我就已经了解过这款产品。当时也有过建站,但是没有坚持下来。现在是第二次尝试。在原先的基础上,增加了更多的自定义。在这里记录一下网站接入闲聊么的过程。

简单接入

xianliao.me是支持最简洁的方式接入到你的网站的,只需要在他们的官网注册一个账号。你就能得到一个web_id和SSO_key。
闲聊么官网截图
根据上面的图片,只需要使用很简单的代码,就可以接入到你的网站。
但是,有一个非常现实的问题,如果访问者想要加入,必须使用微信扫一扫才能登录并且开始聊天。
相信不是流量特别大的网站,这一步应该可以劝退一大波的访问者了。所以就有了下文。我是如何通过他的API搭建起一个只需要输入一个随机用户名就能开始聊天的系统的。

分析API

xianliaome的官网,我们发现他们提供了 定制代码接入 的功能,我们可以通过手机端接入API,模拟出一个“已登录用户”,提供给游客进行聊天。

在手机端接入闲聊么的方法:

https://xianliao.me/s/你的web_id?mobile=1&uid={登录用户的ID}&username={登录用户的用户名,需要做URI encode}&avatar={登录用户的头像URL,需要做URI encode}&ts={当前的Linux timestamp}&token={即xlm_hash,见下附的xlm_hash的生成方法}  

通过上面的接口信息我们可以看出来,其中登录用户id,用户名,用户头像我们都可以通过一个特定规则进行模拟。然后得到一个完整的模拟用户,提供给闲聊么,然后他会返回一个完整的聊天窗口给我们。

模拟用户

用户id

首先要声明的是,经过我的测试,同一个web_id下,用户ID是不能够重复的,重复就会当做同一个用户,所以,我们需要采用一套不会重复或者重复几率较小的生成规则生成用户ID。

我们生成随机数的时候,使用9301, 49297, 233280三个数字作为基数(原理看这儿:https://www.zhihu.com/question/22818104), 生成一个理论上不会重复的用户ID。

用户名

用户名,我们可以通过弹框让访问的游客自己输入一个名称,也可以使用随机生成一个姓名。此处无太多技术点,不做详解。

用户头像

如果不传头像,“闲聊么”会用一个空白作为默认头像,但是不够美观,所以在这里,我们使用 http://www.gravatar.com
使用gravatar后,我们只需要一个简单的http请求,就能够获得一个有趣的头像。

 http://www.gravatar.com/avatar/{hash}?s=256&d=identicon

hash的话,我们前面生成了一个可能不会重复的user_id,在这里,就可以将user_id当做hash传入到请求中。
s,尺寸,可以不用传,默认尺寸即可。
d,风格,我使用了数组保存了五种风格,然后每次创建用户的时候使用一个[0-4]的随机数,使用随机数当做styleArray的下标获得一个随机的风格。

生成chat_url

有了上面的几步,我们获得了web_id,sso_key,user_id,username,avatar。timestamp可以获取。现在,我们已经能够生成一个token了。

create token

按照官方文档,token的生成是由以下几个字段经过SHA512加密之后的字符串。

webid_userid_当前时间戳_ssokey

由于SSO_KEY属于隐私信息,所以我们不能够在前端生成token,这一步,应该交给后台。 后端编写一个**createXianLiaoToken()**接口,前台传入当前时间戳和用户ID即可,web_id和SSO_KEY保存在服务端。
此处,我使用Java演示。

Controller.java

/**
 * 用于生成闲聊么 所需要的token信息
 */
@GetMapping("createXianLiaoToken")
public @ResponseBody String createXianLiaoToken(HttpServletRequest request) {

    String uid = request.getParameter("uid");
    String timestamp = request.getParameter("timestamp");

    String str = WebConst.WEB_ID + "_" + uid + "_" + timestamp + "_" + WebConst.SSO_KEY;
    try {
        return Commons.SHA(str, "SHA-512");
    } catch (Exception e) {
        throw new TipException("系统异常, SHA-512加密失败.");
    }
}

Commons#SHA(网上很多,我在CSDN复制的)

public static String SHA(final String strText, final String strType) {
    // 返回值
    String strResult = null;

    // 是否是有效字符串
    if (strText != null && strText.length() > 0) {
        try {
            // SHA 加密开始
            // 创建加密对象 并傳入加密類型
            MessageDigest messageDigest = MessageDigest
                    .getInstance(strType);
            // 传入要加密的字符串
            messageDigest.update(strText.getBytes());
            // 得到 byte 類型结果
            byte byteBuffer[] = messageDigest.digest();

            // 將 byte 轉換爲 string
            StringBuffer strHexString = new StringBuffer();
            // 遍歷 byte buffer
            for (int i = 0; i < byteBuffer.length; i++) {
                String hex = Integer.toHexString(0xff & byteBuffer[i]);
                if (hex.length() == 1) {
                    strHexString.append('0');
                }
                strHexString.append(hex);
            }
            // 得到返回結果
            strResult = strHexString.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    return strResult;
}

AJAX

// 从后台获取到与闲聊么通信的token
function getToken(xlm_uid, timestamp) {
    var result = null;
    $.ajax({
        url: [[@{/createXianLiaoToken}]],
        type: 'GET',
        dataType: 'text',
        contentType: 'application/json',
        data: {'uid': xlm_uid, 'timestamp': timestamp},
        async: false,
        success: function(data){
            result = data;
        }
    });
    return result;
}

通过上述操作,我们拿到了token,然后我们可以开始组装整个chat_url信息。
根据官网的文档,chat_url的格式为:

https://xianliao.me/s/web_id?mobile=1&uid={登录用户的ID}&username={登录用户的用户名,需要做URI encode}&avatar={登录用户的头像URL,需要做URI encode}&ts={当前的Linux timestamp}&token={即xlm_hash,见下附的xlm_hash的生成方法}

我们将上面步骤获得信息,一一填入到链接中,然后通过window.location.href = chat_url,将当前页面重定向到闲聊么的页面。就得到了一个闲聊么的聊天界面了。下面看一下我的完整前端代码。

xianliaome.html

    <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>闲聊么</title>
    <script th:src="@{//cdn.bootcss.com/jquery/2.2.3/jquery.min.js}"></script>
</head>
<body>

<script th:inline="javascript">
    // thymeleaf 不能输入地址符,但是闲聊么api又不能识别转移后了的&amp;,所以用CDATA包起来.
    /*<![CDATA[*/
    var ds = ['identicon', 'monsterid', 'wavatar', 'retro', 'robohash'];

    // 初始化闲聊么需要的数据
    function init() {
        var web_id = 'xxxxxx';
        var xlm_uid;
        var xlm_name;
        var xlm_avatar;

        if (easyLocalStorage('xlm_uid') != null) {
            xlm_uid = easyLocalStorage('xlm_uid');
            xlm_name = easyLocalStorage('xlm_name');
            xlm_avatar = easyLocalStorage('xlm_avatar');
        }else {
            xlm_uid = decodeURI(Math.seededRandom(Date.parse(new Date()), 1));
            var name = prompt('请输入用户名').trim();
            xlm_name = decodeURI(name === '' ? '匿名' : name);
            xlm_avatar = decodeURI(avatarRandom(xlm_uid));
            easyLocalStorage('xlm_uid', xlm_uid);
            easyLocalStorage('xlm_name', xlm_name);
            easyLocalStorage('xlm_avatar', xlm_avatar);
        }

        var timestamp = Date.parse(new Date());
        var token = getToken(xlm_uid, timestamp);

        window.location.href =  "https://www.xianliao.me/s/" + web_id + "" +
            "?mobile=1" +
            "&uid=" + xlm_uid + "" +
            "&username=" + xlm_name + "" +
            "&avatar=" + xlm_avatar + "" +
            "&ts=" + timestamp + "" +
            "&token=" + token;
    }

    $(function () {
        init()
    })

    // 从后台获取到与闲聊么通信的token
    function getToken(xlm_uid, timestamp) {
        var result = null;
        $.ajax({
            url: [[@{/createXianLiaoToken}]],
            type: 'GET',
            dataType: 'text',
            contentType: 'application/json',
            data: {'uid': xlm_uid, 'timestamp': timestamp},
            async: false,
            success: function(data){
                result = data;
            }
        });
        return result;
    }

    // 通过gravatar.com.随机获取一个有趣的用户头像
    function avatarRandom(xlm_uid) {
        var num = Math.floor(Math.random() * 4 + 1);
        return 'http://www.gravatar.com/avatar/' + xlm_uid + '?d=' + ds[num];
    }

    // 对本地存储的建议封装
    function easyLocalStorage(key, value) {
        if (value == null || value.trim() === '') {
            return localStorage.getItem(key);
        }else {
            localStorage.setItem(key, value);
        }
        return true;
    }

    // 获取一个随机数
    Math.seed = 5;
    Math.seededRandom = function(max, min) {
        max = max || 1;
        min = min || 0;
        Math.seed = (Math.seed * 9301 + 49297) % 233280;
        var rnd = Math.seed / 233280.0;
        return Math.ceil( min + rnd * (max - min) );   // Math.ceil实现取整功能,可以根据需要取消取整
    };
    /*]]>*/
</script>
</body>
</html>

如何使用

上面的教程,我们已经有了一个xianliaome.html的页面,在我们需要使用的地方,我们可以通过iframe将其嵌入到当前页面中。
至此,就完成了我们闲聊么的匿名聊天功能了。

缺陷

正对上面的不足,我将会继续优化。直至完美。

感谢阅读,第一次写长篇博客,排版和描述不是特别友好。