var reqSeq = 0,reqNum = 0; module.exports = function() { /* webim javascript SDK * VER 1.7.0 */ /* webim API definitions */ var msgCache = {}; var webim = { // namespace object webim /* function init * sdk登录 * params: * loginInfo - Object, 登录身份相关参数集合,详见下面 * { * sdkAppID - String, 用户标识接入SDK的应用ID,必填 * accountType - int, 账号类型,必填 * identifier - String, 用户帐号,必须是字符串类型,必填 * identifierNick - String, 用户昵称,选填 * userSig - String, 鉴权Token,必须是字符串类型,必填 * } * listeners - Object, 事件回调函数集合, 详见下面 * { * onConnNotify - function(connInfo), 用于收到连接状态相关通知的回调函数,目前未使用 * jsonpCallback -function(rspData),//IE9(含)以下浏览器用到的jsonp回调函数 * onMsgNotify - function(newMsgList), 用于收到消息通知的回调函数, * newMsgList为新消息数组,格式为[Msg对象] * 使用方有两种处理回调: 1)处理newMsgList中的增量消息,2)直接访问webim.MsgStore获取最新的消息 * onGroupInfoChangeNotify - function(groupInfo), 用于监听群组资料变更的回调函数, * groupInfo为新的群组资料信息 * onGroupSystemNotifys - Object, 用于监听(多终端同步)群系统消息的回调函数对象 * * } * options - Object, 其它选项, 目前未使用 * return: * (无) */ login: function (loginInfo, listeners, options) { }, /* function syncMsgs * 拉取最新C2C消息 * 一般不需要使用方直接调用, SDK底层会自动同步最新消息并通知使用方, 一种有用的调用场景是用户手动触发刷新消息 * params: * cbOk - function(msgList)类型, 当同步消息成功时的回调函数, msgList为新消息数组,格式为[Msg对象], * 如果此参数为null或undefined则同步消息成功后会像自动同步那样回调cbNotify * cbErr - function(err)类型, 当同步消息失败时的回调函数, err为错误对象 * return: * (无) */ syncMsgs: function (cbOk, cbErr) { }, /* function getC2CHistoryMsgs * 拉取C2C漫游消息 * params: * options - 请求参数 * cbOk - function(msgList)类型, 成功时的回调函数, msgList为消息数组,格式为[Msg对象], * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ getC2CHistoryMsgs: function (options, cbOk, cbErr) { }, /* function syncGroupMsgs * 拉取群漫游消息 * params: * options - 请求参数 * cbOk - function(msgList)类型, 成功时的回调函数, msgList为消息数组,格式为[Msg对象], * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ syncGroupMsgs: function (options, cbOk, cbErr) { }, /* function sendMsg * 发送一条消息 * params: * msg - webim.Msg类型, 要发送的消息对象 * cbOk - function()类型, 当发送消息成功时的回调函数 * cbErr - function(err)类型, 当发送消息失败时的回调函数, err为错误对象 * return: * (无) */ sendMsg: function (msg, cbOk, cbErr) { }, /* function logout * sdk登出 * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ logout: function (cbOk, cbErr) { }, /* function setAutoRead * 设置会话自动已读上报标志 * params: * selSess - webim.Session类型, 当前会话 * isOn - boolean, 将selSess的自动已读消息标志改为isOn,同时是否上报当前会话已读消息 * isResetAll - boolean,是否重置所有会话的自动已读标志 * return: * (无) */ setAutoRead: function (selSess, isOn, isResetAll) { }, /* function getProfilePortrait * 拉取资料(搜索用户) * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ getProfilePortrait: function (options, cbOk, cbErr) { }, /* function setProfilePortrait * 设置个人资料 * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ setProfilePortrait: function (options, cbOk, cbErr) { }, /* function applyAddFriend * 申请添加好友 * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ applyAddFriend: function (options, cbOk, cbErr) { }, /* function getPendency * 拉取好友申请 * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ getPendency: function (options, cbOk, cbErr) { }, /* function deletePendency * 删除好友申请 * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ deletePendency: function (options, cbOk, cbErr) { }, /* function responseFriend * 响应好友申请 * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ responseFriend: function (options, cbOk, cbErr) { }, /* function getAllFriend * 拉取我的好友 * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ getAllFriend: function (options, cbOk, cbErr) { }, /* function deleteFriend * 删除好友 * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ deleteFriend: function (options, cbOk, cbErr) { }, /* function addBlackList * 增加黑名单 * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ addBlackList: function (options, cbOk, cbErr) { }, /* function getBlackList * 删除黑名单 * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ getBlackList: function (options, cbOk, cbErr) { }, /* function deleteBlackList * 我的黑名单 * params: * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ deleteBlackList: function (options, cbOk, cbErr) { }, /* function uploadPic * 上传图片 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ uploadPic: function (options, cbOk, cbErr) { }, /* function createGroup * 创建群 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ createGroup: function (options, cbOk, cbErr) { }, /* function applyJoinGroup * 申请加群 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ applyJoinGroup: function (options, cbOk, cbErr) { }, /* function handleApplyJoinGroup * 处理申请加群(同意或拒绝) * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ handleApplyJoinGroup: function (options, cbOk, cbErr) { }, /* function deleteApplyJoinGroupPendency * 删除加群申请 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ deleteApplyJoinGroupPendency: function (options, cbOk, cbErr) { }, /* function quitGroup * 主动退群 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ quitGroup: function (options, cbOk, cbErr) { }, /* function getGroupPublicInfo * 读取群公开资料-高级接口 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ getGroupPublicInfo: function (options, cbOk, cbErr) { }, /* function getGroupInfo * 读取群详细资料-高级接口 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ getGroupInfo: function (options, cbOk, cbErr) { }, /* function modifyGroupBaseInfo * 修改群基本资料 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ modifyGroupBaseInfo: function (options, cbOk, cbErr) { }, /* function destroyGroup * 解散群 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ destroyGroup: function (options, cbOk, cbErr) { }, /* function getJoinedGroupListHigh * 获取我的群组-高级接口 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ getJoinedGroupListHigh: function (options, cbOk, cbErr) { }, /* function getGroupMemberInfo * 获取群组成员列表 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ getGroupMemberInfo: function (options, cbOk, cbErr) { }, /* function addGroupMember * 邀请好友加群 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ addGroupMember: function (options, cbOk, cbErr) { }, /* function modifyGroupMember * 修改群成员资料(角色或者群消息提类型示) * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ modifyGroupMember: function (options, cbOk, cbErr) { }, /* function forbidSendMsg * 设置群成员禁言时间 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ forbidSendMsg: function (options, cbOk, cbErr) { }, /* function deleteGroupMember * 删除群成员 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ deleteGroupMember: function (options, cbOk, cbErr) { }, /* function sendCustomGroupNotify * 发送自定义群通知 * params: * options - 请求参数,详见api文档 * cbOk - function()类型, 成功时回调函数 * cbErr - function(err)类型, 失败时回调函数, err为错误对象 * return: * (无) */ sendCustomGroupNotify: function (options, cbOk, cbErr) { }, /* class webim.Msg * 一条消息的描述类, 消息发送、接收的API中都会涉及此类型的对象 * properties: * sess - Session object-ref, 消息所属的会话(e.g:我与好友A的C2C会话,我与群组G的GROUP会话) * isSend - Boolean, true表示是我发出消息, false表示是发给我的消息) * seq - Integer, 消息序列号, 用于判断消息是否同一条 * random - Integer, 消息随机数,用于判断消息是否同一条 * time - Integer, 消息时间戳, 为unix timestamp * fromAccount -String, 消息发送者帐号 * subType -Integer, 消息子类型,c2c消息时,0-表示普通消息;群消息时,0-普通消息,1-点赞消息,2-提示消息 * fromAccountNick -String, 消息发送者昵称 * elems - Array of webim.Msg.Elem, 描述消息内容的元素列表 * constructor: * Msg(sess, isSend, seq,random time,fromAccount) - 构造函数, 参数定义同上面properties中定义 * methods: * addText(text) - 向elems中添加一个TEXT元素 * addFace(face) - 向elems中添加一个FACE元素 * toHtml() - 转成可展示的html String *addFace * sub-class webim.Msg.Elem * 消息中一个组成元素的描述类, 一条消息的内容被抽象描述为N个元素的有序列表 * properties: * type - 元素类型, 目前有TEXT(文本)、FACE(表情)、IMAGE(图片)等 * content- 元素内容体, 当TEXT时为String, 当PIC时为UrlString * constructor: * Elem(type, content) - 构造函数, 参数定义同上面properties中定义 * * sub-class webim.Msg.Elem.TextElem * 文本 * properties: * text - String 内容 * constructor: * TextElem(text) - 构造函数, 参数定义同上面properties中定义 * * sub-class webim.Msg.Elem.FaceElem * 表情 * properties: * index - Integer 表情索引, 用户自定义 * data - String 额外数据,用户自定义 * constructor: * FaceElem(index,data) - 构造函数, 参数定义同上面properties中定义 * * */ Msg: function (sess, isSend, seq, random, time, fromAccount, subType, fromAccountNick) {/*Class constructor*/ }, /* singleton object MsgStore * webim.MsgStore是消息数据的Model对象(参考MVC概念), 它提供接口访问当前存储的会话和消息数据 * 下面说明下会话数据类型: Session * * class Session * 一个Session对象描述一个会话,会话可简单理解为最近会话列表的一个条目,它由两个字段唯一标识: * type - String, 会话类型(如"C2C", "GROUP", ...) * id - String, 会话ID(如C2C类型中为对方帐号,"C2C"时为好友ID,"GROUP"时为群ID) * properties: * (Session对象未对外暴露任何属性字段, 所有访问通过下面的getter方法进行) * methods: * type() - String, 返回会话类型,"C2C"表示与好友私聊,"GROUP"表示群聊 * id() - String, 返回会话ID * name() - String, 返回会话标题(如C2C类型中为对方的昵称,暂不支持) * icon() - String, 返回会话图标(对C2C类型中为对方的头像URL,暂不支持) * unread() - Integer, 返回会话未读条数 * time() - Integer, 返回会话最后活跃时间, 为unix timestamp * curMaxMsgSeq() - Integer, 返回会话最大消息序列号 * msgCount() - Integer, 返回会话中所有消息条数 * msg(index) - webim.Msg, 返回会话中第index条消息 */ MsgStore: { /* function sessMap * 获取所有会话 * return: * 所有会话对象 */ sessMap: function () { return {/*Object*/}; }, /* function sessCount * 获取当前会话的个数 * return: * Integer, 会话个数 */ sessCount: function () { return 0; }, /* function sessByTypeId * 根据会话类型和会话ID取得相应会话 * params: * type - String, 会话类型(如"C2C", "GROUP", ...) * id - String, 会话ID(如对方ID) * return: * Session, 会话对象(说明见上面) */ sessByTypeId: function (type, id) { return {/*Session Object*/}; }, /* function delSessByTypeId * 根据会话类型和会话ID删除相应会话 * params: * type - String, 会话类型(如"C2C", "GROUP", ...) * id - String, 会话ID(如对方ID) * return: * Boolean, 布尔类型 */ delSessByTypeId: function (type, id) { return true; }, /* function resetCookieAndSyncFlag * 重置上一次读取新c2c消息Cookie和是否继续拉取标记 * return: * */ resetCookieAndSyncFlag: function () { }, downloadMap: {} } }; /* webim API implementation */ (function (webim) { //sdk版本 var SDK = { 'VERSION': '1.7.0',//sdk版本号 'APPID': '537048168'//web im sdk 版本 APPID }; //是否启用正式环境,默认启用 var isAccessFormaEnvironment = true; // var isAccessFormaEnvironment = false; //后台接口主机 var SRV_HOST = { 'FORMAL': { 'COMMON': 'https://webim.tim.qq.com', 'PIC': 'https://pic.tim.qq.com' }, 'TEST': { 'COMMON': 'https://test.tim.qq.com', 'PIC': 'https://pic.tim.qq.com' } }; //浏览器版本信息 var BROWSER_INFO = {}; //是否为ie9(含)以下 var lowerBR = false; //服务名称 var SRV_NAME = { 'OPEN_IM': 'openim',//私聊(拉取未读c2c消息,长轮询,c2c消息已读上报等)服务名 'GROUP': 'group_open_http_svc',//群组管理(拉取群消息,创建群,群成员管理,群消息已读上报等)服务名 'FRIEND': 'sns',//关系链管理(好友管理,黑名单管理等)服务名 'PROFILE': 'profile',//资料管理(查询,设置个人资料等)服务名 'RECENT_CONTACT': 'recentcontact',//最近联系人服务名 'PIC': 'openpic',//图片(或文件)服务名 'BIG_GROUP': 'group_open_http_noauth_svc',//直播大群 群组管理(申请加大群)服务名 'BIG_GROUP_LONG_POLLING': 'group_open_long_polling_http_noauth_svc',//直播大群 长轮询(拉取消息等)服务名 'IM_OPEN_STAT': 'imopenstat'//质量上报,统计接口错误率 }; //不同服务对应的版本号 var SRV_NAME_VER = { 'openim': 'v4', 'group_open_http_svc': 'v4', 'sns': 'v4', 'profile': 'v4', 'recentcontact': 'v4', 'openpic': 'v4', 'group_open_http_noauth_svc': 'v1', 'group_open_long_polling_http_noauth_svc': 'v1', 'imopenstat': 'v4' }; //不同的命令名对应的上报类型ID,用于接口质量上报 var CMD_EVENT_ID_MAP = { 'login': 1,//登录 'pic_up': 3,//上传图片 'apply_join_group': 9,//申请加入群组 'create_group': 10,//创建群组 'longpolling': 18,//普通长轮询 'send_group_msg': 19,//群聊 'sendmsg': 20//私聊 }; //聊天类型 var SESSION_TYPE = { 'C2C': 'C2C',//私聊 'GROUP': 'GROUP'//群聊 }; //最近联系人类型 var RECENT_CONTACT_TYPE = { 'C2C': 1,//好友 'GROUP': 2//群 }; //消息最大长度(字节) var MSG_MAX_LENGTH = { 'C2C': 12000,//私聊消息 'GROUP': 8898//群聊 }; //后台接口返回类型 var ACTION_STATUS = { 'OK': 'OK',//成功 'FAIL': 'FAIL'//失败 }; var ERROR_CODE_CUSTOM = 99999;//自定义后台接口返回错误码 //消息元素类型 var MSG_ELEMENT_TYPE = { 'TEXT': 'TIMTextElem',//文本 'FACE': 'TIMFaceElem',//表情 'IMAGE': 'TIMImageElem',//图片 'CUSTOM': 'TIMCustomElem',//自定义 'SOUND': 'TIMSoundElem',//语音,只支持显示 'FILE': 'TIMFileElem',//文件,只支持显示 'LOCATION': 'TIMLocationElem',//地理位置 'GROUP_TIP': 'TIMGroupTipElem'//群提示消息,只支持显示 }; //图片类型 var IMAGE_TYPE = { 'ORIGIN': 1,//原图 'LARGE': 2,//缩略大图 'SMALL': 3//缩略小图 }; //上传资源包类型 var UPLOAD_RES_PKG_FLAG = { 'RAW_DATA': 0,//原始数据 'BASE64_DATA': 1//base64编码数据 }; //下载文件配置 var DOWNLOAD_FILE = { 'BUSSINESS_ID': '10001',//下载文件业务ID 'AUTH_KEY': '617574686b6579',//下载文件authkey 'SERVER_IP': '182.140.186.147'//下载文件服务器IP }; //下载文件类型 var DOWNLOAD_FILE_TYPE = { "SOUND": 2106,//语音 "FILE": 2107//普通文件 }; //上传资源类型 var UPLOAD_RES_TYPE = { "IMAGE": 1,//图片 "FILE": 2,//文件 "SHORT_VIDEO": 3,//短视频 "SOUND": 4//语音,PTT }; //版本号,用于上传图片或文件接口 var VERSION_INFO = { 'APP_VERSION': '2.1',//应用版本号 'SERVER_VERSION': 1//服务端版本号 }; //长轮询消息类型 var LONG_POLLINNG_EVENT_TYPE = { "C2C": 1//新的c2c消息通知 ,"GROUP_COMMON": 3//新的群普通消息 ,"GROUP_TIP": 4//新的群提示消息 ,"GROUP_SYSTEM": 5//新的群系统消息 ,"GROUP_TIP2": 6//新的群提示消息2 ,"FRIEND_NOTICE": 7//好友系统通知 ,"PROFILE_NOTICE": 8//资料系统通知 ,"C2C_COMMON": 9//新的C2C消息 ,"C2C_EVENT": 10 }; //c2c消息子类型 var C2C_MSG_SUB_TYPE = { "COMMON": 0//普通消息 }; //c2c消息子类型 var C2C_EVENT_SUB_TYPE = { "READED": 92//已读消息同步 }; //群消息子类型 var GROUP_MSG_SUB_TYPE = { "COMMON": 0,//普通消息 "LOVEMSG": 1,//点赞消息 "TIP": 2,//提示消息 "REDPACKET": 3//红包消息 }; //群消息优先级类型 var GROUP_MSG_PRIORITY_TYPE = { "REDPACKET": 1,//红包消息 "COMMON": 2,//普通消息 "LOVEMSG": 3//点赞消息 }; //群提示消息类型 var GROUP_TIP_TYPE = { "JOIN": 1,//加入群组 "QUIT": 2,//退出群组 "KICK": 3,//被踢出群组 "SET_ADMIN": 4,//被设置为管理员 "CANCEL_ADMIN": 5,//被取消管理员 "MODIFY_GROUP_INFO": 6,//修改群资料 "MODIFY_MEMBER_INFO": 7//修改群成员信息 }; //群提示消息-群资料变更类型 var GROUP_TIP_MODIFY_GROUP_INFO_TYPE = { "FACE_URL": 1,//修改群头像URL "NAME": 2,//修改群名称 "OWNER": 3,//修改群主 "NOTIFICATION": 4,//修改群公告 "INTRODUCTION": 5//修改群简介 }; //群系统消息类型 var GROUP_SYSTEM_TYPE = { "JOIN_GROUP_REQUEST": 1,//申请加群请求(只有管理员会收到) "JOIN_GROUP_ACCEPT": 2,//申请加群被同意(只有申请人能够收到) "JOIN_GROUP_REFUSE": 3,//申请加群被拒绝(只有申请人能够收到) "KICK": 4,//被管理员踢出群(只有被踢者接收到) "DESTORY": 5,//群被解散(全员接收) "CREATE": 6,//创建群(创建者接收, 不展示) "INVITED_JOIN_GROUP_REQUEST": 7,//邀请加群(被邀请者接收) "QUIT": 8,//主动退群(主动退出者接收, 不展示) "SET_ADMIN": 9,//设置管理员(被设置者接收) "CANCEL_ADMIN": 10,//取消管理员(被取消者接收) "REVOKE": 11,//群已被回收(全员接收, 不展示) "READED": 15,//群消息已读同步 "CUSTOM": 255//用户自定义通知(默认全员接收) }; //好友系统通知子类型 var FRIEND_NOTICE_TYPE = { "FRIEND_ADD": 1,//好友表增加 "FRIEND_DELETE": 2,//好友表删除 "PENDENCY_ADD": 3,//未决增加 "PENDENCY_DELETE": 4,//未决删除 "BLACK_LIST_ADD": 5,//黑名单增加 "BLACK_LIST_DELETE": 6,//黑名单删除 "PENDENCY_REPORT": 7,//未决已读上报 "FRIEND_UPDATE": 8//好友数据更新 }; //资料系统通知子类型 var PROFILE_NOTICE_TYPE = { "PROFILE_MODIFY": 1//资料修改 }; //腾讯登录服务错误码(用于托管模式) var TLS_ERROR_CODE = { 'OK': 0,//成功 'SIGNATURE_EXPIRATION': 11//用户身份凭证过期 }; //长轮询连接状态 var CONNECTION_STATUS = { 'INIT': -1,//初始化 'ON': 0,//连接正常 'RECONNECT': 1,//连接恢复正常 'OFF': 9999//连接已断开,可能是用户网络问题,或者长轮询接口报错引起的 }; var UPLOAD_PIC_BUSSINESS_TYPE = {//图片业务类型 'GROUP_MSG': 1,//私聊图片 'C2C_MSG': 2,//群聊图片 'USER_HEAD': 3,//用户头像 'GROUP_HEAD': 4//群头像 }; var FRIEND_WRITE_MSG_ACTION = {//好友输入消息状态 'ING': 14,//正在输入 'STOP': 15//停止输入 }; //ajax默认超时时间,单位:毫秒 var ajaxDefaultTimeOut = 15000; //大群长轮询接口返回正常时,延时一定时间再发起下一次请求 var OK_DELAY_TIME = 1000; //大群长轮询接口发生错误时,延时一定时间再发起下一次请求 var ERROR_DELAY_TIME = 5000; //群提示消息最多显示人数 var GROUP_TIP_MAX_USER_COUNT = 10; //长轮询连接状态 var curLongPollingStatus = CONNECTION_STATUS.INIT; //当长轮询连接断开后,是否已经回调过 var longPollingOffCallbackFlag = false; //当前长轮询返回错误次数 var curLongPollingRetErrorCount = 0; //长轮询默认超时时间,单位:毫秒 var longPollingDefaultTimeOut = 60000; //长轮询返回错误次数达到一定值后,发起新的长轮询请求间隔时间,单位:毫秒 var longPollingIntervalTime = 5000; //没有新消息时,长轮询返回60008错误码是正常的 var longPollingTimeOutErrorCode = 60008; //多实例登录被kick的错误码 var longPollingKickedErrorCode = 91101; var LongPollingId = null; //当前大群长轮询返回错误次数 var curBigGroupLongPollingRetErrorCount = 0; //最大允许长轮询返回错误次数 var LONG_POLLING_MAX_RET_ERROR_COUNT = 10; //上传重试累计 var Upload_Retry_Times = 0; //最大上传重试 var Upload_Retry_Max_Times = 20; //ie7/8/9采用jsonp方法解决ajax跨域限制 var jsonpRequestId = 0;//jsonp请求id //最新jsonp请求返回的json数据 var jsonpLastRspData = null; //兼容ie7/8/9,jsonp回调函数 var jsonpCallback = null; var uploadResultIframeId = 0;//用于上传图片的iframe id var ipList = [];//文件下载地址 var authkey = null;//文件下载票据 var expireTime = null;//文件下载票据超时时间 //错误码 var ERROR = {}; //当前登录用户 var ctx = { sdkAppID: null, appIDAt3rd: null, accountType: null, identifier: null, tinyid: null, identifierNick: null, userSig: null, a2: null, contentType: 'json', apn: 1 }; var opt = {}; var xmlHttpObjSeq = 0;//ajax请求id var xmlHttpObjMap = {};//发起的ajax请求 var curSeq = 0;//消息seq var tempC2CMsgList = [];//新c2c消息临时缓存 var tempC2CHistoryMsgList = [];//漫游c2c消息临时缓存 var maxApiReportItemCount = 20;//一次最多上报条数 var apiReportItems = [];//暂存api接口质量上报数据 var Resources = { downloadMap : {} }; //表情标识字符和索引映射关系对象,用户可以自定义 var emotionDataIndexs = { "[惊讶]": 0, "[撇嘴]": 1, "[色]": 2, "[发呆]": 3, "[得意]": 4, "[流泪]": 5, "[害羞]": 6, "[闭嘴]": 7, "[睡]": 8, "[大哭]": 9, "[尴尬]": 10, "[发怒]": 11, "[调皮]": 12, "[龇牙]": 13, "[微笑]": 14, "[难过]": 15, "[酷]": 16, "[冷汗]": 17, "[抓狂]": 18, "[吐]": 19, "[偷笑]": 20, "[可爱]": 21, "[白眼]": 22, "[傲慢]": 23, "[饿]": 24, "[困]": 25, "[惊恐]": 26, "[流汗]": 27, "[憨笑]": 28, "[大兵]": 29, "[奋斗]": 30, "[咒骂]": 31, "[疑问]": 32, "[嘘]": 33, "[晕]": 34 }; //表情对象,用户可以自定义 var emotions = { }; //工具类 var tool = new function () { //格式化时间戳 //format格式如下: //yyyy-MM-dd hh:mm:ss 年月日时分秒(默认格式) //yyyy-MM-dd 年月日 //hh:mm:ss 时分秒 this.formatTimeStamp = function (timestamp, format) { if (!timestamp) { return 0; } var formatTime; format = format || 'yyyy-MM-dd hh:mm:ss'; var date = new Date(timestamp * 1000); var o = { "M+": date.getMonth() + 1, //月份 "d+": date.getDate(), //日 "h+": date.getHours(), //小时 "m+": date.getMinutes(), //分 "s+": date.getSeconds() //秒 }; if (/(y+)/.test(format)) { formatTime = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); } else { formatTime = format; } for (var k in o) { if (new RegExp("(" + k + ")").test(formatTime)) formatTime = formatTime.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); } return formatTime; }; //根据群类型英文名转换成中文名 this.groupTypeEn2Ch = function (type_en) { var type_ch = null; switch (type_en) { case 'Public': type_ch = '公开群'; break; case 'ChatRoom': type_ch = '聊天室'; break; case 'Private': type_ch = '讨论组'; break; case 'AVChatRoom': type_ch = '直播聊天室'; break; default: type_ch = type_en; break; } return type_ch; }; //根据群类型中文名转换成英文名 this.groupTypeCh2En = function (type_ch) { var type_en = null; switch (type_ch) { case '公开群': type_en = 'Public'; break; case '聊天室': type_en = 'ChatRoom'; break; case '讨论组': type_en = 'Private'; break; case '直播聊天室': type_en = 'AVChatRoom'; break; default: type_en = type_ch; break; } return type_en; }; //根据群身份英文名转换成群身份中文名 this.groupRoleEn2Ch = function (role_en) { var role_ch = null; switch (role_en) { case 'Member': role_ch = '成员'; break; case 'Admin': role_ch = '管理员'; break; case 'Owner': role_ch = '群主'; break; default: role_ch = role_en; break; } return role_ch; }; //根据群身份中文名转换成群身份英文名 this.groupRoleCh2En = function (role_ch) { var role_en = null; switch (role_ch) { case '成员': role_en = 'Member'; break; case '管理员': role_en = 'Admin'; break; case '群主': role_en = 'Owner'; break; default: role_en = role_ch; break; } return role_en; }; //根据群消息提示类型英文转换中文 this.groupMsgFlagEn2Ch = function (msg_flag_en) { var msg_flag_ch = null; switch (msg_flag_en) { case 'AcceptAndNotify': msg_flag_ch = '接收并提示'; break; case 'AcceptNotNotify': msg_flag_ch = '接收不提示'; break; case 'Discard': msg_flag_ch = '屏蔽'; break; default: msg_flag_ch = msg_flag_en; break; } return msg_flag_ch; }; //根据群消息提示类型中文名转换英文名 this.groupMsgFlagCh2En = function (msg_flag_ch) { var msg_flag_en = null; switch (msg_flag_ch) { case '接收并提示': msg_flag_en = 'AcceptAndNotify'; break; case '接收不提示': msg_flag_en = 'AcceptNotNotify'; break; case '屏蔽': msg_flag_en = 'Discard'; break; default: msg_flag_en = msg_flag_ch; break; } return msg_flag_en; }; //将空格和换行符转换成HTML标签 this.formatText2Html = function (text) { var html = text; if (html) { html = this.xssFilter(html);//用户昵称或群名称等字段会出现脚本字符串 html = html.replace(/ /g, " "); html = html.replace(/\n/g, "
"); } return html; }; //将HTML标签转换成空格和换行符 this.formatHtml2Text = function (html) { var text = html; if (text) { text = text.replace(/ /g, " "); text = text.replace(//g, "\n"); } return text; }; //获取字符串(UTF-8编码)所占字节数 //参考:http://zh.wikipedia.org/zh-cn/UTF-8 this.getStrBytes = function (str) { if (str == null || str === undefined) return 0; if (typeof str != "string") { return 0; } var total = 0, charCode, i, len; for (i = 0, len = str.length; i < len; i++) { charCode = str.charCodeAt(i); if (charCode <= 0x007f) { total += 1;//字符代码在000000 – 00007F之间的,用一个字节编码 } else if (charCode <= 0x07ff) { total += 2;//000080 – 0007FF之间的字符用两个字节 } else if (charCode <= 0xffff) { total += 3;//000800 – 00D7FF 和 00E000 – 00FFFF之间的用三个字节,注: Unicode在范围 D800-DFFF 中不存在任何字符 } else { total += 4;//010000 – 10FFFF之间的用4个字节 } } return total; }; //防止XSS攻击 this.xssFilter = function (val) { val = val.toString(); val = val.replace(/[<]/g, "<"); val = val.replace(/[>]/g, ">"); val = val.replace(/"/g, """); //val = val.replace(/'/g, "'"); return val; }; //去掉头尾空白符 this.trimStr = function (str) { if (!str) return ''; str = str.toString(); return str.replace(/(^\s*)|(\s*$)/g, ""); }; //判断是否为8位整数 this.validNumber = function (str) { str = str.toString(); return str.match(/(^\d{1,8}$)/g); }; this.getReturnError = function (errorInfo, errorCode) { if (!errorCode) { errorCode = -100; } var error = { 'ActionStatus': ACTION_STATUS.FAIL, 'ErrorCode': errorCode, 'ErrorInfo': errorInfo + "[" + errorCode + "]" }; return error; }; //设置cookie //name 名字 //value 值 //expires 有效期(单位:秒) //path //domain 作用域 this.setCookie = function (name, value, expires, path, domain) { var exp = new Date(); exp.setTime(exp.getTime() + expires * 1000); document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString(); }; //获取cookie this.getCookie = function (name) { var result = document.cookie.match(new RegExp("(^| )" + name + "=([^;]*)(;|$)")); if (result != null) { return unescape(result[2]); } return null; }; //删除cookie this.delCookie = function (name) { var exp = new Date(); exp.setTime(exp.getTime() - 1); var value = this.getCookie(name); if (value != null) document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString(); }; //根据名字获取url参数值 this.getQueryString = function (name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); var r = location.search.substr(1).match(reg); if (r != null) return unescape(r[2]); return null; }; }; //日志对象 var log = new function () { var on = true; this.setOn = function (onFlag) { on = onFlag; }; this.getOn = function () { return on; }; this.error = function (logStr) { try { on && console.error(logStr); } catch (e) { } }; this.warn = function (logStr) { try { on && console.warn(logStr); } catch (e) { } }; this.info = function (logStr) { try { on && console.info(logStr); } catch (e) { } }; this.debug = function (logStr) { try { on && console.debug(logStr); } catch (e) { } }; }; //获取unix时间戳 var unixtime = function (d) { if (!d) d = new Date(); return Math.round(d.getTime() / 1000); }; //时间戳转日期 var fromunixtime = function (t) { return new Date(t * 1000); }; //获取下一个消息序号 var nextSeq = function () { if (curSeq) { curSeq = curSeq + 1; } else { curSeq = Math.round(Math.random() * 10000000); } return curSeq; }; //产生随机数 var createRandom = function () { return Math.round(Math.random() * 4294967296); }; //发起ajax请求 var ajaxRequest = function (meth, url, req, timeout, isLongPolling, cbOk, cbErr) { reqSeq++; reqNum++; // console.log('IM reqNum: ',reqNum); xmlHttpObjMap[xmlHttpObjSeq++] = wx.request({ url : url + "&reqSeq="+reqSeq, data : req, dataType : 'json', method : meth, header: { 'Content-Type': 'application/json' }, success : function(res){ reqNum--; // console.log('IM complete reqNum: ',reqNum); curLongPollingRetErrorCount = curBigGroupLongPollingRetErrorCount = 0; if (cbOk) cbOk(res.data); }, fail : function(res){ reqNum--; // console.log('IM complete reqNum: ',reqNum); setTimeout(function(){ // console.log('IM请求服务器失败:'+reqSeq,url,req,res); var errInfo = "请求服务器失败,请检查你的网络是否正常"; var error = tool.getReturnError(errInfo, -2); //if (!isLongPolling && cbErr) cbErr(error); if (cbErr) cbErr(error); },16); } }); } //发起ajax请求(json格式数据) var ajaxRequestJson = function (meth, url, req, timeout, isLongPolling, cbOk, cbErr) { ajaxRequest(meth, url, JSON.stringify(req), timeout, isLongPolling, function (resp) { var json = null; //if (resp) eval('json=('+resp+');');//将返回的json字符串转换成json对象 //if (resp) json=eval('('+resp+')');//将返回的json字符串转换成json对象 if (resp) json = resp; //将返回的json字符串转换成json对象 if (cbOk) cbOk(json); }, cbErr); } //判断用户是否已登录 var isLogin = function () { return ctx.sdkAppID && ctx.identifier; }; //检查是否登录 var checkLogin = function (cbErr, isNeedCallBack) { if (!isLogin()) { if (isNeedCallBack) { var errInfo = "请登录"; var error = tool.getReturnError(errInfo, -4); if (cbErr) cbErr(error); } return false; } return true; }; //检查是否访问正式环境 var isAccessFormalEnv = function () { return isAccessFormaEnvironment; }; //根据不同的服务名和命令,获取对应的接口地址 var getApiUrl = function (srvName, cmd, cbOk, cbErr) { var srvHost = SRV_HOST; if (isAccessFormalEnv()) { srvHost = SRV_HOST.FORMAL.COMMON; } else { srvHost = SRV_HOST.TEST.COMMON; } //if (srvName == SRV_NAME.RECENT_CONTACT) { // srvHost = SRV_HOST.TEST.COMMON; //} if (srvName == SRV_NAME.PIC) { if (isAccessFormalEnv()) { srvHost = SRV_HOST.FORMAL.PIC; } else { srvHost = SRV_HOST.TEST.PIC; } } var url = srvHost + '/' + SRV_NAME_VER[srvName] + '/' + srvName + '/' + cmd + '?websdkappid=' + SDK.APPID + "&v=" + SDK.VERSION; if (isLogin()) { if (cmd == 'login') { url += '&identifier=' + encodeURIComponent(ctx.identifier) + '&usersig=' + ctx.userSig; } else { if (ctx.tinyid && ctx.a2) { url += '&tinyid=' + ctx.tinyid + '&a2=' + ctx.a2; } else { if (cbErr) { log.error("tinyid或a2为空[" + srvName + "][" + cmd + "]"); cbErr(tool.getReturnError("tinyid或a2为空[" + srvName + "][" + cmd + "]", -5)); return false; } } } url += '&contenttype=' + ctx.contentType; } url += '&sdkappid=' + ctx.sdkAppID + '&accounttype=' + ctx.accountType + '&apn=' + ctx.apn + '&reqtime=' + unixtime(); return url; }; //获取语音下载url var getSoundDownUrl = function (uuid, senderId) { var soundUrl = null; if (authkey && ipList[0]) { soundUrl = "http://" + ipList[0] + "/asn.com/stddownload_common_file?authkey=" + authkey + "&bid=" + DOWNLOAD_FILE.BUSSINESS_ID + "&subbid=" + ctx.sdkAppID + "&fileid=" + uuid + "&filetype=" + DOWNLOAD_FILE_TYPE.SOUND + "&openid=" + senderId + "&ver=0"; } else { log.error("拼接语音下载url不报错:ip或者authkey为空"); } return soundUrl; }; //获取文件下载地址 var getFileDownUrl = function (uuid, senderId, fileName) { var fileUrl = null; if (authkey && ipList[0]) { fileUrl = "http://" + ipList[0] + "/asn.com/stddownload_common_file?authkey=" + authkey + "&bid=" + DOWNLOAD_FILE.BUSSINESS_ID + "&subbid=" + ctx.sdkAppID + "&fileid=" + uuid + "&filetype=" + DOWNLOAD_FILE_TYPE.FILE + "&openid=" + senderId + "&ver=0&filename=" + encodeURIComponent(fileName); } else { log.error("拼接文件下载url不报错:ip或者authkey为空"); } Resources.downloadMap["uuid_"+uuid] = fileUrl; return fileUrl; }; //获取文件下载地址 var getFileDownUrlV2 = function (uuid, senderId, fileName, downFlag, receiverId, busiId, type) { var options = { "From_Account": senderId,//"identifer_0", // 类型: String, 发送者tinyid "To_Account": receiverId,//"identifer_1", // 类型: String, 接收者tinyid "os_platform": 10, // 类型: Number, 终端的类型 1(android) 2(ios) 3(windows) 10(others...) "Timestamp": unixtime().toString(), // 类型: Number, 时间戳 "Random": createRandom().toString(), // 类型: Number, 随机值 "request_info": [ // 类型: Array { "busi_id": busiId, // 类型: Number, 群(1) C2C(2) 其他请联系sdk开发者分配 "download_flag": downFlag, // 类型: Number, 申请下载地址标识 0(申请架平下载地址) 1(申请COS平台下载地址) 2(不需要申请, 直接拿url下载(这里应该不会为2)) "type": type, // 类型: Number, 0(短视频缩略图), 1(文件), 2(短视频), 3(ptt), 其他待分配 "uuid": uuid, // 类型: Number, 唯一标识一个文件的uuid "version": VERSION_INFO.SERVER_VERSION, // 类型: Number, 架平server版本 "auth_key": authkey, // 类型: String, 认证签名 "ip": ipList[0] // 类型: Number, 架平IP } ] }; //获取下载地址 proto_applyDownload(options,function(resp){ if(resp.error_code == 0 && resp.response_info){ Resources.downloadMap["uuid_"+options.uuid] = resp.response_info.url; } if(onAppliedDownloadUrl){ onAppliedDownloadUrl({ uuid : options.uuid, url : resp.response_info.url, maps : Resources.downloadMap }); } }, function(resp){ log.error("获取下载地址失败",options.uuid) }); }; //重置ajax请求 var clearXmlHttpObjMap = function () { //遍历xmlHttpObjMap{} for (var seq in xmlHttpObjMap) { var xmlHttpObj = xmlHttpObjMap[seq]; if (xmlHttpObj) { xmlHttpObj.abort();//中断ajax请求(长轮询) xmlHttpObjMap[xmlHttpObjSeq] = null;//清空 } } xmlHttpObjSeq = 0; xmlHttpObjMap = {}; }; //重置sdk全局变量 var clearSdk = function () { clearXmlHttpObjMap(); //当前登录用户 ctx = { sdkAppID: null, appIDAt3rd: null, accountType: null, identifier: null, identifierNick: null, userSig: null, contentType: 'json', apn: 1 }; opt = {}; curSeq = 0; //ie8,9采用jsonp方法解决ajax跨域限制 jsonpRequestId = 0;//jsonp请求id //最新jsonp请求返回的json数据 jsonpLastRspData = null; apiReportItems = []; MsgManager.clear(); //重置longpollingId LongPollingId = null; }; //登录 var _login = function (loginInfo, listeners, options, cbOk, cbErr) { clearSdk(); if (options) opt = options; if (opt.isAccessFormalEnv == false) { log.error("请切换为正式环境"); isAccessFormaEnvironment = opt.isAccessFormalEnv; } if (opt.isLogOn == false) { log.setOn(opt.isLogOn); } /* if(opt.emotions){ emotions=opt.emotions; webim.Emotions= emotions; } if(opt.emotionDataIndexs){ emotionDataIndexs=opt.emotionDataIndexs; webim.EmotionDataIndexs= emotionDataIndexs; }*/ if (!loginInfo) { if (cbErr) { cbErr(tool.getReturnError("loginInfo is empty", -6)); return; } } if (!loginInfo.sdkAppID) { if (cbErr) { cbErr(tool.getReturnError("loginInfo.sdkAppID is empty", -7)); return; } } if (!loginInfo.accountType) { if (cbErr) { cbErr(tool.getReturnError("loginInfo.accountType is empty", -8)); return; } } if (loginInfo.identifier) { ctx.identifier = loginInfo.identifier.toString(); } if (loginInfo.identifier && !loginInfo.userSig) { if (cbErr) { cbErr(tool.getReturnError("loginInfo.userSig is empty", -9)); return; } } if (loginInfo.userSig) { ctx.userSig = loginInfo.userSig.toString(); } ctx.sdkAppID = loginInfo.sdkAppID; ctx.accountType = loginInfo.accountType; if (ctx.identifier && ctx.userSig) {//带登录态 //登录 proto_login( function(identifierNick, headurl) { MsgManager.init( listeners, function (mmInitResp) { if (cbOk) { mmInitResp.identifierNick = identifierNick; mmInitResp.headurl = headurl; cbOk(mmInitResp); } }, cbErr ); }, cbErr ); } else {//不带登录态,进入直播场景sdk MsgManager.init( listeners, cbOk, cbErr ); } }; //初始化浏览器信息 var initBrowserInfo = function () { //初始化浏览器类型 BROWSER_INFO = "wechat" }; //接口质量上报 var reportApiQuality = function (cmd, errorCode, errorInfo) { if (cmd == 'longpolling' && (errorCode == longPollingTimeOutErrorCode || errorCode == longPollingKickedErrorCode)) {//longpolling 返回60008错误可以视为正常,可以不上报 return; } var eventId = CMD_EVENT_ID_MAP[cmd]; if (eventId) { var reportTime = unixtime(); var uniqKey = null; var msgCmdErrorCode = { 'Code': errorCode, 'ErrMsg': errorInfo }; if (ctx.a2) { uniqKey = ctx.a2.substring(0, 10) + "_" + reportTime + "_" + createRandom(); } else if (ctx.userSig) { uniqKey = ctx.userSig.substring(0, 10) + "_" + reportTime + "_" + createRandom(); } if (uniqKey) { var rptEvtItem = { "UniqKey": uniqKey, "EventId": eventId, "ReportTime": reportTime, "MsgCmdErrorCode": msgCmdErrorCode }; if (cmd == 'login') { var loginApiReportItems = []; loginApiReportItems.push(rptEvtItem); var loginReportOpt = { "EvtItems": loginApiReportItems, "MainVersion": SDK.VERSION, "Version": "0" }; proto_reportApiQuality(loginReportOpt, function (resp) { loginApiReportItems = null;// }, function (err) { loginApiReportItems = null;// } ); } else { apiReportItems.push(rptEvtItem); if (apiReportItems.length >= maxApiReportItemCount) {//累计一定条数再上报 var reportOpt = { "EvtItems": apiReportItems, "MainVersion": SDK.VERSION, "Version": "0" }; proto_reportApiQuality(reportOpt, function (resp) { apiReportItems = [];//清空 }, function (err) { apiReportItems = [];//清空 } ); } } } } }; // REST API calls //上线 var proto_login = function (cbOk, cbErr) { ConnManager.apiCall(SRV_NAME.OPEN_IM, "login", {"State": "Online"}, function (loginResp) { if (loginResp.TinyId) { ctx.tinyid = loginResp.TinyId; } else { if (cbErr) { cbErr(tool.getReturnError("TinyId is empty", -10)); return; } } if (loginResp.A2Key) { ctx.a2 = loginResp.A2Key; } else { if (cbErr) { cbErr(tool.getReturnError("A2Key is empty", -11)); return; } } var tag_list = [ "Tag_Profile_IM_Nick", "Tag_Profile_IM_Image" ]; var options = { 'From_Account': ctx.identifier, 'To_Account': [ctx.identifier], 'LastStandardSequence': 0, 'TagList': tag_list }; proto_getProfilePortrait( options, function (resp) { var nick, gender, allowType, image; if (resp.UserProfileItem && resp.UserProfileItem.length > 0) { for (var i in resp.UserProfileItem) { for (var j in resp.UserProfileItem[i].ProfileItem) { switch (resp.UserProfileItem[i].ProfileItem[j].Tag) { case 'Tag_Profile_IM_Nick': nick = resp.UserProfileItem[i].ProfileItem[j].Value; if (nick) ctx.identifierNick = nick; break; case 'Tag_Profile_IM_Image': image = resp.UserProfileItem[i].ProfileItem[j].Value; if (image) ctx.headurl = image; break; } } } } if (cbOk) cbOk(ctx.identifierNick, ctx.headurl); //回传当前用户昵称 }, cbErr); } , cbErr); }; //下线 var proto_logout = function (type , cbOk, cbErr) { if (!checkLogin(cbErr, false)) {//不带登录态 clearSdk(); if (cbOk) cbOk({ 'ActionStatus': ACTION_STATUS.OK, 'ErrorCode': 0, 'ErrorInfo': 'logout success' }); return; } if(type == "all"){ ConnManager.apiCall(SRV_NAME.OPEN_IM, "logout", {}, function (resp) { clearSdk(); if (cbOk) cbOk(resp); }, cbErr); }else{ ConnManager.apiCall(SRV_NAME.OPEN_IM, "longpollinglogout", { LongPollingId : LongPollingId }, function (resp) { clearSdk(); if (cbOk) cbOk(resp); }, cbErr); } }; //发送消息,包括私聊和群聊 var proto_sendMsg = function (msg, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; var msgInfo = null; switch (msg.sess.type()) { case SESSION_TYPE.C2C: msgInfo = { 'From_Account': ctx.identifier, 'To_Account': msg.sess.id().toString(), 'MsgTimeStamp': msg.time, 'MsgSeq': msg.seq, 'MsgRandom': msg.random, 'MsgBody': [] }; break; case SESSION_TYPE.GROUP: var subType = msg.getSubType(); msgInfo = { 'GroupId': msg.sess.id().toString(), 'From_Account': ctx.identifier, 'Random': msg.random, 'MsgBody': [] }; switch (subType) { case GROUP_MSG_SUB_TYPE.COMMON: msgInfo.MsgPriority = "COMMON"; break; case GROUP_MSG_SUB_TYPE.REDPACKET: msgInfo.MsgPriority = "REDPACKET"; break; case GROUP_MSG_SUB_TYPE.LOVEMSG: msgInfo.MsgPriority = "LOVEMSG"; break; case GROUP_MSG_SUB_TYPE.TIP: log.error("不能主动发送群提示消息,subType=" + subType); break; default: log.error("发送群消息时,出现未知子消息类型:subType=" + subType); return; break; } break; default: break; } for (var i in msg.elems) { var elem = msg.elems[i]; var msgContent = null; var msgType = elem.type; switch (msgType) { case MSG_ELEMENT_TYPE.TEXT://文本 msgContent = {'Text': elem.content.text}; break; case MSG_ELEMENT_TYPE.FACE://表情 msgContent = {'Index': elem.content.index, 'Data': elem.content.data}; break; case MSG_ELEMENT_TYPE.IMAGE://图片 var ImageInfoArray = []; for (var j in elem.content.ImageInfoArray) { ImageInfoArray.push( { 'Type': elem.content.ImageInfoArray[j].type, 'Size': elem.content.ImageInfoArray[j].size, 'Width': elem.content.ImageInfoArray[j].width, 'Height': elem.content.ImageInfoArray[j].height, 'URL': elem.content.ImageInfoArray[j].url } ); } msgContent = {'UUID': elem.content.UUID, 'ImageInfoArray': ImageInfoArray}; break; case MSG_ELEMENT_TYPE.SOUND:// log.warn('web端暂不支持发送语音消息'); continue; break; case MSG_ELEMENT_TYPE.LOCATION:// log.warn('web端暂不支持发送地理位置消息'); continue; break; case MSG_ELEMENT_TYPE.FILE:// msgContent = { 'UUID': elem.content.uuid, 'FileName': elem.content.name, 'FileSize': elem.content.size, 'DownloadFlag' : elem.content.downFlag }; break; case MSG_ELEMENT_TYPE.CUSTOM:// msgContent = {'Data': elem.content.data, 'Desc': elem.content.desc, 'Ext': elem.content.ext}; msgType = MSG_ELEMENT_TYPE.CUSTOM; break; default : log.warn('web端暂不支持发送' + elem.type + '消息'); continue; break; } if (msg.PushInfoBoolean) { msgInfo.OfflinePushInfo = msg.PushInfo; //当android终端进程被杀掉时才走push,IOS退到后台即可 } msgInfo.MsgBody.push({ 'MsgType': msgType, 'MsgContent': msgContent }); } if (msg.sess.type() == SESSION_TYPE.C2C) {//私聊 ConnManager.apiCall(SRV_NAME.OPEN_IM, "sendmsg", msgInfo, cbOk, cbErr); } else if (msg.sess.type() == SESSION_TYPE.GROUP) {//群聊 ConnManager.apiCall(SRV_NAME.GROUP, "send_group_msg", msgInfo, cbOk, cbErr); } }; //长轮询接口 var proto_longPolling = function (options, cbOk, cbErr) { if(!isAccessFormaEnvironment && typeof stopPolling !="undefined" && stopPolling == true){ return; } if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.OPEN_IM, "longpolling", options, cbOk, cbErr, longPollingDefaultTimeOut, true); }; //长轮询接口(拉取直播聊天室新消息) var proto_bigGroupLongPolling = function (options, cbOk, cbErr, timeout) { ConnManager.apiCall(SRV_NAME.BIG_GROUP_LONG_POLLING, "get_msg", options, cbOk, cbErr, timeout); }; //拉取未读c2c消息接口 var proto_getMsgs = function (cookie, syncFlag, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.OPEN_IM, "getmsg", {'Cookie': cookie, 'SyncFlag': syncFlag}, function (resp) { if (resp.MsgList && resp.MsgList.length) { for (var i in resp.MsgList) { tempC2CMsgList.push(resp.MsgList[i]); } } if (resp.SyncFlag == 1) { proto_getMsgs(resp.Cookie, resp.SyncFlag, cbOk, cbErr); } else { resp.MsgList = tempC2CMsgList; tempC2CMsgList = []; if (cbOk) cbOk(resp); } }, cbErr); }; //C2C消息已读上报接口 var proto_c2CMsgReaded = function (cookie, c2CMsgReadedItem, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; var tmpC2CMsgReadedItem = []; for (var i in c2CMsgReadedItem) { var item = { 'To_Account': c2CMsgReadedItem[i].toAccount, 'LastedMsgTime': c2CMsgReadedItem[i].lastedMsgTime }; tmpC2CMsgReadedItem.push(item); } ConnManager.apiCall(SRV_NAME.OPEN_IM, "msgreaded", { C2CMsgReaded: { 'Cookie': cookie, 'C2CMsgReadedItem': tmpC2CMsgReadedItem } }, cbOk, cbErr); }; //删除c2c消息 var proto_deleteC2CMsg = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.OPEN_IM, "deletemsg", options, cbOk, cbErr); }; //拉取c2c历史消息接口 var proto_getC2CHistoryMsgs = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.OPEN_IM, "getroammsg", options, function (resp) { var reqMsgCount = options.MaxCnt; var complete = resp.Complete; var rspMsgCount = resp.MaxCnt; var msgKey = resp.MsgKey; var lastMsgTime = resp.LastMsgTime; if (resp.MsgList && resp.MsgList.length) { for (var i in resp.MsgList) { tempC2CHistoryMsgList.push(resp.MsgList[i]); } } var netxOptions = null; if (complete == 0) {//还有历史消息可拉取 if (rspMsgCount < reqMsgCount) { netxOptions = { 'Peer_Account': options.Peer_Account, 'MaxCnt': reqMsgCount - rspMsgCount, 'LastMsgTime': lastMsgTime, 'MsgKey': msgKey }; } } if (netxOptions) {//继续拉取 proto_getC2CHistoryMsgs(netxOptions, cbOk, cbErr); } else { resp.MsgList = tempC2CHistoryMsgList; tempC2CHistoryMsgList = []; if (cbOk) cbOk(resp); } }, cbErr); }; //群组接口 //创建群组 //协议参考:https://www.qcloud.com/doc/product/269/1615 var proto_createGroup = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; var opt = { //必填 群组形态,包括Public(公开群),Private(私密群),ChatRoom(聊天室),AVChatRoom(互动直播聊天室)。 'Type': options.Type, //必填 群名称,最长30字节。 'Name': options.Name }; var member_list = []; //Array 选填 初始群成员列表,最多500个。成员信息字段详情参见:群成员资料。 for (var i = 0; i < options.MemberList.length; i++) { member_list.push({'Member_Account': options.MemberList[i]}) } opt.MemberList = member_list; //选填 为了使得群组ID更加简单,便于记忆传播,腾讯云支持APP在通过REST API创建群组时自定义群组ID。详情参见:自定义群组ID。 if (options.GroupId) { opt.GroupId = options.GroupId; } //选填 群主id,自动添加到群成员中。如果不填,群没有群主。 if (options.Owner_Account) { opt.Owner_Account = options.Owner_Account; } //选填 群简介,最长240字节。 if (options.Introduction) { opt.Introduction = options.Introduction; } //选填 群公告,最长300字节。 if (options.Notification) { opt.Notification = options.Notification; } //选填 最大群成员数量,最大为10000,不填默认为2000个。 if (options.MaxMemberCount) { opt.MaxMemberCount = options.MaxMemberCount; } //选填 申请加群处理方式。包含FreeAccess(自由加入),NeedPermission(需要验证),DisableApply(禁止加群),不填默认为NeedPermission(需要验证)。 if (options.ApplyJoinOption) {// opt.ApplyJoinOption = options.ApplyJoinOption; } //Array 选填 群组维度的自定义字段,默认情况是没有的,需要开通,详情参见:自定义字段。 if (options.AppDefinedData) { opt.AppDefinedData = options.AppDefinedData; } //选填 群头像URL,最长100字节。 if (options.FaceUrl) { opt.FaceUrl = options.FaceUrl; } ConnManager.apiCall(SRV_NAME.GROUP, "create_group", opt, cbOk, cbErr); }; //创建群组-高级接口 //协议参考:https://www.qcloud.com/doc/product/269/1615 var proto_createGroupHigh = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "create_group", options, cbOk, cbErr); }; //修改群组基本资料 //协议参考:https://www.qcloud.com/doc/product/269/1620 var proto_modifyGroupBaseInfo = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "modify_group_base_info", options, cbOk, cbErr); }; //申请加群 var proto_applyJoinGroup = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "apply_join_group", { 'GroupId': options.GroupId, 'ApplyMsg': options.ApplyMsg, 'UserDefinedField': options.UserDefinedField }, cbOk, cbErr); }; //申请加入大群 var proto_applyJoinBigGroup = function (options, cbOk, cbErr) { var srvName; if (!checkLogin(cbErr, false)) {//未登录 srvName = SRV_NAME.BIG_GROUP; } else {//已登录 srvName = SRV_NAME.GROUP; } ConnManager.apiCall(srvName, "apply_join_group", { 'GroupId': options.GroupId, 'ApplyMsg': options.ApplyMsg, 'UserDefinedField': options.UserDefinedField }, function (resp) { if (resp.JoinedStatus && resp.JoinedStatus == 'JoinedSuccess') { if (resp.LongPollingKey) { MsgManager.resetBigGroupLongPollingInfo(); MsgManager.setBigGroupLongPollingOn(true);//开启长轮询 MsgManager.setBigGroupLongPollingKey(resp.LongPollingKey);//更新大群长轮询key MsgManager.setBigGroupLongPollingMsgMap(options.GroupId, 0);//收到的群消息置0 MsgManager.bigGroupLongPolling();//开启长轮询 } else {//没有返回LongPollingKey,说明申请加的群不是直播聊天室(AVChatRoom) cbErr && cbErr(tool.getReturnError("The type of group is not AVChatRoom: groupid=" + options.GroupId, -12)); return; } } if (cbOk) cbOk(resp); } , function (err) { if (cbErr) cbErr(err); }); }; //处理加群申请(同意或拒绝) var proto_handleApplyJoinGroupPendency = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "handle_apply_join_group", { 'GroupId': options.GroupId, 'Applicant_Account': options.Applicant_Account, 'HandleMsg': options.HandleMsg, 'Authentication': options.Authentication, 'MsgKey': options.MsgKey, 'ApprovalMsg': options.ApprovalMsg, 'UserDefinedField': options.UserDefinedField }, cbOk, function (err) { if (err.ErrorCode == 10024) {//apply has be handled if (cbOk) { var resp = { 'ActionStatus': ACTION_STATUS.OK, 'ErrorCode': 0, 'ErrorInfo': '该申请已经被处理过' }; cbOk(resp); } } else { if (cbErr) cbErr(err); } } ); }; //主动退群 var proto_quitGroup = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "quit_group", { 'GroupId': options.GroupId }, cbOk, cbErr); }; //退出大群 var proto_quitBigGroup = function (options, cbOk, cbErr) { var srvName; if (!checkLogin(cbErr, false)) {//未登录 srvName = SRV_NAME.BIG_GROUP; } else {//已登录 srvName = SRV_NAME.GROUP; } MsgManager.resetBigGroupLongPollingInfo(); ConnManager.apiCall(srvName, "quit_group", {'GroupId': options.GroupId}, function (resp) { //重置当前再请求中的ajax //clearXmlHttpObjMap(); //退出大群成功之后需要重置长轮询信息 MsgManager.resetBigGroupLongPollingInfo(); if (cbOk) cbOk(resp); }, cbErr); }; //查找群(按名称) var proto_searchGroupByName = function (options, cbOk, cbErr) { ConnManager.apiCall(SRV_NAME.GROUP, "search_group", options, cbOk, cbErr); }; //获取群组公开资料 var proto_getGroupPublicInfo = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "get_group_public_info", { 'GroupIdList': options.GroupIdList, 'ResponseFilter': { 'GroupBasePublicInfoFilter': options.GroupBasePublicInfoFilter } }, function (resp) { resp.ErrorInfo = ''; if (resp.GroupInfo) { for (var i in resp.GroupInfo) { var errorCode = resp.GroupInfo[i].ErrorCode; if (errorCode > 0) { resp.ActionStatus = ACTION_STATUS.FAIL; resp.GroupInfo[i].ErrorInfo = "[" + errorCode + "]" + resp.GroupInfo[i].ErrorInfo; resp.ErrorInfo += resp.GroupInfo[i].ErrorInfo + '\n'; } } } if (resp.ActionStatus == ACTION_STATUS.FAIL) { if (cbErr) { cbErr(resp); } } else if (cbOk) { cbOk(resp); } }, cbErr); }; //获取群组详细资料--高级 //请求协议参考:https://www.qcloud.com/doc/product/269/1616 var proto_getGroupInfo = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; var opt = { 'GroupIdList': options.GroupIdList, 'ResponseFilter': { 'GroupBaseInfoFilter': options.GroupBaseInfoFilter, 'MemberInfoFilter': options.MemberInfoFilter } }; if (options.AppDefinedDataFilter_Group) { opt.ResponseFilter.AppDefinedDataFilter_Group = options.AppDefinedDataFilter_Group; } if (options.AppDefinedDataFilter_GroupMember) { opt.ResponseFilter.AppDefinedDataFilter_GroupMember = options.AppDefinedDataFilter_GroupMember; } ConnManager.apiCall(SRV_NAME.GROUP, "get_group_info", opt, cbOk, cbErr); }; //获取群组成员-高级接口 //协议参考:https://www.qcloud.com/doc/product/269/1617 var proto_getGroupMemberInfo = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "get_group_member_info", { 'GroupId': options.GroupId, 'Offset': options.Offset, 'Limit': options.Limit, 'MemberInfoFilter': options.MemberInfoFilter, 'MemberRoleFilter': options.MemberRoleFilter, 'AppDefinedDataFilter_GroupMember': options.AppDefinedDataFilter_GroupMember }, cbOk, cbErr); }; //增加群组成员 //协议参考:https://www.qcloud.com/doc/product/269/1621 var proto_addGroupMember = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "add_group_member", { 'GroupId': options.GroupId, 'Silence': options.Silence, 'MemberList': options.MemberList }, cbOk, cbErr); }; //修改群组成员资料 //协议参考:https://www.qcloud.com/doc/product/269/1623 var proto_modifyGroupMember = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; var opt = {}; if (options.GroupId) { opt.GroupId = options.GroupId; } if (options.Member_Account) { opt.Member_Account = options.Member_Account; } //Admin或者Member if (options.Role) { opt.Role = options.Role; } // AcceptAndNotify代表解收并提示消息,Discard代表不接收也不提示消息,AcceptNotNotify代表接收消息但不提示 if (options.MsgFlag) { opt.MsgFlag = options.MsgFlag; } if (options.ShutUpTime) {//禁言时间 opt.ShutUpTime = options.ShutUpTime; } if (options.NameCard) {//群名片,最大不超过50个字节 opt.NameCard = options.NameCard; } if (options.AppMemberDefinedData) {//群成员维度的自定义字段,默认情况是没有的,需要开通 opt.AppMemberDefinedData = options.AppMemberDefinedData; } ConnManager.apiCall(SRV_NAME.GROUP, "modify_group_member_info", opt, cbOk, cbErr); }; //删除群组成员 //协议参考:https://www.qcloud.com/doc/product/269/1622 var proto_deleteGroupMember = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "delete_group_member", { 'GroupId': options.GroupId, 'Silence': options.Silence, 'MemberToDel_Account': options.MemberToDel_Account, 'Reason': options.Reason }, cbOk, cbErr); }; //解散群组 //协议参考:https://www.qcloud.com/doc/product/269/1624 var proto_destroyGroup = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "destroy_group", { 'GroupId': options.GroupId }, cbOk, cbErr); }; //转让群组 //协议参考:https://www.qcloud.com/doc/product/269/1633 var proto_changeGroupOwner = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "change_group_owner", options, cbOk, cbErr); }; //获取用户所加入的群组-高级接口 //协议参考:https://www.qcloud.com/doc/product/269/1625 var proto_getJoinedGroupListHigh = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "get_joined_group_list", { 'Member_Account': options.Member_Account, 'Limit': options.Limit, 'Offset': options.Offset, 'GroupType': options.GroupType, 'ResponseFilter': { 'GroupBaseInfoFilter': options.GroupBaseInfoFilter, 'SelfInfoFilter': options.SelfInfoFilter } }, cbOk, cbErr); }; //查询一组UserId在群中的身份 //协议参考:https://www.qcloud.com/doc/product/269/1626 var proto_getRoleInGroup = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "get_role_in_group", { 'GroupId': options.GroupId, 'User_Account': options.User_Account }, cbOk, cbErr); }; //设置取消成员禁言时间 //协议参考:https://www.qcloud.com/doc/product/269/1627 var proto_forbidSendMsg = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "forbid_send_msg", { 'GroupId': options.GroupId, 'Members_Account': options.Members_Account, 'ShutUpTime': options.ShutUpTime//单位为秒,为0时表示取消禁言 }, cbOk, cbErr); }; //发送自定义群系统通知 var proto_sendCustomGroupNotify = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "send_group_system_notification", options, cbOk, cbErr); }; //拉取群消息接口 var proto_getGroupMsgs = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "group_msg_get", { "GroupId": options.GroupId, "ReqMsgSeq": options.ReqMsgSeq, "ReqMsgNumber": options.ReqMsgNumber }, cbOk, cbErr); }; //群消息已读上报接口 var proto_groupMsgReaded = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.GROUP, "msg_read_report", { 'GroupId': options.GroupId, 'MsgReadedSeq': options.MsgReadedSeq }, cbOk, cbErr); }; //end //好友接口 //处理好友接口返回的错误码 var convertErrorEn2ZhFriend = function (resp) { var errorAccount = []; if (resp.Fail_Account && resp.Fail_Account.length) { errorAccount = resp.Fail_Account; } if (resp.Invalid_Account && resp.Invalid_Account.length) { for (var k in resp.Invalid_Account) { errorAccount.push(resp.Invalid_Account[k]); } } if (errorAccount.length) { resp.ActionStatus = ACTION_STATUS.FAIL; resp.ErrorCode = ERROR_CODE_CUSTOM; resp.ErrorInfo = ''; for (var i in errorAccount) { var failCount = errorAccount[i]; for (var j in resp.ResultItem) { if (resp.ResultItem[j].To_Account == failCount) { var resultCode = resp.ResultItem[j].ResultCode; resp.ResultItem[j].ResultInfo = "[" + resultCode + "]" + resp.ResultItem[j].ResultInfo; resp.ErrorInfo += resp.ResultItem[j].ResultInfo + "\n"; break; } } } } return resp; }; //添加好友 var proto_applyAddFriend = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.FRIEND, "friend_add", { 'From_Account': ctx.identifier, 'AddFriendItem': options.AddFriendItem }, function (resp) { var newResp = convertErrorEn2ZhFriend(resp); if (newResp.ActionStatus == ACTION_STATUS.FAIL) { if (cbErr) cbErr(newResp); } else if (cbOk) { cbOk(newResp); } }, cbErr); }; //删除好友 var proto_deleteFriend = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.FRIEND, "friend_delete", { 'From_Account': ctx.identifier, 'To_Account': options.To_Account, 'DeleteType': options.DeleteType }, function (resp) { var newResp = convertErrorEn2ZhFriend(resp); if (newResp.ActionStatus == ACTION_STATUS.FAIL) { if (cbErr) cbErr(newResp); } else if (cbOk) { cbOk(newResp); } }, cbErr); }; //获取好友申请 var proto_getPendency = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.FRIEND, "pendency_get", { "From_Account": ctx.identifier, "PendencyType": options.PendencyType, "StartTime": options.StartTime, "MaxLimited": options.MaxLimited, "LastSequence": options.LastSequence }, cbOk, cbErr); }; //删除好友申请 var proto_deletePendency = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.FRIEND, "pendency_delete", { "From_Account": ctx.identifier, "PendencyType": options.PendencyType, "To_Account": options.To_Account }, function (resp) { var newResp = convertErrorEn2ZhFriend(resp); if (newResp.ActionStatus == ACTION_STATUS.FAIL) { if (cbErr) cbErr(newResp); } else if (cbOk) { cbOk(newResp); } }, cbErr); }; //处理好友申请 var proto_responseFriend = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.FRIEND, "friend_response", { 'From_Account': ctx.identifier, 'ResponseFriendItem': options.ResponseFriendItem }, function (resp) { var newResp = convertErrorEn2ZhFriend(resp); if (newResp.ActionStatus == ACTION_STATUS.FAIL) { if (cbErr) cbErr(newResp); } else if (cbOk) { cbOk(newResp); } }, cbErr); }; //我的好友 var proto_getAllFriend = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.FRIEND, "friend_get_all", { 'From_Account': ctx.identifier, 'TimeStamp': options.TimeStamp, 'StartIndex': options.StartIndex, 'GetCount': options.GetCount, 'LastStandardSequence': options.LastStandardSequence, 'TagList': options.TagList }, cbOk, cbErr); }; //资料接口 //查看个人资料 var proto_getProfilePortrait = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.PROFILE, "portrait_get", { 'From_Account': ctx.identifier, 'To_Account': options.To_Account, //'LastStandardSequence':options.LastStandardSequence, 'TagList': options.TagList }, function (resp) { var errorAccount = []; if (resp.Fail_Account && resp.Fail_Account.length) { errorAccount = resp.Fail_Account; } if (resp.Invalid_Account && resp.Invalid_Account.length) { for (var k in resp.Invalid_Account) { errorAccount.push(resp.Invalid_Account[k]); } } if (errorAccount.length) { resp.ActionStatus = ACTION_STATUS.FAIL; resp.ErrorCode = ERROR_CODE_CUSTOM; resp.ErrorInfo = ''; for (var i in errorAccount) { var failCount = errorAccount[i]; for (var j in resp.UserProfileItem) { if (resp.UserProfileItem[j].To_Account == failCount) { var resultCode = resp.UserProfileItem[j].ResultCode; resp.UserProfileItem[j].ResultInfo = "[" + resultCode + "]" + resp.UserProfileItem[j].ResultInfo; resp.ErrorInfo += "账号:" + failCount + "," + resp.UserProfileItem[j].ResultInfo + "\n"; break; } } } } if (resp.ActionStatus == ACTION_STATUS.FAIL) { if (cbErr) cbErr(resp); } else if (cbOk) { cbOk(resp); } }, cbErr); }; //设置个人资料 var proto_setProfilePortrait = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.PROFILE, "portrait_set", { 'From_Account': ctx.identifier, 'ProfileItem': options.ProfileItem }, function (resp) { for (var i in options.ProfileItem) { var profile = options.ProfileItem[i]; if (profile.Tag == 'Tag_Profile_IM_Nick') { ctx.identifierNick = profile.Value;//更新昵称 break; } } if (cbOk) cbOk(resp); } , cbErr); }; //增加黑名单 var proto_addBlackList = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.FRIEND, "black_list_add", { 'From_Account': ctx.identifier, 'To_Account': options.To_Account }, function (resp) { var newResp = convertErrorEn2ZhFriend(resp); if (newResp.ActionStatus == ACTION_STATUS.FAIL) { if (cbErr) cbErr(newResp); } else if (cbOk) { cbOk(newResp); } }, cbErr); }; //删除黑名单 var proto_deleteBlackList = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.FRIEND, "black_list_delete", { 'From_Account': ctx.identifier, 'To_Account': options.To_Account }, function (resp) { var newResp = convertErrorEn2ZhFriend(resp); if (newResp.ActionStatus == ACTION_STATUS.FAIL) { if (cbErr) cbErr(newResp); } else if (cbOk) { cbOk(newResp); } }, cbErr); }; //我的黑名单 var proto_getBlackList = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.FRIEND, "black_list_get", { 'From_Account': ctx.identifier, 'StartIndex': options.StartIndex, 'MaxLimited': options.MaxLimited, 'LastSequence': options.LastSequence }, cbOk, cbErr); }; //获取最近联系人 var proto_getRecentContactList = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.RECENT_CONTACT, "get", { 'From_Account': ctx.identifier, 'Count': options.Count }, cbOk, cbErr); }; //上传图片或文件 var proto_uploadPic = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; var cmdName; if (isAccessFormalEnv()) { cmdName = 'pic_up'; } else { cmdName = 'pic_up_test'; } ConnManager.apiCall(SRV_NAME.PIC, cmdName, { 'App_Version': VERSION_INFO.APP_VERSION, 'From_Account': ctx.identifier, 'To_Account': options.To_Account, 'Seq': options.Seq, 'Timestamp': options.Timestamp, 'Random': options.Random, 'File_Str_Md5': options.File_Str_Md5, 'File_Size': options.File_Size, 'File_Type': options.File_Type, 'Server_Ver': VERSION_INFO.SERVER_VERSION, 'Auth_Key': authkey, 'Busi_Id': options.Busi_Id, 'PkgFlag': options.PkgFlag, 'Slice_Offset': options.Slice_Offset, 'Slice_Size': options.Slice_Size, 'Slice_Data': options.Slice_Data }, cbOk, cbErr); }; //获取语音和文件下载IP和authkey var proto_getIpAndAuthkey = function (cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.OPEN_IM, "authkey", {}, cbOk, cbErr); }; //接口质量上报 var proto_reportApiQuality = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall(SRV_NAME.IM_OPEN_STAT, "web_report", options, cbOk, cbErr); }; var proto_getLongPollingId = function (options, cbOk, cbErr) { if (!checkLogin(cbErr, true)) return; ConnManager.apiCall( SRV_NAME.OPEN_IM, "getlongpollingid",{}, function (resp) { cbOk && cbOk(resp); }, cbErr); } var proto_applyDownload = function (options, cbOk, cbErr) { //把下载地址push到map中 ConnManager.apiCall( SRV_NAME.PIC, "apply_download", options, cbOk, cbErr); } //end initBrowserInfo(); // singleton object ConnManager var ConnManager = lowerBR == false ? new function () { var onConnCallback = null; //回调函数 this.init = function (onConnNotify, cbOk, cbErr) { if (onConnNotify) onConnCallback = onConnNotify; }; this.callBack = function (info) { if (onConnCallback) onConnCallback(info); }; this.clear = function () { onConnCallback = null; }; //请求后台服务接口 this.apiCall = function (type, cmd, data, cbOk, cbErr, timeout, isLongPolling) { //封装后台服务接口地址 var url = getApiUrl(type, cmd, cbOk, cbErr); if (url == false) return; //发起ajax请求 ajaxRequestJson("POST", url, data, timeout, isLongPolling, function (resp) { var errorCode = null, tempErrorInfo = ''; if(cmd=='pic_up'){ data.Slice_Data=''; } var info = "\n request url: \n" + url + "\n request body: \n" + JSON.stringify(data) + "\n response: \n" + JSON.stringify(resp); //成功 if (resp.ActionStatus == ACTION_STATUS.OK) { log.info("[" + type + "][" + cmd + "]success: " + info); if (cbOk) cbOk(resp);//回调 errorCode = 0; tempErrorInfo = ''; } else { errorCode = resp.ErrorCode; tempErrorInfo = resp.ErrorInfo; //报错 if (cbErr) { resp.SrcErrorInfo = resp.ErrorInfo; resp.ErrorInfo = "[" + type + "][" + cmd + "]failed: " + info; if (cmd != 'longpolling' || resp.ErrorCode != longPollingTimeOutErrorCode) { log.error(resp.ErrorInfo); } cbErr(resp); } } reportApiQuality(cmd, errorCode, tempErrorInfo);//接口质量上报 }, function (err) { cbErr && cbErr(err); reportApiQuality(cmd, err.ErrorCode, err.ErrorInfo);//接口质量上报 }); }; } : new function () { var onConnCallback = null; //回调函数 this.init = function (onConnNotify, cbOk, cbErr) { if (onConnNotify) onConnCallback = onConnNotify; }; this.callBack = function (info) { if (onConnCallback) onConnCallback(info); }; this.clear = function () { onConnCallback = null; }; //请求后台服务接口 this.apiCall = function (type, cmd, data, cbOk, cbErr, timeout, isLongPolling) { //封装后台服务接口地址 var url = getApiUrl(type, cmd, cbOk, cbErr); if (url == false) return; //发起jsonp请求 var reqId = jsonpRequestId++, cbkey = 'jsonpcallback', // the 'callback' key cbval = 'jsonpRequest' + reqId, // the 'callback' value script = document.createElement('script'), loaded = 0; window[cbval] = jsonpCallback; script.type = 'text/javascript'; url = url + "&" + cbkey + "=" + cbval + "&jsonpbody=" + encodeURIComponent(JSON.stringify(data)); script.src = url; script.async = true; if (typeof script.onreadystatechange !== 'undefined') { // need this for IE due to out-of-order onreadystatechange(), binding script // execution to an event listener gives us control over when the script // is executed. See http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html script.event = 'onclick'; script.htmlFor = script.id = '_jsonpRequest_' + reqId; } script.onload = script.onreadystatechange = function () { if ((this.readyState && this.readyState !== 'complete' && this.readyState !== 'loaded') || loaded) { return false; } script.onload = script.onreadystatechange = null; script.onclick && script.onclick(); // Call the user callback with the last value stored and clean up values and scripts. var resp = jsonpLastRspData; var info = "\n request url: \n" + url + "\n request body: \n" + JSON.stringify(data) + "\n response: \n" + JSON.stringify(resp); if (resp.ActionStatus == ACTION_STATUS.OK) { log.info("[" + type + "][" + cmd + "]success: " + info); cbOk && cbOk(resp); } else { resp.ErrorInfo = "[" + type + "][" + cmd + "]failed " + info; if (cmd != 'longpolling' || resp.ErrorCode != longPollingTimeOutErrorCode) { log.error(resp.ErrorInfo); } else { log.warn("[" + type + "][" + cmd + "]success: " + info); } cbErr && cbErr(resp); } jsonpLastRspData = undefined; document.body.removeChild(script); loaded = 1; }; // Add the script to the DOM head document.body.appendChild(script); }; }; // class Session var Session = function (type, id, name, icon, time, seq) { this._impl = { skey: Session.skey(type, id), type: type, id: id, name: name, icon: icon, unread: 0,//未读消息数 isAutoRead: false, time: time >= 0 ? time : 0, curMaxMsgSeq: seq >= 0 ? seq : 0, msgs: [], isFinished : 1 }; }; Session.skey = function (type, id) { return type + id; }; Session.prototype.type = function () { return this._impl.type; }; Session.prototype.id = function () { return this._impl.id; }; Session.prototype.name = function () { return this._impl.name; }; Session.prototype.icon = function () { return this._impl.icon; }; Session.prototype.unread = function (val) { if(typeof val !== 'undefined'){ this._impl.unread = val; }else{ return this._impl.unread; } }; Session.prototype.isFinished = function (val) { if(typeof val !== 'undefined'){ this._impl.isFinished = val; }else{ return this._impl.isFinished; } }; Session.prototype.time = function () { return this._impl.time; }; Session.prototype.curMaxMsgSeq = function (seq) { if(typeof seq !== 'undefined'){ this._impl.curMaxMsgSeq = seq; }else{ return this._impl.curMaxMsgSeq; } }; Session.prototype.msgCount = function () { return this._impl.msgs.length; }; Session.prototype.msg = function (index) { return this._impl.msgs[index]; }; Session.prototype.msgs = function () { return this._impl.msgs; }; Session.prototype._impl_addMsg = function (msg) { this._impl.msgs.push(msg); //if (!msg.isSend && msg.time > this._impl.time) if (msg.time > this._impl.time) this._impl.time = msg.time; //if (!msg.isSend && msg.seq > this._impl.curMaxMsgSeq) if (msg.seq > this._impl.curMaxMsgSeq) this._impl.curMaxMsgSeq = msg.seq; //自己发送的消息不计入未读数 if (!msg.isSend && !this._impl.isAutoRead) { this._impl.unread++; } }; //class C2CMsgReadedItem var C2CMsgReadedItem = function (toAccount, lastedMsgTime) { this.toAccount = toAccount; this.lastedMsgTime = lastedMsgTime; } // class Msg var Msg = function (sess, isSend, seq, random, time, fromAccount, subType, fromAccountNick) { this.sess = sess; this.subType = subType >= 0 ? subType : 0;//消息类型,c2c消息时,type取值为0;group消息时,type取值0和1,0-普通群消息,1-群提示消息 this.fromAccount = fromAccount; this.fromAccountNick = fromAccountNick ? fromAccountNick : fromAccount; this.isSend = Boolean(isSend); this.seq = seq >= 0 ? seq : nextSeq(); this.random = random >= 0 ? random : createRandom(); this.time = time >= 0 ? time : unixtime(); this.elems = []; }; Msg.prototype.getSession = function () { return this.sess; }; Msg.prototype.getType = function () { return this.subType; }; Msg.prototype.getSubType = function () { return this.subType; }; Msg.prototype.getFromAccount = function () { return this.fromAccount; }; Msg.prototype.getFromAccountNick = function () { return this.fromAccountNick; }; Msg.prototype.getIsSend = function () { return this.isSend; }; Msg.prototype.getSeq = function () { return this.seq; }; Msg.prototype.getTime = function () { return this.time; }; Msg.prototype.getRandom = function () { return this.random; }; Msg.prototype.getElems = function () { return this.elems; }; //文本 Msg.prototype.addText = function (text) { this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.TEXT, text)); }; //表情 Msg.prototype.addFace = function (face) { this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.FACE, face)); }; //图片 Msg.prototype.addImage = function (image) { this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.IMAGE, image)); }; //地理位置 Msg.prototype.addLocation = function (location) { this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.LOCATION, location)); }; //文件 Msg.prototype.addFile = function (file) { this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.FILE, file)); }; //自定义 Msg.prototype.addCustom = function (custom) { this.addElem(new webim.Msg.Elem(MSG_ELEMENT_TYPE.CUSTOM, custom)); }; Msg.prototype.addElem = function (elem) { this.elems.push(elem); }; Msg.prototype.toHtml = function () { var html = ""; for (var i in this.elems) { var elem = this.elems[i]; html += elem.toHtml(); } return html; }; // class Msg.Elem Msg.Elem = function (type, value) { this.type = type; this.content = value; }; Msg.Elem.prototype.getType = function () { return this.type; }; Msg.Elem.prototype.getContent = function () { return this.content; }; Msg.Elem.prototype.toHtml = function () { var html; html = this.content.toHtml(); return html; }; // class Msg.Elem.Text Msg.Elem.Text = function (text) { this.text = tool.xssFilter(text); }; Msg.Elem.Text.prototype.getText = function () { return this.text; }; Msg.Elem.Text.prototype.toHtml = function () { return this.text; }; // class Msg.Elem.Face Msg.Elem.Face = function (index, data) { this.index = index; this.data = data; }; Msg.Elem.Face.prototype.getIndex = function () { return this.index; }; Msg.Elem.Face.prototype.getData = function () { return this.data; }; Msg.Elem.Face.prototype.toHtml = function () { var faceUrl = null; var index = emotionDataIndexs[this.data]; var emotion = emotions[index]; if (emotion && emotion[1]) { faceUrl = emotion[1]; } if (faceUrl) { return ""; } else { return this.data; } }; // 地理位置消息 class Msg.Elem.Location Msg.Elem.Location = function (longitude, latitude, desc) { this.latitude = latitude;//纬度 this.longitude = longitude;//经度 this.desc = desc;//描述 }; Msg.Elem.Location.prototype.getLatitude = function () { return this.latitude; }; Msg.Elem.Location.prototype.getLongitude = function () { return this.longitude; }; Msg.Elem.Location.prototype.getDesc = function () { return this.desc; }; Msg.Elem.Location.prototype.toHtml = function () { return '经度=' + this.longitude + ',纬度=' + this.latitude + ',描述=' + this.desc; }; //图片消息 // class Msg.Elem.Images Msg.Elem.Images = function (imageId) { this.UUID = imageId; this.ImageInfoArray = []; }; Msg.Elem.Images.prototype.addImage = function (image) { this.ImageInfoArray.push(image); }; Msg.Elem.Images.prototype.toHtml = function () { var smallImage = this.getImage(IMAGE_TYPE.SMALL); var bigImage = this.getImage(IMAGE_TYPE.LARGE); var oriImage = this.getImage(IMAGE_TYPE.ORIGIN); if (!bigImage) { bigImage = smallImage; } if (!oriImage) { oriImage = smallImage; } return ""; }; Msg.Elem.Images.prototype.getImageId = function () { return this.UUID; }; Msg.Elem.Images.prototype.getImage = function (type) { for (var i in this.ImageInfoArray) { if (this.ImageInfoArray[i].getType() == type) { return this.ImageInfoArray[i]; } } return null; }; // class Msg.Elem.Images.Image Msg.Elem.Images.Image = function (type, size, width, height, url) { this.type = type; this.size = size; this.width = width; this.height = height; this.url = url; }; Msg.Elem.Images.Image.prototype.getType = function () { return this.type; }; Msg.Elem.Images.Image.prototype.getSize = function () { return this.size; }; Msg.Elem.Images.Image.prototype.getWidth = function () { return this.width; }; Msg.Elem.Images.Image.prototype.getHeight = function () { return this.height; }; Msg.Elem.Images.Image.prototype.getUrl = function () { return this.url; }; // class Msg.Elem.Sound Msg.Elem.Sound = function (uuid, second, size, senderId, receiverId, downFlag, chatType) { this.uuid = uuid;//文件id this.second = second;//时长,单位:秒 this.size = size;//大小,单位:字节 this.senderId = senderId;//发送者 this.receiverId = receiverId;//接收方id this.downFlag = downFlag;//下载标志位 this.busiId = chatType == SESSION_TYPE.C2C ? 2 : 1;//busi_id ( 1:群 2:C2C) //根据不同情况拉取数据 //是否需要申请下载地址 0:到架平申请 1:到cos申请 2:不需要申请, 直接拿url下载 if(downFlag !== undefined && busiId !== undefined){ getFileDownUrlV2(uuid, senderId, second, downFlag,receiverId, this.busiId , UPLOAD_RES_TYPE.SOUND); }else{ this.downUrl = getSoundDownUrl(uuid, senderId, second);//下载地址 } }; Msg.Elem.Sound.prototype.getUUID = function () { return this.uuid; }; Msg.Elem.Sound.prototype.getSecond = function () { return this.second; }; Msg.Elem.Sound.prototype.getSize = function () { return this.size; }; Msg.Elem.Sound.prototype.getSenderId = function () { return this.senderId; }; Msg.Elem.Sound.prototype.getDownUrl = function () { return this.downUrl; }; Msg.Elem.Sound.prototype.toHtml = function () { if (BROWSER_INFO.type == 'ie' && parseInt(BROWSER_INFO.ver) <= 8) { return '[这是一条语音消息]demo暂不支持ie8(含)以下浏览器播放语音,语音URL:' + this.downUrl; } return ''; }; // class Msg.Elem.File Msg.Elem.File = function (uuid, name, size, senderId, receiverId, downFlag, chatType) { this.uuid = uuid;//文件id this.name = name;//文件名 this.size = size;//大小,单位:字节 this.senderId = senderId;//发送者 this.receiverId = receiverId;//接收方id this.downFlag = downFlag;//下载标志位 this.busiId = chatType == SESSION_TYPE.C2C ? 2 : 1;//busi_id ( 1:群 2:C2C) //根据不同情况拉取数据 //是否需要申请下载地址 0:到架平申请 1:到cos申请 2:不需要申请, 直接拿url下载 if(downFlag !== undefined && busiId !== undefined){ getFileDownUrlV2(uuid, senderId, name, downFlag,receiverId, this.busiId , UPLOAD_RES_TYPE.FILE); }else{ this.downUrl = getFileDownUrl(uuid, senderId, name);//下载地址 } }; Msg.Elem.File.prototype.getUUID = function () { return this.uuid; }; Msg.Elem.File.prototype.getName = function () { return this.name; }; Msg.Elem.File.prototype.getSize = function () { return this.size; }; Msg.Elem.File.prototype.getSenderId = function () { return this.senderId; }; Msg.Elem.File.prototype.getDownUrl = function () { return this.downUrl; }; Msg.Elem.File.prototype.getDownFlag = function () { return this.downFlag; }; Msg.Elem.File.prototype.toHtml = function () { var fileSize, unitStr; fileSize = this.size; unitStr = "Byte"; if (this.size >= 1024) { fileSize = Math.round(this.size / 1024); unitStr = "KB"; } return ' ' + this.name + '(' + fileSize + unitStr + ')'; }; // class Msg.Elem.GroupTip 群提示消息对象 Msg.Elem.GroupTip = function (opType, opUserId, groupId, groupName, userIdList) { this.opType = opType;//操作类型 this.opUserId = opUserId;//操作者id this.groupId = groupId;//群id this.groupName = groupName;//群名称 this.userIdList = userIdList ? userIdList : [];//被操作的用户id列表 this.groupInfoList = [];//新的群资料信息,群资料变更时才有值 this.memberInfoList = [];//新的群成员资料信息,群成员资料变更时才有值 this.groupMemberNum = null;//群成员数,操作类型为加群或者退群时才有值 }; Msg.Elem.GroupTip.prototype.addGroupInfo = function (groupInfo) { this.groupInfoList.push(groupInfo); }; Msg.Elem.GroupTip.prototype.addMemberInfo = function (memberInfo) { this.memberInfoList.push(memberInfo); }; Msg.Elem.GroupTip.prototype.getOpType = function () { return this.opType; }; Msg.Elem.GroupTip.prototype.getOpUserId = function () { return this.opUserId; }; Msg.Elem.GroupTip.prototype.getGroupId = function () { return this.groupId; }; Msg.Elem.GroupTip.prototype.getGroupName = function () { return this.groupName; }; Msg.Elem.GroupTip.prototype.getUserIdList = function () { return this.userIdList; }; Msg.Elem.GroupTip.prototype.getGroupInfoList = function () { return this.groupInfoList; }; Msg.Elem.GroupTip.prototype.getMemberInfoList = function () { return this.memberInfoList; }; Msg.Elem.GroupTip.prototype.getGroupMemberNum = function () { return this.groupMemberNum; }; Msg.Elem.GroupTip.prototype.setGroupMemberNum = function (groupMemberNum) { return this.groupMemberNum = groupMemberNum; }; Msg.Elem.GroupTip.prototype.toHtml = function () { var text = "[群提示消息]"; var maxIndex = GROUP_TIP_MAX_USER_COUNT - 1; switch (this.opType) { case GROUP_TIP_TYPE.JOIN://加入群 text += this.opUserId + "邀请了"; for (var m in this.userIdList) { text += this.userIdList[m] + ","; if (this.userIdList.length > GROUP_TIP_MAX_USER_COUNT && m == maxIndex) { text += "等" + this.userIdList.length + "人"; break; } } text += "加入该群"; break; case GROUP_TIP_TYPE.QUIT://退出群 text += this.opUserId + "主动退出该群"; break; case GROUP_TIP_TYPE.KICK://踢出群 text += this.opUserId + "将"; for (var m in this.userIdList) { text += this.userIdList[m] + ","; if (this.userIdList.length > GROUP_TIP_MAX_USER_COUNT && m == maxIndex) { text += "等" + this.userIdList.length + "人"; break; } } text += "踢出该群"; break; case GROUP_TIP_TYPE.SET_ADMIN://设置管理员 text += this.opUserId + "将"; for (var m in this.userIdList) { text += this.userIdList[m] + ","; if (this.userIdList.length > GROUP_TIP_MAX_USER_COUNT && m == maxIndex) { text += "等" + this.userIdList.length + "人"; break; } } text += "设为管理员"; break; case GROUP_TIP_TYPE.CANCEL_ADMIN://取消管理员 text += this.opUserId + "取消"; for (var m in this.userIdList) { text += this.userIdList[m] + ","; if (this.userIdList.length > GROUP_TIP_MAX_USER_COUNT && m == maxIndex) { text += "等" + this.userIdList.length + "人"; break; } } text += "的管理员资格"; break; case GROUP_TIP_TYPE.MODIFY_GROUP_INFO://群资料变更 text += this.opUserId + "修改了群资料:"; for (var m in this.groupInfoList) { var type = this.groupInfoList[m].getType(); var value = this.groupInfoList[m].getValue(); switch (type) { case GROUP_TIP_MODIFY_GROUP_INFO_TYPE.FACE_URL: text += "群头像为" + value + "; "; break; case GROUP_TIP_MODIFY_GROUP_INFO_TYPE.NAME: text += "群名称为" + value + "; "; break; case GROUP_TIP_MODIFY_GROUP_INFO_TYPE.OWNER: text += "群主为" + value + "; "; break; case GROUP_TIP_MODIFY_GROUP_INFO_TYPE.NOTIFICATION: text += "群公告为" + value + "; "; break; case GROUP_TIP_MODIFY_GROUP_INFO_TYPE.INTRODUCTION: text += "群简介为" + value + "; "; break; default: text += "未知信息为:type=" + type + ",value=" + value + "; "; break; } } break; case GROUP_TIP_TYPE.MODIFY_MEMBER_INFO://群成员资料变更(禁言时间) text += this.opUserId + "修改了群成员资料:"; for (var m in this.memberInfoList) { var userId = this.memberInfoList[m].getUserId(); var shutupTime = this.memberInfoList[m].getShutupTime(); text += userId + ": "; if (shutupTime != null && shutupTime !== undefined) { if (shutupTime == 0) { text += "取消禁言; "; } else { text += "禁言" + shutupTime + "秒; "; } } else { text += " shutupTime为空"; } if (this.memberInfoList.length > GROUP_TIP_MAX_USER_COUNT && m == maxIndex) { text += "等" + this.memberInfoList.length + "人"; break; } } break; case GROUP_TIP_TYPE.READED://消息已读 /**/ Log.info("消息已读同步") break; default: text += "未知群提示消息类型:type=" + this.opType; break; } return text; }; // class Msg.Elem.GroupTip.GroupInfo,变更的群资料信息对象 Msg.Elem.GroupTip.GroupInfo = function (type, value) { this.type = type;//群资料信息类型 this.value = value;//对应的值 }; Msg.Elem.GroupTip.GroupInfo.prototype.getType = function () { return this.type; }; Msg.Elem.GroupTip.GroupInfo.prototype.getValue = function () { return this.value; }; // class Msg.Elem.GroupTip.MemberInfo,变更的群成员资料信息对象 Msg.Elem.GroupTip.MemberInfo = function (userId, shutupTime) { this.userId = userId;//群成员id this.shutupTime = shutupTime;//群成员被禁言时间,0表示取消禁言,大于0表示被禁言时长,单位:秒 }; Msg.Elem.GroupTip.MemberInfo.prototype.getUserId = function () { return this.userId; }; Msg.Elem.GroupTip.MemberInfo.prototype.getShutupTime = function () { return this.shutupTime; }; // 自定义消息类型 class Msg.Elem.Custom Msg.Elem.Custom = function (data, desc, ext) { this.data = data;//数据 this.desc = desc;//描述 this.ext = ext;//扩展字段 }; Msg.Elem.Custom.prototype.getData = function () { return this.data; }; Msg.Elem.Custom.prototype.getDesc = function () { return this.desc; }; Msg.Elem.Custom.prototype.getExt = function () { return this.ext; }; Msg.Elem.Custom.prototype.toHtml = function () { return this.data; }; // singleton object MsgStore var MsgStore = new function () { var sessMap = {};//跟所有用户或群的聊天记录MAP var sessTimeline = [];//按时间降序排列的会话列表 msgCache = {};//消息缓存,用于判重 //C2C this.cookie = "";//上一次拉取新c2c消息的cookie this.syncFlag = 0;//上一次拉取新c2c消息的是否继续拉取标记 var visitSess = function (visitor) { for (var i in sessMap) { visitor(sessMap[i]); } }; // window.msgCache = msgCache; //消息查重 var checkDupMsg = function (msg) { var dup = false; var first_key = msg.sess._impl.skey; var second_key = msg.isSend + msg.seq + msg.random; var tempMsg = msgCache[first_key] && msgCache[first_key][second_key]; if (tempMsg){ dup = true; } if (msgCache[first_key]) { msgCache[first_key][second_key] = {time: msg.time}; } else { msgCache[first_key] = {}; msgCache[first_key][second_key] = {time: msg.time}; } return dup; }; this.sessMap = function () { return sessMap; }; this.sessCount = function () { return sessTimeline.length; }; this.sessByTypeId = function (type, id) { var skey = Session.skey(type, id); if (skey === undefined || skey == null) return null; return sessMap[skey]; }; this.delSessByTypeId = function (type, id) { var skey = Session.skey(type, id); if (skey === undefined || skey == null) return false; if (sessMap[skey]) { delete sessMap[skey]; delete msgCache[skey]; } return true; }; this.resetCookieAndSyncFlag = function () { this.cookie = ""; this.syncFlag = 0; }; //切换将当前会话的自动读取消息标志为isOn,重置其他会话的自动读取消息标志为false this.setAutoRead = function (selSess, isOn, isResetAll) { if (isResetAll) visitSess(function (s) { s._impl.isAutoRead = false; }); if (selSess) { selSess._impl.isAutoRead = isOn;// if (isOn) {//是否调用已读上报接口 selSess._impl.unread = 0; if (selSess._impl.type == SESSION_TYPE.C2C) {//私聊消息已读上报 var tmpC2CMsgReadedItem = []; tmpC2CMsgReadedItem.push(new C2CMsgReadedItem(selSess._impl.id, selSess._impl.time)); //调用C2C消息已读上报接口 proto_c2CMsgReaded(MsgStore.cookie, tmpC2CMsgReadedItem, function (resp) { log.info("[setAutoRead]: c2CMsgReaded success"); }, function (err) { log.error("[setAutoRead}: c2CMsgReaded failed:" + err.ErrorInfo); }); } else if (selSess._impl.type == SESSION_TYPE.GROUP) {//群聊消息已读上报 var tmpOpt = { 'GroupId': selSess._impl.id, 'MsgReadedSeq': selSess._impl.curMaxMsgSeq }; //调用group消息已读上报接口 proto_groupMsgReaded(tmpOpt, function (resp) { log.info("groupMsgReaded success"); }, function (err) { log.error("groupMsgReaded failed:" + err.ErrorInfo); }); } } } }; this.c2CMsgReaded = function (opts, cbOk, cbErr) { var tmpC2CMsgReadedItem = []; tmpC2CMsgReadedItem.push(new C2CMsgReadedItem(opts.To_Account, opts.LastedMsgTime)); //调用C2C消息已读上报接口 proto_c2CMsgReaded(MsgStore.cookie, tmpC2CMsgReadedItem, function (resp) { if (cbOk) { log.info("c2CMsgReaded success"); cbOk(resp); } }, function (err) { if (cbErr) { log.error("c2CMsgReaded failed:" + err.ErrorInfo); cbErr(err); } }); }; this.addSession = function (sess) { sessMap[sess._impl.skey] = sess; }; this.delSession = function (sess) { delete sessMap[sess._impl.skey]; }; this.addMsg = function (msg) { if (checkDupMsg(msg)) return false; var sess = msg.sess; if (!sessMap[sess._impl.skey]) this.addSession(sess); sess._impl_addMsg(msg); return true; }; this.updateTimeline = function () { var arr = new Array; visitSess(function (sess) { arr.push(sess); }); arr.sort(function (a, b) { return b.time - a.time; }); sessTimeline = arr; }; }; // singleton object MsgManager var MsgManager = new function () { var onMsgCallback = null;//新消息(c2c和group)回调 var onGroupInfoChangeCallback = null;//群资料变化回调 //收到新群系统消息回调列表 var onGroupSystemNotifyCallbacks = { "1": null, "2": null, "3": null, "4": null, "5": null, "6": null, "7": null, "8": null, "9": null, "10": null, "11": null, "15": null, "255": null }; //监听好友系统通知函数 var onFriendSystemNotifyCallbacks={ "1":null, "2":null, "3":null, "4":null, "5":null, "6":null, "7":null, "8":null }; var onProfileSystemNotifyCallbacks= { "1" :null }; var onKickedEventCall = null; var onMsgReadCallback = null; //普通长轮询 var longPollingOn = false;//是否开启普通长轮询 var isLongPollingRequesting = false;//是否在长轮询ing var notifySeq = 0;//c2c通知seq var noticeSeq = 0;//群消息seq //大群长轮询 var onBigGroupMsgCallback = null;//大群消息回调 var bigGroupLongPollingOn = false;//是否开启长轮询 var bigGroupLongPollingStartSeq = 0;//请求拉消息的起始seq(大群长轮询) var bigGroupLongPollingHoldTime = 90;//客户端长轮询的超时时间,单位是秒(大群长轮询) var bigGroupLongPollingKey = null;//客户端加入群组后收到的的Key(大群长轮询) var bigGroupLongPollingMsgMap = {};//记录收到的群消息数 var getLostGroupMsgCount = 0;//补拉丢失的群消息次数 //我的群当前最大的seq var myGroupMaxSeqs = {};//用于补拉丢失的群消息 var groupSystemMsgsCache = {};//群组系统消息缓存,用于判重 //设置长轮询开关 //isOn=true 开启 //isOn=false 停止 this.setLongPollingOn = function (isOn) { longPollingOn = isOn; }; this.getLongPollingOn = function () { return longPollingOn; }; //重置长轮询变量 this.resetLongPollingInfo = function () { longPollingOn = false; notifySeq = 0; noticeSeq = 0; }; //设置大群长轮询开关 //isOn=true 开启 //isOn=false 停止 this.setBigGroupLongPollingOn = function (isOn) { bigGroupLongPollingOn = isOn; }; //设置大群长轮询key this.setBigGroupLongPollingKey = function (key) { bigGroupLongPollingKey = key; }; //重置大群长轮询变量 this.resetBigGroupLongPollingInfo = function () { bigGroupLongPollingOn = false; bigGroupLongPollingStartSeq = 0; bigGroupLongPollingKey = null; bigGroupLongPollingMsgMap = {}; }; //设置群消息数据条数 this.setBigGroupLongPollingMsgMap = function (groupId, count) { var bigGroupLongPollingMsgCount = bigGroupLongPollingMsgMap[groupId]; if (bigGroupLongPollingMsgCount) { bigGroupLongPollingMsgCount = parseInt(bigGroupLongPollingMsgCount) + count; bigGroupLongPollingMsgMap[groupId] = bigGroupLongPollingMsgCount; } else { bigGroupLongPollingMsgMap[groupId] = count; } }; //重置 this.clear = function () { onGroupInfoChangeCallback = null; onGroupSystemNotifyCallbacks = { "1": null,//申请加群请求(只有管理员会收到) "2": null,//申请加群被同意(只有申请人能够收到) "3": null,//申请加群被拒绝(只有申请人能够收到) "4": null,//被管理员踢出群(只有被踢者接收到) "5": null,//群被解散(全员接收) "6": null,//创建群(创建者接收) "7": null,//邀请加群(被邀请者接收) "8": null,//主动退群(主动退出者接收) "9": null,//设置管理员(被设置者接收) "10": null,//取消管理员(被取消者接收) "11": null,//群已被回收(全员接收) "15": null,//群已被回收(全员接收) "255": null//用户自定义通知(默认全员接收) }; onFriendSystemNotifyCallbacks = { "1": null,//好友表增加 "2": null,//好友表删除 "3": null,//未决增加 "4": null,//未决删除 "5": null,//黑名单增加 "6": null,//黑名单删除 "7": null,//未决已读上报 "8": null//好友信息(备注,分组)变更 }; onProfileSystemNotifyCallbacks = { "1": null//资料修改 }; //重置普通长轮询参数 onMsgCallback = null; longPollingOn = false; notifySeq = 0;//c2c新消息通知seq noticeSeq = 0;//group新消息seq //重置大群长轮询参数 onBigGroupMsgCallback = null; bigGroupLongPollingOn = false; bigGroupLongPollingStartSeq = 0; bigGroupLongPollingKey = null; bigGroupLongPollingMsgMap = {}; groupSystemMsgsCache = {}; ipList = [];//文件下载地址 authkey = null;//文件下载票据 expireTime = null;//票据超时时间 }; //初始化文件下载ip和票据 var initIpAndAuthkey = function (cbOk, cbErr) { proto_getIpAndAuthkey(function (resp) { ipList = resp.IpList; authkey = resp.AuthKey; expireTime = resp.ExpireTime; if (cbOk) cbOk(resp); }, function (err) { log.error("initIpAndAuthkey failed:" + err.ErrorInfo); if (cbErr) cbErr(err); } ); }; //初始化我的群当前最大的seq,用于补拉丢失的群消息 var initMyGroupMaxSeqs = function (cbOk, cbErr) { var opts = { 'Member_Account': ctx.identifier, 'Limit': 1000, 'Offset': 0, 'GroupBaseInfoFilter': [ 'NextMsgSeq' ] }; proto_getJoinedGroupListHigh(opts, function (resp) { if (!resp.GroupIdList || resp.GroupIdList.length == 0) { log.info("initMyGroupMaxSeqs: 目前还没有加入任何群组"); if (cbOk) cbOk(resp); return; } for (var i = 0; i < resp.GroupIdList.length; i++) { var group_id = resp.GroupIdList[i].GroupId; var curMaxSeq = resp.GroupIdList[i].NextMsgSeq - 1; myGroupMaxSeqs[group_id] = curMaxSeq; } if (cbOk) cbOk(resp); }, function (err) { log.error("initMyGroupMaxSeqs failed:" + err.ErrorInfo); if (cbErr) cbErr(err); } ); }; //补拉群消息 var getLostGroupMsgs = function (groupId, reqMsgSeq, reqMsgNumber) { getLostGroupMsgCount++; //发起一个拉群群消息请求 var tempOpts = { 'GroupId': groupId, 'ReqMsgSeq': reqMsgSeq, 'ReqMsgNumber': reqMsgNumber }; //发起一个拉群群消息请求 log.warn("第" + getLostGroupMsgCount + "次补齐群消息,参数=" + JSON.stringify(tempOpts)); MsgManager.syncGroupMsgs(tempOpts); }; //更新群当前最大消息seq var updateMyGroupCurMaxSeq=function(groupId,msgSeq){ //更新myGroupMaxSeqs中的群最大seq var curMsgSeq=myGroupMaxSeqs[groupId] if(curMsgSeq){//如果存在,比较大小 if(msgSeq>curMsgSeq){ myGroupMaxSeqs[groupId]=msgSeq; } }else{//不存在,新增 myGroupMaxSeqs[groupId]=msgSeq; } }; //添加群消息列表 var addGroupMsgList = function (msgs, new_group_msgs) { for (var p in msgs) { var newGroupMsg = msgs[p]; //发群消息时,长轮询接口会返回用户自己发的群消息 //if(newGroupMsg.From_Account && newGroupMsg.From_Account!=ctx.identifier ){ if (newGroupMsg.From_Account) { //false-不是主动拉取的历史消息 //true-需要保存到sdk本地session,并且需要判重 var msg = handlerGroupMsg(newGroupMsg, false, true); if (msg) {//不为空,加到新消息里 new_group_msgs.push(msg); } //更新myGroupMaxSeqs中的群最大seq updateMyGroupCurMaxSeq(newGroupMsg.ToGroupId,newGroupMsg.MsgSeq); } } return new_group_msgs; }; //处理收到的群普通和提示消息 var handlerOrdinaryAndTipC2cMsgs = function (eventType, groupMsgArray) { var groupMsgMap = {};//保存收到的C2c消息信息(群号,最小,最大消息seq,消息列表) var new_group_msgs = []; var minGroupMsgSeq = 99999999; var maxGroupMsgSeq = -1; for (var j in groupMsgArray) { var groupMsgs = groupMsgMap[groupMsgArray[j].ToGroupId]; if (!groupMsgs) { groupMsgs = groupMsgMap[groupMsgArray[j].ToGroupId] = { "min": minGroupMsgSeq,//收到新消息最小seq "max": maxGroupMsgSeq,//收到新消息最大seq "msgs": []//收到的新消息 }; } //更新长轮询的群NoticeSeq if (groupMsgArray[j].NoticeSeq > noticeSeq) { log.warn("noticeSeq=" + noticeSeq + ",msgNoticeSeq=" + groupMsgArray[j].NoticeSeq); noticeSeq = groupMsgArray[j].NoticeSeq; } groupMsgArray[j].Event = eventType; groupMsgMap[groupMsgArray[j].ToGroupId].msgs.push(groupMsgArray[j]);//新增一条消息 if (groupMsgArray[j].MsgSeq < groupMsgs.min) {//记录最小的消息seq groupMsgMap[groupMsgArray[j].ToGroupId].min = groupMsgArray[j].MsgSeq; } if (groupMsgArray[j].MsgSeq > groupMsgs.max) {//记录最大的消息seq groupMsgMap[groupMsgArray[j].ToGroupId].max = groupMsgArray[j].MsgSeq; } } for (var groupId in groupMsgMap) { var tempCount = groupMsgMap[groupId].max - groupMsgMap[groupId].min + 1;//收到的新的群消息数 var curMaxMsgSeq = myGroupMaxSeqs[groupId];//获取本地保存的群最大消息seq if (curMaxMsgSeq) {//存在这个群的最大消息seq //高并发情况下,长轮询可能存在丢消息,这时需要客户端通过拉取群消息接口补齐下 //1、如果收到的新消息最小seq比当前最大群消息seq大于1,则表示收到的群消息发生跳跃,需要补齐 //2、收到的新群消息seq存在不连续情况,也需要补齐 if (groupMsgMap[groupId].min - curMaxMsgSeq > 1 || groupMsgMap[groupId].msgs.length < tempCount) { //发起一个拉群群消息请求 log.warn("发起一次补齐群消息请求,curMaxMsgSeq=" + curMaxMsgSeq + ", minMsgSeq=" + groupMsgMap[groupId].min + ", maxMsgSeq=" + groupMsgMap[groupId].max + ", msgs.length=" + groupMsgMap[groupId].msgs.length + ", tempCount=" + tempCount); getLostGroupMsgs(groupId, groupMsgMap[groupId].max, groupMsgMap[groupId].max - curMaxMsgSeq); //更新myGroupMaxSeqs中的群最大seq updateMyGroupCurMaxSeq(groupId,groupMsgMap[groupId].max); } else { new_group_msgs = addGroupMsgList(groupMsgMap[groupId].msgs, new_group_msgs); } } else {//不存在该群的最大消息seq log.warn("不存在该群的最大消息seq,群id=" + groupId); //高并发情况下,长轮询可能存在丢消息,这时需要客户端通过拉取群消息接口补齐下 //1、收到的新群消息seq存在不连续情况,也需要补齐 if (groupMsgMap[groupId].msgs.length < tempCount) { //发起一个拉群群消息请求 log.warn("发起一次补齐群消息请求,minMsgSeq=" + groupMsgMap[groupId].min + ", maxMsgSeq=" + groupMsgMap[groupId].max + ", msgs.length=" + groupMsgMap[groupId].msgs.length + ", tempCount=" + tempCount); getLostGroupMsgs(groupId, groupMsgMap[groupId].max, tempCount); //更新myGroupMaxSeqs中的群最大seq updateMyGroupCurMaxSeq(groupId,groupMsgMap[groupId].max); } else { new_group_msgs = addGroupMsgList(groupMsgMap[groupId].msgs, new_group_msgs); } } } if (new_group_msgs.length) { MsgStore.updateTimeline(); } if (onMsgCallback && new_group_msgs.length) onMsgCallback(new_group_msgs); }; //处理收到的群普通和提示消息 var handlerOrdinaryAndTipGroupMsgs = function (eventType, groupMsgArray) { var groupMsgMap = {};//保存收到的群消息信息(群号,最小,最大消息seq,消息列表) var new_group_msgs = []; var minGroupMsgSeq = 99999999; var maxGroupMsgSeq = -1; for (var j in groupMsgArray) { var groupMsgs = groupMsgMap[groupMsgArray[j].ToGroupId]; if (!groupMsgs) { groupMsgs = groupMsgMap[groupMsgArray[j].ToGroupId] = { "min": minGroupMsgSeq,//收到新消息最小seq "max": maxGroupMsgSeq,//收到新消息最大seq "msgs": []//收到的新消息 }; } //更新长轮询的群NoticeSeq if (groupMsgArray[j].NoticeSeq > noticeSeq) { log.warn("noticeSeq=" + noticeSeq + ",msgNoticeSeq=" + groupMsgArray[j].NoticeSeq); noticeSeq = groupMsgArray[j].NoticeSeq; } groupMsgArray[j].Event = eventType; groupMsgMap[groupMsgArray[j].ToGroupId].msgs.push(groupMsgArray[j]);//新增一条消息 if (groupMsgArray[j].MsgSeq < groupMsgs.min) {//记录最小的消息seq groupMsgMap[groupMsgArray[j].ToGroupId].min = groupMsgArray[j].MsgSeq; } if (groupMsgArray[j].MsgSeq > groupMsgs.max) {//记录最大的消息seq groupMsgMap[groupMsgArray[j].ToGroupId].max = groupMsgArray[j].MsgSeq; } } for (var groupId in groupMsgMap) { var tempCount = groupMsgMap[groupId].max - groupMsgMap[groupId].min + 1;//收到的新的群消息数 var curMaxMsgSeq = myGroupMaxSeqs[groupId];//获取本地保存的群最大消息seq if (curMaxMsgSeq) {//存在这个群的最大消息seq //高并发情况下,长轮询可能存在丢消息,这时需要客户端通过拉取群消息接口补齐下 //1、如果收到的新消息最小seq比当前最大群消息seq大于1,则表示收到的群消息发生跳跃,需要补齐 //2、收到的新群消息seq存在不连续情况,也需要补齐 if (groupMsgMap[groupId].min - curMaxMsgSeq > 1 || groupMsgMap[groupId].msgs.length < tempCount) { //发起一个拉群群消息请求 log.warn("发起一次补齐群消息请求,curMaxMsgSeq=" + curMaxMsgSeq + ", minMsgSeq=" + groupMsgMap[groupId].min + ", maxMsgSeq=" + groupMsgMap[groupId].max + ", msgs.length=" + groupMsgMap[groupId].msgs.length + ", tempCount=" + tempCount); getLostGroupMsgs(groupId, groupMsgMap[groupId].max, groupMsgMap[groupId].max - curMaxMsgSeq); //更新myGroupMaxSeqs中的群最大seq updateMyGroupCurMaxSeq(groupId,groupMsgMap[groupId].max); } else { new_group_msgs = addGroupMsgList(groupMsgMap[groupId].msgs, new_group_msgs); } } else {//不存在该群的最大消息seq log.warn("不存在该群的最大消息seq,群id=" + groupId); //高并发情况下,长轮询可能存在丢消息,这时需要客户端通过拉取群消息接口补齐下 //1、收到的新群消息seq存在不连续情况,也需要补齐 if (groupMsgMap[groupId].msgs.length < tempCount) { //发起一个拉群群消息请求 log.warn("发起一次补齐群消息请求,minMsgSeq=" + groupMsgMap[groupId].min + ", maxMsgSeq=" + groupMsgMap[groupId].max + ", msgs.length=" + groupMsgMap[groupId].msgs.length + ", tempCount=" + tempCount); getLostGroupMsgs(groupId, groupMsgMap[groupId].max, tempCount); //更新myGroupMaxSeqs中的群最大seq updateMyGroupCurMaxSeq(groupId,groupMsgMap[groupId].max); } else { new_group_msgs = addGroupMsgList(groupMsgMap[groupId].msgs, new_group_msgs); } } } if (new_group_msgs.length) { MsgStore.updateTimeline(); } if (onMsgCallback && new_group_msgs.length) onMsgCallback(new_group_msgs); }; //处理新的群提示消息 var handlerGroupTips = function (groupTips) { var new_group_msgs = []; for (var o in groupTips) { var groupTip = groupTips[o]; //添加event字段 groupTip.Event = LONG_POLLINNG_EVENT_TYPE.GROUP_TIP; //更新群消息通知seq if (groupTip.NoticeSeq > noticeSeq) { noticeSeq = groupTip.NoticeSeq; } var msg = handlerGroupMsg(groupTip, false, true); if (msg) { new_group_msgs.push(msg); } } if (new_group_msgs.length) { MsgStore.updateTimeline(); } if (onMsgCallback && new_group_msgs.length) onMsgCallback(new_group_msgs); }; //处理新的群系统消息 //isNeedValidRepeatMsg 是否需要判重 var handlerGroupSystemMsgs = function (groupSystemMsgs, isNeedValidRepeatMsg) { for (var k in groupSystemMsgs) { var groupTip = groupSystemMsgs[k]; var groupReportTypeMsg = groupTip.MsgBody; var reportType = groupReportTypeMsg.ReportType; //当长轮询返回的群系统消息,才需要更新群消息通知seq if (isNeedValidRepeatMsg == false && groupTip.NoticeSeq && groupTip.NoticeSeq > noticeSeq) { noticeSeq = groupTip.NoticeSeq; } var toAccount = groupTip.GroupInfo.To_Account; //过滤本不应该给自己的系统消息 /*if (!toAccount || toAccount != ctx.identifier) { log.error("收到本不应该给自己的系统消息: To_Account=" + toAccount); continue; }*/ if (isNeedValidRepeatMsg) { //var key=groupTip.ToGroupId+"_"+reportType+"_"+groupTip.MsgTimeStamp+"_"+groupReportTypeMsg.Operator_Account; var key = groupTip.ToGroupId + "_" + reportType + "_" + groupReportTypeMsg.Operator_Account; var isExist = groupSystemMsgsCache[key]; if (isExist) { log.warn("收到重复的群系统消息:key=" + key); continue; } groupSystemMsgsCache[key] = true; } var notify = { "SrcFlag": 0, "ReportType": reportType, "GroupId": groupTip.ToGroupId, "GroupName": groupTip.GroupInfo.GroupName, "Operator_Account": groupReportTypeMsg.Operator_Account, "MsgTime": groupTip.MsgTimeStamp, "groupReportTypeMsg" : groupReportTypeMsg }; switch (reportType) { case GROUP_SYSTEM_TYPE.JOIN_GROUP_REQUEST://申请加群(只有管理员会接收到) notify["RemarkInfo"] = groupReportTypeMsg.RemarkInfo; notify["MsgKey"] = groupReportTypeMsg.MsgKey; notify["Authentication"] = groupReportTypeMsg.Authentication; notify["UserDefinedField"] = groupTip.UserDefinedField; notify["From_Account"] = groupTip.From_Account; notify["MsgSeq"] = groupTip.ClientSeq; notify["MsgRandom"] = groupTip.MsgRandom; break; case GROUP_SYSTEM_TYPE.JOIN_GROUP_ACCEPT://申请加群被同意(只有申请人自己接收到) case GROUP_SYSTEM_TYPE.JOIN_GROUP_REFUSE://申请加群被拒绝(只有申请人自己接收到) notify["RemarkInfo"] = groupReportTypeMsg.RemarkInfo; break; case GROUP_SYSTEM_TYPE.KICK://被管理员踢出群(只有被踢者接收到) case GROUP_SYSTEM_TYPE.DESTORY://群被解散(全员接收) case GROUP_SYSTEM_TYPE.CREATE://创建群(创建者接收, 不展示) case GROUP_SYSTEM_TYPE.INVITED_JOIN_GROUP_REQUEST://邀请加群(被邀请者接收) case GROUP_SYSTEM_TYPE.QUIT://主动退群(主动退出者接收, 不展示) case GROUP_SYSTEM_TYPE.SET_ADMIN://群设置管理员(被设置者接收) case GROUP_SYSTEM_TYPE.CANCEL_ADMIN://取消管理员(被取消者接收) case GROUP_SYSTEM_TYPE.REVOKE://群已被回收(全员接收, 不展示) break; case GROUP_SYSTEM_TYPE.READED://群消息已读同步 break; case GROUP_SYSTEM_TYPE.CUSTOM://用户自定义通知(默认全员接收) notify["MsgSeq"] = groupTip.MsgSeq; notify["UserDefinedField"] = groupReportTypeMsg.UserDefinedField; break; default: log.error("未知群系统消息类型:reportType=" + reportType); break; } if (isNeedValidRepeatMsg) { if (reportType == GROUP_SYSTEM_TYPE.JOIN_GROUP_REQUEST) { //回调 if (onGroupSystemNotifyCallbacks[reportType]) onGroupSystemNotifyCallbacks[reportType](notify); } } else { //回调 if (onGroupSystemNotifyCallbacks[reportType]) { if(reportType == GROUP_SYSTEM_TYPE.READED){ var arr = notify.groupReportTypeMsg.GroupReadInfoArray; for(var i = 0 , l = arr.length; i < l ; i++){ var item = arr[i]; onGroupSystemNotifyCallbacks[reportType](item); } }else{ onGroupSystemNotifyCallbacks[reportType](notify); } } } }//loop }; //处理新的好友系统通知 //isNeedValidRepeatMsg 是否需要判重 var handlerFriendSystemNotices = function (friendSystemNotices, isNeedValidRepeatMsg) { var friendNotice, type, notify; for (var k in friendSystemNotices) { friendNotice = friendSystemNotices[k]; type = friendNotice.PushType; //当长轮询返回的群系统消息,才需要更新通知seq if (isNeedValidRepeatMsg == false && friendNotice.NoticeSeq && friendNotice.NoticeSeq > noticeSeq) { noticeSeq = friendNotice.NoticeSeq; } notify = {'Type': type}; switch (type) { case FRIEND_NOTICE_TYPE.FRIEND_ADD://好友表增加 notify["Accounts"] = friendNotice.FriendAdd_Account; break; case FRIEND_NOTICE_TYPE.FRIEND_DELETE://好友表删除 notify["Accounts"] = friendNotice.FriendDel_Account; break; case FRIEND_NOTICE_TYPE.PENDENCY_ADD://未决增加 notify["PendencyList"] = friendNotice.PendencyAdd; break; case FRIEND_NOTICE_TYPE.PENDENCY_DELETE://未决删除 notify["Accounts"] = friendNotice.FrienPencydDel_Account; break; case FRIEND_NOTICE_TYPE.BLACK_LIST_ADD://黑名单增加 notify["Accounts"] = friendNotice.BlackListAdd_Account; break; case FRIEND_NOTICE_TYPE.BLACK_LIST_DELETE://黑名单删除 notify["Accounts"] = friendNotice.BlackListDel_Account; break; /*case FRIEND_NOTICE_TYPE.PENDENCY_REPORT://未决已读上报 break; case FRIEND_NOTICE_TYPE.FRIEND_UPDATE://好友数据更新 break; */ default: log.error("未知好友系统通知类型:friendNotice=" + JSON.stringify(friendNotice)); break; } if (isNeedValidRepeatMsg) { if (type == FRIEND_NOTICE_TYPE.PENDENCY_ADD) { //回调 if (onFriendSystemNotifyCallbacks[type]) onFriendSystemNotifyCallbacks[type](notify); } } else { //回调 if (onFriendSystemNotifyCallbacks[type]) onFriendSystemNotifyCallbacks[type](notify); } }//loop }; //处理新的资料系统通知 //isNeedValidRepeatMsg 是否需要判重 var handlerProfileSystemNotices = function (profileSystemNotices, isNeedValidRepeatMsg) { var profileNotice, type, notify; for (var k in profileSystemNotices) { profileNotice = profileSystemNotices[k]; type = profileNotice.PushType; //当长轮询返回的群系统消息,才需要更新通知seq if (isNeedValidRepeatMsg == false && profileNotice.NoticeSeq && profileNotice.NoticeSeq > noticeSeq) { noticeSeq = profileNotice.NoticeSeq; } notify = {'Type': type}; switch (type) { case PROFILE_NOTICE_TYPE.PROFILE_MODIFY://资料修改 notify["Profile_Account"] = profileNotice.Profile_Account; notify["ProfileList"] = profileNotice.ProfileList; break; default: log.error("未知资料系统通知类型:profileNotice=" + JSON.stringify(profileNotice)); break; } if (isNeedValidRepeatMsg) { if (type == PROFILE_NOTICE_TYPE.PROFILE_MODIFY) { //回调 if (onProfileSystemNotifyCallbacks[type]) onProfileSystemNotifyCallbacks[type](notify); } } else { //回调 if (onProfileSystemNotifyCallbacks[type]) onProfileSystemNotifyCallbacks[type](notify); } }//loop }; //处理新的群系统消息(用于直播大群长轮询) var handlerGroupSystemMsg = function (groupTip) { var groupReportTypeMsg = groupTip.MsgBody; var reportType = groupReportTypeMsg.ReportType; var toAccount = groupTip.GroupInfo.To_Account; //过滤本不应该给自己的系统消息 //if(!toAccount || toAccount!=ctx.identifier){ // log.error("收到本不应该给自己的系统消息: To_Account="+toAccount); // continue; //} var notify = { "SrcFlag": 1, "ReportType": reportType, "GroupId": groupTip.ToGroupId, "GroupName": groupTip.GroupInfo.GroupName, "Operator_Account": groupReportTypeMsg.Operator_Account, "MsgTime": groupTip.MsgTimeStamp }; switch (reportType) { case GROUP_SYSTEM_TYPE.JOIN_GROUP_REQUEST://申请加群(只有管理员会接收到) notify["RemarkInfo"] = groupReportTypeMsg.RemarkInfo; notify["MsgKey"] = groupReportTypeMsg.MsgKey; notify["Authentication"] = groupReportTypeMsg.Authentication; notify["UserDefinedField"] = groupTip.UserDefinedField; notify["From_Account"] = groupTip.From_Account; notify["MsgSeq"] = groupTip.ClientSeq; notify["MsgRandom"] = groupTip.MsgRandom; break; case GROUP_SYSTEM_TYPE.JOIN_GROUP_ACCEPT://申请加群被同意(只有申请人自己接收到) case GROUP_SYSTEM_TYPE.JOIN_GROUP_REFUSE://申请加群被拒绝(只有申请人自己接收到) notify["RemarkInfo"] = groupReportTypeMsg.RemarkInfo; break; case GROUP_SYSTEM_TYPE.KICK://被管理员踢出群(只有被踢者接收到) case GROUP_SYSTEM_TYPE.DESTORY://群被解散(全员接收) case GROUP_SYSTEM_TYPE.CREATE://创建群(创建者接收, 不展示) case GROUP_SYSTEM_TYPE.INVITED_JOIN_GROUP_REQUEST://邀请加群(被邀请者接收) case GROUP_SYSTEM_TYPE.QUIT://主动退群(主动退出者接收, 不展示) case GROUP_SYSTEM_TYPE.SET_ADMIN://群设置管理员(被设置者接收) case GROUP_SYSTEM_TYPE.CANCEL_ADMIN://取消管理员(被取消者接收) case GROUP_SYSTEM_TYPE.REVOKE://群已被回收(全员接收, 不展示) break; case GROUP_SYSTEM_TYPE.CUSTOM://用户自定义通知(默认全员接收) notify["MsgSeq"] = groupTip.MsgSeq; notify["UserDefinedField"] = groupReportTypeMsg.UserDefinedField; break; default: log.error("未知群系统消息类型:reportType=" + reportType); break; } //回调 if (onGroupSystemNotifyCallbacks[reportType]) onGroupSystemNotifyCallbacks[reportType](notify); }; //处理C2C EVENT 消息通道Array var handlerC2cNotifyMsgArray = function(arr){ for(var i =0,l=arr.length; i 0 && MsgStore.addMsg(msg)) { notifyInfo.push(msg); } } // for loop if (notifyInfo.length > 0) MsgStore.updateTimeline(); if (notifyInfo.length > 0) { if (onMsgCallback) onMsgCallback(notifyInfo); } }; //发起新的长轮询请求 var startNextLongPolling = function () { longPollingOn && MsgManager.longPolling(); }; //处理未决的加群申请消息列表 var handlerApplyJoinGroupSystemMsgs = function (eventArray) { for (var i in eventArray) { var e = eventArray[i]; switch (e.Event) { case LONG_POLLINNG_EVENT_TYPE.GROUP_SYSTEM://(多终端同步)群系统消息 log.warn("handlerApplyJoinGroupSystemMsgs: handler new group system msg"); //true 表示 解决加群申请通知存在重复的问题(已处理的通知,下次登录还会拉到),需要判重 handlerGroupSystemMsgs(e.GroupTips, true); break; default: log.error("syncMsgs收到未知的群系统消息类型: Event=" + e.Event); break; } } }; //拉取c2c消息(包含加群未决消息,需要处理) this.syncMsgs = function (cbOk, cbErr) { var notifyInfo = []; var msgInfos = []; //读取C2C消息 proto_getMsgs(MsgStore.cookie, MsgStore.syncFlag, function (resp) { //拉取完毕 if (resp.SyncFlag == 2) { MsgStore.syncFlag = 0; } //处理c2c消息 msgInfos = resp.MsgList;//返回的消息列表 MsgStore.cookie = resp.Cookie;//cookies,记录当前读到的最新消息位置 for (var i in msgInfos) { var msgInfo = msgInfos[i]; var isSendMsg, id, headUrl; if (msgInfo.From_Account == ctx.identifier) {//当前用户发送的消息 isSendMsg = true; id = msgInfo.To_Account;//读取接收者信息 headUrl = ''; } else {//当前用户收到的消息 isSendMsg = false; id = msgInfo.From_Account;//读取发送者信息 headUrl = ''; } var sess = MsgStore.sessByTypeId(SESSION_TYPE.C2C, id); if (!sess) { sess = new Session(SESSION_TYPE.C2C, id, id, headUrl, 0, 0); } var msg = new Msg(sess, isSendMsg, msgInfo.MsgSeq, msgInfo.MsgRandom, msgInfo.MsgTimeStamp, msgInfo.From_Account); var msgBody = null; var msgContent = null; var msgType = null; for (var mi in msgInfo.MsgBody) { msgBody = msgInfo.MsgBody[mi]; msgType = msgBody.MsgType; switch (msgType) { case MSG_ELEMENT_TYPE.TEXT: msgContent = new Msg.Elem.Text(msgBody.MsgContent.Text); break; case MSG_ELEMENT_TYPE.FACE: msgContent = new Msg.Elem.Face( msgBody.MsgContent.Index, msgBody.MsgContent.Data ); break; case MSG_ELEMENT_TYPE.IMAGE: msgContent = new Msg.Elem.Images( msgBody.MsgContent.UUID ); for (var j in msgBody.MsgContent.ImageInfoArray) { var tempImg = msgBody.MsgContent.ImageInfoArray[j]; msgContent.addImage( new Msg.Elem.Images.Image( tempImg.Type, tempImg.Size, tempImg.Width, tempImg.Height, tempImg.URL ) ); } break; case MSG_ELEMENT_TYPE.SOUND: // var soundUrl = getSoundDownUrl(msgBody.MsgContent.UUID, msgInfo.From_Account); if (msgBody.MsgContent) { msgContent = new Msg.Elem.Sound( msgBody.MsgContent.UUID, msgBody.MsgContent.Second, msgBody.MsgContent.Size, msgInfo.From_Account, msgInfo.To_Account, msgBody.MsgContent.Download_Flag, SESSION_TYPE.C2C ); } else { msgType = MSG_ELEMENT_TYPE.TEXT; msgContent = new Msg.Elem.Text('[语音消息]下载地址解析出错'); } break; case MSG_ELEMENT_TYPE.LOCATION: msgContent = new Msg.Elem.Location( msgBody.MsgContent.Longitude, msgBody.MsgContent.Latitude, msgBody.MsgContent.Desc ); break; case MSG_ELEMENT_TYPE.FILE: case MSG_ELEMENT_TYPE.FILE + " ": msgType = MSG_ELEMENT_TYPE.FILE; // var fileUrl = getFileDownUrl(msgBody.MsgContent.UUID, msgInfo.From_Account, msgBody.MsgContent.FileName); if (msgBody.MsgContent) { msgContent = new Msg.Elem.File( msgBody.MsgContent.UUID, msgBody.MsgContent.FileName, msgBody.MsgContent.FileSize, msgInfo.From_Account, msgInfo.To_Account, msgBody.MsgContent.Download_Flag, SESSION_TYPE.C2C ); } else { msgType = MSG_ELEMENT_TYPE.TEXT; msgContent = new Msg.Elem.Text('[文件消息下载地址解析出错]'); } break; case MSG_ELEMENT_TYPE.CUSTOM: try { var data = JSON.parse(msgBody.MsgContent.Data); if (data && data.userAction && data.userAction == FRIEND_WRITE_MSG_ACTION.ING) {//过滤安卓或ios的正在输入自定义消息 continue; } } catch (e) { } msgType = MSG_ELEMENT_TYPE.CUSTOM; msgContent = new Msg.Elem.Custom( msgBody.MsgContent.Data, msgBody.MsgContent.Desc, msgBody.MsgContent.Ext ); break; default : msgType = MSG_ELEMENT_TYPE.TEXT; msgContent = new Msg.Elem.Text('web端暂不支持' + msgBody.MsgType + '消息'); break; } msg.elems.push(new Msg.Elem(msgType, msgContent)); } if (msg.elems.length > 0 && MsgStore.addMsg(msg)) { notifyInfo.push(msg); } } // for loop //处理加群未决申请消息 handlerApplyJoinGroupSystemMsgs(resp.EventArray); if (notifyInfo.length > 0) MsgStore.updateTimeline(); if (cbOk) cbOk(notifyInfo); else if (notifyInfo.length > 0) { if (onMsgCallback) onMsgCallback(notifyInfo); } }, function (err) { log.error("getMsgs failed:" + err.ErrorInfo); if (cbErr) cbErr(err); }); }; //拉取C2C漫游消息 this.getC2CHistoryMsgs = function (options, cbOk, cbErr) { if (!options.Peer_Account) { if (cbErr) { cbErr(tool.getReturnError("Peer_Account is empty", -13)); return; } } if (!options.MaxCnt) { options.MaxCnt = 15; } if (options.MaxCnt <= 0) { if (cbErr) { cbErr(tool.getReturnError("MaxCnt should be greater than 0", -14)); return; } } if (options.MaxCnt > 15) { if (cbErr) { cbErr(tool.getReturnError("MaxCnt can not be greater than 15", -15)); return; } return; } if (options.MsgKey == null || options.MsgKey === undefined) { options.MsgKey = ''; } var opts = { 'Peer_Account': options.Peer_Account, 'MaxCnt': options.MaxCnt, 'LastMsgTime': options.LastMsgTime, 'MsgKey': options.MsgKey }; //读取c2c漫游消息 proto_getC2CHistoryMsgs(opts, function (resp) { var msgObjList = []; var msgInfos = []; //处理c2c消息 msgInfos = resp.MsgList;//返回的消息列表 var sess = MsgStore.sessByTypeId(SESSION_TYPE.C2C, options.Peer_Account); if (!sess) { sess = new Session(SESSION_TYPE.C2C, options.Peer_Account, options.Peer_Account, '', 0, 0); } for (var i in msgInfos) { var msgInfo = msgInfos[i]; var isSendMsg, id, headUrl; if (msgInfo.From_Account == ctx.identifier) {//当前用户发送的消息 isSendMsg = true; id = msgInfo.To_Account;//读取接收者信息 headUrl = ''; } else {//当前用户收到的消息 isSendMsg = false; id = msgInfo.From_Account;//读取发送者信息 headUrl = ''; } var msg = new Msg(sess, isSendMsg, msgInfo.MsgSeq, msgInfo.MsgRandom, msgInfo.MsgTimeStamp, msgInfo.From_Account); var msgBody = null; var msgContent = null; var msgType = null; for (var mi in msgInfo.MsgBody) { msgBody = msgInfo.MsgBody[mi]; msgType = msgBody.MsgType; switch (msgType) { case MSG_ELEMENT_TYPE.TEXT: msgContent = new Msg.Elem.Text(msgBody.MsgContent.Text); break; case MSG_ELEMENT_TYPE.FACE: msgContent = new Msg.Elem.Face( msgBody.MsgContent.Index, msgBody.MsgContent.Data ); break; case MSG_ELEMENT_TYPE.IMAGE: msgContent = new Msg.Elem.Images( msgBody.MsgContent.UUID ); for (var j in msgBody.MsgContent.ImageInfoArray) { var tempImg = msgBody.MsgContent.ImageInfoArray[j]; msgContent.addImage( new Msg.Elem.Images.Image( tempImg.Type, tempImg.Size, tempImg.Width, tempImg.Height, tempImg.URL ) ); } break; case MSG_ELEMENT_TYPE.SOUND: // var soundUrl = getSoundDownUrl(msgBody.MsgContent.UUID, msgInfo.From_Account); if (msgBody.MsgContent) { msgContent = new Msg.Elem.Sound( msgBody.MsgContent.UUID, msgBody.MsgContent.Second, msgBody.MsgContent.Size, msgInfo.From_Account, msgInfo.To_Account, msgBody.MsgContent.Download_Flag, SESSION_TYPE.C2C ); } else { msgType = MSG_ELEMENT_TYPE.TEXT; msgContent = new Msg.Elem.Text('[语音消息]下载地址解析出错'); } break; case MSG_ELEMENT_TYPE.LOCATION: msgContent = new Msg.Elem.Location( msgBody.MsgContent.Longitude, msgBody.MsgContent.Latitude, msgBody.MsgContent.Desc ); break; case MSG_ELEMENT_TYPE.FILE: case MSG_ELEMENT_TYPE.FILE + " ": msgType = MSG_ELEMENT_TYPE.FILE; // var fileUrl = getFileDownUrl(msgBody.MsgContent.UUID, msgInfo.From_Account, msgBody.MsgContent.FileName); if (msgBody.MsgContent) { msgContent = new Msg.Elem.File( msgBody.MsgContent.UUID, msgBody.MsgContent.FileName, msgBody.MsgContent.FileSize, msgInfo.From_Account, msgInfo.To_Account, msgBody.MsgContent.Download_Flag, SESSION_TYPE.C2C ); } else { msgType = MSG_ELEMENT_TYPE.TEXT; msgContent = new Msg.Elem.Text('[文件消息下载地址解析出错]'); } break; case MSG_ELEMENT_TYPE.CUSTOM: msgType = MSG_ELEMENT_TYPE.CUSTOM; msgContent = new Msg.Elem.Custom( msgBody.MsgContent.Data, msgBody.MsgContent.Desc, msgBody.MsgContent.Ext ); break; default : msgType = MSG_ELEMENT_TYPE.TEXT; msgContent = new Msg.Elem.Text('web端暂不支持' + msgBody.MsgType + '消息'); break; } msg.elems.push(new Msg.Elem(msgType, msgContent)); } MsgStore.addMsg(msg); msgObjList.push(msg); } // for loop MsgStore.updateTimeline(); if (cbOk) { var newResp = { 'Complete': resp.Complete, 'MsgCount': msgObjList.length, 'LastMsgTime': resp.LastMsgTime, 'MsgKey': resp.MsgKey, 'MsgList': msgObjList }; sess.isFinished(resp.Complete); cbOk(newResp); } }, function (err) { log.error("getC2CHistoryMsgs failed:" + err.ErrorInfo); if (cbErr) cbErr(err); }); }; //拉群历史消息 //不传cbOk 和 cbErr,则会调用新消息回调函数 this.syncGroupMsgs = function (options, cbOk, cbErr) { if (options.ReqMsgSeq <= 0) { if (cbErr) { var errInfo = "ReqMsgSeq must be greater than 0"; var error = tool.getReturnError(errInfo, -16); cbErr(error); } return; } var opts = { 'GroupId': options.GroupId, 'ReqMsgSeq': options.ReqMsgSeq, 'ReqMsgNumber': options.ReqMsgNumber }; //读群漫游消息 proto_getGroupMsgs(opts, function (resp) { var notifyInfo = []; var group_id = resp.GroupId;//返回的群id var msgInfos = resp.RspMsgList;//返回的消息列表 var isFinished = resp.IsFinished; if (msgInfos == null || msgInfos === undefined) { if (cbOk) { cbOk([]); } return; } for (var i = msgInfos.length - 1; i >= 0; i--) { var msgInfo = msgInfos[i]; //如果是已经删除的消息或者发送者帐号为空或者消息内容为空 //IsPlaceMsg=1 if (msgInfo.IsPlaceMsg || !msgInfo.From_Account || !msgInfo.MsgBody || msgInfo.MsgBody.length == 0) { continue; } var msg = handlerGroupMsg(msgInfo, true, true,isFinished); if (msg) { notifyInfo.push(msg); } } // for loop if (notifyInfo.length > 0) MsgStore.updateTimeline(); if (cbOk) cbOk(notifyInfo); else if (notifyInfo.length > 0) { if (onMsgCallback) onMsgCallback(notifyInfo); } }, function (err) { log.error("getGroupMsgs failed:" + err.ErrorInfo); if (cbErr) cbErr(err); }); }; //处理群消息(普通消息+提示消息) //isSyncGroupMsgs 是否主动拉取群消息标志 //isAddMsgFlag 是否需要保存到MsgStore,如果需要,这里会存在判重逻辑 var handlerGroupMsg = function (msgInfo, isSyncGroupMsgs, isAddMsgFlag ,isFinished) { if (msgInfo.IsPlaceMsg || !msgInfo.From_Account || !msgInfo.MsgBody || msgInfo.MsgBody.length == 0) { return null; } var isSendMsg, id, headUrl, fromAccountNick; var group_id = msgInfo.ToGroupId; var group_name = group_id; if (msgInfo.GroupInfo) {//取出群名称 if (msgInfo.GroupInfo.GroupName) { group_name = msgInfo.GroupInfo.GroupName; } } //取出成员昵称 fromAccountNick = msgInfo.From_Account; if (msgInfo.GroupInfo) { if (msgInfo.GroupInfo.From_AccountNick) { fromAccountNick = msgInfo.GroupInfo.From_AccountNick; } } if (msgInfo.From_Account == ctx.identifier) {//当前用户发送的消息 isSendMsg = true; id = msgInfo.From_Account;//读取接收者信息 headUrl = ''; } else {//当前用户收到的消息 isSendMsg = false; id = msgInfo.From_Account;//读取发送者信息 headUrl = ''; } var sess = MsgStore.sessByTypeId(SESSION_TYPE.GROUP, group_id); if (!sess) { sess = new Session(SESSION_TYPE.GROUP, group_id, group_name, headUrl, 0, 0); } if(typeof isFinished !=="undefined") { sess.isFinished(isFinished || 0); } var subType = GROUP_MSG_SUB_TYPE.COMMON;//消息类型 //群提示消息,重新封装下 if (LONG_POLLINNG_EVENT_TYPE.GROUP_TIP == msgInfo.Event || LONG_POLLINNG_EVENT_TYPE.GROUP_TIP2 == msgInfo.Event) { subType = GROUP_MSG_SUB_TYPE.TIP; var groupTip = msgInfo.MsgBody; msgInfo.MsgBody = []; msgInfo.MsgBody.push({ "MsgType": MSG_ELEMENT_TYPE.GROUP_TIP, "MsgContent": groupTip } ); } else if (msgInfo.MsgPriority) {//群点赞消息 if (msgInfo.MsgPriority == GROUP_MSG_PRIORITY_TYPE.REDPACKET) { subType = GROUP_MSG_SUB_TYPE.REDPACKET; } else if (msgInfo.MsgPriority == GROUP_MSG_PRIORITY_TYPE.LOVEMSG) { subType = GROUP_MSG_SUB_TYPE.LOVEMSG; } } var msg = new Msg(sess, isSendMsg, msgInfo.MsgSeq, msgInfo.MsgRandom, msgInfo.MsgTimeStamp, msgInfo.From_Account, subType, fromAccountNick); var msgBody = null; var msgContent = null; var msgType = null; for (var mi in msgInfo.MsgBody) { msgBody = msgInfo.MsgBody[mi]; msgType = msgBody.MsgType; switch (msgType) { case MSG_ELEMENT_TYPE.TEXT: msgContent = new Msg.Elem.Text(msgBody.MsgContent.Text); break; case MSG_ELEMENT_TYPE.FACE: msgContent = new Msg.Elem.Face( msgBody.MsgContent.Index, msgBody.MsgContent.Data ); break; case MSG_ELEMENT_TYPE.IMAGE: msgContent = new Msg.Elem.Images( msgBody.MsgContent.UUID ); for (var j in msgBody.MsgContent.ImageInfoArray) { msgContent.addImage( new Msg.Elem.Images.Image( msgBody.MsgContent.ImageInfoArray[j].Type, msgBody.MsgContent.ImageInfoArray[j].Size, msgBody.MsgContent.ImageInfoArray[j].Width, msgBody.MsgContent.ImageInfoArray[j].Height, msgBody.MsgContent.ImageInfoArray[j].URL ) ); } break; case MSG_ELEMENT_TYPE.SOUND: if (msgBody.MsgContent) { msgContent = new Msg.Elem.Sound( msgBody.MsgContent.UUID, msgBody.MsgContent.Second, msgBody.MsgContent.Size, msgInfo.From_Account, msgInfo.To_Account, msgBody.MsgContent.Download_Flag, SESSION_TYPE.GROUP ); } else { msgType = MSG_ELEMENT_TYPE.TEXT; msgContent = new Msg.Elem.Text('[语音消息]下载地址解析出错'); } break; case MSG_ELEMENT_TYPE.LOCATION: msgContent = new Msg.Elem.Location( msgBody.MsgContent.Longitude, msgBody.MsgContent.Latitude, msgBody.MsgContent.Desc ); break; case MSG_ELEMENT_TYPE.FILE: case MSG_ELEMENT_TYPE.FILE + " ": msgType = MSG_ELEMENT_TYPE.FILE; var fileUrl = getFileDownUrl(msgBody.MsgContent.UUID, msgInfo.From_Account, msgBody.MsgContent.FileName); if (msgBody.MsgContent) { msgContent = new Msg.Elem.File( msgBody.MsgContent.UUID, msgBody.MsgContent.FileName, msgBody.MsgContent.FileSize, msgInfo.From_Account, msgInfo.To_Account, msgBody.MsgContent.Download_Flag, SESSION_TYPE.GROUP ); } else { msgType = MSG_ELEMENT_TYPE.TEXT; msgContent = new Msg.Elem.Text('[文件消息]地址解析出错'); } break; case MSG_ELEMENT_TYPE.GROUP_TIP: var opType = msgBody.MsgContent.OpType; msgContent = new Msg.Elem.GroupTip( opType, msgBody.MsgContent.Operator_Account, group_id, msgInfo.GroupInfo.GroupName, msgBody.MsgContent.List_Account ); if (GROUP_TIP_TYPE.JOIN == opType || GROUP_TIP_TYPE.QUIT == opType) {//加群或退群时,设置最新群成员数 msgContent.setGroupMemberNum(msgBody.MsgContent.MemberNum); } else if (GROUP_TIP_TYPE.MODIFY_GROUP_INFO == opType) {//群资料变更 var tempIsCallbackFlag = false; var tempNewGroupInfo = { "GroupId": group_id, "GroupFaceUrl": null, "GroupName": null, "OwnerAccount": null, "GroupNotification": null, "GroupIntroduction": null }; var msgGroupNewInfo = msgBody.MsgContent.MsgGroupNewInfo; if (msgGroupNewInfo.GroupFaceUrl) { var tmpNGIFaceUrl = new Msg.Elem.GroupTip.GroupInfo( GROUP_TIP_MODIFY_GROUP_INFO_TYPE.FACE_URL, msgGroupNewInfo.GroupFaceUrl ); msgContent.addGroupInfo(tmpNGIFaceUrl); tempIsCallbackFlag = true; tempNewGroupInfo.GroupFaceUrl = msgGroupNewInfo.GroupFaceUrl; } if (msgGroupNewInfo.GroupName) { var tmpNGIName = new Msg.Elem.GroupTip.GroupInfo( GROUP_TIP_MODIFY_GROUP_INFO_TYPE.NAME, msgGroupNewInfo.GroupName ); msgContent.addGroupInfo(tmpNGIName); tempIsCallbackFlag = true; tempNewGroupInfo.GroupName = msgGroupNewInfo.GroupName; } if (msgGroupNewInfo.Owner_Account) { var tmpNGIOwner = new Msg.Elem.GroupTip.GroupInfo( GROUP_TIP_MODIFY_GROUP_INFO_TYPE.OWNER, msgGroupNewInfo.Owner_Account ); msgContent.addGroupInfo(tmpNGIOwner); tempIsCallbackFlag = true; tempNewGroupInfo.OwnerAccount = msgGroupNewInfo.Owner_Account; } if (msgGroupNewInfo.GroupNotification) { var tmpNGINotification = new Msg.Elem.GroupTip.GroupInfo( GROUP_TIP_MODIFY_GROUP_INFO_TYPE.NOTIFICATION, msgGroupNewInfo.GroupNotification ); msgContent.addGroupInfo(tmpNGINotification); tempIsCallbackFlag = true; tempNewGroupInfo.GroupNotification = msgGroupNewInfo.GroupNotification; } if (msgGroupNewInfo.GroupIntroduction) { var tmpNGIIntroduction = new Msg.Elem.GroupTip.GroupInfo( GROUP_TIP_MODIFY_GROUP_INFO_TYPE.INTRODUCTION, msgGroupNewInfo.GroupIntroduction ); msgContent.addGroupInfo(tmpNGIIntroduction); tempIsCallbackFlag = true; tempNewGroupInfo.GroupIntroduction = msgGroupNewInfo.GroupIntroduction; } //回调群资料变化通知方法 if (isSyncGroupMsgs == false && tempIsCallbackFlag && onGroupInfoChangeCallback) { onGroupInfoChangeCallback(tempNewGroupInfo); } } else if (GROUP_TIP_TYPE.MODIFY_MEMBER_INFO == opType) {//群成员变更 var memberInfos = msgBody.MsgContent.MsgMemberInfo; for (var n in memberInfos) { var memberInfo = memberInfos[n]; msgContent.addMemberInfo( new Msg.Elem.GroupTip.MemberInfo( memberInfo.User_Account, memberInfo.ShutupTime ) ); } } break; case MSG_ELEMENT_TYPE.CUSTOM: msgType = MSG_ELEMENT_TYPE.CUSTOM; msgContent = new Msg.Elem.Custom( msgBody.MsgContent.Data, msgBody.MsgContent.Desc, msgBody.MsgContent.Ext ); break; default : msgType = MSG_ELEMENT_TYPE.TEXT; msgContent = new Msg.Elem.Text('web端暂不支持' + msgBody.MsgType + '消息'); break; } msg.elems.push(new Msg.Elem(msgType, msgContent)); } if (isAddMsgFlag == false) {//不需要保存消息 return msg; } if (MsgStore.addMsg(msg)) { return msg; } else { return null; } }; //初始化 this.init = function (listeners, cbOk, cbErr) { if (!listeners.onMsgNotify) { log.warn('listeners.onMsgNotify is empty'); } onMsgCallback = listeners.onMsgNotify; if (listeners.onBigGroupMsgNotify) { onBigGroupMsgCallback = listeners.onBigGroupMsgNotify; } else { log.warn('listeners.onBigGroupMsgNotify is empty'); } if (listeners.onC2cEventNotifys) { onC2cEventCallbacks = listeners.onC2cEventNotifys; } else { log.warn('listeners.onC2cEventNotifys is empty'); } if (listeners.onGroupSystemNotifys) { onGroupSystemNotifyCallbacks = listeners.onGroupSystemNotifys; } else { log.warn('listeners.onGroupSystemNotifys is empty'); } if (listeners.onGroupInfoChangeNotify) { onGroupInfoChangeCallback = listeners.onGroupInfoChangeNotify; } else { log.warn('listeners.onGroupInfoChangeNotify is empty'); } if (listeners.onFriendSystemNotifys) { onFriendSystemNotifyCallbacks = listeners.onFriendSystemNotifys; } else { log.warn('listeners.onFriendSystemNotifys is empty'); } if (listeners.onProfileSystemNotifys) { onProfileSystemNotifyCallbacks = listeners.onProfileSystemNotifys; } else { log.warn('listeners.onProfileSystemNotifys is empty'); } if (listeners.onKickedEventCall) { onKickedEventCall = listeners.onKickedEventCall; } else { log.warn('listeners.onKickedEventCall is empty'); } if (listeners.onAppliedDownloadUrl) { onAppliedDownloadUrl = listeners.onAppliedDownloadUrl; } else { log.warn('listeners.onAppliedDownloadUrl is empty'); } if (!ctx.identifier || !ctx.userSig) { if (cbOk) { var success = { 'ActionStatus': ACTION_STATUS.OK, 'ErrorCode': 0, 'ErrorInfo': "login success(no login state)" }; cbOk(success); } return; } //初始化 initMyGroupMaxSeqs( function (resp) { log.info('initMyGroupMaxSeqs success'); //初始化文件 initIpAndAuthkey( function (initIpAndAuthkeyResp) { log.info('initIpAndAuthkey success'); if (cbOk) { log.info('login success(have login state))'); var success = { 'ActionStatus': ACTION_STATUS.OK, 'ErrorCode': 0, 'ErrorInfo': "login success" }; cbOk(success); } MsgManager.setLongPollingOn(true);//开启长轮询 longPollingOn && MsgManager.longPolling(cbOk); }, cbErr); }, cbErr); }; //发消息(私聊或群聊) this.sendMsg = function (msg, cbOk, cbErr) { proto_sendMsg(msg, function (resp) { //私聊时,加入自己的发的消息,群聊时,由于seq和服务器的seq不一样,所以不作处理 if (msg.sess.type() == SESSION_TYPE.C2C) { if (!MsgStore.addMsg(msg)) { var errInfo = "sendMsg: addMsg failed!"; var error = tool.getReturnError(errInfo, -17); log.error(errInfo); if (cbErr) cbErr(error); return; } //更新信息流时间 MsgStore.updateTimeline(); } if (cbOk) cbOk(resp); }, function (err) { if (cbErr) cbErr(err); }); }; }; //上传文件 var FileUploader = new function () { this.fileMd5 = null; //获取文件MD5 var getFileMD5 = function (file, cbOk, cbErr) { //FileReader pc浏览器兼容性 //Feature Firefox (Gecko) Chrome Internet Explorer Opera Safari //Basic support 3.6 7 10 12.02 6.0.2 var fileReader = null; try { fileReader = new FileReader();//分块读取文件对象 } catch (e) { if (cbErr) { cbErr(tool.getReturnError('当前浏览器不支持FileReader', -18)); return; } } //file的slice方法,注意它的兼容性,在不同浏览器的写法不同 var blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice; if (!blobSlice) { if (cbErr) { cbErr(tool.getReturnError('当前浏览器不支持FileAPI', -19)); return; } } var chunkSize = 2 * 1024 * 1024;//分块大小,2M var chunks = Math.ceil(file.size / chunkSize);//总块数 var currentChunk = 0;//当前块数 var spark = new SparkMD5();//获取MD5对象 fileReader.onload = function (e) {//数据加载完毕事件 var binaryStr = ""; var bytes = new Uint8Array(e.target.result); var length = bytes.byteLength; for (var i = 0; i < length; i++) { binaryStr += String.fromCharCode(bytes[i]);//二进制转换字符串 } spark.appendBinary(binaryStr); currentChunk++; if (currentChunk < chunks) { loadNext();//读取下一块数据 } else { this.fileMd5 = spark.end();//得到文件MD5值 if (cbOk) { cbOk(this.fileMd5); } } }; //分片读取文件 function loadNext() { var start = currentChunk * chunkSize, end = start + chunkSize >= file.size ? file.size : start + chunkSize; //根据开始和结束位置,切割文件 var b = blobSlice.call(file, start, end); //readAsBinaryString ie浏览器不兼容此方法 //fileReader.readAsBinaryString(blobSlice.call(file, start, end)); fileReader.readAsArrayBuffer(b);//ie,chrome,firefox等主流浏览器兼容此方法 } loadNext();//开始读取 }; //提交上传图片表单(用于低版本IE9以下) this.submitUploadFileForm = function (options, cbOk, cbErr) { var errInfo; var error; var formId = options.formId; var fileId = options.fileId; var iframeNum = uploadResultIframeId++; var iframeName = "uploadResultIframe_" + iframeNum; var toAccount = options.To_Account; var businessType = options.businessType; var form = document.getElementById(formId); if (!form) { errInfo = "获取表单对象为空: formId=" + formId + "(formId非法)"; error = tool.getReturnError(errInfo, -20); if (cbErr) cbErr(error); return; } var fileObj = document.getElementById(fileId); if (!fileObj) { errInfo = "获取文件对象为空: fileId=" + fileId + "(没有选择文件或者fileId非法)"; error = tool.getReturnError(errInfo, -21); if (cbErr) cbErr(error); return; } //fileObj.type="file";//ie8下不起作用,必须由业务自己设置 fileObj.name = "file"; var iframe = document.createElement("iframe"); iframe.name = iframeName; iframe.id = iframeName; iframe.style.display = "none"; document.body.appendChild(iframe); var cmdName; if (isAccessFormalEnv()) { cmdName = 'pic_up'; } else { cmdName = 'pic_up_test'; } var uploadApiUrl = "https://pic.tim.qq.com/v4/openpic/" + cmdName + "?tinyid=" + ctx.tinyid + "&a2=" + ctx.a2 + "&sdkappid=" + ctx.sdkAppID + "&accounttype=" + ctx.accountType + "&contenttype=http"; form.action = uploadApiUrl; form.method = 'post'; //form.enctype='multipart/form-data';//ie8下不起作用,必须由业务自己设置 form.target = iframeName; function createFormInput(name, value) { var tempInput = document.createElement("input"); tempInput.type = "hidden"; tempInput.name = name; tempInput.value = value; form.appendChild(tempInput); } createFormInput("App_Version", VERSION_INFO.APP_VERSION); createFormInput("From_Account", ctx.identifier); createFormInput("To_Account", toAccount); createFormInput("Seq", nextSeq().toString()); createFormInput("Timestamp", unixtime().toString()); createFormInput("Random", createRandom().toString()); createFormInput("Busi_Id", businessType); createFormInput("PkgFlag", UPLOAD_RES_PKG_FLAG.RAW_DATA.toString()); createFormInput("Auth_Key", authkey); createFormInput("Server_Ver", VERSION_INFO.SERVER_VERSION.toString()); createFormInput("File_Type", options.fileType); //检测iframe.contentWindow.name是否有值 function checkFrameName() { var resp; try { resp = JSON.parse(iframe.contentWindow.name) || {}; } catch (e) { resp = {}; } if (resp.ActionStatus) {//上传接口返回 // We've got what we need. Stop the iframe from loading further content. iframe.src = "about:blank"; iframe.parentNode.removeChild(iframe); iframe = null; if (resp.ActionStatus == ACTION_STATUS.OK) { cbOk && cbOk(resp); } else { cbErr && cbErr(resp); } } else { setTimeout(checkFrameName, 100); } } setTimeout(checkFrameName, 500); form.submit();//提交上传图片表单 }; //上传图片或文件(用于高版本浏览器,支持FileAPI) this.uploadFile = function (options, cbOk, cbErr) { var file_upload = { //初始化 init: function (options, cbOk, cbErr) { var me = this; me.file = options.file; //分片上传进度回调事件 me.onProgressCallBack = options.onProgressCallBack; //停止上传图片按钮 if (options.abortButton) { options.abortButton.onclick = me.abortHandler; } me.total = me.file.size;//文件总大小 me.loaded = 0;//已读取字节数 me.step = 1080 * 1024;//分块大小,1080K me.sliceSize = 0;//分片大小 me.sliceOffset = 0;//当前分片位置 me.timestamp = unixtime();//当前时间戳 me.seq = nextSeq();//请求seq me.random = createRandom();//请求随机数 me.fromAccount = ctx.identifier;//发送者 me.toAccount = options.To_Account;//接收者 me.fileMd5 = options.fileMd5;//文件MD5 me.businessType = options.businessType;//图片或文件的业务类型,群消息:1; c2c消息:2; 个人头像:3; 群头像:4; me.fileType = options.fileType;//文件类型,不填为默认认为上传的是图片;1:图片;2:文件;3:短视频;4:PTT me.cbOk = cbOk;//上传成功回调事件 me.cbErr = cbErr;//上传失败回调事件 me.reader = new FileReader();//读取文件对象 me.blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice;//file的slice方法,不同浏览器不一样 me.reader.onloadstart = me.onLoadStart;//开始读取回调事件 me.reader.onprogress = me.onProgress;//读取文件进度回调事件 me.reader.onabort = me.onAbort;//停止读取回调事件 me.reader.onerror = me.onerror;//读取发生错误回调事件 me.reader.onload = me.onLoad;//分片加载完毕回调事件 me.reader.onloadend = me.onLoadEnd;//读取文件完毕回调事件 }, //上传方法 upload: function () { var me = file_upload; //读取第一块 me.readBlob(0); }, onLoadStart: function () { var me = file_upload; }, onProgress: function (e) { var me = file_upload; me.loaded += e.loaded; if (me.onProgressCallBack) { me.onProgressCallBack(me.loaded, me.total); } }, onAbort: function () { var me = file_upload; }, onError: function () { var me = file_upload; }, onLoad: function (e) { var me = file_upload; if (e.target.readyState == FileReader.DONE) { var slice_data_base64 = e.target.result; //注意,一定要去除base64编码头部 var pos = slice_data_base64.indexOf(","); if (pos != -1) { slice_data_base64 = slice_data_base64.substr(pos + 1); } //封装上传图片接口的请求参数 var opt = { 'From_Account': me.fromAccount, 'To_Account': me.toAccount, 'Busi_Id': me.businessType, 'File_Type': me.fileType, 'File_Str_Md5': me.fileMd5, 'PkgFlag': UPLOAD_RES_PKG_FLAG.BASE64_DATA, 'File_Size': me.total, 'Slice_Offset': me.sliceOffset, 'Slice_Size': me.sliceSize, 'Slice_Data': slice_data_base64, 'Seq': me.seq, 'Timestamp': me.timestamp, 'Random': me.random }; //上传成功的成功回调 var succCallback = function (resp) { if (resp.IsFinish == 0) { me.loaded = resp.Next_Offset; if (me.loaded < me.total) { me.readBlob(me.loaded); } else { me.loaded = me.total; } } else { if (me.cbOk) { var tempResp = { 'ActionStatus': resp.ActionStatus, 'ErrorCode': resp.ErrorCode, 'ErrorInfo': resp.ErrorInfo, 'File_UUID': resp.File_UUID, 'File_Size': resp.Next_Offset, 'URL_INFO': resp.URL_INFO, 'Download_Flag':resp.Download_Flag }; if (me.fileType == UPLOAD_RES_TYPE.FILE) {//如果上传的是文件,下载地址需要sdk内部拼接 tempResp.URL_INFO = getFileDownUrl(resp.File_UUID, ctx.identifier, me.file.name); } me.cbOk(tempResp); } } Upload_Retry_Times = 0; }; //上传失败的回调 var errorCallback = function(resp){ if(Upload_Retry_Times < Upload_Retry_Max_Times){ Upload_Retry_Times ++; setTimeout(function(){ proto_uploadPic(opt,succCallback,errorCallback); },1000); }else{ me.cbErr(resp); } //me.cbErr }; //分片上传图片接口 proto_uploadPic(opt, succCallback,errorCallback); } }, onLoadEnd: function () { var me = file_upload; }, //分片读取文件方法 readBlob: function (start) { var me = file_upload; var blob, file = me.file; var end = start + me.step; if (end > me.total) { end = me.total; me.sliceSize = end - start; } else { me.sliceSize = me.step; } me.sliceOffset = start; //根据起始和结束位置,分片读取文件 blob = me.blobSlice.call(file, start, end); //将分片的二进制数据转换为base64编码 me.reader.readAsDataURL(blob); }, abortHandler: function () { var me = file_upload; if (me.reader) { me.reader.abort(); } } }; //读取文件MD5 getFileMD5(options.file, function (fileMd5) { log.info('fileMd5: ' + fileMd5); options.fileMd5 = fileMd5; //初始化上传参数 file_upload.init(options, cbOk, cbErr); //开始上传文件 file_upload.upload(); }, cbErr ); }; }; //web im 基础对象 //常量对象 //会话类型 webim.SESSION_TYPE = SESSION_TYPE; webim.MSG_MAX_LENGTH = MSG_MAX_LENGTH; //c2c消息子类型 webim.C2C_MSG_SUB_TYPE = C2C_MSG_SUB_TYPE; //群消息子类型 webim.GROUP_MSG_SUB_TYPE = GROUP_MSG_SUB_TYPE; //消息元素类型 webim.MSG_ELEMENT_TYPE = MSG_ELEMENT_TYPE; //群提示消息类型 webim.GROUP_TIP_TYPE = GROUP_TIP_TYPE; //图片类型 webim.IMAGE_TYPE = IMAGE_TYPE; //群系统消息类型 webim.GROUP_SYSTEM_TYPE = GROUP_SYSTEM_TYPE; //好友系统通知子类型 webim.FRIEND_NOTICE_TYPE = FRIEND_NOTICE_TYPE; //群提示消息-群资料变更类型 webim.GROUP_TIP_MODIFY_GROUP_INFO_TYPE = GROUP_TIP_MODIFY_GROUP_INFO_TYPE; //浏览器信息 webim.BROWSER_INFO = BROWSER_INFO; //表情对象 webim.Emotions = webim.EmotionPicData = emotions; //表情标识符和index Map webim.EmotionDataIndexs = webim.EmotionPicDataIndex = emotionDataIndexs; //腾讯登录服务错误码(托管模式) webim.TLS_ERROR_CODE = TLS_ERROR_CODE; //连接状态 webim.CONNECTION_STATUS = CONNECTION_STATUS; //上传图片业务类型 webim.UPLOAD_PIC_BUSSINESS_TYPE = UPLOAD_PIC_BUSSINESS_TYPE; //最近联系人类型 webim.RECENT_CONTACT_TYPE = RECENT_CONTACT_TYPE; //上传资源类型 webim.UPLOAD_RES_TYPE = UPLOAD_RES_TYPE; /**************************************/ //类对象 // //工具对象 webim.Tool = tool; //控制台打印日志对象 webim.Log = log; //消息对象 webim.Msg = Msg; //会话对象 webim.Session = Session; //会话存储对象 webim.MsgStore = { sessMap: function () { return MsgStore.sessMap(); }, sessCount: function () { return MsgStore.sessCount(); }, sessByTypeId: function (type, id) { return MsgStore.sessByTypeId(type, id); }, delSessByTypeId: function (type, id) { return MsgStore.delSessByTypeId(type, id); }, resetCookieAndSyncFlag: function () { return MsgStore.resetCookieAndSyncFlag(); } }; webim.Resources = Resources; /**************************************/ // webim API impl // //基本接口 //登录 webim.login = webim.init = function (loginInfo, listeners, opts, cbOk, cbErr) { //初始化连接状态回调函数 ConnManager.init(listeners.onConnNotify, cbOk, cbErr); //设置ie9以下浏览器jsonp回调 if (listeners.jsonpCallback) jsonpCallback = listeners.jsonpCallback; //登录 _login(loginInfo, listeners, opts, cbOk, cbErr); }; //登出 //需要传长轮询id //这样登出之后其他的登录实例还可以继续收取消息 webim.logout = webim.offline = function (cbOk, cbErr) { return proto_logout('instance',cbOk, cbErr); }; //登出 //这种登出方式,所有的实例都将不会收到消息推送,直到重新登录 webim.logoutAll = function (cbOk, cbErr) { return proto_logout('all',cbOk, cbErr); }; //消息管理接口 //发消息接口(私聊和群聊) webim.sendMsg = function (msg, cbOk, cbErr) { return MsgManager.sendMsg(msg, cbOk, cbErr); }; //拉取未读c2c消息 webim.syncMsgs = function (cbOk, cbErr) { return MsgManager.syncMsgs(cbOk, cbErr); }; //拉取C2C漫游消息 webim.getC2CHistoryMsgs = function (options, cbOk, cbErr) { return MsgManager.getC2CHistoryMsgs(options, cbOk, cbErr); }; //拉取群漫游消息 webim.syncGroupMsgs = function (options, cbOk, cbErr) { return MsgManager.syncGroupMsgs(options, cbOk, cbErr); }; //上报c2c消息已读 webim.c2CMsgReaded = function (options, cbOk, cbErr) { return MsgStore.c2CMsgReaded(options, cbOk, cbErr); }; //上报群消息已读 webim.groupMsgReaded = function (options, cbOk, cbErr) { return proto_groupMsgReaded(options, cbOk, cbErr); }; //设置聊天会话自动标记已读 webim.setAutoRead = function (selSess, isOn, isResetAll) { return MsgStore.setAutoRead(selSess, isOn, isResetAll); }; //群组管理接口 // //创建群 webim.createGroup = function (options, cbOk, cbErr) { return proto_createGroup(options, cbOk, cbErr); }; //创建群-高级接口 webim.createGroupHigh = function (options, cbOk, cbErr) { return proto_createGroupHigh(options, cbOk, cbErr); }; //申请加群 webim.applyJoinGroup = function (options, cbOk, cbErr) { return proto_applyJoinGroup(options, cbOk, cbErr); }; //处理加群申请(同意或拒绝) webim.handleApplyJoinGroupPendency = function (options, cbOk, cbErr) { return proto_handleApplyJoinGroupPendency(options, cbOk, cbErr); }; //删除加群申请 webim.deleteApplyJoinGroupPendency = function (options, cbOk, cbErr) { return proto_deleteC2CMsg(options, cbOk, cbErr); }; //主动退群 webim.quitGroup = function (options, cbOk, cbErr) { return proto_quitGroup(options, cbOk, cbErr); }; //搜索群组(根据名称) webim.searchGroupByName = function (options, cbOk, cbErr) { return proto_searchGroupByName(options, cbOk, cbErr); }; //获取群组公开资料(根据群id搜索) webim.getGroupPublicInfo = function (options, cbOk, cbErr) { return proto_getGroupPublicInfo(options, cbOk, cbErr); }; //获取群组详细资料-高级接口 webim.getGroupInfo = function (options, cbOk, cbErr) { return proto_getGroupInfo(options, cbOk, cbErr); }; //修改群基本资料 webim.modifyGroupBaseInfo = function (options, cbOk, cbErr) { return proto_modifyGroupBaseInfo(options, cbOk, cbErr); }; //获取群成员列表 webim.getGroupMemberInfo = function (options, cbOk, cbErr) { return proto_getGroupMemberInfo(options, cbOk, cbErr); }; //邀请好友加群 webim.addGroupMember = function (options, cbOk, cbErr) { return proto_addGroupMember(options, cbOk, cbErr); }; //修改群成员资料 webim.modifyGroupMember = function (options, cbOk, cbErr) { return proto_modifyGroupMember(options, cbOk, cbErr); }; //删除群成员 webim.deleteGroupMember = function (options, cbOk, cbErr) { return proto_deleteGroupMember(options, cbOk, cbErr); }; //解散群 webim.destroyGroup = function (options, cbOk, cbErr) { return proto_destroyGroup(options, cbOk, cbErr); }; //转让群组 webim.changeGroupOwner = function (options, cbOk, cbErr) { return proto_changeGroupOwner(options, cbOk, cbErr); }; //获取我的群组列表-高级接口 webim.getJoinedGroupListHigh = function (options, cbOk, cbErr) { return proto_getJoinedGroupListHigh(options, cbOk, cbErr); }; //获取群成员角色 webim.getRoleInGroup = function (options, cbOk, cbErr) { return proto_getRoleInGroup(options, cbOk, cbErr); }; //设置群成员禁言时间 webim.forbidSendMsg = function (options, cbOk, cbErr) { return proto_forbidSendMsg(options, cbOk, cbErr); }; //发送自定义群系统通知 webim.sendCustomGroupNotify = function (options, cbOk, cbErr) { return proto_sendCustomGroupNotify(options, cbOk, cbErr); }; //进入大群 webim.applyJoinBigGroup = function (options, cbOk, cbErr) { return proto_applyJoinBigGroup(options, cbOk, cbErr); }; //退出大群 webim.quitBigGroup = function (options, cbOk, cbErr) { return proto_quitBigGroup(options, cbOk, cbErr); }; //资料关系链管理接口 // //获取个人资料接口,可用于搜索用户 webim.getProfilePortrait = function (options, cbOk, cbErr) { return proto_getProfilePortrait(options, cbOk, cbErr); }; //设置个人资料 webim.setProfilePortrait = function (options, cbOk, cbErr) { return proto_setProfilePortrait(options, cbOk, cbErr); }; //申请加好友 webim.applyAddFriend = function (options, cbOk, cbErr) { return proto_applyAddFriend(options, cbOk, cbErr); }; //获取好友申请列表 webim.getPendency = function (options, cbOk, cbErr) { return proto_getPendency(options, cbOk, cbErr); }; //删除好友申请 webim.deletePendency = function (options, cbOk, cbErr) { return proto_deletePendency(options, cbOk, cbErr); }; //处理好友申请 webim.responseFriend = function (options, cbOk, cbErr) { return proto_responseFriend(options, cbOk, cbErr); }; //获取我的好友 webim.getAllFriend = function (options, cbOk, cbErr) { return proto_getAllFriend(options, cbOk, cbErr); }; //删除好友 webim.deleteFriend = function (options, cbOk, cbErr) { return proto_deleteFriend(options, cbOk, cbErr); }; //拉黑 webim.addBlackList = function (options, cbOk, cbErr) { return proto_addBlackList(options, cbOk, cbErr); }; //删除黑名单 webim.deleteBlackList = function (options, cbOk, cbErr) { return proto_deleteBlackList(options, cbOk, cbErr); }; //获取我的黑名单 webim.getBlackList = function (options, cbOk, cbErr) { return proto_getBlackList(options, cbOk, cbErr); }; //获取最近会话 webim.getRecentContactList = function (options, cbOk, cbErr) { return proto_getRecentContactList(options, cbOk, cbErr); }; //图片或文件服务接口 // //上传文件接口(高版本浏览器) webim.uploadFile = webim.uploadPic = function (options, cbOk, cbErr) { return FileUploader.uploadFile(options, cbOk, cbErr); }; //提交上传图片表单接口(用于低版本ie) webim.submitUploadFileForm = function (options, cbOk, cbErr) { return FileUploader.submitUploadFileForm(options, cbOk, cbErr); }; //上传图片或文件(Base64)接口 webim.uploadFileByBase64 = webim.uploadPicByBase64 = function (options, cbOk, cbErr) { //请求参数 var opt = { 'To_Account': options.toAccount, 'Busi_Id': options.businessType, 'File_Type': options.File_Type, 'File_Str_Md5': options.fileMd5, 'PkgFlag': UPLOAD_RES_PKG_FLAG.BASE64_DATA, 'File_Size': options.totalSize, 'Slice_Offset': 0, 'Slice_Size': options.totalSize, 'Slice_Data': options.base64Str, 'Seq': nextSeq(), 'Timestamp': unixtime(), 'Random': createRandom() }; return proto_uploadPic(opt, cbOk, cbErr); }; //设置jsonp返回的值 webim.setJsonpLastRspData = function (rspData) { jsonpLastRspData = typeof (rspData) == "string" ? JSON.parse(rspData) : rspData; }; //获取长轮询ID webim.getLongPollingId = function (options, cbOk, cbErr) { return proto_getLongPollingId(options, cbOk, cbErr); }; //获取下载地址 webim.applyDownload = function (options, cbOk, cbErr) { return proto_applyDownload(options, cbOk, cbErr); }; //获取下载地址 webim.onDownFile = function (uuid) { window.open(Resources.downloadMap["uuid_"+uuid]); }; //检查是否登录 webim.checkLogin = function (cbErr, isNeedCallBack) { return checkLogin(cbErr, isNeedCallBack); }; })(webim); return webim; }();